1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* AFS vlserver probing |
3 | * |
4 | * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. |
5 | * Written by David Howells (dhowells@redhat.com) |
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 | |
14 | |
15 | /* |
16 | * Handle the completion of a set of probes. |
17 | */ |
18 | static void afs_finished_vl_probe(struct afs_vlserver *server) |
19 | { |
20 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) { |
21 | server->rtt = UINT_MAX; |
22 | clear_bit(AFS_VLSERVER_FL_RESPONDING, addr: &server->flags); |
23 | } |
24 | |
25 | clear_bit_unlock(AFS_VLSERVER_FL_PROBING, addr: &server->flags); |
26 | wake_up_bit(word: &server->flags, AFS_VLSERVER_FL_PROBING); |
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(v: &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); |
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->vl_probe; |
50 | struct afs_vlserver *server = call->vlserver; |
51 | struct afs_address *addr = &alist->addrs[call->probe_index]; |
52 | unsigned int server_index = call->server_index; |
53 | unsigned int rtt_us = 0; |
54 | unsigned int index = call->probe_index; |
55 | bool have_result = false; |
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(lock: &server->probe_lock); |
61 | |
62 | switch (ret) { |
63 | case 0: |
64 | server->probe.error = 0; |
65 | goto responded; |
66 | case -ECONNABORTED: |
67 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED)) { |
68 | server->probe.abort_code = call->abort_code; |
69 | server->probe.error = ret; |
70 | } |
71 | goto responded; |
72 | case -ENOMEM: |
73 | case -ENONET: |
74 | case -EKEYEXPIRED: |
75 | case -EKEYREVOKED: |
76 | case -EKEYREJECTED: |
77 | server->probe.flags |= AFS_VLSERVER_PROBE_LOCAL_FAILURE; |
78 | if (server->probe.error == 0) |
79 | server->probe.error = ret; |
80 | trace_afs_io_error(call: call->debug_id, error: ret, where: afs_io_error_vl_probe_fail); |
81 | goto out; |
82 | case -ECONNRESET: /* Responded, but call expired. */ |
83 | case -ERFKILL: |
84 | case -EADDRNOTAVAIL: |
85 | case -ENETUNREACH: |
86 | case -EHOSTUNREACH: |
87 | case -EHOSTDOWN: |
88 | case -ECONNREFUSED: |
89 | case -ETIMEDOUT: |
90 | case -ETIME: |
91 | default: |
92 | clear_bit(nr: index, addr: &alist->responded); |
93 | set_bit(nr: index, addr: &alist->probe_failed); |
94 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) && |
95 | (server->probe.error == 0 || |
96 | server->probe.error == -ETIMEDOUT || |
97 | server->probe.error == -ETIME)) |
98 | server->probe.error = ret; |
99 | trace_afs_io_error(call: call->debug_id, error: ret, where: afs_io_error_vl_probe_fail); |
100 | goto out; |
101 | } |
102 | |
103 | responded: |
104 | set_bit(nr: index, addr: &alist->responded); |
105 | clear_bit(nr: index, addr: &alist->probe_failed); |
106 | |
107 | if (call->service_id == YFS_VL_SERVICE) { |
108 | server->probe.flags |= AFS_VLSERVER_PROBE_IS_YFS; |
109 | set_bit(AFS_VLSERVER_FL_IS_YFS, addr: &server->flags); |
110 | server->service_id = call->service_id; |
111 | } else { |
112 | server->probe.flags |= AFS_VLSERVER_PROBE_NOT_YFS; |
113 | if (!(server->probe.flags & AFS_VLSERVER_PROBE_IS_YFS)) { |
114 | clear_bit(AFS_VLSERVER_FL_IS_YFS, addr: &server->flags); |
115 | server->service_id = call->service_id; |
116 | } |
117 | } |
118 | |
119 | rtt_us = rxrpc_kernel_get_srtt(addr->peer); |
120 | if (rtt_us < server->probe.rtt) { |
121 | server->probe.rtt = rtt_us; |
122 | server->rtt = rtt_us; |
123 | alist->preferred = index; |
124 | } |
125 | |
126 | smp_wmb(); /* Set rtt before responded. */ |
127 | server->probe.flags |= AFS_VLSERVER_PROBE_RESPONDED; |
128 | set_bit(AFS_VLSERVER_FL_PROBED, addr: &server->flags); |
129 | set_bit(AFS_VLSERVER_FL_RESPONDING, addr: &server->flags); |
130 | have_result = true; |
131 | out: |
132 | spin_unlock(lock: &server->probe_lock); |
133 | |
134 | trace_afs_vl_probe(server, tx: false, alist, addr_index: index, error: call->error, abort_code: call->abort_code, rtt_us); |
135 | _debug("probe [%u][%u] %pISpc rtt=%d ret=%d" , |
136 | server_index, index, rxrpc_kernel_remote_addr(addr->peer), |
137 | rtt_us, ret); |
138 | |
139 | afs_done_one_vl_probe(server, wake_up: have_result); |
140 | } |
141 | |
142 | /* |
143 | * Probe all of a vlserver's addresses to find out the best route and to |
144 | * query its capabilities. |
145 | */ |
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) |
151 | { |
152 | struct afs_addr_list *alist; |
153 | struct afs_call *call; |
154 | unsigned long unprobed; |
155 | unsigned int index, i; |
156 | bool in_progress = false; |
157 | int best_prio; |
158 | |
159 | _enter("%s" , server->name); |
160 | |
161 | read_lock(&server->lock); |
162 | alist = rcu_dereference_protected(server->addresses, |
163 | lockdep_is_held(&server->lock)); |
164 | afs_get_addrlist(alist, reason: afs_alist_trace_get_vlprobe); |
165 | read_unlock(&server->lock); |
166 | |
167 | atomic_set(v: &server->probe_outstanding, i: alist->nr_addrs); |
168 | memset(&server->probe, 0, sizeof(server->probe)); |
169 | server->probe.rtt = UINT_MAX; |
170 | |
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, tx: true, alist, addr_index: index, error: 0, abort_code: 0, rtt_us: 0); |
185 | call = afs_vl_get_capabilities(net, alist, addr_index: index, key, server, |
186 | server_index); |
187 | if (!IS_ERR(ptr: call)) { |
188 | afs_prioritise_error(_e, call->error, call->abort_code); |
189 | afs_put_call(call); |
190 | in_progress = true; |
191 | } else { |
192 | afs_prioritise_error(_e, PTR_ERR(ptr: call), 0); |
193 | afs_done_one_vl_probe(server, wake_up: false); |
194 | } |
195 | } |
196 | |
197 | afs_put_addrlist(alist, reason: afs_alist_trace_put_vlprobe); |
198 | return in_progress; |
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; |
208 | struct afs_error e = {}; |
209 | bool in_progress = false; |
210 | int i; |
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 | |
217 | if (!test_and_set_bit_lock(AFS_VLSERVER_FL_PROBING, addr: &server->flags) && |
218 | afs_do_probe_vlserver(net, server, key, server_index: i, e: &e)) |
219 | in_progress = true; |
220 | } |
221 | |
222 | return in_progress ? 0 : e.error; |
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; |
233 | unsigned int rtt = UINT_MAX, rtt_s; |
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); |
245 | if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) |
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(wq_entry: &waits[i], current); |
260 | add_wait_queue(wq_head: &server->probe_wq, wq_entry: &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; |
271 | if (server->probe.flags & AFS_VLSERVER_PROBE_RESPONDED) |
272 | goto stop; |
273 | if (test_bit(AFS_VLSERVER_FL_PROBING, &server->flags)) |
274 | still_probing = true; |
275 | } |
276 | } |
277 | |
278 | if (!still_probing || signal_pending(current)) |
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; |
289 | rtt_s = READ_ONCE(server->rtt); |
290 | if (test_bit(AFS_VLSERVER_FL_RESPONDING, &server->flags) && |
291 | rtt_s < rtt) { |
292 | pref = i; |
293 | rtt = rtt_s; |
294 | } |
295 | |
296 | remove_wait_queue(wq_head: &server->probe_wq, wq_entry: &waits[i]); |
297 | } |
298 | } |
299 | |
300 | kfree(objp: 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 | } |
311 | |