Commit 8d73a73a authored by David S. Miller's avatar David S. Miller
Browse files


David Howells says:

====================
rxrpc, afs: Fix probing issues

Here are some fixes for rxrpc and afs to fix issues in the RTT measuring in
rxrpc and thence the Volume Location server probing in afs:

 (1) Move the serial number of a received ACK into a local variable to
     simplify the next patch.

 (2) Fix the loss of RTT samples due to extra interposed ACKs causing
     baseline information to be discarded too early.  This is a particular
     problem for afs when it sends a single very short call to probe a
     server it hasn't talked to recently.

 (3) Fix rxrpc_kernel_get_srtt() to indicate whether it actually has seen
     any valid samples or not.

 (4) Remove a field that's set/woken, but never read/waited on.

 (5) Expose the RTT and other probe information through procfs to make
     debugging of this stuff easier.

 (6) Fix VL rotation in afs to only use summary information from VL probing
     and not the probe running state (which gets clobbered when next a
     probe is issued).

 (7) Fix VL rotation to actually return the error aggregated from the probe
     errors.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents af8ea111 e4686c79
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -161,8 +161,8 @@ void afs_fileserver_probe_result(struct afs_call *call)
		}
	}

	rtt_us = rxrpc_kernel_get_srtt(call->net->socket, call->rxcall);
	if (rtt_us < server->probe.rtt) {
	if (rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us) &&
	    rtt_us < server->probe.rtt) {
		server->probe.rtt = rtt_us;
		server->rtt = rtt_us;
		alist->preferred = index;
+8 −6
Original line number Diff line number Diff line
@@ -401,22 +401,24 @@ struct afs_vlserver {
#define AFS_VLSERVER_FL_PROBED	0		/* The VL server has been probed */
#define AFS_VLSERVER_FL_PROBING	1		/* VL server is being probed */
#define AFS_VLSERVER_FL_IS_YFS	2		/* Server is YFS not AFS */
#define AFS_VLSERVER_FL_RESPONDING 3		/* VL server is responding */
	rwlock_t		lock;		/* Lock on addresses */
	atomic_t		usage;
	unsigned int		rtt;		/* Server's current RTT in uS */

	/* Probe state */
	wait_queue_head_t	probe_wq;
	atomic_t		probe_outstanding;
	spinlock_t		probe_lock;
	struct {
		unsigned int	rtt;		/* RTT as ktime/64 */
		unsigned int	rtt;		/* RTT in uS */
		u32		abort_code;
		short		error;
		bool		have_result;
		bool		responded:1;
		bool		is_yfs:1;
		bool		not_yfs:1;
		bool		local_failure:1;
		unsigned short	flags;
#define AFS_VLSERVER_PROBE_RESPONDED		0x01 /* At least once response (may be abort) */
#define AFS_VLSERVER_PROBE_IS_YFS		0x02 /* The peer appears to be YFS */
#define AFS_VLSERVER_PROBE_NOT_YFS		0x04 /* The peer appears not to be YFS */
#define AFS_VLSERVER_PROBE_LOCAL_FAILURE	0x08 /* A local failure prevented a probe */
	} probe;

	u16			port;
+5 −0
Original line number Diff line number Diff line
@@ -310,6 +310,11 @@ static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
				   alist->preferred == i ? '>' : '-',
				   &alist->addrs[i].transport);
	}
	seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
	seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
		   vlserver->probe.flags, vlserver->probe.error,
		   vlserver->probe.abort_code,
		   atomic_read(&vlserver->probe_outstanding));
	return 0;
}

+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ struct afs_vlserver *afs_alloc_vlserver(const char *name, size_t name_len,
		rwlock_init(&vlserver->lock);
		init_waitqueue_head(&vlserver->probe_wq);
		spin_lock_init(&vlserver->probe_lock);
		vlserver->rtt = UINT_MAX;
		vlserver->name_len = name_len;
		vlserver->port = port;
		memcpy(vlserver->name, name, name_len);
+51 −31
Original line number Diff line number Diff line
@@ -11,15 +11,33 @@
#include "internal.h"
#include "protocol_yfs.h"

static bool afs_vl_probe_done(struct afs_vlserver *server)

/*
 * Handle the completion of a set of probes.
 */
static void afs_finished_vl_probe(struct afs_vlserver *server)
{
	if (!atomic_dec_and_test(&server->probe_outstanding))
		return false;
	if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) {
		server->rtt = UINT_MAX;
		clear_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags);
	}

	wake_up_var(&server->probe_outstanding);
	clear_bit_unlock(AFS_VLSERVER_FL_PROBING, &server->flags);
	wake_up_bit(&server->flags, AFS_VLSERVER_FL_PROBING);
	return true;
}

/*
 * Handle the completion of a probe RPC call.
 */
static void afs_done_one_vl_probe(struct afs_vlserver *server, bool wake_up)
{
	if (atomic_dec_and_test(&server->probe_outstanding)) {
		afs_finished_vl_probe(server);
		wake_up = true;
	}

	if (wake_up)
		wake_up_all(&server->probe_wq);
}

