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