]>
Commit | Line | Data |
---|---|---|
b4d0d230 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
3bf0fb6f DH |
2 | /* AFS vlserver probing |
3 | * | |
4 | * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. | |
5 | * Written by David Howells ([email protected]) | |
3bf0fb6f DH |
6 | */ |
7 | ||
8 | #include <linux/sched.h> | |
9 | #include <linux/slab.h> | |
10 | #include "afs_fs.h" | |
11 | #include "internal.h" | |
12 | #include "protocol_yfs.h" | |
13 | ||
b95b3094 DH |
14 | |
15 | /* | |
16 | * Handle the completion of a set of probes. | |
17 | */ | |
18 | static void afs_finished_vl_probe(struct afs_vlserver *server) | |
3bf0fb6f | 19 | { |
b95b3094 DH |
20 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) { |
21 | server->rtt = UINT_MAX; | |
22 | clear_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags); | |
23 | } | |
3bf0fb6f | 24 | |
3bf0fb6f DH |
25 | clear_bit_unlock(AFS_VLSERVER_FL_PROBING, &server->flags); |
26 | wake_up_bit(&server->flags, AFS_VLSERVER_FL_PROBING); | |
b95b3094 DH |
27 | } |
28 | ||
29 | /* | |
30 | * Handle the completion of a probe RPC call. | |
31 | */ | |
32 | static void afs_done_one_vl_probe(struct afs_vlserver *server, bool wake_up) | |
33 | { | |
34 | if (atomic_dec_and_test(&server->probe_outstanding)) { | |
35 | afs_finished_vl_probe(server); | |
36 | wake_up = true; | |
37 | } | |
38 | ||
39 | if (wake_up) | |
40 | wake_up_all(&server->probe_wq); | |
3bf0fb6f DH |
41 | } |
42 | ||
43 | /* | |
44 | * Process the result of probing a vlserver. This is called after successful | |
45 | * or failed delivery of an VL.GetCapabilities operation. | |
46 | */ | |
47 | void afs_vlserver_probe_result(struct afs_call *call) | |
48 | { | |
f49b594d | 49 | struct afs_addr_list *alist = call->vl_probe; |
ffba718e | 50 | struct afs_vlserver *server = call->vlserver; |
98f9fda2 | 51 | struct afs_address *addr = &alist->addrs[call->probe_index]; |
ffba718e | 52 | unsigned int server_index = call->server_index; |
c410bf01 | 53 | unsigned int rtt_us = 0; |
98f9fda2 | 54 | unsigned int index = call->probe_index; |
3bf0fb6f | 55 | bool have_result = false; |
3bf0fb6f DH |
56 | int ret = call->error; |
57 | ||
58 | _enter("%s,%u,%u,%d,%d", server->name, server_index, index, ret, call->abort_code); | |
59 | ||
60 | spin_lock(&server->probe_lock); | |
61 | ||
62 | switch (ret) { | |
63 | case 0: | |
64 | server->probe.error = 0; | |
65 | goto responded; | |
66 | case -ECONNABORTED: | |
fb72cd3d | 67 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) { |
3bf0fb6f DH |
68 | server->probe.abort_code = call->abort_code; |
69 | server->probe.error = ret; | |
70 | } | |
71 | goto responded; | |
72 | case -ENOMEM: | |
73 | case -ENONET: | |
b95b3094 DH |
74 | case -EKEYEXPIRED: |
75 | case -EKEYREVOKED: | |
76 | case -EKEYREJECTED: | |
fb72cd3d | 77 | server->probe.flags |= AFS_VLSERVER_PROBE_LOCAL_FAILURE; |
b95b3094 DH |
78 | if (server->probe.error == 0) |
79 | server->probe.error = ret; | |
80 | trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail); | |
3bf0fb6f DH |
81 | goto out; |
82 | case -ECONNRESET: /* Responded, but call expired. */ | |
4584ae96 DH |
83 | case -ERFKILL: |
84 | case -EADDRNOTAVAIL: | |
3bf0fb6f DH |
85 | case -ENETUNREACH: |
86 | case -EHOSTUNREACH: | |
4584ae96 | 87 | case -EHOSTDOWN: |
3bf0fb6f DH |
88 | case -ECONNREFUSED: |
89 | case -ETIMEDOUT: | |
90 | case -ETIME: | |
91 | default: | |
92 | clear_bit(index, &alist->responded); | |
aa4917d6 | 93 | set_bit(index, &alist->probe_failed); |
fb72cd3d | 94 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) && |
3bf0fb6f DH |
95 | (server->probe.error == 0 || |
96 | server->probe.error == -ETIMEDOUT || | |
97 | server->probe.error == -ETIME)) | |
98 | server->probe.error = ret; | |
b95b3094 | 99 | trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail); |
3bf0fb6f DH |
100 | goto out; |
101 | } | |
102 | ||
103 | responded: | |
104 | set_bit(index, &alist->responded); | |
aa4917d6 | 105 | clear_bit(index, &alist->probe_failed); |
3bf0fb6f DH |
106 | |
107 | if (call->service_id == YFS_VL_SERVICE) { | |
fb72cd3d | 108 | server->probe.flags |= AFS_VLSERVER_PROBE_IS_YFS; |
3bf0fb6f | 109 | set_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags); |
e38f299e | 110 | server->service_id = call->service_id; |
3bf0fb6f | 111 | } else { |
fb72cd3d DH |
112 | server->probe.flags |= AFS_VLSERVER_PROBE_NOT_YFS; |
113 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_IS_YFS)) { | |
3bf0fb6f | 114 | clear_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags); |
e38f299e | 115 | server->service_id = call->service_id; |
3bf0fb6f DH |
116 | } |
117 | } | |
118 | ||
72904d7b | 119 | rtt_us = rxrpc_kernel_get_srtt(addr->peer); |
ba00b190 | 120 | if (rtt_us < server->probe.rtt) { |
c410bf01 | 121 | server->probe.rtt = rtt_us; |
b95b3094 | 122 | server->rtt = rtt_us; |
3bf0fb6f | 123 | alist->preferred = index; |
3bf0fb6f DH |
124 | } |
125 | ||
126 | smp_wmb(); /* Set rtt before responded. */ | |
fb72cd3d | 127 | server->probe.flags |= AFS_VLSERVER_PROBE_RESPONDED; |
3bf0fb6f | 128 | set_bit(AFS_VLSERVER_FL_PROBED, &server->flags); |
b95b3094 DH |
129 | set_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags); |
130 | have_result = true; | |
3bf0fb6f DH |
131 | out: |
132 | spin_unlock(&server->probe_lock); | |
133 | ||
e6a7d7f7 | 134 | trace_afs_vl_probe(server, false, alist, index, call->error, call->abort_code, rtt_us); |
72904d7b DH |
135 | _debug("probe [%u][%u] %pISpc rtt=%d ret=%d", |
136 | server_index, index, rxrpc_kernel_remote_addr(addr->peer), | |
137 | rtt_us, ret); | |
3bf0fb6f | 138 | |
b95b3094 | 139 | afs_done_one_vl_probe(server, have_result); |
3bf0fb6f DH |
140 | } |
141 | ||
142 | /* | |
143 | * Probe all of a vlserver's addresses to find out the best route and to | |
144 | * query its capabilities. | |
145 | */ | |
4584ae96 DH |
146 | static bool afs_do_probe_vlserver(struct afs_net *net, |
147 | struct afs_vlserver *server, | |
148 | struct key *key, | |
149 | unsigned int server_index, | |
150 | struct afs_error *_e) | |
3bf0fb6f | 151 | { |
98f9fda2 | 152 | struct afs_addr_list *alist; |
0b9bf381 | 153 | struct afs_call *call; |
e6a7d7f7 DH |
154 | unsigned long unprobed; |
155 | unsigned int index, i; | |
4584ae96 | 156 | bool in_progress = false; |
e6a7d7f7 | 157 | int best_prio; |
3bf0fb6f DH |
158 | |
159 | _enter("%s", server->name); | |
160 | ||
161 | read_lock(&server->lock); | |
98f9fda2 DH |
162 | alist = rcu_dereference_protected(server->addresses, |
163 | lockdep_is_held(&server->lock)); | |
164 | afs_get_addrlist(alist, afs_alist_trace_get_vlprobe); | |
3bf0fb6f DH |
165 | read_unlock(&server->lock); |
166 | ||
98f9fda2 | 167 | atomic_set(&server->probe_outstanding, alist->nr_addrs); |
3bf0fb6f DH |
168 | memset(&server->probe, 0, sizeof(server->probe)); |
169 | server->probe.rtt = UINT_MAX; | |
170 | ||
e6a7d7f7 DH |
171 | unprobed = (1UL << alist->nr_addrs) - 1; |
172 | while (unprobed) { | |
173 | best_prio = -1; | |
174 | index = 0; | |
175 | for (i = 0; i < alist->nr_addrs; i++) { | |
176 | if (test_bit(i, &unprobed) && | |
177 | alist->addrs[i].prio > best_prio) { | |
178 | index = i; | |
179 | best_prio = alist->addrs[i].prio; | |
180 | } | |
181 | } | |
182 | __clear_bit(index, &unprobed); | |
183 | ||
184 | trace_afs_vl_probe(server, true, alist, index, 0, 0, 0); | |
98f9fda2 | 185 | call = afs_vl_get_capabilities(net, alist, index, key, server, |
0b9bf381 DH |
186 | server_index); |
187 | if (!IS_ERR(call)) { | |
aa453bec | 188 | afs_prioritise_error(_e, call->error, call->abort_code); |
0b9bf381 | 189 | afs_put_call(call); |
4584ae96 | 190 | in_progress = true; |
0b9bf381 | 191 | } else { |
aa453bec | 192 | afs_prioritise_error(_e, PTR_ERR(call), 0); |
b95b3094 | 193 | afs_done_one_vl_probe(server, false); |
0b9bf381 | 194 | } |
3bf0fb6f DH |
195 | } |
196 | ||
98f9fda2 | 197 | afs_put_addrlist(alist, afs_alist_trace_put_vlprobe); |
4584ae96 | 198 | return in_progress; |
3bf0fb6f DH |
199 | } |
200 | ||
201 | /* | |
202 | * Send off probes to all unprobed servers. | |
203 | */ | |
204 | int afs_send_vl_probes(struct afs_net *net, struct key *key, | |
205 | struct afs_vlserver_list *vllist) | |
206 | { | |
207 | struct afs_vlserver *server; | |
aa453bec | 208 | struct afs_error e = {}; |
4584ae96 DH |
209 | bool in_progress = false; |
210 | int i; | |
3bf0fb6f DH |
211 | |
212 | for (i = 0; i < vllist->nr_servers; i++) { | |
213 | server = vllist->servers[i].server; | |
214 | if (test_bit(AFS_VLSERVER_FL_PROBED, &server->flags)) | |
215 | continue; | |
216 | ||
4584ae96 DH |
217 | if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, &server->flags) && |
218 | afs_do_probe_vlserver(net, server, key, i, &e)) | |
219 | in_progress = true; | |
3bf0fb6f DH |
220 | } |
221 | ||
4584ae96 | 222 | return in_progress ? 0 : e.error; |
3bf0fb6f DH |
223 | } |
224 | ||
225 | /* | |
226 | * Wait for the first as-yet untried server to respond. | |
227 | */ | |
228 | int afs_wait_for_vl_probes(struct afs_vlserver_list *vllist, | |
229 | unsigned long untried) | |
230 | { | |
231 | struct wait_queue_entry *waits; | |
232 | struct afs_vlserver *server; | |
b95b3094 | 233 | unsigned int rtt = UINT_MAX, rtt_s; |
3bf0fb6f DH |
234 | bool have_responders = false; |
235 | int pref = -1, i; | |
236 | ||
237 | _enter("%u,%lx", vllist->nr_servers, untried); | |
238 | ||
239 | /* Only wait for servers that have a probe outstanding. */ | |
240 | for (i = 0; i < vllist->nr_servers; i++) { | |
241 | if (test_bit(i, &untried)) { | |
242 | server = vllist->servers[i].server; | |
243 | if (!test_bit(AFS_VLSERVER_FL_PROBING, &server->flags)) | |
244 | __clear_bit(i, &untried); | |
fb72cd3d | 245 | if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) |
3bf0fb6f DH |
246 | have_responders = true; |
247 | } | |
248 | } | |
249 | if (have_responders || !untried) | |
250 | return 0; | |
251 | ||
252 | waits = kmalloc(array_size(vllist->nr_servers, sizeof(*waits)), GFP_KERNEL); | |
253 | if (!waits) | |
254 | return -ENOMEM; | |
255 | ||
256 | for (i = 0; i < vllist->nr_servers; i++) { | |
257 | if (test_bit(i, &untried)) { | |
258 | server = vllist->servers[i].server; | |
259 | init_waitqueue_entry(&waits[i], current); | |
260 | add_wait_queue(&server->probe_wq, &waits[i]); | |
261 | } | |
262 | } | |
263 | ||
264 | for (;;) { | |
265 | bool still_probing = false; | |
266 | ||
267 | set_current_state(TASK_INTERRUPTIBLE); | |
268 | for (i = 0; i < vllist->nr_servers; i++) { | |
269 | if (test_bit(i, &untried)) { | |
270 | server = vllist->servers[i].server; | |
fb72cd3d | 271 | if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) |
3bf0fb6f DH |
272 | goto stop; |
273 | if (test_bit(AFS_VLSERVER_FL_PROBING, &server->flags)) | |
274 | still_probing = true; | |
275 | } | |
276 | } | |
277 | ||
08d405c8 | 278 | if (!still_probing || signal_pending(current)) |
3bf0fb6f DH |
279 | goto stop; |
280 | schedule(); | |
281 | } | |
282 | ||
283 | stop: | |
284 | set_current_state(TASK_RUNNING); | |
285 | ||
286 | for (i = 0; i < vllist->nr_servers; i++) { | |
287 | if (test_bit(i, &untried)) { | |
288 | server = vllist->servers[i].server; | |
b95b3094 DH |
289 | rtt_s = READ_ONCE(server->rtt); |
290 | if (test_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags) && | |
291 | rtt_s < rtt) { | |
3bf0fb6f | 292 | pref = i; |
b95b3094 | 293 | rtt = rtt_s; |
3bf0fb6f DH |
294 | } |
295 | ||
296 | remove_wait_queue(&server->probe_wq, &waits[i]); | |
297 | } | |
298 | } | |
299 | ||
300 | kfree(waits); | |
301 | ||
302 | if (pref == -1 && signal_pending(current)) | |
303 | return -ERESTARTSYS; | |
304 | ||
305 | if (pref >= 0) | |
306 | vllist->preferred = pref; | |
307 | ||
308 | _leave(" = 0 [%u]", pref); | |
309 | return 0; | |
310 | } |