1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AFS fileserver probing
4 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
8 #include <linux/sched.h>
9 #include <linux/slab.h>
12 #include "protocol_yfs.h"
14 static bool afs_fs_probe_done(struct afs_server
*server
)
16 if (!atomic_dec_and_test(&server
->probe_outstanding
))
19 wake_up_var(&server
->probe_outstanding
);
20 clear_bit_unlock(AFS_SERVER_FL_PROBING
, &server
->flags
);
21 wake_up_bit(&server
->flags
, AFS_SERVER_FL_PROBING
);
26 * Process the result of probing a fileserver. This is called after successful
27 * or failed delivery of an FS.GetCapabilities operation.
29 void afs_fileserver_probe_result(struct afs_call
*call
)
31 struct afs_addr_list
*alist
= call
->alist
;
32 struct afs_server
*server
= call
->server
;
33 unsigned int server_index
= call
->server_index
;
34 unsigned int index
= call
->addr_ix
;
35 unsigned int rtt_us
= 0;
36 bool have_result
= false;
37 int ret
= call
->error
;
39 _enter("%pU,%u", &server
->uuid
, index
);
41 spin_lock(&server
->probe_lock
);
45 server
->probe
.error
= 0;
48 if (!server
->probe
.responded
) {
49 server
->probe
.abort_code
= call
->abort_code
;
50 server
->probe
.error
= ret
;
55 server
->probe
.local_failure
= true;
56 afs_io_error(call
, afs_io_error_fs_probe_fail
);
58 case -ECONNRESET
: /* Responded, but call expired. */
68 clear_bit(index
, &alist
->responded
);
69 set_bit(index
, &alist
->failed
);
70 if (!server
->probe
.responded
&&
71 (server
->probe
.error
== 0 ||
72 server
->probe
.error
== -ETIMEDOUT
||
73 server
->probe
.error
== -ETIME
))
74 server
->probe
.error
= ret
;
75 afs_io_error(call
, afs_io_error_fs_probe_fail
);
80 set_bit(index
, &alist
->responded
);
81 clear_bit(index
, &alist
->failed
);
83 if (call
->service_id
== YFS_FS_SERVICE
) {
84 server
->probe
.is_yfs
= true;
85 set_bit(AFS_SERVER_FL_IS_YFS
, &server
->flags
);
86 alist
->addrs
[index
].srx_service
= call
->service_id
;
88 server
->probe
.not_yfs
= true;
89 if (!server
->probe
.is_yfs
) {
90 clear_bit(AFS_SERVER_FL_IS_YFS
, &server
->flags
);
91 alist
->addrs
[index
].srx_service
= call
->service_id
;
95 rtt_us
= rxrpc_kernel_get_srtt(call
->net
->socket
, call
->rxcall
);
96 if (rtt_us
< server
->probe
.rtt
) {
97 server
->probe
.rtt
= rtt_us
;
98 alist
->preferred
= index
;
102 smp_wmb(); /* Set rtt before responded. */
103 server
->probe
.responded
= true;
104 set_bit(AFS_SERVER_FL_PROBED
, &server
->flags
);
106 spin_unlock(&server
->probe_lock
);
108 _debug("probe [%u][%u] %pISpc rtt=%u ret=%d",
109 server_index
, index
, &alist
->addrs
[index
].transport
, rtt_us
, ret
);
111 have_result
|= afs_fs_probe_done(server
);
113 wake_up_all(&server
->probe_wq
);
117 * Probe all of a fileserver's addresses to find out the best route and to
118 * query its capabilities.
120 static int afs_do_probe_fileserver(struct afs_net
*net
,
121 struct afs_server
*server
,
123 unsigned int server_index
,
124 struct afs_error
*_e
)
126 struct afs_addr_cursor ac
= {
129 struct afs_call
*call
;
130 bool in_progress
= false;
132 _enter("%pU", &server
->uuid
);
134 read_lock(&server
->fs_lock
);
135 ac
.alist
= rcu_dereference_protected(server
->addresses
,
136 lockdep_is_held(&server
->fs_lock
));
137 afs_get_addrlist(ac
.alist
);
138 read_unlock(&server
->fs_lock
);
140 atomic_set(&server
->probe_outstanding
, ac
.alist
->nr_addrs
);
141 memset(&server
->probe
, 0, sizeof(server
->probe
));
142 server
->probe
.rtt
= UINT_MAX
;
144 for (ac
.index
= 0; ac
.index
< ac
.alist
->nr_addrs
; ac
.index
++) {
145 call
= afs_fs_get_capabilities(net
, server
, &ac
, key
, server_index
);
150 afs_prioritise_error(_e
, PTR_ERR(call
), ac
.abort_code
);
155 afs_fs_probe_done(server
);
156 afs_put_addrlist(ac
.alist
);
161 * Send off probes to all unprobed servers.
163 int afs_probe_fileservers(struct afs_net
*net
, struct key
*key
,
164 struct afs_server_list
*list
)
166 struct afs_server
*server
;
168 bool in_progress
= false;
173 for (i
= 0; i
< list
->nr_servers
; i
++) {
174 server
= list
->servers
[i
].server
;
175 if (test_bit(AFS_SERVER_FL_PROBED
, &server
->flags
))
178 if (!test_and_set_bit_lock(AFS_SERVER_FL_PROBING
, &server
->flags
) &&
179 afs_do_probe_fileserver(net
, server
, key
, i
, &e
))
183 return in_progress
? 0 : e
.error
;
187 * Wait for the first as-yet untried fileserver to respond.
189 int afs_wait_for_fs_probes(struct afs_server_list
*slist
, unsigned long untried
)
191 struct wait_queue_entry
*waits
;
192 struct afs_server
*server
;
193 unsigned int rtt
= UINT_MAX
;
194 bool have_responders
= false;
197 _enter("%u,%lx", slist
->nr_servers
, untried
);
199 /* Only wait for servers that have a probe outstanding. */
200 for (i
= 0; i
< slist
->nr_servers
; i
++) {
201 if (test_bit(i
, &untried
)) {
202 server
= slist
->servers
[i
].server
;
203 if (!test_bit(AFS_SERVER_FL_PROBING
, &server
->flags
))
204 __clear_bit(i
, &untried
);
205 if (server
->probe
.responded
)
206 have_responders
= true;
209 if (have_responders
|| !untried
)
212 waits
= kmalloc(array_size(slist
->nr_servers
, sizeof(*waits
)), GFP_KERNEL
);
216 for (i
= 0; i
< slist
->nr_servers
; i
++) {
217 if (test_bit(i
, &untried
)) {
218 server
= slist
->servers
[i
].server
;
219 init_waitqueue_entry(&waits
[i
], current
);
220 add_wait_queue(&server
->probe_wq
, &waits
[i
]);
225 bool still_probing
= false;
227 set_current_state(TASK_INTERRUPTIBLE
);
228 for (i
= 0; i
< slist
->nr_servers
; i
++) {
229 if (test_bit(i
, &untried
)) {
230 server
= slist
->servers
[i
].server
;
231 if (server
->probe
.responded
)
233 if (test_bit(AFS_SERVER_FL_PROBING
, &server
->flags
))
234 still_probing
= true;
238 if (!still_probing
|| signal_pending(current
))
244 set_current_state(TASK_RUNNING
);
246 for (i
= 0; i
< slist
->nr_servers
; i
++) {
247 if (test_bit(i
, &untried
)) {
248 server
= slist
->servers
[i
].server
;
249 if (server
->probe
.responded
&&
250 server
->probe
.rtt
< rtt
) {
252 rtt
= server
->probe
.rtt
;
255 remove_wait_queue(&server
->probe_wq
, &waits
[i
]);
261 if (pref
== -1 && signal_pending(current
))
265 slist
->preferred
= pref
;