blob: 4e66608fc80547847517d23c1743ec6a15e91b79 [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 Howells4d9df982017-11-02 15:27:47 +000059 diff = memcmp(&server->addr, &xserver->addr, sizeof(server->addr));
60 if (diff < 0)
David Howells08e0e7c2007-04-26 15:55:03 -070061 pp = &(*pp)->rb_left;
David Howells4d9df982017-11-02 15:27:47 +000062 else if (diff > 0)
David Howells08e0e7c2007-04-26 15:55:03 -070063 pp = &(*pp)->rb_right;
64 else
65 goto error;
66 }
67
68 rb_link_node(&server->master_rb, p, pp);
David Howellsf044c882017-11-02 15:27:45 +000069 rb_insert_color(&server->master_rb, &net->servers);
David Howells08e0e7c2007-04-26 15:55:03 -070070 ret = 0;
71
72error:
David Howellsf044c882017-11-02 15:27:45 +000073 write_unlock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -070074 return ret;
75}
76
77/*
78 * allocate a new server record
79 */
80static struct afs_server *afs_alloc_server(struct afs_cell *cell,
David Howells4d9df982017-11-02 15:27:47 +000081 const struct sockaddr_rxrpc *addr)
David Howells08e0e7c2007-04-26 15:55:03 -070082{
83 struct afs_server *server;
84
85 _enter("");
86
Yan Burmanb593e482006-12-06 20:40:32 -080087 server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
David Howells08e0e7c2007-04-26 15:55:03 -070088 if (server) {
89 atomic_set(&server->usage, 1);
David Howells9ed900b2017-11-02 15:27:46 +000090 server->net = cell->net;
David Howells08e0e7c2007-04-26 15:55:03 -070091 server->cell = cell;
92
93 INIT_LIST_HEAD(&server->link);
94 INIT_LIST_HEAD(&server->grave);
95 init_rwsem(&server->sem);
96 spin_lock_init(&server->fs_lock);
David Howellsc435ee32017-11-02 15:27:49 +000097 INIT_LIST_HEAD(&server->cb_interests);
98 rwlock_init(&server->cb_break_lock);
David Howells08e0e7c2007-04-26 15:55:03 -070099
David Howells4d9df982017-11-02 15:27:47 +0000100 server->addr = *addr;
David Howells59fa1c42017-11-02 15:27:45 +0000101 afs_inc_servers_outstanding(cell->net);
Denis Kirjanov037776f2010-06-01 17:15:39 +0100102 _leave(" = %p{%d}", server, atomic_read(&server->usage));
103 } else {
104 _leave(" = NULL [nomem]");
David Howells08e0e7c2007-04-26 15:55:03 -0700105 }
David Howells08e0e7c2007-04-26 15:55:03 -0700106 return server;
107}
108
109/*
110 * get an FS-server record for a cell
111 */
112struct afs_server *afs_lookup_server(struct afs_cell *cell,
David Howells4d9df982017-11-02 15:27:47 +0000113 struct sockaddr_rxrpc *addr)
David Howells08e0e7c2007-04-26 15:55:03 -0700114{
115 struct afs_server *server, *candidate;
116
David Howells4d9df982017-11-02 15:27:47 +0000117 _enter("%p,%pIS", cell, &addr->transport);
David Howells08e0e7c2007-04-26 15:55:03 -0700118
119 /* quick scan of the list to see if we already have the server */
120 read_lock(&cell->servers_lock);
121
122 list_for_each_entry(server, &cell->servers, link) {
David Howells4d9df982017-11-02 15:27:47 +0000123 if (memcmp(&server->addr, addr, sizeof(*addr)) == 0)
David Howells08e0e7c2007-04-26 15:55:03 -0700124 goto found_server_quickly;
125 }
126 read_unlock(&cell->servers_lock);
127
128 candidate = afs_alloc_server(cell, addr);
129 if (!candidate) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 _leave(" = -ENOMEM");
David Howells08e0e7c2007-04-26 15:55:03 -0700131 return ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133
David Howells08e0e7c2007-04-26 15:55:03 -0700134 write_lock(&cell->servers_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
David Howells08e0e7c2007-04-26 15:55:03 -0700136 /* check the cell's server list again */
137 list_for_each_entry(server, &cell->servers, link) {
David Howells4d9df982017-11-02 15:27:47 +0000138 if (memcmp(&server->addr, addr, sizeof(*addr)) == 0)
David Howells08e0e7c2007-04-26 15:55:03 -0700139 goto found_server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 }
141
David Howells08e0e7c2007-04-26 15:55:03 -0700142 _debug("new");
143 server = candidate;
144 if (afs_install_server(server) < 0)
145 goto server_in_two_cells;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
147 afs_get_cell(cell);
David Howells08e0e7c2007-04-26 15:55:03 -0700148 list_add_tail(&server->link, &cell->servers);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
David Howells08e0e7c2007-04-26 15:55:03 -0700150 write_unlock(&cell->servers_lock);
151 _leave(" = %p{%d}", server, atomic_read(&server->usage));
152 return server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
David Howells08e0e7c2007-04-26 15:55:03 -0700154 /* found a matching server quickly */
155found_server_quickly:
156 _debug("found quickly");
157 afs_get_server(server);
158 read_unlock(&cell->servers_lock);
159no_longer_unused:
160 if (!list_empty(&server->grave)) {
David Howellsf044c882017-11-02 15:27:45 +0000161 spin_lock(&cell->net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700162 list_del_init(&server->grave);
David Howellsf044c882017-11-02 15:27:45 +0000163 spin_unlock(&cell->net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700164 }
165 _leave(" = %p{%d}", server, atomic_read(&server->usage));
166 return server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
David Howells08e0e7c2007-04-26 15:55:03 -0700168 /* found a matching server on the second pass */
169found_server:
170 _debug("found");
171 afs_get_server(server);
172 write_unlock(&cell->servers_lock);
173 kfree(candidate);
174 goto no_longer_unused;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
David Howells08e0e7c2007-04-26 15:55:03 -0700176 /* found a server that seems to be in two cells */
177server_in_two_cells:
178 write_unlock(&cell->servers_lock);
179 kfree(candidate);
David Howells59fa1c42017-11-02 15:27:45 +0000180 afs_dec_servers_outstanding(cell->net);
Harvey Harrisonbe859402008-10-31 00:56:28 -0700181 printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n",
182 addr);
David Howells08e0e7c2007-04-26 15:55:03 -0700183 _leave(" = -EEXIST");
184 return ERR_PTR(-EEXIST);
185}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
David Howells08e0e7c2007-04-26 15:55:03 -0700187/*
188 * look up a server by its IP address
189 */
David Howellsf044c882017-11-02 15:27:45 +0000190struct afs_server *afs_find_server(struct afs_net *net,
191 const struct sockaddr_rxrpc *srx)
David Howells08e0e7c2007-04-26 15:55:03 -0700192{
193 struct afs_server *server = NULL;
194 struct rb_node *p;
David Howells4d9df982017-11-02 15:27:47 +0000195 int diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
David Howells4d9df982017-11-02 15:27:47 +0000197 _enter("{%d,%pIS}", srx->transport.family, &srx->transport);
David Howells8324f0b2016-08-30 09:49:29 +0100198
David Howellsf044c882017-11-02 15:27:45 +0000199 read_lock(&net->servers_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200
David Howellsf044c882017-11-02 15:27:45 +0000201 p = net->servers.rb_node;
David Howells08e0e7c2007-04-26 15:55:03 -0700202 while (p) {
203 server = rb_entry(p, struct afs_server, master_rb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
David Howells08e0e7c2007-04-26 15:55:03 -0700205 _debug("- consider %p", p);
206
David Howells4d9df982017-11-02 15:27:47 +0000207 diff = memcmp(srx, &server->addr, sizeof(*srx));
208 if (diff < 0) {
David Howells08e0e7c2007-04-26 15:55:03 -0700209 p = p->rb_left;
David Howells4d9df982017-11-02 15:27:47 +0000210 } else if (diff > 0) {
David Howells08e0e7c2007-04-26 15:55:03 -0700211 p = p->rb_right;
212 } else {
213 afs_get_server(server);
214 goto found;
215 }
216 }
217
218 server = NULL;
219found:
David Howellsf044c882017-11-02 15:27:45 +0000220 read_unlock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700221 _leave(" = %p", server);
222 return server;
David Howellsec268152007-04-26 15:49:28 -0700223}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
David Howells59fa1c42017-11-02 15:27:45 +0000225static void afs_set_server_timer(struct afs_net *net, time64_t delay)
226{
227 afs_inc_servers_outstanding(net);
228 if (net->live) {
229 if (timer_reduce(&net->server_timer, jiffies + delay * HZ))
230 afs_dec_servers_outstanding(net);
231 } else {
232 if (!queue_work(afs_wq, &net->server_reaper))
233 afs_dec_servers_outstanding(net);
234 }
235}
236
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237/*
238 * destroy a server record
239 * - removes from the cell list
240 */
David Howells9ed900b2017-11-02 15:27:46 +0000241void afs_put_server(struct afs_net *net, struct afs_server *server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 if (!server)
244 return;
245
David Howells08e0e7c2007-04-26 15:55:03 -0700246 _enter("%p{%d}", server, atomic_read(&server->usage));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
David Howells260a9802007-04-26 15:59:35 -0700248 _debug("PUT SERVER %d", atomic_read(&server->usage));
249
David Howells08e0e7c2007-04-26 15:55:03 -0700250 ASSERTCMP(atomic_read(&server->usage), >, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
252 if (likely(!atomic_dec_and_test(&server->usage))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 _leave("");
254 return;
255 }
256
David Howellsf044c882017-11-02 15:27:45 +0000257 spin_lock(&net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700258 if (atomic_read(&server->usage) == 0) {
David Howellsf044c882017-11-02 15:27:45 +0000259 list_move_tail(&server->grave, &net->server_graveyard);
Tina Ruchandani8a797902017-03-16 16:27:46 +0000260 server->time_of_death = ktime_get_real_seconds();
David Howells59fa1c42017-11-02 15:27:45 +0000261 afs_set_server_timer(net, afs_server_timeout);
David Howells08e0e7c2007-04-26 15:55:03 -0700262 }
David Howellsf044c882017-11-02 15:27:45 +0000263 spin_unlock(&net->server_graveyard_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700264 _leave(" [dead]");
David Howellsec268152007-04-26 15:49:28 -0700265}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267/*
David Howells08e0e7c2007-04-26 15:55:03 -0700268 * destroy a dead server
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 */
David Howells59fa1c42017-11-02 15:27:45 +0000270static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 _enter("%p", server);
273
David Howellsc435ee32017-11-02 15:27:49 +0000274 afs_fs_give_up_all_callbacks(server, NULL, false);
275 afs_put_cell(net, server->cell);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 kfree(server);
David Howells59fa1c42017-11-02 15:27:45 +0000277 afs_dec_servers_outstanding(net);
David Howellsec268152007-04-26 15:49:28 -0700278}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280/*
David Howells08e0e7c2007-04-26 15:55:03 -0700281 * reap dead server records
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 */
David Howellsf044c882017-11-02 15:27:45 +0000283void afs_reap_server(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
David Howells08e0e7c2007-04-26 15:55:03 -0700285 LIST_HEAD(corpses);
286 struct afs_server *server;
David Howells59fa1c42017-11-02 15:27:45 +0000287 struct afs_net *net = container_of(work, struct afs_net, server_reaper);
David Howells08e0e7c2007-04-26 15:55:03 -0700288 unsigned long delay, expiry;
Tina Ruchandani8a797902017-03-16 16:27:46 +0000289 time64_t now;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
Tina Ruchandani8a797902017-03-16 16:27:46 +0000291 now = ktime_get_real_seconds();
David Howellsf044c882017-11-02 15:27:45 +0000292 spin_lock(&net->server_graveyard_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
David Howellsf044c882017-11-02 15:27:45 +0000294 while (!list_empty(&net->server_graveyard)) {
295 server = list_entry(net->server_graveyard.next,
David Howells08e0e7c2007-04-26 15:55:03 -0700296 struct afs_server, grave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297
David Howells08e0e7c2007-04-26 15:55:03 -0700298 /* the queue is ordered most dead first */
David Howellsf044c882017-11-02 15:27:45 +0000299 if (net->live) {
300 expiry = server->time_of_death + afs_server_timeout;
301 if (expiry > now) {
David Howells59fa1c42017-11-02 15:27:45 +0000302 delay = (expiry - now);
303 afs_set_server_timer(net, delay);
David Howellsf044c882017-11-02 15:27:45 +0000304 break;
305 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 }
307
David Howells08e0e7c2007-04-26 15:55:03 -0700308 write_lock(&server->cell->servers_lock);
David Howellsf044c882017-11-02 15:27:45 +0000309 write_lock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700310 if (atomic_read(&server->usage) > 0) {
311 list_del_init(&server->grave);
312 } else {
313 list_move_tail(&server->grave, &corpses);
314 list_del_init(&server->link);
David Howellsf044c882017-11-02 15:27:45 +0000315 rb_erase(&server->master_rb, &net->servers);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
David Howellsf044c882017-11-02 15:27:45 +0000317 write_unlock(&net->servers_lock);
David Howells08e0e7c2007-04-26 15:55:03 -0700318 write_unlock(&server->cell->servers_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 }
320
David Howellsf044c882017-11-02 15:27:45 +0000321 spin_unlock(&net->server_graveyard_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
David Howells08e0e7c2007-04-26 15:55:03 -0700323 /* now reap the corpses we've extracted */
324 while (!list_empty(&corpses)) {
325 server = list_entry(corpses.next, struct afs_server, grave);
326 list_del(&server->grave);
David Howells59fa1c42017-11-02 15:27:45 +0000327 afs_destroy_server(net, server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 }
David Howells59fa1c42017-11-02 15:27:45 +0000329
330 afs_dec_servers_outstanding(net);
David Howellsec268152007-04-26 15:49:28 -0700331}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333/*
David Howellsf044c882017-11-02 15:27:45 +0000334 * Discard all the server records from a net namespace when it is destroyed or
335 * the afs module is removed.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 */
David Howellsf044c882017-11-02 15:27:45 +0000337void __net_exit afs_purge_servers(struct afs_net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338{
David Howells59fa1c42017-11-02 15:27:45 +0000339 if (del_timer_sync(&net->server_timer))
340 atomic_dec(&net->servers_outstanding);
341
342 afs_inc_servers_outstanding(net);
343 if (!queue_work(afs_wq, &net->server_reaper))
344 afs_dec_servers_outstanding(net);
345
346 wait_on_atomic_t(&net->servers_outstanding, atomic_t_wait,
347 TASK_UNINTERRUPTIBLE);
David Howellsec268152007-04-26 15:49:28 -0700348}