blob: 9ca174b24f5b7d7b5269ef2964fee26b84910a3a [file] [log] [blame]
David Howellsec268152007-04-26 15:49:28 -07001/* AFS server record management
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
David Howells08e0e7c2007-04-26 15:55:03 -07003 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/sched.h>
13#include <linux/slab.h>
David Howells4d9df982017-11-02 15:27:47 +000014#include "afs_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include "internal.h"
16
Adrian Bunkc1206a22007-10-16 23:26:41 -070017static unsigned afs_server_timeout = 10; /* server timeout in seconds */
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
David Howells59fa1c42017-11-02 15:27:45 +000019static void afs_inc_servers_outstanding(struct afs_net *net)
20{
21 atomic_inc(&net->servers_outstanding);
22}
23
24static void afs_dec_servers_outstanding(struct afs_net *net)
25{
26 if (atomic_dec_and_test(&net->servers_outstanding))
27 wake_up_atomic_t(&net->servers_outstanding);
28}
29
30void afs_server_timer(struct timer_list *timer)
31{
32 struct afs_net *net = container_of(timer, struct afs_net, server_timer);
33
34 if (!queue_work(afs_wq, &net->server_reaper))
35 afs_dec_servers_outstanding(net);
36}
37
Linus Torvalds1da177e2005-04-16 15:20:36 -070038/*
David Howells08e0e7c2007-04-26 15:55:03 -070039 * install a server record in the master tree
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 */
David Howells08e0e7c2007-04-26 15:55:03 -070041static int afs_install_server(struct afs_server *server)
Linus Torvalds1da177e2005-04-16 15:20:36 -070042{
David Howells08e0e7c2007-04-26 15:55:03 -070043 struct afs_server *xserver;
David Howellsf044c882017-11-02 15:27:45 +000044 struct afs_net *net = server->cell->net;
David Howells08e0e7c2007-04-26 15:55:03 -070045 struct rb_node **pp, *p;
David Howells4d9df982017-11-02 15:27:47 +000046 int ret, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
David Howells08e0e7c2007-04-26 15:55:03 -070048 _enter("%p", server);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
David Howellsf044c882017-11-02 15:27:45 +000050 write_lock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -070051
52 ret = -EEXIST;
David Howellsf044c882017-11-02 15:27:45 +000053 pp = &net->servers.rb_node;
David Howells08e0e7c2007-04-26 15:55:03 -070054 p = NULL;
55 while (*pp) {
56 p = *pp;
57 _debug("- consider %p", p);
58 xserver = rb_entry(p, struct afs_server, master_rb);
David Howells8b2a4642017-11-02 15:27:50 +000059 diff = memcmp(&server->addrs->addrs[0],
60 &xserver->addrs->addrs[0],
61 sizeof(sizeof(server->addrs->addrs[0])));
David Howells4d9df982017-11-02 15:27:47 +000062 if (diff < 0)
David Howells08e0e7c2007-04-26 15:55:03 -070063 pp = &(*pp)->rb_left;
David Howells4d9df982017-11-02 15:27:47 +000064 else if (diff > 0)
David Howells08e0e7c2007-04-26 15:55:03 -070065 pp = &(*pp)->rb_right;
66 else
67 goto error;
68 }
69
70 rb_link_node(&server->master_rb, p, pp);
David Howellsf044c882017-11-02 15:27:45 +000071 rb_insert_color(&server->master_rb, &net->servers);
David Howells08e0e7c2007-04-26 15:55:03 -070072 ret = 0;
73
74error:
David Howellsf044c882017-11-02 15:27:45 +000075 write_unlock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -070076 return ret;
77}
78
79/*
80 * allocate a new server record
81 */
82static struct afs_server *afs_alloc_server(struct afs_cell *cell,
David Howells4d9df982017-11-02 15:27:47 +000083 const struct sockaddr_rxrpc *addr)
David Howells08e0e7c2007-04-26 15:55:03 -070084{
85 struct afs_server *server;
86
87 _enter("");
88
Yan Burmanb593e482006-12-06 20:40:32 -080089 server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
David Howells8b2a4642017-11-02 15:27:50 +000090 if (!server)
91 goto enomem;
92 server->addrs = kzalloc(sizeof(struct afs_addr_list) +
93 sizeof(struct sockaddr_rxrpc),
94 GFP_KERNEL);
95 if (!server->addrs)
96 goto enomem_server;
David Howells08e0e7c2007-04-26 15:55:03 -070097
David Howells8b2a4642017-11-02 15:27:50 +000098 atomic_set(&server->usage, 1);
99 server->net = cell->net;
100 server->cell = cell;
David Howells08e0e7c2007-04-26 15:55:03 -0700101
David Howells8b2a4642017-11-02 15:27:50 +0000102 INIT_LIST_HEAD(&server->link);
103 INIT_LIST_HEAD(&server->grave);
104 init_rwsem(&server->sem);
105 spin_lock_init(&server->fs_lock);
106 INIT_LIST_HEAD(&server->cb_interests);
107 rwlock_init(&server->cb_break_lock);
108
109 refcount_set(&server->addrs->usage, 1);
110 server->addrs->nr_addrs = 1;
111 server->addrs->addrs[0] = *addr;
112 afs_inc_servers_outstanding(cell->net);
113
114 _leave(" = %p{%d}", server, atomic_read(&server->usage));
David Howells08e0e7c2007-04-26 15:55:03 -0700115 return server;
David Howells8b2a4642017-11-02 15:27:50 +0000116
117enomem_server:
118 kfree(server);
119enomem:
120 _leave(" = NULL [nomem]");
121 return NULL;
David Howells08e0e7c2007-04-26 15:55:03 -0700122}
123
124/*
125 * get an FS-server record for a cell
126 */
127struct afs_server *afs_lookup_server(struct afs_cell *cell,
David Howells4d9df982017-11-02 15:27:47 +0000128 struct sockaddr_rxrpc *addr)
David Howells08e0e7c2007-04-26 15:55:03 -0700129{
130 struct afs_server *server, *candidate;
131
David Howells4d9df982017-11-02 15:27:47 +0000132 _enter("%p,%pIS", cell, &addr->transport);
David Howells08e0e7c2007-04-26 15:55:03 -0700133
134 /* quick scan of the list to see if we already have the server */
135 read_lock(&cell->servers_lock);
136
137 list_for_each_entry(server, &cell->servers, link) {
David Howells8b2a4642017-11-02 15:27:50 +0000138 if (memcmp(&server->addrs->addrs[0], addr, sizeof(*addr)) == 0)
David Howells08e0e7c2007-04-26 15:55:03 -0700139 goto found_server_quickly;
140 }
141 read_unlock(&cell->servers_lock);
142
143 candidate = afs_alloc_server(cell, addr);
144 if (!candidate) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 _leave(" = -ENOMEM");
David Howells08e0e7c2007-04-26 15:55:03 -0700146 return ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 }
148
David Howells08e0e7c2007-04-26 15:55:03 -0700149 write_lock(&cell->servers_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
David Howells08e0e7c2007-04-26 15:55:03 -0700151 /* check the cell's server list again */
152 list_for_each_entry(server, &cell->servers, link) {
David Howells8b2a4642017-11-02 15:27:50 +0000153 if (memcmp(&server->addrs->addrs[0], addr, sizeof(*addr)) == 0)
David Howells08e0e7c2007-04-26 15:55:03 -0700154 goto found_server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 }
156
David Howells08e0e7c2007-04-26 15:55:03 -0700157 _debug("new");
158 server = candidate;
159 if (afs_install_server(server) < 0)
160 goto server_in_two_cells;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
162 afs_get_cell(cell);
David Howells08e0e7c2007-04-26 15:55:03 -0700163 list_add_tail(&server->link, &cell->servers);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
David Howells08e0e7c2007-04-26 15:55:03 -0700165 write_unlock(&cell->servers_lock);
166 _leave(" = %p{%d}", server, atomic_read(&server->usage));
167 return server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
David Howells08e0e7c2007-04-26 15:55:03 -0700169 /* found a matching server quickly */
170found_server_quickly:
171 _debug("found quickly");
172 afs_get_server(server);
173 read_unlock(&cell->servers_lock);
174no_longer_unused:
175 if (!list_empty(&server->grave)) {
David Howellsf044c882017-11-02 15:27:45 +0000176 spin_lock(&cell->net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700177 list_del_init(&server->grave);
David Howellsf044c882017-11-02 15:27:45 +0000178 spin_unlock(&cell->net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700179 }
180 _leave(" = %p{%d}", server, atomic_read(&server->usage));
181 return server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
David Howells08e0e7c2007-04-26 15:55:03 -0700183 /* found a matching server on the second pass */
184found_server:
185 _debug("found");
186 afs_get_server(server);
187 write_unlock(&cell->servers_lock);
188 kfree(candidate);
189 goto no_longer_unused;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
David Howells08e0e7c2007-04-26 15:55:03 -0700191 /* found a server that seems to be in two cells */
192server_in_two_cells:
193 write_unlock(&cell->servers_lock);
194 kfree(candidate);
David Howells59fa1c42017-11-02 15:27:45 +0000195 afs_dec_servers_outstanding(cell->net);
Harvey Harrisonbe859402008-10-31 00:56:28 -0700196 printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n",
197 addr);
David Howells08e0e7c2007-04-26 15:55:03 -0700198 _leave(" = -EEXIST");
199 return ERR_PTR(-EEXIST);
200}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
David Howells08e0e7c2007-04-26 15:55:03 -0700202/*
203 * look up a server by its IP address
204 */
David Howellsf044c882017-11-02 15:27:45 +0000205struct afs_server *afs_find_server(struct afs_net *net,
206 const struct sockaddr_rxrpc *srx)
David Howells08e0e7c2007-04-26 15:55:03 -0700207{
208 struct afs_server *server = NULL;
209 struct rb_node *p;
David Howells4d9df982017-11-02 15:27:47 +0000210 int diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
David Howells4d9df982017-11-02 15:27:47 +0000212 _enter("{%d,%pIS}", srx->transport.family, &srx->transport);
David Howells8324f0b2016-08-30 09:49:29 +0100213
David Howellsf044c882017-11-02 15:27:45 +0000214 read_lock(&net->servers_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
David Howellsf044c882017-11-02 15:27:45 +0000216 p = net->servers.rb_node;
David Howells08e0e7c2007-04-26 15:55:03 -0700217 while (p) {
218 server = rb_entry(p, struct afs_server, master_rb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
David Howells08e0e7c2007-04-26 15:55:03 -0700220 _debug("- consider %p", p);
221
David Howells8b2a4642017-11-02 15:27:50 +0000222 diff = memcmp(srx, &server->addrs->addrs[0], sizeof(*srx));
David Howells4d9df982017-11-02 15:27:47 +0000223 if (diff < 0) {
David Howells08e0e7c2007-04-26 15:55:03 -0700224 p = p->rb_left;
David Howells4d9df982017-11-02 15:27:47 +0000225 } else if (diff > 0) {
David Howells08e0e7c2007-04-26 15:55:03 -0700226 p = p->rb_right;
227 } else {
228 afs_get_server(server);
229 goto found;
230 }
231 }
232
233 server = NULL;
234found:
David Howellsf044c882017-11-02 15:27:45 +0000235 read_unlock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700236 _leave(" = %p", server);
237 return server;
David Howellsec268152007-04-26 15:49:28 -0700238}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
David Howells59fa1c42017-11-02 15:27:45 +0000240static void afs_set_server_timer(struct afs_net *net, time64_t delay)
241{
242 afs_inc_servers_outstanding(net);
243 if (net->live) {
244 if (timer_reduce(&net->server_timer, jiffies + delay * HZ))
245 afs_dec_servers_outstanding(net);
246 } else {
247 if (!queue_work(afs_wq, &net->server_reaper))
248 afs_dec_servers_outstanding(net);
249 }
250}
251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252/*
253 * destroy a server record
254 * - removes from the cell list
255 */
David Howells9ed900b2017-11-02 15:27:46 +0000256void afs_put_server(struct afs_net *net, struct afs_server *server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 if (!server)
259 return;
260
David Howells08e0e7c2007-04-26 15:55:03 -0700261 _enter("%p{%d}", server, atomic_read(&server->usage));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
David Howells260a9802007-04-26 15:59:35 -0700263 _debug("PUT SERVER %d", atomic_read(&server->usage));
264
David Howells08e0e7c2007-04-26 15:55:03 -0700265 ASSERTCMP(atomic_read(&server->usage), >, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
267 if (likely(!atomic_dec_and_test(&server->usage))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 _leave("");
269 return;
270 }
271
David Howellsf044c882017-11-02 15:27:45 +0000272 spin_lock(&net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700273 if (atomic_read(&server->usage) == 0) {
David Howellsf044c882017-11-02 15:27:45 +0000274 list_move_tail(&server->grave, &net->server_graveyard);
Tina Ruchandani8a797902017-03-16 16:27:46 +0000275 server->time_of_death = ktime_get_real_seconds();
David Howells59fa1c42017-11-02 15:27:45 +0000276 afs_set_server_timer(net, afs_server_timeout);
David Howells08e0e7c2007-04-26 15:55:03 -0700277 }
David Howellsf044c882017-11-02 15:27:45 +0000278 spin_unlock(&net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700279 _leave(" [dead]");
David Howellsec268152007-04-26 15:49:28 -0700280}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282/*
David Howells08e0e7c2007-04-26 15:55:03 -0700283 * destroy a dead server
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 */
David Howells59fa1c42017-11-02 15:27:45 +0000285static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
David Howells8b2a4642017-11-02 15:27:50 +0000287 struct afs_addr_list *alist = server->addrs;
288 struct afs_addr_cursor ac = {
289 .alist = alist,
290 .addr = &alist->addrs[0],
291 .start = alist->index,
292 .index = alist->index,
293 .error = 0,
294 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 _enter("%p", server);
296
David Howells8b2a4642017-11-02 15:27:50 +0000297 afs_fs_give_up_all_callbacks(server, &ac, NULL, false);
David Howellsc435ee32017-11-02 15:27:49 +0000298 afs_put_cell(net, server->cell);
David Howells8b2a4642017-11-02 15:27:50 +0000299 afs_put_addrlist(server->addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 kfree(server);
David Howells59fa1c42017-11-02 15:27:45 +0000301 afs_dec_servers_outstanding(net);
David Howellsec268152007-04-26 15:49:28 -0700302}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304/*
David Howells08e0e7c2007-04-26 15:55:03 -0700305 * reap dead server records
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 */
David Howellsf044c882017-11-02 15:27:45 +0000307void afs_reap_server(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308{
David Howells08e0e7c2007-04-26 15:55:03 -0700309 LIST_HEAD(corpses);
310 struct afs_server *server;
David Howells59fa1c42017-11-02 15:27:45 +0000311 struct afs_net *net = container_of(work, struct afs_net, server_reaper);
David Howells08e0e7c2007-04-26 15:55:03 -0700312 unsigned long delay, expiry;
Tina Ruchandani8a797902017-03-16 16:27:46 +0000313 time64_t now;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Tina Ruchandani8a797902017-03-16 16:27:46 +0000315 now = ktime_get_real_seconds();
David Howellsf044c882017-11-02 15:27:45 +0000316 spin_lock(&net->server_graveyard_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
David Howellsf044c882017-11-02 15:27:45 +0000318 while (!list_empty(&net->server_graveyard)) {
319 server = list_entry(net->server_graveyard.next,
David Howells08e0e7c2007-04-26 15:55:03 -0700320 struct afs_server, grave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
David Howells08e0e7c2007-04-26 15:55:03 -0700322 /* the queue is ordered most dead first */
David Howellsf044c882017-11-02 15:27:45 +0000323 if (net->live) {
324 expiry = server->time_of_death + afs_server_timeout;
325 if (expiry > now) {
David Howells59fa1c42017-11-02 15:27:45 +0000326 delay = (expiry - now);
327 afs_set_server_timer(net, delay);
David Howellsf044c882017-11-02 15:27:45 +0000328 break;
329 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 }
331
David Howells08e0e7c2007-04-26 15:55:03 -0700332 write_lock(&server->cell->servers_lock);
David Howellsf044c882017-11-02 15:27:45 +0000333 write_lock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700334 if (atomic_read(&server->usage) > 0) {
335 list_del_init(&server->grave);
336 } else {
337 list_move_tail(&server->grave, &corpses);
338 list_del_init(&server->link);
David Howellsf044c882017-11-02 15:27:45 +0000339 rb_erase(&server->master_rb, &net->servers);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 }
David Howellsf044c882017-11-02 15:27:45 +0000341 write_unlock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700342 write_unlock(&server->cell->servers_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 }
344
David Howellsf044c882017-11-02 15:27:45 +0000345 spin_unlock(&net->server_graveyard_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
David Howells08e0e7c2007-04-26 15:55:03 -0700347 /* now reap the corpses we've extracted */
348 while (!list_empty(&corpses)) {
349 server = list_entry(corpses.next, struct afs_server, grave);
350 list_del(&server->grave);
David Howells59fa1c42017-11-02 15:27:45 +0000351 afs_destroy_server(net, server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 }
David Howells59fa1c42017-11-02 15:27:45 +0000353
354 afs_dec_servers_outstanding(net);
David Howellsec268152007-04-26 15:49:28 -0700355}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357/*
David Howellsf044c882017-11-02 15:27:45 +0000358 * Discard all the server records from a net namespace when it is destroyed or
359 * the afs module is removed.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 */
David Howellsf044c882017-11-02 15:27:45 +0000361void __net_exit afs_purge_servers(struct afs_net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
David Howells59fa1c42017-11-02 15:27:45 +0000363 if (del_timer_sync(&net->server_timer))
364 atomic_dec(&net->servers_outstanding);
365
366 afs_inc_servers_outstanding(net);
367 if (!queue_work(afs_wq, &net->server_reaper))
368 afs_dec_servers_outstanding(net);
369
370 wait_on_atomic_t(&net->servers_outstanding, atomic_t_wait,
371 TASK_UNINTERRUPTIBLE);
David Howellsec268152007-04-26 15:49:28 -0700372}