xref: /openbmc/linux/fs/afs/vl_probe.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23bf0fb6fSDavid Howells /* AFS vlserver probing
33bf0fb6fSDavid Howells  *
43bf0fb6fSDavid Howells  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
53bf0fb6fSDavid Howells  * Written by David Howells (dhowells@redhat.com)
63bf0fb6fSDavid Howells  */
73bf0fb6fSDavid Howells 
83bf0fb6fSDavid Howells #include <linux/sched.h>
93bf0fb6fSDavid Howells #include <linux/slab.h>
103bf0fb6fSDavid Howells #include "afs_fs.h"
113bf0fb6fSDavid Howells #include "internal.h"
123bf0fb6fSDavid Howells #include "protocol_yfs.h"
133bf0fb6fSDavid Howells 
143bf0fb6fSDavid Howells 
15b95b3094SDavid Howells /*
16b95b3094SDavid Howells  * Handle the completion of a set of probes.
17b95b3094SDavid Howells  */
afs_finished_vl_probe(struct afs_vlserver * server)18b95b3094SDavid Howells static void afs_finished_vl_probe(struct afs_vlserver *server)
19b95b3094SDavid Howells {
20b95b3094SDavid Howells 	if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) {
21b95b3094SDavid Howells 		server->rtt = UINT_MAX;
22b95b3094SDavid Howells 		clear_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags);
23b95b3094SDavid Howells 	}
24b95b3094SDavid Howells 
253bf0fb6fSDavid Howells 	clear_bit_unlock(AFS_VLSERVER_FL_PROBING, &server->flags);
263bf0fb6fSDavid Howells 	wake_up_bit(&server->flags, AFS_VLSERVER_FL_PROBING);
27b95b3094SDavid Howells }
28b95b3094SDavid Howells 
29b95b3094SDavid Howells /*
30b95b3094SDavid Howells  * Handle the completion of a probe RPC call.
31b95b3094SDavid Howells  */
afs_done_one_vl_probe(struct afs_vlserver * server,bool wake_up)32b95b3094SDavid Howells static void afs_done_one_vl_probe(struct afs_vlserver *server, bool wake_up)
33b95b3094SDavid Howells {
34b95b3094SDavid Howells 	if (atomic_dec_and_test(&server->probe_outstanding)) {
35b95b3094SDavid Howells 		afs_finished_vl_probe(server);
36b95b3094SDavid Howells 		wake_up = true;
37b95b3094SDavid Howells 	}
38b95b3094SDavid Howells 
39b95b3094SDavid Howells 	if (wake_up)
40b95b3094SDavid Howells 		wake_up_all(&server->probe_wq);
413bf0fb6fSDavid Howells }
423bf0fb6fSDavid Howells 
433bf0fb6fSDavid Howells /*
443bf0fb6fSDavid Howells  * Process the result of probing a vlserver.  This is called after successful
453bf0fb6fSDavid Howells  * or failed delivery of an VL.GetCapabilities operation.
463bf0fb6fSDavid Howells  */
afs_vlserver_probe_result(struct afs_call * call)473bf0fb6fSDavid Howells void afs_vlserver_probe_result(struct afs_call *call)
483bf0fb6fSDavid Howells {
493bf0fb6fSDavid Howells 	struct afs_addr_list *alist = call->alist;
50ffba718eSDavid Howells 	struct afs_vlserver *server = call->vlserver;
51ffba718eSDavid Howells 	unsigned int server_index = call->server_index;
52c410bf01SDavid Howells 	unsigned int rtt_us = 0;
533bf0fb6fSDavid Howells 	unsigned int index = call->addr_ix;
543bf0fb6fSDavid Howells 	bool have_result = false;
553bf0fb6fSDavid Howells 	int ret = call->error;
563bf0fb6fSDavid Howells 
573bf0fb6fSDavid Howells 	_enter("%s,%u,%u,%d,%d", server->name, server_index, index, ret, call->abort_code);
583bf0fb6fSDavid Howells 
593bf0fb6fSDavid Howells 	spin_lock(&server->probe_lock);
603bf0fb6fSDavid Howells 
613bf0fb6fSDavid Howells 	switch (ret) {
623bf0fb6fSDavid Howells 	case 0:
633bf0fb6fSDavid Howells 		server->probe.error = 0;
643bf0fb6fSDavid Howells 		goto responded;
653bf0fb6fSDavid Howells 	case -ECONNABORTED:
66fb72cd3dSDavid Howells 		if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) {
673bf0fb6fSDavid Howells 			server->probe.abort_code = call->abort_code;
683bf0fb6fSDavid Howells 			server->probe.error = ret;
693bf0fb6fSDavid Howells 		}
703bf0fb6fSDavid Howells 		goto responded;
713bf0fb6fSDavid Howells 	case -ENOMEM:
723bf0fb6fSDavid Howells 	case -ENONET:
73b95b3094SDavid Howells 	case -EKEYEXPIRED:
74b95b3094SDavid Howells 	case -EKEYREVOKED:
75b95b3094SDavid Howells 	case -EKEYREJECTED:
76fb72cd3dSDavid Howells 		server->probe.flags |= AFS_VLSERVER_PROBE_LOCAL_FAILURE;
77b95b3094SDavid Howells 		if (server->probe.error == 0)
78b95b3094SDavid Howells 			server->probe.error = ret;
79b95b3094SDavid Howells 		trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail);
803bf0fb6fSDavid Howells 		goto out;
813bf0fb6fSDavid Howells 	case -ECONNRESET: /* Responded, but call expired. */
824584ae96SDavid Howells 	case -ERFKILL:
834584ae96SDavid Howells 	case -EADDRNOTAVAIL:
843bf0fb6fSDavid Howells 	case -ENETUNREACH:
853bf0fb6fSDavid Howells 	case -EHOSTUNREACH:
864584ae96SDavid Howells 	case -EHOSTDOWN:
873bf0fb6fSDavid Howells 	case -ECONNREFUSED:
883bf0fb6fSDavid Howells 	case -ETIMEDOUT:
893bf0fb6fSDavid Howells 	case -ETIME:
903bf0fb6fSDavid Howells 	default:
913bf0fb6fSDavid Howells 		clear_bit(index, &alist->responded);
923bf0fb6fSDavid Howells 		set_bit(index, &alist->failed);
93fb72cd3dSDavid Howells 		if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) &&
943bf0fb6fSDavid Howells 		    (server->probe.error == 0 ||
953bf0fb6fSDavid Howells 		     server->probe.error == -ETIMEDOUT ||
963bf0fb6fSDavid Howells 		     server->probe.error == -ETIME))
973bf0fb6fSDavid Howells 			server->probe.error = ret;
98b95b3094SDavid Howells 		trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail);
993bf0fb6fSDavid Howells 		goto out;
1003bf0fb6fSDavid Howells 	}
1013bf0fb6fSDavid Howells 
1023bf0fb6fSDavid Howells responded:
1033bf0fb6fSDavid Howells 	set_bit(index, &alist->responded);
1043bf0fb6fSDavid Howells 	clear_bit(index, &alist->failed);
1053bf0fb6fSDavid Howells 
1063bf0fb6fSDavid Howells 	if (call->service_id == YFS_VL_SERVICE) {
107fb72cd3dSDavid Howells 		server->probe.flags |= AFS_VLSERVER_PROBE_IS_YFS;
1083bf0fb6fSDavid Howells 		set_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags);
1093bf0fb6fSDavid Howells 		alist->addrs[index].srx_service = call->service_id;
1103bf0fb6fSDavid Howells 	} else {
111fb72cd3dSDavid Howells 		server->probe.flags |= AFS_VLSERVER_PROBE_NOT_YFS;
112fb72cd3dSDavid Howells 		if (!(server->probe.flags & AFS_VLSERVER_PROBE_IS_YFS)) {
1133bf0fb6fSDavid Howells 			clear_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags);
1143bf0fb6fSDavid Howells 			alist->addrs[index].srx_service = call->service_id;
1153bf0fb6fSDavid Howells 		}
1163bf0fb6fSDavid Howells 	}
1173bf0fb6fSDavid Howells 
118*ba00b190SDavid Howells 	rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us);
119*ba00b190SDavid Howells 	if (rtt_us < server->probe.rtt) {
120c410bf01SDavid Howells 		server->probe.rtt = rtt_us;
121b95b3094SDavid Howells 		server->rtt = rtt_us;
1223bf0fb6fSDavid Howells 		alist->preferred = index;
1233bf0fb6fSDavid Howells 	}
1243bf0fb6fSDavid Howells 
1253bf0fb6fSDavid Howells 	smp_wmb(); /* Set rtt before responded. */
126fb72cd3dSDavid Howells 	server->probe.flags |= AFS_VLSERVER_PROBE_RESPONDED;
1273bf0fb6fSDavid Howells 	set_bit(AFS_VLSERVER_FL_PROBED, &server->flags);
128b95b3094SDavid Howells 	set_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags);
129b95b3094SDavid Howells 	have_result = true;
1303bf0fb6fSDavid Howells out:
1313bf0fb6fSDavid Howells 	spin_unlock(&server->probe_lock);
1323bf0fb6fSDavid Howells 
1333bf0fb6fSDavid Howells 	_debug("probe [%u][%u] %pISpc rtt=%u ret=%d",
134c410bf01SDavid Howells 	       server_index, index, &alist->addrs[index].transport, rtt_us, ret);
1353bf0fb6fSDavid Howells 
136b95b3094SDavid Howells 	afs_done_one_vl_probe(server, have_result);
1373bf0fb6fSDavid Howells }
1383bf0fb6fSDavid Howells 
1393bf0fb6fSDavid Howells /*
1403bf0fb6fSDavid Howells  * Probe all of a vlserver's addresses to find out the best route and to
1413bf0fb6fSDavid Howells  * query its capabilities.
1423bf0fb6fSDavid Howells  */
afs_do_probe_vlserver(struct afs_net * net,struct afs_vlserver * server,struct key * key,unsigned int server_index,struct afs_error * _e)1434584ae96SDavid Howells static bool afs_do_probe_vlserver(struct afs_net *net,
1443bf0fb6fSDavid Howells 				  struct afs_vlserver *server,
1453bf0fb6fSDavid Howells 				  struct key *key,
1464584ae96SDavid Howells 				  unsigned int server_index,
1474584ae96SDavid Howells 				  struct afs_error *_e)
1483bf0fb6fSDavid Howells {
1493bf0fb6fSDavid Howells 	struct afs_addr_cursor ac = {
1503bf0fb6fSDavid Howells 		.index = 0,
1513bf0fb6fSDavid Howells 	};
1520b9bf381SDavid Howells 	struct afs_call *call;
1534584ae96SDavid Howells 	bool in_progress = false;
1543bf0fb6fSDavid Howells 
1553bf0fb6fSDavid Howells 	_enter("%s", server->name);
1563bf0fb6fSDavid Howells 
1573bf0fb6fSDavid Howells 	read_lock(&server->lock);
1583bf0fb6fSDavid Howells 	ac.alist = rcu_dereference_protected(server->addresses,
1593bf0fb6fSDavid Howells 					     lockdep_is_held(&server->lock));
1603bf0fb6fSDavid Howells 	read_unlock(&server->lock);
1613bf0fb6fSDavid Howells 
1623bf0fb6fSDavid Howells 	atomic_set(&server->probe_outstanding, ac.alist->nr_addrs);
1633bf0fb6fSDavid Howells 	memset(&server->probe, 0, sizeof(server->probe));
1643bf0fb6fSDavid Howells 	server->probe.rtt = UINT_MAX;
1653bf0fb6fSDavid Howells 
1663bf0fb6fSDavid Howells 	for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
1670b9bf381SDavid Howells 		call = afs_vl_get_capabilities(net, &ac, key, server,
1680b9bf381SDavid Howells 					       server_index);
1690b9bf381SDavid Howells 		if (!IS_ERR(call)) {
1700b9bf381SDavid Howells 			afs_put_call(call);
1714584ae96SDavid Howells 			in_progress = true;
1720b9bf381SDavid Howells 		} else {
1730b9bf381SDavid Howells 			afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code);
174b95b3094SDavid Howells 			afs_done_one_vl_probe(server, false);
1750b9bf381SDavid Howells 		}
1763bf0fb6fSDavid Howells 	}
1773bf0fb6fSDavid Howells 
1784584ae96SDavid Howells 	return in_progress;
1793bf0fb6fSDavid Howells }
1803bf0fb6fSDavid Howells 
1813bf0fb6fSDavid Howells /*
1823bf0fb6fSDavid Howells  * Send off probes to all unprobed servers.
1833bf0fb6fSDavid Howells  */
afs_send_vl_probes(struct afs_net * net,struct key * key,struct afs_vlserver_list * vllist)1843bf0fb6fSDavid Howells int afs_send_vl_probes(struct afs_net *net, struct key *key,
1853bf0fb6fSDavid Howells 		       struct afs_vlserver_list *vllist)
1863bf0fb6fSDavid Howells {
1873bf0fb6fSDavid Howells 	struct afs_vlserver *server;
1884584ae96SDavid Howells 	struct afs_error e;
1894584ae96SDavid Howells 	bool in_progress = false;
1904584ae96SDavid Howells 	int i;
1913bf0fb6fSDavid Howells 
1924584ae96SDavid Howells 	e.error = 0;
1934584ae96SDavid Howells 	e.responded = false;
1943bf0fb6fSDavid Howells 	for (i = 0; i < vllist->nr_servers; i++) {
1953bf0fb6fSDavid Howells 		server = vllist->servers[i].server;
1963bf0fb6fSDavid Howells 		if (test_bit(AFS_VLSERVER_FL_PROBED, &server->flags))
1973bf0fb6fSDavid Howells 			continue;
1983bf0fb6fSDavid Howells 
1994584ae96SDavid Howells 		if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, &server->flags) &&
2004584ae96SDavid Howells 		    afs_do_probe_vlserver(net, server, key, i, &e))
2014584ae96SDavid Howells 			in_progress = true;
2023bf0fb6fSDavid Howells 	}
2033bf0fb6fSDavid Howells 
2044584ae96SDavid Howells 	return in_progress ? 0 : e.error;
2053bf0fb6fSDavid Howells }
2063bf0fb6fSDavid Howells 
2073bf0fb6fSDavid Howells /*
2083bf0fb6fSDavid Howells  * Wait for the first as-yet untried server to respond.
2093bf0fb6fSDavid Howells  */
afs_wait_for_vl_probes(struct afs_vlserver_list * vllist,unsigned long untried)2103bf0fb6fSDavid Howells int afs_wait_for_vl_probes(struct afs_vlserver_list *vllist,
2113bf0fb6fSDavid Howells 			   unsigned long untried)
2123bf0fb6fSDavid Howells {
2133bf0fb6fSDavid Howells 	struct wait_queue_entry *waits;
2143bf0fb6fSDavid Howells 	struct afs_vlserver *server;
215b95b3094SDavid Howells 	unsigned int rtt = UINT_MAX, rtt_s;
2163bf0fb6fSDavid Howells 	bool have_responders = false;
2173bf0fb6fSDavid Howells 	int pref = -1, i;
2183bf0fb6fSDavid Howells 
2193bf0fb6fSDavid Howells 	_enter("%u,%lx", vllist->nr_servers, untried);
2203bf0fb6fSDavid Howells 
2213bf0fb6fSDavid Howells 	/* Only wait for servers that have a probe outstanding. */
2223bf0fb6fSDavid Howells 	for (i = 0; i < vllist->nr_servers; i++) {
2233bf0fb6fSDavid Howells 		if (test_bit(i, &untried)) {
2243bf0fb6fSDavid Howells 			server = vllist->servers[i].server;
2253bf0fb6fSDavid Howells 			if (!test_bit(AFS_VLSERVER_FL_PROBING, &server->flags))
2263bf0fb6fSDavid Howells 				__clear_bit(i, &untried);
227fb72cd3dSDavid Howells 			if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)
2283bf0fb6fSDavid Howells 				have_responders = true;
2293bf0fb6fSDavid Howells 		}
2303bf0fb6fSDavid Howells 	}
2313bf0fb6fSDavid Howells 	if (have_responders || !untried)
2323bf0fb6fSDavid Howells 		return 0;
2333bf0fb6fSDavid Howells 
2343bf0fb6fSDavid Howells 	waits = kmalloc(array_size(vllist->nr_servers, sizeof(*waits)), GFP_KERNEL);
2353bf0fb6fSDavid Howells 	if (!waits)
2363bf0fb6fSDavid Howells 		return -ENOMEM;
2373bf0fb6fSDavid Howells 
2383bf0fb6fSDavid Howells 	for (i = 0; i < vllist->nr_servers; i++) {
2393bf0fb6fSDavid Howells 		if (test_bit(i, &untried)) {
2403bf0fb6fSDavid Howells 			server = vllist->servers[i].server;
2413bf0fb6fSDavid Howells 			init_waitqueue_entry(&waits[i], current);
2423bf0fb6fSDavid Howells 			add_wait_queue(&server->probe_wq, &waits[i]);
2433bf0fb6fSDavid Howells 		}
2443bf0fb6fSDavid Howells 	}
2453bf0fb6fSDavid Howells 
2463bf0fb6fSDavid Howells 	for (;;) {
2473bf0fb6fSDavid Howells 		bool still_probing = false;
2483bf0fb6fSDavid Howells 
2493bf0fb6fSDavid Howells 		set_current_state(TASK_INTERRUPTIBLE);
2503bf0fb6fSDavid Howells 		for (i = 0; i < vllist->nr_servers; i++) {
2513bf0fb6fSDavid Howells 			if (test_bit(i, &untried)) {
2523bf0fb6fSDavid Howells 				server = vllist->servers[i].server;
253fb72cd3dSDavid Howells 				if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)
2543bf0fb6fSDavid Howells 					goto stop;
2553bf0fb6fSDavid Howells 				if (test_bit(AFS_VLSERVER_FL_PROBING, &server->flags))
2563bf0fb6fSDavid Howells 					still_probing = true;
2573bf0fb6fSDavid Howells 			}
2583bf0fb6fSDavid Howells 		}
2593bf0fb6fSDavid Howells 
26008d405c8SDavidlohr Bueso 		if (!still_probing || signal_pending(current))
2613bf0fb6fSDavid Howells 			goto stop;
2623bf0fb6fSDavid Howells 		schedule();
2633bf0fb6fSDavid Howells 	}
2643bf0fb6fSDavid Howells 
2653bf0fb6fSDavid Howells stop:
2663bf0fb6fSDavid Howells 	set_current_state(TASK_RUNNING);
2673bf0fb6fSDavid Howells 
2683bf0fb6fSDavid Howells 	for (i = 0; i < vllist->nr_servers; i++) {
2693bf0fb6fSDavid Howells 		if (test_bit(i, &untried)) {
2703bf0fb6fSDavid Howells 			server = vllist->servers[i].server;
271b95b3094SDavid Howells 			rtt_s = READ_ONCE(server->rtt);
272b95b3094SDavid Howells 			if (test_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags) &&
273b95b3094SDavid Howells 			    rtt_s < rtt) {
2743bf0fb6fSDavid Howells 				pref = i;
275b95b3094SDavid Howells 				rtt = rtt_s;
2763bf0fb6fSDavid Howells 			}
2773bf0fb6fSDavid Howells 
2783bf0fb6fSDavid Howells 			remove_wait_queue(&server->probe_wq, &waits[i]);
2793bf0fb6fSDavid Howells 		}
2803bf0fb6fSDavid Howells 	}
2813bf0fb6fSDavid Howells 
2823bf0fb6fSDavid Howells 	kfree(waits);
2833bf0fb6fSDavid Howells 
2843bf0fb6fSDavid Howells 	if (pref == -1 && signal_pending(current))
2853bf0fb6fSDavid Howells 		return -ERESTARTSYS;
2863bf0fb6fSDavid Howells 
2873bf0fb6fSDavid Howells 	if (pref >= 0)
2883bf0fb6fSDavid Howells 		vllist->preferred = pref;
2893bf0fb6fSDavid Howells 
2903bf0fb6fSDavid Howells 	_leave(" = 0 [%u]", pref);
2913bf0fb6fSDavid Howells 	return 0;
2923bf0fb6fSDavid Howells }
293