Commit 3ac9eb42 authored by David S. Miller's avatar David S. Miller
Browse files


David Howells says:

====================
rxrpc, afs: Interruptibility fixes

Here are a number of fixes for AF_RXRPC and AFS that make AFS system calls
less interruptible and so less likely to leave the filesystem in an
uncertain state.  There's also a miscellaneous patch to make tracing
consistent.

 (1) Firstly, abstract out the Tx space calculation in sendmsg.  Much the
     same code is replicated in a number of places that subsequent patches
     are going to alter, including adding another copy.

 (2) Fix Tx interruptibility by allowing a kernel service, such as AFS, to
     request that a call be interruptible only when waiting for a call slot
     to become available (ie. the call has not taken place yet) or that a
     call be not interruptible at all (e.g. when we want to do writeback
     and don't want a signal interrupting a VM-induced writeback).

 (3) Increase the minimum delay on MSG_WAITALL for userspace sendmsg() when
     waiting for Tx buffer space as a 2*RTT delay is really small over 10G
     ethernet and a 1 jiffy timeout might be essentially 0 if at the end of
     the jiffy period.

 (4) Fix some tracing output in AFS to make it consistent with rxrpc.

 (5) Make sure aborted asynchronous AFS operations are tidied up properly
     so we don't end up with stuck rxrpc calls.

 (6) Make AFS client calls uninterruptible in the Rx phase.  If we don't
     wait for the reply to be fully gathered, we can't update the local VFS
     state and we end up in an indeterminate state with respect to the
     server.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6002059d 7d7587db
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -243,6 +243,17 @@ static void afs_cm_destructor(struct afs_call *call)
	call->buffer = NULL;
}

/*
 * Abort a service call from within an action function.
 */
static void afs_abort_service_call(struct afs_call *call, u32 abort_code, int error,
				   const char *why)
{
	rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
				abort_code, error, why);
	afs_set_call_complete(call, error, 0);
}

/*
 * The server supplied a list of callbacks that it wanted to break.
 */
@@ -510,8 +521,7 @@ static void SRXAFSCB_ProbeUuid(struct work_struct *work)
	if (memcmp(r, &call->net->uuid, sizeof(call->net->uuid)) == 0)
		afs_send_empty_reply(call);
	else
		rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
					1, 1, "K-1");
		afs_abort_service_call(call, 1, 1, "K-1");

	afs_put_call(call);
	_leave("");
+10 −2
Original line number Diff line number Diff line
@@ -154,7 +154,7 @@ struct afs_call {
	};
	unsigned char		unmarshall;	/* unmarshalling phase */
	unsigned char		addr_ix;	/* Address in ->alist */
	bool			incoming;	/* T if incoming call */
	bool			drop_ref;	/* T if need to drop ref for incoming call */
	bool			send_pages;	/* T if data from mapping should be sent */
	bool			need_attention;	/* T if RxRPC poked us */
	bool			async;		/* T if asynchronous */
@@ -1209,8 +1209,16 @@ static inline void afs_set_call_complete(struct afs_call *call,
		ok = true;
	}
	spin_unlock_bh(&call->state_lock);
	if (ok)
	if (ok) {
		trace_afs_call_done(call);

		/* Asynchronous calls have two refs to release - one from the alloc and
		 * one queued with the work item - and we can't just deallocate the
		 * call because the work item may be queued again.
		 */
		if (call->drop_ref)
			afs_put_call(call);
	}
}

/*
+10 −64
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ struct workqueue_struct *afs_async_calls;

static void afs_wake_up_call_waiter(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_wake_up_async_call(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_delete_async_call(struct work_struct *);
static void afs_process_async_call(struct work_struct *);
static void afs_rx_new_call(struct sock *, struct rxrpc_call *, unsigned long);
static void afs_rx_discard_new_call(struct rxrpc_call *, unsigned long);
@@ -169,7 +168,7 @@ void afs_put_call(struct afs_call *call)
	int n = atomic_dec_return(&call->usage);
	int o = atomic_read(&net->nr_outstanding_calls);

	trace_afs_call(call, afs_call_trace_put, n + 1, o,
	trace_afs_call(call, afs_call_trace_put, n, o,
		       __builtin_return_address(0));

	ASSERTCMP(n, >=, 0);
@@ -402,8 +401,10 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
	/* If the call is going to be asynchronous, we need an extra ref for
	 * the call to hold itself so the caller need not hang on to its ref.
	 */
	if (call->async)
	if (call->async) {
		afs_get_call(call, afs_call_trace_get);
		call->drop_ref = true;
	}

	/* create a call */
	rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key,
