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