]>
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 | { | |
49 | struct afs_addr_list *alist = call->alist; | |
ffba718e DH |
50 | struct afs_vlserver *server = call->vlserver; |
51 | unsigned int server_index = call->server_index; | |
c410bf01 | 52 | unsigned int rtt_us = 0; |
3bf0fb6f | 53 | unsigned int index = call->addr_ix; |
3bf0fb6f | 54 | bool have_result = false; |
3bf0fb6f DH |
55 | int ret = call->error; |
56 | ||
57 | _enter("%s,%u,%u,%d,%d", server->name, server_index, index, ret, call->abort_code); | |
58 | ||
59 | spin_lock(&server->probe_lock); | |
60 | ||
61 | switch (ret) { | |
62 | case 0: | |
63 | server->probe.error = 0; | |
64 | goto responded; | |
65 | case -ECONNABORTED: | |
fb72cd3d | 66 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) { |
3bf0fb6f DH |
67 | server->probe.abort_code = call->abort_code; |
68 | server->probe.error = ret; | |
69 | } | |
70 | goto responded; | |
71 | case -ENOMEM: | |
72 | case -ENONET: | |
b95b3094 DH |
73 | case -EKEYEXPIRED: |
74 | case -EKEYREVOKED: | |
75 | case -EKEYREJECTED: | |
fb72cd3d | 76 | server->probe.flags |= AFS_VLSERVER_PROBE_LOCAL_FAILURE; |
b95b3094 DH |
77 | if (server->probe.error == 0) |
78 | server->probe.error = ret; | |
79 | trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail); | |
3bf0fb6f DH |
80 | goto out; |
81 | case -ECONNRESET: /* Responded, but call expired. */ | |
4584ae96 DH |
82 | case -ERFKILL: |
83 | case -EADDRNOTAVAIL: | |
3bf0fb6f DH |
84 | case -ENETUNREACH: |
85 | case -EHOSTUNREACH: | |
4584ae96 | 86 | case -EHOSTDOWN: |
3bf0fb6f DH |
87 | case -ECONNREFUSED: |
88 | case -ETIMEDOUT: | |
89 | case -ETIME: | |
90 | default: | |
91 | clear_bit(index, &alist->responded); | |
92 | set_bit(index, &alist->failed); | |
fb72cd3d | 93 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) && |
3bf0fb6f DH |
94 | (server->probe.error == 0 || |
95 | server->probe.error == -ETIMEDOUT || | |
96 | server->probe.error == -ETIME)) | |
97 | server->probe.error = ret; | |
b95b3094 | 98 | trace_afs_io_error(call->debug_id, ret, afs_io_error_vl_probe_fail); |
3bf0fb6f DH |
99 | goto out; |
100 | } | |
101 | ||
102 | responded: | |
103 | set_bit(index, &alist->responded); | |
104 | clear_bit(index, &alist->failed); | |
105 | ||
106 | if (call->service_id == YFS_VL_SERVICE) { | |
fb72cd3d | 107 | server->probe.flags |= AFS_VLSERVER_PROBE_IS_YFS; |
3bf0fb6f DH |
108 | set_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags); |
109 | alist->addrs[index].srx_service = call->service_id; | |
110 | } else { | |
fb72cd3d DH |
111 | server->probe.flags |= AFS_VLSERVER_PROBE_NOT_YFS; |
112 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_IS_YFS)) { | |
3bf0fb6f DH |
113 | clear_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags); |
114 | alist->addrs[index].srx_service = call->service_id; | |
115 | } | |
116 | } | |
117 | ||
1d4adfaf DH |
118 | if (rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us) && |
119 | rtt_us < server->probe.rtt) { | |
c410bf01 | 120 | server->probe.rtt = rtt_us; |
b95b3094 | 121 | server->rtt = rtt_us; |
3bf0fb6f | 122 | alist->preferred = index; |
3bf0fb6f DH |
123 | } |
124 | ||
125 | smp_wmb(); /* Set rtt before responded. */ | |
fb72cd3d | 126 | server->probe.flags |= AFS_VLSERVER_PROBE_RESPONDED; |
3bf0fb6f | 127 | set_bit(AFS_VLSERVER_FL_PROBED, &server->flags); |
b95b3094 DH |
128 | set_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags); |
129 | have_result = true; | |
3bf0fb6f DH |
130 | out: |
131 | spin_unlock(&server->probe_lock); | |
132 | ||
133 | _debug("probe [%u][%u] %pISpc rtt=%u ret=%d", | |
c410bf01 | 134 | server_index, index, &alist->addrs[index].transport, rtt_us, ret); |
3bf0fb6f | 135 | |
b95b3094 | 136 | afs_done_one_vl_probe(server, have_result); |
3bf0fb6f DH |
137 | } |
138 | ||
139 | /* | |
140 | * Probe all of a vlserver's addresses to find out the best route and to | |
141 | * query its capabilities. | |
142 | */ | |
4584ae96 DH |
143 | static bool afs_do_probe_vlserver(struct afs_net *net, |
144 | struct afs_vlserver *server, | |
145 | struct key *key, | |
146 | unsigned int server_index, | |
147 | struct afs_error *_e) | |
3bf0fb6f DH |
148 | { |
149 | struct afs_addr_cursor ac = { | |
150 | .index = 0, | |
151 | }; | |
0b9bf381 | 152 | struct afs_call *call; |
4584ae96 | 153 | bool in_progress = false; |
3bf0fb6f DH |
154 | |
155 | _enter("%s", server->name); | |
156 | ||
157 | read_lock(&server->lock); | |
158 | ac.alist = rcu_dereference_protected(server->addresses, | |
159 | lockdep_is_held(&server->lock)); | |
160 | read_unlock(&server->lock); | |
161 | ||
162 | atomic_set(&server->probe_outstanding, ac.alist->nr_addrs); | |
163 | memset(&server->probe, 0, sizeof(server->probe)); | |
164 | server->probe.rtt = UINT_MAX; | |
165 | ||
166 | for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) { | |
0b9bf381 DH |
167 | call = afs_vl_get_capabilities(net, &ac, key, server, |
168 | server_index); | |
169 | if (!IS_ERR(call)) { | |
170 | afs_put_call(call); | |
4584ae96 | 171 | in_progress = true; |
0b9bf381 DH |
172 | } else { |
173 | afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code); | |
b95b3094 | 174 | afs_done_one_vl_probe(server, false); |
0b9bf381 | 175 | } |
3bf0fb6f DH |
176 | } |
177 | ||
4584ae96 | 178 | return in_progress; |
3bf0fb6f DH |
179 | } |
180 | ||
181 | /* | |
182 | * Send off probes to all unprobed servers. | |
183 | */ | |
184 | int afs_send_vl_probes(struct afs_net *net, struct key *key, | |
185 | struct afs_vlserver_list *vllist) | |
186 | { | |
187 | struct afs_vlserver *server; | |
4584ae96 DH |
188 | struct afs_error e; |
189 | bool in_progress = false; | |
190 | int i; | |
3bf0fb6f | 191 | |
4584ae96 DH |
192 | e.error = 0; |
193 | e.responded = false; | |
3bf0fb6f DH |
194 | for (i = 0; i < vllist->nr_servers; i++) { |
195 | server = vllist->servers[i].server; | |
196 | if (test_bit(AFS_VLSERVER_FL_PROBED, &server->flags)) | |
197 | continue; | |
198 | ||
4584ae96 DH |
199 | if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, &server->flags) && |
200 | afs_do_probe_vlserver(net, server, key, i, &e)) | |
201 | in_progress = true; | |
3bf0fb6f DH |
202 | } |
203 | ||
4584ae96 | 204 | return in_progress ? 0 : e.error; |
3bf0fb6f DH |
205 | } |
206 | ||
207 | /* | |
208 | * Wait for the first as-yet untried server to respond. | |
209 | */ | |
210 | int afs_wait_for_vl_probes(struct afs_vlserver_list *vllist, | |
211 | unsigned long untried) | |
212 | { | |
213 | struct wait_queue_entry *waits; | |
214 | struct afs_vlserver *server; | |
b95b3094 | 215 | unsigned int rtt = UINT_MAX, rtt_s; |
3bf0fb6f DH |
216 | bool have_responders = false; |
217 | int pref = -1, i; | |
218 | ||
219 | _enter("%u,%lx", vllist->nr_servers, untried); | |
220 | ||
221 | /* Only wait for servers that have a probe outstanding. */ | |
222 | for (i = 0; i < vllist->nr_servers; i++) { | |
223 | if (test_bit(i, &untried)) { | |
224 | server = vllist->servers[i].server; | |
225 | if (!test_bit(AFS_VLSERVER_FL_PROBING, &server->flags)) | |
226 | __clear_bit(i, &untried); | |
fb72cd3d | 227 | if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) |
3bf0fb6f DH |
228 | have_responders = true; |
229 | } | |
230 | } | |
231 | if (have_responders || !untried) | |
232 | return 0; | |
233 | ||
234 | waits = kmalloc(array_size(vllist->nr_servers, sizeof(*waits)), GFP_KERNEL); | |
235 | if (!waits) | |
236 | return -ENOMEM; | |
237 | ||
238 | for (i = 0; i < vllist->nr_servers; i++) { | |
239 | if (test_bit(i, &untried)) { | |
240 | server = vllist->servers[i].server; | |
241 | init_waitqueue_entry(&waits[i], current); | |
242 | add_wait_queue(&server->probe_wq, &waits[i]); | |
243 | } | |
244 | } | |
245 | ||
246 | for (;;) { | |
247 | bool still_probing = false; | |
248 | ||
249 | set_current_state(TASK_INTERRUPTIBLE); | |
250 | for (i = 0; i < vllist->nr_servers; i++) { | |
251 | if (test_bit(i, &untried)) { | |
252 | server = vllist->servers[i].server; | |
fb72cd3d | 253 | if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) |
3bf0fb6f DH |
254 | goto stop; |
255 | if (test_bit(AFS_VLSERVER_FL_PROBING, &server->flags)) | |
256 | still_probing = true; | |
257 | } | |
258 | } | |
259 | ||
08d405c8 | 260 | if (!still_probing || signal_pending(current)) |
3bf0fb6f DH |
261 | goto stop; |
262 | schedule(); | |
263 | } | |
264 | ||
265 | stop: | |
266 | set_current_state(TASK_RUNNING); | |
267 | ||
268 | for (i = 0; i < vllist->nr_servers; i++) { | |
269 | if (test_bit(i, &untried)) { | |
270 | server = vllist->servers[i].server; | |
b95b3094 DH |
271 | rtt_s = READ_ONCE(server->rtt); |
272 | if (test_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags) && | |
273 | rtt_s < rtt) { | |
3bf0fb6f | 274 | pref = i; |
b95b3094 | 275 | rtt = rtt_s; |
3bf0fb6f DH |
276 | } |
277 | ||
278 | remove_wait_queue(&server->probe_wq, &waits[i]); | |
279 | } | |
280 | } | |
281 | ||
282 | kfree(waits); | |
283 | ||
284 | if (pref == -1 && signal_pending(current)) | |
285 | return -ERESTARTSYS; | |
286 | ||
287 | if (pref >= 0) | |
288 | vllist->preferred = pref; | |
289 | ||
290 | _leave(" = 0 [%u]", pref); | |
291 | return 0; | |
292 | } |