@@ -413,7 +414,8 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
					  afs_wake_up_async_call :
					  afs_wake_up_call_waiter),
					 call->upgrade,
					 call->intr,
					 (call->intr ? RXRPC_PREINTERRUPTIBLE :
					  RXRPC_UNINTERRUPTIBLE),
					 call->debug_id);
	if (IS_ERR(rxcall)) {
		ret = PTR_ERR(rxcall);
@@ -584,8 +586,6 @@ static void afs_deliver_to_call(struct afs_call *call)
done:
	if (call->type->done)
		call->type->done(call);
	if (state == AFS_CALL_COMPLETE && call->incoming)
		afs_put_call(call);
out:
	_leave("");
	return;
@@ -604,11 +604,7 @@ call_complete:
long afs_wait_for_call_to_complete(struct afs_call *call,
				   struct afs_addr_cursor *ac)
{
	signed long rtt2, timeout;
	long ret;
	bool stalled = false;
	u64 rtt;
	u32 life, last_life;
	bool rxrpc_complete = false;

	DECLARE_WAITQUEUE(myself, current);
@@ -619,14 +615,6 @@ long afs_wait_for_call_to_complete(struct afs_call *call,
	if (ret < 0)
		goto out;

	rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall);
	rtt2 = nsecs_to_jiffies64(rtt) * 2;
	if (rtt2 < 2)
		rtt2 = 2;

	timeout = rtt2;
	rxrpc_kernel_check_life(call->net->socket, call->rxcall, &last_life);

	add_wait_queue(&call->waitq, &myself);
	for (;;) {
		set_current_state(TASK_UNINTERRUPTIBLE);
@@ -637,37 +625,19 @@ long afs_wait_for_call_to_complete(struct afs_call *call,
			call->need_attention = false;
			__set_current_state(TASK_RUNNING);
			afs_deliver_to_call(call);
			timeout = rtt2;
			continue;
		}

		if (afs_check_call_state(call, AFS_CALL_COMPLETE))
			break;

		if (!rxrpc_kernel_check_life(call->net->socket, call->rxcall, &life)) {
		if (!rxrpc_kernel_check_life(call->net->socket, call->rxcall)) {
			/* rxrpc terminated the call. */
			rxrpc_complete = true;
			break;
		}

		if (call->intr && timeout == 0 &&
		    life == last_life && signal_pending(current)) {
			if (stalled)
				break;
			__set_current_state(TASK_RUNNING);
			rxrpc_kernel_probe_life(call->net->socket, call->rxcall);
			timeout = rtt2;
			stalled = true;
			continue;
		}

		if (life != last_life) {
			timeout = rtt2;
			last_life = life;
			stalled = false;
		}

		timeout = schedule_timeout(timeout);
		schedule();
	}

	remove_wait_queue(&call->waitq, &myself);
@@ -735,7 +705,7 @@ static void afs_wake_up_async_call(struct sock *sk, struct rxrpc_call *rxcall,

	u = atomic_fetch_add_unless(&call->usage, 1, 0);
	if (u != 0) {
		trace_afs_call(call, afs_call_trace_wake, u,
		trace_afs_call(call, afs_call_trace_wake, u + 1,
			       atomic_read(&call->net->nr_outstanding_calls),
			       __builtin_return_address(0));

@@ -744,21 +714,6 @@ static void afs_wake_up_async_call(struct sock *sk, struct rxrpc_call *rxcall,
	}
}

/*
 * Delete an asynchronous call.  The work item carries a ref to the call struct
 * that we need to release.
 */
static void afs_delete_async_call(struct work_struct *work)
{
	struct afs_call *call = container_of(work, struct afs_call, async_work);

	_enter("");

	afs_put_call(call);

	_leave("");
}

/*
 * Perform I/O processing on an asynchronous call.  The work item carries a ref
 * to the call struct that we either need to release or to pass on.
@@ -774,16 +729,6 @@ static void afs_process_async_call(struct work_struct *work)
		afs_deliver_to_call(call);
	}

	if (call->state == AFS_CALL_COMPLETE) {
		/* We have two refs to release - one from the alloc and one
		 * queued with the work item - and we can't just deallocate the
		 * call because the work item may be queued again.
		 */
		call->async_work.func = afs_delete_async_call;
		if (!queue_work(afs_async_calls, &call->async_work))
			afs_put_call(call);
	}

	afs_put_call(call);
	_leave("");
}
@@ -810,6 +755,7 @@ void afs_charge_preallocation(struct work_struct *work)
			if (!call)
				break;

			call->drop_ref = true;
			call->async = true;
			call->state = AFS_CALL_SV_AWAIT_OP_ID;
			init_waitqueue_head(&call->waitq);
+8 −4
Original line number Diff line number Diff line
@@ -16,6 +16,12 @@ struct sock;
struct socket;
struct rxrpc_call;

enum rxrpc_interruptibility {
	RXRPC_INTERRUPTIBLE,	/* Call is interruptible */
	RXRPC_PREINTERRUPTIBLE,	/* Call can be cancelled whilst waiting for a slot */
	RXRPC_UNINTERRUPTIBLE,	/* Call should not be interruptible at all */
};

/*
 * Debug ID counter for tracing.
 */
@@ -41,7 +47,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *,
					   gfp_t,
					   rxrpc_notify_rx_t,
					   bool,
					   bool,
					   enum rxrpc_interruptibility,
					   unsigned int);
int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *,
			   struct msghdr *, size_t,
@@ -58,9 +64,7 @@ int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t,
			       rxrpc_user_attach_call_t, unsigned long, gfp_t,
			       unsigned int);
void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64);
bool rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *,
			     u32 *);
void rxrpc_kernel_probe_life(struct socket *, struct rxrpc_call *);
bool rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *);
u32 rxrpc_kernel_get_epoch(struct socket *, struct rxrpc_call *);
bool rxrpc_kernel_get_reply_time(struct socket *, struct rxrpc_call *,
				 ktime_t *);
+1 −1
Original line number Diff line number Diff line
@@ -233,7 +233,7 @@ enum afs_cb_break_reason {
	EM(afs_call_trace_get,			"GET  ") \
	EM(afs_call_trace_put,			"PUT  ") \
	EM(afs_call_trace_wake,			"WAKE ") \
	E_(afs_call_trace_work,			"WORK ")
	E_(afs_call_trace_work,			"QUEUE")

#define afs_server_traces \
	EM(afs_server_trace_alloc,		"ALLOC    ") \
Loading