]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ec26815a | 2 | /* AFS server record management |
1da177e4 | 3 | * |
08e0e7c8 | 4 | * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. |
1da177e4 | 5 | * Written by David Howells (dhowells@redhat.com) |
1da177e4 LT |
6 | */ |
7 | ||
8 | #include <linux/sched.h> | |
9 | #include <linux/slab.h> | |
4d9df986 | 10 | #include "afs_fs.h" |
1da177e4 | 11 | #include "internal.h" |
30062bd1 | 12 | #include "protocol_yfs.h" |
1da177e4 | 13 | |
d2ddc776 DH |
14 | static unsigned afs_server_gc_delay = 10; /* Server record timeout in seconds */ |
15 | static unsigned afs_server_update_delay = 30; /* Time till VLDB recheck in secs */ | |
45218193 | 16 | static atomic_t afs_server_debug_id; |
1da177e4 | 17 | |
59fa1c4a DH |
18 | static void afs_inc_servers_outstanding(struct afs_net *net) |
19 | { | |
20 | atomic_inc(&net->servers_outstanding); | |
21 | } | |
22 | ||
23 | static void afs_dec_servers_outstanding(struct afs_net *net) | |
24 | { | |
25 | if (atomic_dec_and_test(&net->servers_outstanding)) | |
ab1fbe32 | 26 | wake_up_var(&net->servers_outstanding); |
59fa1c4a DH |
27 | } |
28 | ||
d2ddc776 DH |
29 | /* |
30 | * Find a server by one of its addresses. | |
31 | */ | |
32 | struct afs_server *afs_find_server(struct afs_net *net, | |
33 | const struct sockaddr_rxrpc *srx) | |
59fa1c4a | 34 | { |
d2ddc776 DH |
35 | const struct afs_addr_list *alist; |
36 | struct afs_server *server = NULL; | |
37 | unsigned int i; | |
d2ddc776 DH |
38 | int seq = 0, diff; |
39 | ||
d2ddc776 DH |
40 | rcu_read_lock(); |
41 | ||
42 | do { | |
43 | if (server) | |
45218193 | 44 | afs_put_server(net, server, afs_server_trace_put_find_rsq); |
d2ddc776 DH |
45 | server = NULL; |
46 | read_seqbegin_or_lock(&net->fs_addr_lock, &seq); | |
47 | ||
9bd0160d MD |
48 | if (srx->transport.family == AF_INET6) { |
49 | const struct sockaddr_in6 *a = &srx->transport.sin6, *b; | |
d2ddc776 DH |
50 | hlist_for_each_entry_rcu(server, &net->fs_addresses6, addr6_link) { |
51 | alist = rcu_dereference(server->addresses); | |
52 | for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { | |
53 | b = &alist->addrs[i].transport.sin6; | |
fe342cf7 DH |
54 | diff = ((u16 __force)a->sin6_port - |
55 | (u16 __force)b->sin6_port); | |
d2ddc776 DH |
56 | if (diff == 0) |
57 | diff = memcmp(&a->sin6_addr, | |
58 | &b->sin6_addr, | |
59 | sizeof(struct in6_addr)); | |
60 | if (diff == 0) | |
61 | goto found; | |
d2ddc776 DH |
62 | } |
63 | } | |
64 | } else { | |
9bd0160d | 65 | const struct sockaddr_in *a = &srx->transport.sin, *b; |
d2ddc776 DH |
66 | hlist_for_each_entry_rcu(server, &net->fs_addresses4, addr4_link) { |
67 | alist = rcu_dereference(server->addresses); | |
68 | for (i = 0; i < alist->nr_ipv4; i++) { | |
9bd0160d MD |
69 | b = &alist->addrs[i].transport.sin; |
70 | diff = ((u16 __force)a->sin_port - | |
71 | (u16 __force)b->sin_port); | |
d2ddc776 | 72 | if (diff == 0) |
9bd0160d MD |
73 | diff = ((u32 __force)a->sin_addr.s_addr - |
74 | (u32 __force)b->sin_addr.s_addr); | |
d2ddc776 DH |
75 | if (diff == 0) |
76 | goto found; | |
d2ddc776 DH |
77 | } |
78 | } | |
79 | } | |
59fa1c4a | 80 | |
d2ddc776 DH |
81 | server = NULL; |
82 | found: | |
83 | if (server && !atomic_inc_not_zero(&server->usage)) | |
84 | server = NULL; | |
85 | ||
86 | } while (need_seqretry(&net->fs_addr_lock, seq)); | |
87 | ||
88 | done_seqretry(&net->fs_addr_lock, seq); | |
89 | ||
90 | rcu_read_unlock(); | |
91 | return server; | |
59fa1c4a DH |
92 | } |
93 | ||
08e0e7c8 | 94 | /* |
d2ddc776 | 95 | * Look up a server by its UUID |
08e0e7c8 | 96 | */ |
d2ddc776 | 97 | struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uuid) |
1da177e4 | 98 | { |
d2ddc776 DH |
99 | struct afs_server *server = NULL; |
100 | struct rb_node *p; | |
101 | int diff, seq = 0; | |
102 | ||
103 | _enter("%pU", uuid); | |
104 | ||
105 | do { | |
106 | /* Unfortunately, rbtree walking doesn't give reliable results | |
107 | * under just the RCU read lock, so we have to check for | |
108 | * changes. | |
109 | */ | |
110 | if (server) | |
45218193 | 111 | afs_put_server(net, server, afs_server_trace_put_uuid_rsq); |
d2ddc776 DH |
112 | server = NULL; |
113 | ||
114 | read_seqbegin_or_lock(&net->fs_lock, &seq); | |
115 | ||
116 | p = net->fs_servers.rb_node; | |
117 | while (p) { | |
118 | server = rb_entry(p, struct afs_server, uuid_rb); | |
119 | ||
120 | diff = memcmp(uuid, &server->uuid, sizeof(*uuid)); | |
121 | if (diff < 0) { | |
122 | p = p->rb_left; | |
123 | } else if (diff > 0) { | |
124 | p = p->rb_right; | |
125 | } else { | |
45218193 | 126 | afs_get_server(server, afs_server_trace_get_by_uuid); |
d2ddc776 DH |
127 | break; |
128 | } | |
129 | ||
130 | server = NULL; | |
131 | } | |
132 | } while (need_seqretry(&net->fs_lock, seq)); | |
133 | ||
134 | done_seqretry(&net->fs_lock, seq); | |
135 | ||
136 | _leave(" = %p", server); | |
137 | return server; | |
138 | } | |
139 | ||
140 | /* | |
141 | * Install a server record in the namespace tree | |
142 | */ | |
143 | static struct afs_server *afs_install_server(struct afs_net *net, | |
144 | struct afs_server *candidate) | |
145 | { | |
146 | const struct afs_addr_list *alist; | |
147 | struct afs_server *server; | |
08e0e7c8 | 148 | struct rb_node **pp, *p; |
4fe171bb | 149 | int diff; |
08e0e7c8 | 150 | |
d2ddc776 | 151 | _enter("%p", candidate); |
1da177e4 | 152 | |
d2ddc776 | 153 | write_seqlock(&net->fs_lock); |
08e0e7c8 | 154 | |
d2ddc776 DH |
155 | /* Firstly install the server in the UUID lookup tree */ |
156 | pp = &net->fs_servers.rb_node; | |
08e0e7c8 DH |
157 | p = NULL; |
158 | while (*pp) { | |
159 | p = *pp; | |
160 | _debug("- consider %p", p); | |
d2ddc776 DH |
161 | server = rb_entry(p, struct afs_server, uuid_rb); |
162 | diff = memcmp(&candidate->uuid, &server->uuid, sizeof(uuid_t)); | |
4d9df986 | 163 | if (diff < 0) |
08e0e7c8 | 164 | pp = &(*pp)->rb_left; |
4d9df986 | 165 | else if (diff > 0) |
08e0e7c8 DH |
166 | pp = &(*pp)->rb_right; |
167 | else | |
d2ddc776 | 168 | goto exists; |
08e0e7c8 | 169 | } |
1da177e4 | 170 | |
d2ddc776 DH |
171 | server = candidate; |
172 | rb_link_node(&server->uuid_rb, p, pp); | |
173 | rb_insert_color(&server->uuid_rb, &net->fs_servers); | |
174 | hlist_add_head_rcu(&server->proc_link, &net->fs_proc); | |
175 | ||
176 | write_seqlock(&net->fs_addr_lock); | |
177 | alist = rcu_dereference_protected(server->addresses, | |
178 | lockdep_is_held(&net->fs_addr_lock.lock)); | |
179 | ||
180 | /* Secondly, if the server has any IPv4 and/or IPv6 addresses, install | |
181 | * it in the IPv4 and/or IPv6 reverse-map lists. | |
182 | * | |
183 | * TODO: For speed we want to use something other than a flat list | |
184 | * here; even sorting the list in terms of lowest address would help a | |
185 | * bit, but anything we might want to do gets messy and memory | |
186 | * intensive. | |
187 | */ | |
188 | if (alist->nr_ipv4 > 0) | |
189 | hlist_add_head_rcu(&server->addr4_link, &net->fs_addresses4); | |
190 | if (alist->nr_addrs > alist->nr_ipv4) | |
191 | hlist_add_head_rcu(&server->addr6_link, &net->fs_addresses6); | |
192 | ||
193 | write_sequnlock(&net->fs_addr_lock); | |
1da177e4 | 194 | |
d2ddc776 | 195 | exists: |
45218193 | 196 | afs_get_server(server, afs_server_trace_get_install); |
d2ddc776 DH |
197 | write_sequnlock(&net->fs_lock); |
198 | return server; | |
08e0e7c8 | 199 | } |
1da177e4 | 200 | |
1da177e4 | 201 | /* |
08e0e7c8 | 202 | * allocate a new server record |
1da177e4 | 203 | */ |
d2ddc776 DH |
204 | static struct afs_server *afs_alloc_server(struct afs_net *net, |
205 | const uuid_t *uuid, | |
206 | struct afs_addr_list *alist) | |
1da177e4 | 207 | { |
08e0e7c8 | 208 | struct afs_server *server; |
1da177e4 | 209 | |
08e0e7c8 | 210 | _enter(""); |
1da177e4 | 211 | |
b593e48d | 212 | server = kzalloc(sizeof(struct afs_server), GFP_KERNEL); |
8b2a464c DH |
213 | if (!server) |
214 | goto enomem; | |
8b2a464c DH |
215 | |
216 | atomic_set(&server->usage, 1); | |
45218193 | 217 | server->debug_id = atomic_inc_return(&afs_server_debug_id); |
d2ddc776 DH |
218 | RCU_INIT_POINTER(server->addresses, alist); |
219 | server->addr_version = alist->version; | |
220 | server->uuid = *uuid; | |
d2ddc776 DH |
221 | server->update_at = ktime_get_real_seconds() + afs_server_update_delay; |
222 | rwlock_init(&server->fs_lock); | |
47ea0f2e | 223 | INIT_HLIST_HEAD(&server->cb_volumes); |
8b2a464c | 224 | rwlock_init(&server->cb_break_lock); |
3bf0fb6f DH |
225 | init_waitqueue_head(&server->probe_wq); |
226 | spin_lock_init(&server->probe_lock); | |
8b2a464c | 227 | |
d2ddc776 | 228 | afs_inc_servers_outstanding(net); |
45218193 | 229 | trace_afs_server(server, 1, afs_server_trace_alloc); |
d2ddc776 | 230 | _leave(" = %p", server); |
08e0e7c8 | 231 | return server; |
8b2a464c | 232 | |
8b2a464c DH |
233 | enomem: |
234 | _leave(" = NULL [nomem]"); | |
235 | return NULL; | |
08e0e7c8 | 236 | } |
1da177e4 | 237 | |
08e0e7c8 | 238 | /* |
d2ddc776 | 239 | * Look up an address record for a server |
08e0e7c8 | 240 | */ |
d2ddc776 DH |
241 | static struct afs_addr_list *afs_vl_lookup_addrs(struct afs_cell *cell, |
242 | struct key *key, const uuid_t *uuid) | |
08e0e7c8 | 243 | { |
0a5143f2 DH |
244 | struct afs_vl_cursor vc; |
245 | struct afs_addr_list *alist = NULL; | |
d2ddc776 DH |
246 | int ret; |
247 | ||
0a5143f2 DH |
248 | ret = -ERESTARTSYS; |
249 | if (afs_begin_vlserver_operation(&vc, cell, key)) { | |
250 | while (afs_select_vlserver(&vc)) { | |
3bf0fb6f | 251 | if (test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) |
0a5143f2 DH |
252 | alist = afs_yfsvl_get_endpoints(&vc, uuid); |
253 | else | |
254 | alist = afs_vl_get_addrs_u(&vc, uuid); | |
d2ddc776 | 255 | } |
0a5143f2 DH |
256 | |
257 | ret = afs_end_vlserver_operation(&vc); | |
08e0e7c8 | 258 | } |
08e0e7c8 | 259 | |
0a5143f2 | 260 | return ret < 0 ? ERR_PTR(ret) : alist; |
08e0e7c8 | 261 | } |
1da177e4 | 262 | |
08e0e7c8 | 263 | /* |
d2ddc776 | 264 | * Get or create a fileserver record. |
08e0e7c8 | 265 | */ |
d2ddc776 DH |
266 | struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, |
267 | const uuid_t *uuid) | |
08e0e7c8 | 268 | { |
d2ddc776 DH |
269 | struct afs_addr_list *alist; |
270 | struct afs_server *server, *candidate; | |
1da177e4 | 271 | |
d2ddc776 | 272 | _enter("%p,%pU", cell->net, uuid); |
8324f0bc | 273 | |
d2ddc776 DH |
274 | server = afs_find_server_by_uuid(cell->net, uuid); |
275 | if (server) | |
276 | return server; | |
1da177e4 | 277 | |
d2ddc776 DH |
278 | alist = afs_vl_lookup_addrs(cell, key, uuid); |
279 | if (IS_ERR(alist)) | |
280 | return ERR_CAST(alist); | |
1da177e4 | 281 | |
d2ddc776 DH |
282 | candidate = afs_alloc_server(cell->net, uuid, alist); |
283 | if (!candidate) { | |
284 | afs_put_addrlist(alist); | |
285 | return ERR_PTR(-ENOMEM); | |
286 | } | |
1da177e4 | 287 | |
d2ddc776 DH |
288 | server = afs_install_server(cell->net, candidate); |
289 | if (server != candidate) { | |
290 | afs_put_addrlist(alist); | |
291 | kfree(candidate); | |
08e0e7c8 | 292 | } |
1da177e4 | 293 | |
d2ddc776 | 294 | _leave(" = %p{%d}", server, atomic_read(&server->usage)); |
08e0e7c8 | 295 | return server; |
ec26815a | 296 | } |
1da177e4 | 297 | |
d2ddc776 DH |
298 | /* |
299 | * Set the server timer to fire after a given delay, assuming it's not already | |
300 | * set for an earlier time. | |
301 | */ | |
59fa1c4a DH |
302 | static void afs_set_server_timer(struct afs_net *net, time64_t delay) |
303 | { | |
59fa1c4a | 304 | if (net->live) { |
d2ddc776 DH |
305 | afs_inc_servers_outstanding(net); |
306 | if (timer_reduce(&net->fs_timer, jiffies + delay * HZ)) | |
59fa1c4a DH |
307 | afs_dec_servers_outstanding(net); |
308 | } | |
309 | } | |
310 | ||
1da177e4 | 311 | /* |
d2ddc776 DH |
312 | * Server management timer. We have an increment on fs_outstanding that we |
313 | * need to pass along to the work item. | |
314 | */ | |
315 | void afs_servers_timer(struct timer_list *timer) | |
316 | { | |
317 | struct afs_net *net = container_of(timer, struct afs_net, fs_timer); | |
318 | ||
319 | _enter(""); | |
320 | if (!queue_work(afs_wq, &net->fs_manager)) | |
321 | afs_dec_servers_outstanding(net); | |
322 | } | |
323 | ||
45218193 DH |
324 | /* |
325 | * Get a reference on a server object. | |
326 | */ | |
327 | struct afs_server *afs_get_server(struct afs_server *server, | |
328 | enum afs_server_trace reason) | |
329 | { | |
330 | unsigned int u = atomic_inc_return(&server->usage); | |
331 | ||
332 | trace_afs_server(server, u, reason); | |
333 | return server; | |
334 | } | |
335 | ||
d2ddc776 DH |
336 | /* |
337 | * Release a reference on a server record. | |
1da177e4 | 338 | */ |
45218193 DH |
339 | void afs_put_server(struct afs_net *net, struct afs_server *server, |
340 | enum afs_server_trace reason) | |
1da177e4 | 341 | { |
d2ddc776 DH |
342 | unsigned int usage; |
343 | ||
1da177e4 LT |
344 | if (!server) |
345 | return; | |
346 | ||
d2ddc776 | 347 | server->put_time = ktime_get_real_seconds(); |
1da177e4 | 348 | |
d2ddc776 | 349 | usage = atomic_dec_return(&server->usage); |
260a9803 | 350 | |
45218193 | 351 | trace_afs_server(server, usage, reason); |
1da177e4 | 352 | |
d2ddc776 | 353 | if (likely(usage > 0)) |
1da177e4 | 354 | return; |
1da177e4 | 355 | |
d2ddc776 DH |
356 | afs_set_server_timer(net, afs_server_gc_delay); |
357 | } | |
358 | ||
359 | static void afs_server_rcu(struct rcu_head *rcu) | |
360 | { | |
361 | struct afs_server *server = container_of(rcu, struct afs_server, rcu); | |
362 | ||
45218193 DH |
363 | trace_afs_server(server, atomic_read(&server->usage), |
364 | afs_server_trace_free); | |
fe342cf7 | 365 | afs_put_addrlist(rcu_access_pointer(server->addresses)); |
d2ddc776 | 366 | kfree(server); |
ec26815a | 367 | } |
1da177e4 | 368 | |
1da177e4 | 369 | /* |
08e0e7c8 | 370 | * destroy a dead server |
1da177e4 | 371 | */ |
59fa1c4a | 372 | static void afs_destroy_server(struct afs_net *net, struct afs_server *server) |
1da177e4 | 373 | { |
fe342cf7 | 374 | struct afs_addr_list *alist = rcu_access_pointer(server->addresses); |
8b2a464c DH |
375 | struct afs_addr_cursor ac = { |
376 | .alist = alist, | |
3bf0fb6f | 377 | .index = alist->preferred, |
8b2a464c DH |
378 | .error = 0, |
379 | }; | |
45218193 DH |
380 | |
381 | trace_afs_server(server, atomic_read(&server->usage), | |
382 | afs_server_trace_give_up_cb); | |
1da177e4 | 383 | |
f2686b09 DH |
384 | if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags)) |
385 | afs_fs_give_up_all_callbacks(net, server, &ac, NULL); | |
386 | ||
3bf0fb6f DH |
387 | wait_var_event(&server->probe_outstanding, |
388 | atomic_read(&server->probe_outstanding) == 0); | |
389 | ||
45218193 DH |
390 | trace_afs_server(server, atomic_read(&server->usage), |
391 | afs_server_trace_destroy); | |
d2ddc776 | 392 | call_rcu(&server->rcu, afs_server_rcu); |
59fa1c4a | 393 | afs_dec_servers_outstanding(net); |
ec26815a | 394 | } |
1da177e4 | 395 | |
1da177e4 | 396 | /* |
d2ddc776 | 397 | * Garbage collect any expired servers. |
1da177e4 | 398 | */ |
d2ddc776 | 399 | static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list) |
1da177e4 | 400 | { |
08e0e7c8 | 401 | struct afs_server *server; |
d2ddc776 DH |
402 | bool deleted; |
403 | int usage; | |
404 | ||
405 | while ((server = gc_list)) { | |
406 | gc_list = server->gc_next; | |
407 | ||
408 | write_seqlock(&net->fs_lock); | |
409 | usage = 1; | |
410 | deleted = atomic_try_cmpxchg(&server->usage, &usage, 0); | |
45218193 | 411 | trace_afs_server(server, usage, afs_server_trace_gc); |
d2ddc776 DH |
412 | if (deleted) { |
413 | rb_erase(&server->uuid_rb, &net->fs_servers); | |
414 | hlist_del_rcu(&server->proc_link); | |
1da177e4 | 415 | } |
d2ddc776 | 416 | write_sequnlock(&net->fs_lock); |
1da177e4 | 417 | |
66062592 DH |
418 | if (deleted) { |
419 | write_seqlock(&net->fs_addr_lock); | |
420 | if (!hlist_unhashed(&server->addr4_link)) | |
421 | hlist_del_rcu(&server->addr4_link); | |
422 | if (!hlist_unhashed(&server->addr6_link)) | |
423 | hlist_del_rcu(&server->addr6_link); | |
424 | write_sequnlock(&net->fs_addr_lock); | |
d2ddc776 | 425 | afs_destroy_server(net, server); |
66062592 | 426 | } |
d2ddc776 DH |
427 | } |
428 | } | |
429 | ||
430 | /* | |
431 | * Manage the records of servers known to be within a network namespace. This | |
432 | * includes garbage collecting unused servers. | |
433 | * | |
434 | * Note also that we were given an increment on net->servers_outstanding by | |
435 | * whoever queued us that we need to deal with before returning. | |
436 | */ | |
437 | void afs_manage_servers(struct work_struct *work) | |
438 | { | |
439 | struct afs_net *net = container_of(work, struct afs_net, fs_manager); | |
440 | struct afs_server *gc_list = NULL; | |
441 | struct rb_node *cursor; | |
442 | time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX; | |
443 | bool purging = !net->live; | |
444 | ||
445 | _enter(""); | |
446 | ||
447 | /* Trawl the server list looking for servers that have expired from | |
448 | * lack of use. | |
449 | */ | |
450 | read_seqlock_excl(&net->fs_lock); | |
451 | ||
452 | for (cursor = rb_first(&net->fs_servers); cursor; cursor = rb_next(cursor)) { | |
453 | struct afs_server *server = | |
454 | rb_entry(cursor, struct afs_server, uuid_rb); | |
455 | int usage = atomic_read(&server->usage); | |
456 | ||
457 | _debug("manage %pU %u", &server->uuid, usage); | |
458 | ||
459 | ASSERTCMP(usage, >=, 1); | |
460 | ASSERTIFCMP(purging, usage, ==, 1); | |
461 | ||
462 | if (usage == 1) { | |
463 | time64_t expire_at = server->put_time; | |
464 | ||
465 | if (!test_bit(AFS_SERVER_FL_VL_FAIL, &server->flags) && | |
466 | !test_bit(AFS_SERVER_FL_NOT_FOUND, &server->flags)) | |
467 | expire_at += afs_server_gc_delay; | |
468 | if (purging || expire_at <= now) { | |
469 | server->gc_next = gc_list; | |
470 | gc_list = server; | |
471 | } else if (expire_at < next_manage) { | |
472 | next_manage = expire_at; | |
473 | } | |
1da177e4 LT |
474 | } |
475 | } | |
476 | ||
d2ddc776 | 477 | read_sequnlock_excl(&net->fs_lock); |
1da177e4 | 478 | |
d2ddc776 DH |
479 | /* Update the timer on the way out. We have to pass an increment on |
480 | * servers_outstanding in the namespace that we are in to the timer or | |
481 | * the work scheduler. | |
482 | */ | |
483 | if (!purging && next_manage < TIME64_MAX) { | |
484 | now = ktime_get_real_seconds(); | |
485 | ||
486 | if (next_manage - now <= 0) { | |
487 | if (queue_work(afs_wq, &net->fs_manager)) | |
488 | afs_inc_servers_outstanding(net); | |
489 | } else { | |
490 | afs_set_server_timer(net, next_manage - now); | |
491 | } | |
1da177e4 | 492 | } |
59fa1c4a | 493 | |
d2ddc776 DH |
494 | afs_gc_servers(net, gc_list); |
495 | ||
59fa1c4a | 496 | afs_dec_servers_outstanding(net); |
d2ddc776 DH |
497 | _leave(" [%d]", atomic_read(&net->servers_outstanding)); |
498 | } | |
499 | ||
500 | static void afs_queue_server_manager(struct afs_net *net) | |
501 | { | |
502 | afs_inc_servers_outstanding(net); | |
503 | if (!queue_work(afs_wq, &net->fs_manager)) | |
504 | afs_dec_servers_outstanding(net); | |
ec26815a | 505 | } |
1da177e4 | 506 | |
1da177e4 | 507 | /* |
d2ddc776 | 508 | * Purge list of servers. |
1da177e4 | 509 | */ |
d2ddc776 | 510 | void afs_purge_servers(struct afs_net *net) |
1da177e4 | 511 | { |
d2ddc776 DH |
512 | _enter(""); |
513 | ||
514 | if (del_timer_sync(&net->fs_timer)) | |
59fa1c4a DH |
515 | atomic_dec(&net->servers_outstanding); |
516 | ||
d2ddc776 | 517 | afs_queue_server_manager(net); |
59fa1c4a | 518 | |
d2ddc776 | 519 | _debug("wait"); |
ab1fbe32 PZ |
520 | wait_var_event(&net->servers_outstanding, |
521 | !atomic_read(&net->servers_outstanding)); | |
d2ddc776 DH |
522 | _leave(""); |
523 | } | |
524 | ||
d2ddc776 DH |
525 | /* |
526 | * Get an update for a server's address list. | |
527 | */ | |
528 | static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct afs_server *server) | |
529 | { | |
530 | struct afs_addr_list *alist, *discard; | |
531 | ||
532 | _enter(""); | |
533 | ||
45218193 DH |
534 | trace_afs_server(server, atomic_read(&server->usage), afs_server_trace_update); |
535 | ||
d2ddc776 DH |
536 | alist = afs_vl_lookup_addrs(fc->vnode->volume->cell, fc->key, |
537 | &server->uuid); | |
538 | if (IS_ERR(alist)) { | |
20b8391f DH |
539 | if ((PTR_ERR(alist) == -ERESTARTSYS || |
540 | PTR_ERR(alist) == -EINTR) && | |
541 | !(fc->flags & AFS_FS_CURSOR_INTR) && | |
542 | server->addresses) { | |
543 | _leave(" = t [intr]"); | |
544 | return true; | |
545 | } | |
0ab4c959 DH |
546 | fc->error = PTR_ERR(alist); |
547 | _leave(" = f [%d]", fc->error); | |
d2ddc776 DH |
548 | return false; |
549 | } | |
550 | ||
551 | discard = alist; | |
552 | if (server->addr_version != alist->version) { | |
553 | write_lock(&server->fs_lock); | |
554 | discard = rcu_dereference_protected(server->addresses, | |
555 | lockdep_is_held(&server->fs_lock)); | |
556 | rcu_assign_pointer(server->addresses, alist); | |
557 | server->addr_version = alist->version; | |
558 | write_unlock(&server->fs_lock); | |
559 | } | |
560 | ||
561 | server->update_at = ktime_get_real_seconds() + afs_server_update_delay; | |
562 | afs_put_addrlist(discard); | |
563 | _leave(" = t"); | |
564 | return true; | |
565 | } | |
566 | ||
567 | /* | |
568 | * See if a server's address list needs updating. | |
569 | */ | |
570 | bool afs_check_server_record(struct afs_fs_cursor *fc, struct afs_server *server) | |
571 | { | |
572 | time64_t now = ktime_get_real_seconds(); | |
573 | long diff; | |
574 | bool success; | |
575 | int ret, retries = 0; | |
576 | ||
577 | _enter(""); | |
578 | ||
579 | ASSERT(server); | |
580 | ||
581 | retry: | |
582 | diff = READ_ONCE(server->update_at) - now; | |
583 | if (diff > 0) { | |
584 | _leave(" = t [not now %ld]", diff); | |
585 | return true; | |
586 | } | |
587 | ||
588 | if (!test_and_set_bit_lock(AFS_SERVER_FL_UPDATING, &server->flags)) { | |
589 | success = afs_update_server_record(fc, server); | |
590 | clear_bit_unlock(AFS_SERVER_FL_UPDATING, &server->flags); | |
591 | wake_up_bit(&server->flags, AFS_SERVER_FL_UPDATING); | |
592 | _leave(" = %d", success); | |
593 | return success; | |
594 | } | |
595 | ||
596 | ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING, | |
c4bfda16 DH |
597 | (fc->flags & AFS_FS_CURSOR_INTR) ? |
598 | TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | |
d2ddc776 | 599 | if (ret == -ERESTARTSYS) { |
0ab4c959 | 600 | fc->error = ret; |
d2ddc776 DH |
601 | _leave(" = f [intr]"); |
602 | return false; | |
603 | } | |
604 | ||
605 | retries++; | |
606 | if (retries == 4) { | |
607 | _leave(" = f [stale]"); | |
608 | ret = -ESTALE; | |
609 | return false; | |
610 | } | |
611 | goto retry; | |
ec26815a | 612 | } |