Commit | Line | Data |
---|---|---|
b4d0d230 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
0a5143f2 DH |
2 | /* Handle vlserver selection and rotation. |
3 | * | |
4 | * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. | |
5 | * Written by David Howells (dhowells@redhat.com) | |
0a5143f2 DH |
6 | */ |
7 | ||
8 | #include <linux/kernel.h> | |
9 | #include <linux/sched.h> | |
10 | #include <linux/sched/signal.h> | |
11 | #include "internal.h" | |
12 | #include "afs_vl.h" | |
13 | ||
14 | /* | |
15 | * Begin an operation on a volume location server. | |
16 | */ | |
17 | bool afs_begin_vlserver_operation(struct afs_vl_cursor *vc, struct afs_cell *cell, | |
18 | struct key *key) | |
19 | { | |
20 | memset(vc, 0, sizeof(*vc)); | |
21 | vc->cell = cell; | |
22 | vc->key = key; | |
23 | vc->error = -EDESTADDRREQ; | |
24 | vc->ac.error = SHRT_MAX; | |
25 | ||
26 | if (signal_pending(current)) { | |
27 | vc->error = -EINTR; | |
28 | vc->flags |= AFS_VL_CURSOR_STOP; | |
29 | return false; | |
30 | } | |
31 | ||
32 | return true; | |
33 | } | |
34 | ||
35 | /* | |
36 | * Begin iteration through a server list, starting with the last used server if | |
37 | * possible, or the last recorded good server if not. | |
38 | */ | |
39 | static bool afs_start_vl_iteration(struct afs_vl_cursor *vc) | |
40 | { | |
41 | struct afs_cell *cell = vc->cell; | |
d5c32c89 | 42 | unsigned int dns_lookup_count; |
0a5143f2 | 43 | |
d5c32c89 DH |
44 | if (cell->dns_source == DNS_RECORD_UNAVAILABLE || |
45 | cell->dns_expiry <= ktime_get_real_seconds()) { | |
46 | dns_lookup_count = smp_load_acquire(&cell->dns_lookup_count); | |
47 | set_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags); | |
48 | queue_work(afs_wq, &cell->manager); | |
49 | ||
50 | if (cell->dns_source == DNS_RECORD_UNAVAILABLE) { | |
51 | if (wait_var_event_interruptible( | |
52 | &cell->dns_lookup_count, | |
53 | smp_load_acquire(&cell->dns_lookup_count) | |
54 | != dns_lookup_count) < 0) { | |
55 | vc->error = -ERESTARTSYS; | |
56 | return false; | |
57 | } | |
58 | } | |
59 | ||
60 | /* Status load is ordered after lookup counter load */ | |
61 | if (cell->dns_source == DNS_RECORD_UNAVAILABLE) { | |
62 | vc->error = -EDESTADDRREQ; | |
63 | return false; | |
64 | } | |
0a5143f2 DH |
65 | } |
66 | ||
67 | read_lock(&cell->vl_servers_lock); | |
68 | vc->server_list = afs_get_vlserverlist( | |
69 | rcu_dereference_protected(cell->vl_servers, | |
70 | lockdep_is_held(&cell->vl_servers_lock))); | |
71 | read_unlock(&cell->vl_servers_lock); | |
ca1cbbdc | 72 | if (!vc->server_list->nr_servers) |
0a5143f2 DH |
73 | return false; |
74 | ||
3bf0fb6f DH |
75 | vc->untried = (1UL << vc->server_list->nr_servers) - 1; |
76 | vc->index = -1; | |
0a5143f2 DH |
77 | return true; |
78 | } | |
79 | ||
80 | /* | |
81 | * Select the vlserver to use. May be called multiple times to rotate | |
82 | * through the vlservers. | |
83 | */ | |
84 | bool afs_select_vlserver(struct afs_vl_cursor *vc) | |
85 | { | |
86 | struct afs_addr_list *alist; | |
87 | struct afs_vlserver *vlserver; | |
4584ae96 | 88 | struct afs_error e; |
3bf0fb6f | 89 | u32 rtt; |
4584ae96 | 90 | int error = vc->ac.error, i; |
0a5143f2 | 91 | |
3bf0fb6f DH |
92 | _enter("%lx[%d],%lx[%d],%d,%d", |
93 | vc->untried, vc->index, | |
94 | vc->ac.tried, vc->ac.index, | |
0a5143f2 DH |
95 | error, vc->ac.abort_code); |
96 | ||
97 | if (vc->flags & AFS_VL_CURSOR_STOP) { | |
98 | _leave(" = f [stopped]"); | |
99 | return false; | |
100 | } | |
101 | ||
744bcd71 DH |
102 | vc->nr_iterations++; |
103 | ||
0a5143f2 DH |
104 | /* Evaluate the result of the previous operation, if there was one. */ |
105 | switch (error) { | |
106 | case SHRT_MAX: | |
107 | goto start; | |
108 | ||
109 | default: | |
110 | case 0: | |
111 | /* Success or local failure. Stop. */ | |
112 | vc->error = error; | |
113 | vc->flags |= AFS_VL_CURSOR_STOP; | |
114 | _leave(" = f [okay/local %d]", vc->ac.error); | |
115 | return false; | |
116 | ||
117 | case -ECONNABORTED: | |
118 | /* The far side rejected the operation on some grounds. This | |
119 | * might involve the server being busy or the volume having been moved. | |
120 | */ | |
121 | switch (vc->ac.abort_code) { | |
122 | case AFSVL_IO: | |
123 | case AFSVL_BADVOLOPER: | |
124 | case AFSVL_NOMEM: | |
125 | /* The server went weird. */ | |
126 | vc->error = -EREMOTEIO; | |
127 | //write_lock(&vc->cell->vl_servers_lock); | |
128 | //vc->server_list->weird_mask |= 1 << vc->index; | |
129 | //write_unlock(&vc->cell->vl_servers_lock); | |
130 | goto next_server; | |
131 | ||
132 | default: | |
133 | vc->error = afs_abort_to_error(vc->ac.abort_code); | |
134 | goto failed; | |
135 | } | |
136 | ||
4584ae96 DH |
137 | case -ERFKILL: |
138 | case -EADDRNOTAVAIL: | |
0a5143f2 DH |
139 | case -ENETUNREACH: |
140 | case -EHOSTUNREACH: | |
4584ae96 | 141 | case -EHOSTDOWN: |
0a5143f2 DH |
142 | case -ECONNREFUSED: |
143 | case -ETIMEDOUT: | |
144 | case -ETIME: | |
145 | _debug("no conn %d", error); | |
146 | vc->error = error; | |
147 | goto iterate_address; | |
148 | ||
149 | case -ECONNRESET: | |
150 | _debug("call reset"); | |
151 | vc->error = error; | |
152 | vc->flags |= AFS_VL_CURSOR_RETRY; | |
153 | goto next_server; | |
154 | } | |
155 | ||
156 | restart_from_beginning: | |
157 | _debug("restart"); | |
158 | afs_end_cursor(&vc->ac); | |
159 | afs_put_vlserverlist(vc->cell->net, vc->server_list); | |
160 | vc->server_list = NULL; | |
161 | if (vc->flags & AFS_VL_CURSOR_RETRIED) | |
162 | goto failed; | |
163 | vc->flags |= AFS_VL_CURSOR_RETRIED; | |
164 | start: | |
165 | _debug("start"); | |
166 | ||
0a5143f2 DH |
167 | if (!afs_start_vl_iteration(vc)) |
168 | goto failed; | |
169 | ||
3bf0fb6f DH |
170 | error = afs_send_vl_probes(vc->cell->net, vc->key, vc->server_list); |
171 | if (error < 0) | |
172 | goto failed_set_error; | |
173 | ||
174 | pick_server: | |
175 | _debug("pick [%lx]", vc->untried); | |
176 | ||
177 | error = afs_wait_for_vl_probes(vc->server_list, vc->untried); | |
178 | if (error < 0) | |
179 | goto failed_set_error; | |
180 | ||
181 | /* Pick the untried server with the lowest RTT. */ | |
182 | vc->index = vc->server_list->preferred; | |
183 | if (test_bit(vc->index, &vc->untried)) | |
184 | goto selected_server; | |
185 | ||
186 | vc->index = -1; | |
187 | rtt = U32_MAX; | |
188 | for (i = 0; i < vc->server_list->nr_servers; i++) { | |
189 | struct afs_vlserver *s = vc->server_list->servers[i].server; | |
190 | ||
191 | if (!test_bit(i, &vc->untried) || !s->probe.responded) | |
192 | continue; | |
193 | if (s->probe.rtt < rtt) { | |
194 | vc->index = i; | |
195 | rtt = s->probe.rtt; | |
196 | } | |
197 | } | |
198 | ||
199 | if (vc->index == -1) | |
200 | goto no_more_servers; | |
201 | ||
202 | selected_server: | |
203 | _debug("use %d", vc->index); | |
204 | __clear_bit(vc->index, &vc->untried); | |
205 | ||
0a5143f2 DH |
206 | /* We're starting on a different vlserver from the list. We need to |
207 | * check it, find its address list and probe its capabilities before we | |
208 | * use it. | |
209 | */ | |
210 | ASSERTCMP(vc->ac.alist, ==, NULL); | |
211 | vlserver = vc->server_list->servers[vc->index].server; | |
3bf0fb6f | 212 | vc->server = vlserver; |
0a5143f2 DH |
213 | |
214 | _debug("USING VLSERVER: %s", vlserver->name); | |
215 | ||
216 | read_lock(&vlserver->lock); | |
217 | alist = rcu_dereference_protected(vlserver->addresses, | |
218 | lockdep_is_held(&vlserver->lock)); | |
219 | afs_get_addrlist(alist); | |
220 | read_unlock(&vlserver->lock); | |
221 | ||
222 | memset(&vc->ac, 0, sizeof(vc->ac)); | |
223 | ||
0a5143f2 DH |
224 | if (!vc->ac.alist) |
225 | vc->ac.alist = alist; | |
226 | else | |
227 | afs_put_addrlist(alist); | |
228 | ||
3bf0fb6f | 229 | vc->ac.index = -1; |
0a5143f2 DH |
230 | |
231 | iterate_address: | |
232 | ASSERT(vc->ac.alist); | |
0a5143f2 DH |
233 | /* Iterate over the current server's address list to try and find an |
234 | * address on which it will respond to us. | |
235 | */ | |
236 | if (!afs_iterate_addresses(&vc->ac)) | |
237 | goto next_server; | |
238 | ||
3bf0fb6f DH |
239 | _debug("VL address %d/%d", vc->ac.index, vc->ac.alist->nr_addrs); |
240 | ||
2feeaf84 | 241 | _leave(" = t %pISpc", &vc->ac.alist->addrs[vc->ac.index].transport); |
0a5143f2 DH |
242 | return true; |
243 | ||
244 | next_server: | |
245 | _debug("next"); | |
246 | afs_end_cursor(&vc->ac); | |
3bf0fb6f | 247 | goto pick_server; |
0a5143f2 | 248 | |
3bf0fb6f | 249 | no_more_servers: |
0a5143f2 DH |
250 | /* That's all the servers poked to no good effect. Try again if some |
251 | * of them were busy. | |
252 | */ | |
253 | if (vc->flags & AFS_VL_CURSOR_RETRY) | |
254 | goto restart_from_beginning; | |
255 | ||
4584ae96 DH |
256 | e.error = -EDESTADDRREQ; |
257 | e.responded = false; | |
3bf0fb6f DH |
258 | for (i = 0; i < vc->server_list->nr_servers; i++) { |
259 | struct afs_vlserver *s = vc->server_list->servers[i].server; | |
3bf0fb6f | 260 | |
4584ae96 DH |
261 | afs_prioritise_error(&e, READ_ONCE(s->probe.error), |
262 | s->probe.abort_code); | |
3bf0fb6f DH |
263 | } |
264 | ||
3bf0fb6f DH |
265 | failed_set_error: |
266 | vc->error = error; | |
0a5143f2 DH |
267 | failed: |
268 | vc->flags |= AFS_VL_CURSOR_STOP; | |
269 | afs_end_cursor(&vc->ac); | |
270 | _leave(" = f [failed %d]", vc->error); | |
271 | return false; | |
272 | } | |
273 | ||
744bcd71 DH |
274 | /* |
275 | * Dump cursor state in the case of the error being EDESTADDRREQ. | |
276 | */ | |
277 | static void afs_vl_dump_edestaddrreq(const struct afs_vl_cursor *vc) | |
278 | { | |
279 | static int count; | |
280 | int i; | |
281 | ||
282 | if (!IS_ENABLED(CONFIG_AFS_DEBUG_CURSOR) || count > 3) | |
283 | return; | |
284 | count++; | |
285 | ||
286 | rcu_read_lock(); | |
287 | pr_notice("EDESTADDR occurred\n"); | |
3bf0fb6f DH |
288 | pr_notice("VC: ut=%lx ix=%u ni=%hu fl=%hx err=%hd\n", |
289 | vc->untried, vc->index, vc->nr_iterations, vc->flags, vc->error); | |
744bcd71 DH |
290 | |
291 | if (vc->server_list) { | |
292 | const struct afs_vlserver_list *sl = vc->server_list; | |
293 | pr_notice("VC: SL nr=%u ix=%u\n", | |
294 | sl->nr_servers, sl->index); | |
295 | for (i = 0; i < sl->nr_servers; i++) { | |
296 | const struct afs_vlserver *s = sl->servers[i].server; | |
3bf0fb6f DH |
297 | pr_notice("VC: server %s+%hu fl=%lx E=%hd\n", |
298 | s->name, s->port, s->flags, s->probe.error); | |
744bcd71 DH |
299 | if (s->addresses) { |
300 | const struct afs_addr_list *a = | |
301 | rcu_dereference(s->addresses); | |
3bf0fb6f | 302 | pr_notice("VC: - nr=%u/%u/%u pf=%u\n", |
744bcd71 | 303 | a->nr_ipv4, a->nr_addrs, a->max_addrs, |
3bf0fb6f DH |
304 | a->preferred); |
305 | pr_notice("VC: - pr=%lx R=%lx F=%lx\n", | |
306 | a->probed, a->responded, a->failed); | |
744bcd71 DH |
307 | if (a == vc->ac.alist) |
308 | pr_notice("VC: - current\n"); | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
3bf0fb6f DH |
313 | pr_notice("AC: t=%lx ax=%u ac=%d er=%d r=%u ni=%u\n", |
314 | vc->ac.tried, vc->ac.index, vc->ac.abort_code, vc->ac.error, | |
315 | vc->ac.responded, vc->ac.nr_iterations); | |
744bcd71 DH |
316 | rcu_read_unlock(); |
317 | } | |
318 | ||
0a5143f2 DH |
319 | /* |
320 | * Tidy up a volume location server cursor and unlock the vnode. | |
321 | */ | |
322 | int afs_end_vlserver_operation(struct afs_vl_cursor *vc) | |
323 | { | |
324 | struct afs_net *net = vc->cell->net; | |
325 | ||
744bcd71 | 326 | if (vc->error == -EDESTADDRREQ || |
4584ae96 | 327 | vc->error == -EADDRNOTAVAIL || |
744bcd71 DH |
328 | vc->error == -ENETUNREACH || |
329 | vc->error == -EHOSTUNREACH) | |
330 | afs_vl_dump_edestaddrreq(vc); | |
331 | ||
0a5143f2 DH |
332 | afs_end_cursor(&vc->ac); |
333 | afs_put_vlserverlist(net, vc->server_list); | |
334 | ||
335 | if (vc->error == -ECONNABORTED) | |
336 | vc->error = afs_abort_to_error(vc->ac.abort_code); | |
337 | ||
338 | return vc->error; | |
339 | } |