162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* AFS fileserver probing 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2018, 2020 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/sched.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include "afs_fs.h" 1162306a36Sopenharmony_ci#include "internal.h" 1262306a36Sopenharmony_ci#include "protocol_afs.h" 1362306a36Sopenharmony_ci#include "protocol_yfs.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic unsigned int afs_fs_probe_fast_poll_interval = 30 * HZ; 1662306a36Sopenharmony_cistatic unsigned int afs_fs_probe_slow_poll_interval = 5 * 60 * HZ; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * Start the probe polling timer. We have to supply it with an inc on the 2062306a36Sopenharmony_ci * outstanding server count. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic void afs_schedule_fs_probe(struct afs_net *net, 2362306a36Sopenharmony_ci struct afs_server *server, bool fast) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned long atj; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (!net->live) 2862306a36Sopenharmony_ci return; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci atj = server->probed_at; 3162306a36Sopenharmony_ci atj += fast ? afs_fs_probe_fast_poll_interval : afs_fs_probe_slow_poll_interval; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci afs_inc_servers_outstanding(net); 3462306a36Sopenharmony_ci if (timer_reduce(&net->fs_probe_timer, atj)) 3562306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * Handle the completion of a set of probes. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic void afs_finished_fs_probe(struct afs_net *net, struct afs_server *server) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci bool responded = server->probe.responded; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci write_seqlock(&net->fs_lock); 4662306a36Sopenharmony_ci if (responded) { 4762306a36Sopenharmony_ci list_add_tail(&server->probe_link, &net->fs_probe_slow); 4862306a36Sopenharmony_ci } else { 4962306a36Sopenharmony_ci server->rtt = UINT_MAX; 5062306a36Sopenharmony_ci clear_bit(AFS_SERVER_FL_RESPONDING, &server->flags); 5162306a36Sopenharmony_ci list_add_tail(&server->probe_link, &net->fs_probe_fast); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci write_sequnlock(&net->fs_lock); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci afs_schedule_fs_probe(net, server, !responded); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Handle the completion of a probe. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic void afs_done_one_fs_probe(struct afs_net *net, struct afs_server *server) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci _enter(""); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (atomic_dec_and_test(&server->probe_outstanding)) 6662306a36Sopenharmony_ci afs_finished_fs_probe(net, server); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci wake_up_all(&server->probe_wq); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * Handle inability to send a probe due to ENOMEM when trying to allocate a 7362306a36Sopenharmony_ci * call struct. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic void afs_fs_probe_not_done(struct afs_net *net, 7662306a36Sopenharmony_ci struct afs_server *server, 7762306a36Sopenharmony_ci struct afs_addr_cursor *ac) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct afs_addr_list *alist = ac->alist; 8062306a36Sopenharmony_ci unsigned int index = ac->index; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci _enter(""); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci trace_afs_io_error(0, -ENOMEM, afs_io_error_fs_probe_fail); 8562306a36Sopenharmony_ci spin_lock(&server->probe_lock); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci server->probe.local_failure = true; 8862306a36Sopenharmony_ci if (server->probe.error == 0) 8962306a36Sopenharmony_ci server->probe.error = -ENOMEM; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci set_bit(index, &alist->failed); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci spin_unlock(&server->probe_lock); 9462306a36Sopenharmony_ci return afs_done_one_fs_probe(net, server); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * Process the result of probing a fileserver. This is called after successful 9962306a36Sopenharmony_ci * or failed delivery of an FS.GetCapabilities operation. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_civoid afs_fileserver_probe_result(struct afs_call *call) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct afs_addr_list *alist = call->alist; 10462306a36Sopenharmony_ci struct afs_server *server = call->server; 10562306a36Sopenharmony_ci unsigned int index = call->addr_ix; 10662306a36Sopenharmony_ci unsigned int rtt_us = 0, cap0; 10762306a36Sopenharmony_ci int ret = call->error; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci _enter("%pU,%u", &server->uuid, index); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci spin_lock(&server->probe_lock); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci switch (ret) { 11462306a36Sopenharmony_ci case 0: 11562306a36Sopenharmony_ci server->probe.error = 0; 11662306a36Sopenharmony_ci goto responded; 11762306a36Sopenharmony_ci case -ECONNABORTED: 11862306a36Sopenharmony_ci if (!server->probe.responded) { 11962306a36Sopenharmony_ci server->probe.abort_code = call->abort_code; 12062306a36Sopenharmony_ci server->probe.error = ret; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci goto responded; 12362306a36Sopenharmony_ci case -ENOMEM: 12462306a36Sopenharmony_ci case -ENONET: 12562306a36Sopenharmony_ci clear_bit(index, &alist->responded); 12662306a36Sopenharmony_ci server->probe.local_failure = true; 12762306a36Sopenharmony_ci trace_afs_io_error(call->debug_id, ret, afs_io_error_fs_probe_fail); 12862306a36Sopenharmony_ci goto out; 12962306a36Sopenharmony_ci case -ECONNRESET: /* Responded, but call expired. */ 13062306a36Sopenharmony_ci case -ERFKILL: 13162306a36Sopenharmony_ci case -EADDRNOTAVAIL: 13262306a36Sopenharmony_ci case -ENETUNREACH: 13362306a36Sopenharmony_ci case -EHOSTUNREACH: 13462306a36Sopenharmony_ci case -EHOSTDOWN: 13562306a36Sopenharmony_ci case -ECONNREFUSED: 13662306a36Sopenharmony_ci case -ETIMEDOUT: 13762306a36Sopenharmony_ci case -ETIME: 13862306a36Sopenharmony_ci default: 13962306a36Sopenharmony_ci clear_bit(index, &alist->responded); 14062306a36Sopenharmony_ci set_bit(index, &alist->failed); 14162306a36Sopenharmony_ci if (!server->probe.responded && 14262306a36Sopenharmony_ci (server->probe.error == 0 || 14362306a36Sopenharmony_ci server->probe.error == -ETIMEDOUT || 14462306a36Sopenharmony_ci server->probe.error == -ETIME)) 14562306a36Sopenharmony_ci server->probe.error = ret; 14662306a36Sopenharmony_ci trace_afs_io_error(call->debug_id, ret, afs_io_error_fs_probe_fail); 14762306a36Sopenharmony_ci goto out; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciresponded: 15162306a36Sopenharmony_ci clear_bit(index, &alist->failed); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (call->service_id == YFS_FS_SERVICE) { 15462306a36Sopenharmony_ci server->probe.is_yfs = true; 15562306a36Sopenharmony_ci set_bit(AFS_SERVER_FL_IS_YFS, &server->flags); 15662306a36Sopenharmony_ci alist->addrs[index].srx_service = call->service_id; 15762306a36Sopenharmony_ci } else { 15862306a36Sopenharmony_ci server->probe.not_yfs = true; 15962306a36Sopenharmony_ci if (!server->probe.is_yfs) { 16062306a36Sopenharmony_ci clear_bit(AFS_SERVER_FL_IS_YFS, &server->flags); 16162306a36Sopenharmony_ci alist->addrs[index].srx_service = call->service_id; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci cap0 = ntohl(call->tmp); 16462306a36Sopenharmony_ci if (cap0 & AFS3_VICED_CAPABILITY_64BITFILES) 16562306a36Sopenharmony_ci set_bit(AFS_SERVER_FL_HAS_FS64, &server->flags); 16662306a36Sopenharmony_ci else 16762306a36Sopenharmony_ci clear_bit(AFS_SERVER_FL_HAS_FS64, &server->flags); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us); 17162306a36Sopenharmony_ci if (rtt_us < server->probe.rtt) { 17262306a36Sopenharmony_ci server->probe.rtt = rtt_us; 17362306a36Sopenharmony_ci server->rtt = rtt_us; 17462306a36Sopenharmony_ci alist->preferred = index; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci smp_wmb(); /* Set rtt before responded. */ 17862306a36Sopenharmony_ci server->probe.responded = true; 17962306a36Sopenharmony_ci set_bit(index, &alist->responded); 18062306a36Sopenharmony_ci set_bit(AFS_SERVER_FL_RESPONDING, &server->flags); 18162306a36Sopenharmony_ciout: 18262306a36Sopenharmony_ci spin_unlock(&server->probe_lock); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci _debug("probe %pU [%u] %pISpc rtt=%u ret=%d", 18562306a36Sopenharmony_ci &server->uuid, index, &alist->addrs[index].transport, 18662306a36Sopenharmony_ci rtt_us, ret); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return afs_done_one_fs_probe(call->net, server); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* 19262306a36Sopenharmony_ci * Probe one or all of a fileserver's addresses to find out the best route and 19362306a36Sopenharmony_ci * to query its capabilities. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_civoid afs_fs_probe_fileserver(struct afs_net *net, struct afs_server *server, 19662306a36Sopenharmony_ci struct key *key, bool all) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct afs_addr_cursor ac = { 19962306a36Sopenharmony_ci .index = 0, 20062306a36Sopenharmony_ci }; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci _enter("%pU", &server->uuid); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci read_lock(&server->fs_lock); 20562306a36Sopenharmony_ci ac.alist = rcu_dereference_protected(server->addresses, 20662306a36Sopenharmony_ci lockdep_is_held(&server->fs_lock)); 20762306a36Sopenharmony_ci afs_get_addrlist(ac.alist); 20862306a36Sopenharmony_ci read_unlock(&server->fs_lock); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci server->probed_at = jiffies; 21162306a36Sopenharmony_ci atomic_set(&server->probe_outstanding, all ? ac.alist->nr_addrs : 1); 21262306a36Sopenharmony_ci memset(&server->probe, 0, sizeof(server->probe)); 21362306a36Sopenharmony_ci server->probe.rtt = UINT_MAX; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ac.index = ac.alist->preferred; 21662306a36Sopenharmony_ci if (ac.index < 0 || ac.index >= ac.alist->nr_addrs) 21762306a36Sopenharmony_ci all = true; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (all) { 22062306a36Sopenharmony_ci for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) 22162306a36Sopenharmony_ci if (!afs_fs_get_capabilities(net, server, &ac, key)) 22262306a36Sopenharmony_ci afs_fs_probe_not_done(net, server, &ac); 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci if (!afs_fs_get_capabilities(net, server, &ac, key)) 22562306a36Sopenharmony_ci afs_fs_probe_not_done(net, server, &ac); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci afs_put_addrlist(ac.alist); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* 23262306a36Sopenharmony_ci * Wait for the first as-yet untried fileserver to respond. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ciint afs_wait_for_fs_probes(struct afs_server_list *slist, unsigned long untried) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct wait_queue_entry *waits; 23762306a36Sopenharmony_ci struct afs_server *server; 23862306a36Sopenharmony_ci unsigned int rtt = UINT_MAX, rtt_s; 23962306a36Sopenharmony_ci bool have_responders = false; 24062306a36Sopenharmony_ci int pref = -1, i; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci _enter("%u,%lx", slist->nr_servers, untried); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Only wait for servers that have a probe outstanding. */ 24562306a36Sopenharmony_ci for (i = 0; i < slist->nr_servers; i++) { 24662306a36Sopenharmony_ci if (test_bit(i, &untried)) { 24762306a36Sopenharmony_ci server = slist->servers[i].server; 24862306a36Sopenharmony_ci if (!atomic_read(&server->probe_outstanding)) 24962306a36Sopenharmony_ci __clear_bit(i, &untried); 25062306a36Sopenharmony_ci if (server->probe.responded) 25162306a36Sopenharmony_ci have_responders = true; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci if (have_responders || !untried) 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci waits = kmalloc(array_size(slist->nr_servers, sizeof(*waits)), GFP_KERNEL); 25862306a36Sopenharmony_ci if (!waits) 25962306a36Sopenharmony_ci return -ENOMEM; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for (i = 0; i < slist->nr_servers; i++) { 26262306a36Sopenharmony_ci if (test_bit(i, &untried)) { 26362306a36Sopenharmony_ci server = slist->servers[i].server; 26462306a36Sopenharmony_ci init_waitqueue_entry(&waits[i], current); 26562306a36Sopenharmony_ci add_wait_queue(&server->probe_wq, &waits[i]); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci for (;;) { 27062306a36Sopenharmony_ci bool still_probing = false; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 27362306a36Sopenharmony_ci for (i = 0; i < slist->nr_servers; i++) { 27462306a36Sopenharmony_ci if (test_bit(i, &untried)) { 27562306a36Sopenharmony_ci server = slist->servers[i].server; 27662306a36Sopenharmony_ci if (server->probe.responded) 27762306a36Sopenharmony_ci goto stop; 27862306a36Sopenharmony_ci if (atomic_read(&server->probe_outstanding)) 27962306a36Sopenharmony_ci still_probing = true; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!still_probing || signal_pending(current)) 28462306a36Sopenharmony_ci goto stop; 28562306a36Sopenharmony_ci schedule(); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistop: 28962306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci for (i = 0; i < slist->nr_servers; i++) { 29262306a36Sopenharmony_ci if (test_bit(i, &untried)) { 29362306a36Sopenharmony_ci server = slist->servers[i].server; 29462306a36Sopenharmony_ci rtt_s = READ_ONCE(server->rtt); 29562306a36Sopenharmony_ci if (test_bit(AFS_SERVER_FL_RESPONDING, &server->flags) && 29662306a36Sopenharmony_ci rtt_s < rtt) { 29762306a36Sopenharmony_ci pref = i; 29862306a36Sopenharmony_ci rtt = rtt_s; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci remove_wait_queue(&server->probe_wq, &waits[i]); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci kfree(waits); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (pref == -1 && signal_pending(current)) 30862306a36Sopenharmony_ci return -ERESTARTSYS; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (pref >= 0) 31162306a36Sopenharmony_ci slist->preferred = pref; 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* 31662306a36Sopenharmony_ci * Probe timer. We have an increment on fs_outstanding that we need to pass 31762306a36Sopenharmony_ci * along to the work item. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_civoid afs_fs_probe_timer(struct timer_list *timer) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct afs_net *net = container_of(timer, struct afs_net, fs_probe_timer); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!net->live || !queue_work(afs_wq, &net->fs_prober)) 32462306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* 32862306a36Sopenharmony_ci * Dispatch a probe to a server. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic void afs_dispatch_fs_probe(struct afs_net *net, struct afs_server *server, bool all) 33162306a36Sopenharmony_ci __releases(&net->fs_lock) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct key *key = NULL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* We remove it from the queues here - it will be added back to 33662306a36Sopenharmony_ci * one of the queues on the completion of the probe. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci list_del_init(&server->probe_link); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci afs_get_server(server, afs_server_trace_get_probe); 34162306a36Sopenharmony_ci write_sequnlock(&net->fs_lock); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci afs_fs_probe_fileserver(net, server, key, all); 34462306a36Sopenharmony_ci afs_put_server(net, server, afs_server_trace_put_probe); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/* 34862306a36Sopenharmony_ci * Probe a server immediately without waiting for its due time to come 34962306a36Sopenharmony_ci * round. This is used when all of the addresses have been tried. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_civoid afs_probe_fileserver(struct afs_net *net, struct afs_server *server) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci write_seqlock(&net->fs_lock); 35462306a36Sopenharmony_ci if (!list_empty(&server->probe_link)) 35562306a36Sopenharmony_ci return afs_dispatch_fs_probe(net, server, true); 35662306a36Sopenharmony_ci write_sequnlock(&net->fs_lock); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * Probe dispatcher to regularly dispatch probes to keep NAT alive. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_civoid afs_fs_probe_dispatcher(struct work_struct *work) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct afs_net *net = container_of(work, struct afs_net, fs_prober); 36562306a36Sopenharmony_ci struct afs_server *fast, *slow, *server; 36662306a36Sopenharmony_ci unsigned long nowj, timer_at, poll_at; 36762306a36Sopenharmony_ci bool first_pass = true, set_timer = false; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (!net->live) { 37062306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci _enter(""); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (list_empty(&net->fs_probe_fast) && list_empty(&net->fs_probe_slow)) { 37762306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 37862306a36Sopenharmony_ci _leave(" [none]"); 37962306a36Sopenharmony_ci return; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciagain: 38362306a36Sopenharmony_ci write_seqlock(&net->fs_lock); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci fast = slow = server = NULL; 38662306a36Sopenharmony_ci nowj = jiffies; 38762306a36Sopenharmony_ci timer_at = nowj + MAX_JIFFY_OFFSET; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!list_empty(&net->fs_probe_fast)) { 39062306a36Sopenharmony_ci fast = list_first_entry(&net->fs_probe_fast, struct afs_server, probe_link); 39162306a36Sopenharmony_ci poll_at = fast->probed_at + afs_fs_probe_fast_poll_interval; 39262306a36Sopenharmony_ci if (time_before(nowj, poll_at)) { 39362306a36Sopenharmony_ci timer_at = poll_at; 39462306a36Sopenharmony_ci set_timer = true; 39562306a36Sopenharmony_ci fast = NULL; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!list_empty(&net->fs_probe_slow)) { 40062306a36Sopenharmony_ci slow = list_first_entry(&net->fs_probe_slow, struct afs_server, probe_link); 40162306a36Sopenharmony_ci poll_at = slow->probed_at + afs_fs_probe_slow_poll_interval; 40262306a36Sopenharmony_ci if (time_before(nowj, poll_at)) { 40362306a36Sopenharmony_ci if (time_before(poll_at, timer_at)) 40462306a36Sopenharmony_ci timer_at = poll_at; 40562306a36Sopenharmony_ci set_timer = true; 40662306a36Sopenharmony_ci slow = NULL; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci server = fast ?: slow; 41162306a36Sopenharmony_ci if (server) 41262306a36Sopenharmony_ci _debug("probe %pU", &server->uuid); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (server && (first_pass || !need_resched())) { 41562306a36Sopenharmony_ci afs_dispatch_fs_probe(net, server, server == fast); 41662306a36Sopenharmony_ci first_pass = false; 41762306a36Sopenharmony_ci goto again; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci write_sequnlock(&net->fs_lock); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (server) { 42362306a36Sopenharmony_ci if (!queue_work(afs_wq, &net->fs_prober)) 42462306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 42562306a36Sopenharmony_ci _leave(" [requeue]"); 42662306a36Sopenharmony_ci } else if (set_timer) { 42762306a36Sopenharmony_ci if (timer_reduce(&net->fs_probe_timer, timer_at)) 42862306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 42962306a36Sopenharmony_ci _leave(" [timer]"); 43062306a36Sopenharmony_ci } else { 43162306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 43262306a36Sopenharmony_ci _leave(" [quiesce]"); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/* 43762306a36Sopenharmony_ci * Wait for a probe on a particular fileserver to complete for 2s. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ciint afs_wait_for_one_fs_probe(struct afs_server *server, bool is_intr) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct wait_queue_entry wait; 44262306a36Sopenharmony_ci unsigned long timo = 2 * HZ; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (atomic_read(&server->probe_outstanding) == 0) 44562306a36Sopenharmony_ci goto dont_wait; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci init_wait_entry(&wait, 0); 44862306a36Sopenharmony_ci for (;;) { 44962306a36Sopenharmony_ci prepare_to_wait_event(&server->probe_wq, &wait, 45062306a36Sopenharmony_ci is_intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); 45162306a36Sopenharmony_ci if (timo == 0 || 45262306a36Sopenharmony_ci server->probe.responded || 45362306a36Sopenharmony_ci atomic_read(&server->probe_outstanding) == 0 || 45462306a36Sopenharmony_ci (is_intr && signal_pending(current))) 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci timo = schedule_timeout(timo); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci finish_wait(&server->probe_wq, &wait); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cidont_wait: 46262306a36Sopenharmony_ci if (server->probe.responded) 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci if (is_intr && signal_pending(current)) 46562306a36Sopenharmony_ci return -ERESTARTSYS; 46662306a36Sopenharmony_ci if (timo == 0) 46762306a36Sopenharmony_ci return -ETIME; 46862306a36Sopenharmony_ci return -EDESTADDRREQ; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* 47262306a36Sopenharmony_ci * Clean up the probing when the namespace is killed off. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_civoid afs_fs_probe_cleanup(struct afs_net *net) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci if (del_timer_sync(&net->fs_probe_timer)) 47762306a36Sopenharmony_ci afs_dec_servers_outstanding(net); 47862306a36Sopenharmony_ci} 479