/*
@@ -45,15 +63,20 @@ void afs_vlserver_probe_result(struct afs_call *call)
		server->probe.error = 0;
		goto responded;
	case -ECONNABORTED:
		if (!server->probe.responded) {
		if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) {
			server->probe.abort_code = call->abort_code;
			server->probe.error = ret;
		}
		goto responded;
	case -ENOMEM:
	case -ENONET:
		server->probe.local_failure = true;
		afs_io_error(call, afs_io_error_vl_probe_fail);
	case -EKEYEXPIRED:
	case -EKEYREVOKED:
	case -EKEYREJECTED:
		server->probe.flags |= AFS_VLSERVER_PROBE_LOCAL_FAILURE;
		if (server->probe.error == 0)
			server->probe.error = ret;
		trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail);
		goto out;
	case -ECONNRESET: /* Responded, but call expired. */
	case -ERFKILL:
@@ -67,12 +90,12 @@ void afs_vlserver_probe_result(struct afs_call *call)
	default:
		clear_bit(index, &alist->responded);
		set_bit(index, &alist->failed);
		if (!server->probe.responded &&
		if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) &&
		    (server->probe.error == 0 ||
		     server->probe.error == -ETIMEDOUT ||
		     server->probe.error == -ETIME))
			server->probe.error = ret;
		afs_io_error(call, afs_io_error_vl_probe_fail);
		trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail);
		goto out;
	}

@@ -81,39 +104,36 @@ void afs_vlserver_probe_result(struct afs_call *call)
	clear_bit(index, &alist->failed);

	if (call->service_id == YFS_VL_SERVICE) {
		server->probe.is_yfs = true;
		server->probe.flags |= AFS_VLSERVER_PROBE_IS_YFS;
		set_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags);
		alist->addrs[index].srx_service = call->service_id;
	} else {
		server->probe.not_yfs = true;
		if (!server->probe.is_yfs) {
		server->probe.flags |= AFS_VLSERVER_PROBE_NOT_YFS;
		if (!(server->probe.flags & AFS_VLSERVER_PROBE_IS_YFS)) {
			clear_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags);
			alist->addrs[index].srx_service = call->service_id;
		}
	}

	rtt_us = rxrpc_kernel_get_srtt(call->net->socket, call->rxcall);
	if (rtt_us < server->probe.rtt) {
	if (rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us) &&
	    rtt_us < server->probe.rtt) {
		server->probe.rtt = rtt_us;
		server->rtt = rtt_us;
		alist->preferred = index;
		have_result = true;
	}

	smp_wmb(); /* Set rtt before responded. */
	server->probe.responded = true;
	server->probe.flags |= AFS_VLSERVER_PROBE_RESPONDED;
	set_bit(AFS_VLSERVER_FL_PROBED, &server->flags);
	set_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags);
	have_result = true;
out:
	spin_unlock(&server->probe_lock);

	_debug("probe [%u][%u] %pISpc rtt=%u ret=%d",
	       server_index, index, &alist->addrs[index].transport, rtt_us, ret);

	have_result |= afs_vl_probe_done(server);
	if (have_result) {
		server->probe.have_result = true;
		wake_up_var(&server->probe.have_result);
		wake_up_all(&server->probe_wq);
	}
	afs_done_one_vl_probe(server, have_result);
}

/*
@@ -151,11 +171,10 @@ static bool afs_do_probe_vlserver(struct afs_net *net,
			in_progress = true;
		} else {
			afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code);
			afs_done_one_vl_probe(server, false);
		}
	}

	if (!in_progress)
		afs_vl_probe_done(server);
	return in_progress;
}

@@ -193,7 +212,7 @@ int afs_wait_for_vl_probes(struct afs_vlserver_list *vllist,
{
	struct wait_queue_entry *waits;
	struct afs_vlserver *server;
	unsigned int rtt = UINT_MAX;
	unsigned int rtt = UINT_MAX, rtt_s;
	bool have_responders = false;
	int pref = -1, i;

@@ -205,7 +224,7 @@ int afs_wait_for_vl_probes(struct afs_vlserver_list *vllist,
			server = vllist->servers[i].server;
			if (!test_bit(AFS_VLSERVER_FL_PROBING, &server->flags))
				__clear_bit(i, &untried);
			if (server->probe.responded)
			if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)
				have_responders = true;
		}
	}
@@ -231,7 +250,7 @@ int afs_wait_for_vl_probes(struct afs_vlserver_list *vllist,
		for (i = 0; i < vllist->nr_servers; i++) {
			if (test_bit(i, &untried)) {
				server = vllist->servers[i].server;
				if (server->probe.responded)
				if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)
					goto stop;
				if (test_bit(AFS_VLSERVER_FL_PROBING, &server->flags))
					still_probing = true;
@@ -249,10 +268,11 @@ int afs_wait_for_vl_probes(struct afs_vlserver_list *vllist,
	for (i = 0; i < vllist->nr_servers; i++) {
		if (test_bit(i, &untried)) {
			server = vllist->servers[i].server;
			if (server->probe.responded &&
			    server->probe.rtt < rtt) {
			rtt_s = READ_ONCE(server->rtt);
			if (test_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags) &&
			    rtt_s < rtt) {
				pref = i;
				rtt = server->probe.rtt;
				rtt = rtt_s;
			}

			remove_wait_queue(&server->probe_wq, &waits[i]);
Loading