]> Git Repo - linux.git/blob - fs/afs/server.c
afs: Overhaul permit caching
[linux.git] / fs / afs / server.c
1 /* AFS server record management
2  *
3  * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells ([email protected])
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>
14 #include "afs_fs.h"
15 #include "internal.h"
16
17 static unsigned afs_server_timeout = 10;        /* server timeout in seconds */
18
19 static void afs_inc_servers_outstanding(struct afs_net *net)
20 {
21         atomic_inc(&net->servers_outstanding);
22 }
23
24 static 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
30 void 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
38 /*
39  * install a server record in the master tree
40  */
41 static int afs_install_server(struct afs_server *server)
42 {
43         struct afs_server *xserver;
44         struct afs_net *net = server->cell->net;
45         struct rb_node **pp, *p;
46         int ret, diff;
47
48         _enter("%p", server);
49
50         write_lock(&net->servers_lock);
51
52         ret = -EEXIST;
53         pp = &net->servers.rb_node;
54         p = NULL;
55         while (*pp) {
56                 p = *pp;
57                 _debug("- consider %p", p);
58                 xserver = rb_entry(p, struct afs_server, master_rb);
59                 diff = memcmp(&server->addr, &xserver->addr, sizeof(server->addr));
60                 if (diff < 0)
61                         pp = &(*pp)->rb_left;
62                 else if (diff > 0)
63                         pp = &(*pp)->rb_right;
64                 else
65                         goto error;
66         }
67
68         rb_link_node(&server->master_rb, p, pp);
69         rb_insert_color(&server->master_rb, &net->servers);
70         ret = 0;
71
72 error:
73         write_unlock(&net->servers_lock);
74         return ret;
75 }
76
77 /*
78  * allocate a new server record
79  */
80 static struct afs_server *afs_alloc_server(struct afs_cell *cell,
81                                            const struct sockaddr_rxrpc *addr)
82 {
83         struct afs_server *server;
84
85         _enter("");
86
87         server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
88         if (server) {
89                 atomic_set(&server->usage, 1);
90                 server->net = cell->net;
91                 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);
97                 INIT_LIST_HEAD(&server->cb_interests);
98                 rwlock_init(&server->cb_break_lock);
99
100                 server->addr = *addr;
101                 afs_inc_servers_outstanding(cell->net);
102                 _leave(" = %p{%d}", server, atomic_read(&server->usage));
103         } else {
104                 _leave(" = NULL [nomem]");
105         }
106         return server;
107 }
108
109 /*
110  * get an FS-server record for a cell
111  */
112 struct afs_server *afs_lookup_server(struct afs_cell *cell,
113                                      struct sockaddr_rxrpc *addr)
114 {
115         struct afs_server *server, *candidate;
116
117         _enter("%p,%pIS", cell, &addr->transport);
118
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) {
123                 if (memcmp(&server->addr, addr, sizeof(*addr)) == 0)
124                         goto found_server_quickly;
125         }
126         read_unlock(&cell->servers_lock);
127
128         candidate = afs_alloc_server(cell, addr);
129         if (!candidate) {
130                 _leave(" = -ENOMEM");
131                 return ERR_PTR(-ENOMEM);
132         }
133
134         write_lock(&cell->servers_lock);
135
136         /* check the cell's server list again */
137         list_for_each_entry(server, &cell->servers, link) {
138                 if (memcmp(&server->addr, addr, sizeof(*addr)) == 0)
139                         goto found_server;
140         }
141
142         _debug("new");
143         server = candidate;
144         if (afs_install_server(server) < 0)
145                 goto server_in_two_cells;
146
147         afs_get_cell(cell);
148         list_add_tail(&server->link, &cell->servers);
149
150         write_unlock(&cell->servers_lock);
151         _leave(" = %p{%d}", server, atomic_read(&server->usage));
152         return server;
153
154         /* found a matching server quickly */
155 found_server_quickly:
156         _debug("found quickly");
157         afs_get_server(server);
158         read_unlock(&cell->servers_lock);
159 no_longer_unused:
160         if (!list_empty(&server->grave)) {
161                 spin_lock(&cell->net->server_graveyard_lock);
162                 list_del_init(&server->grave);
163                 spin_unlock(&cell->net->server_graveyard_lock);
164         }
165         _leave(" = %p{%d}", server, atomic_read(&server->usage));
166         return server;
167
168         /* found a matching server on the second pass */
169 found_server:
170         _debug("found");
171         afs_get_server(server);
172         write_unlock(&cell->servers_lock);
173         kfree(candidate);
174         goto no_longer_unused;
175
176         /* found a server that seems to be in two cells */
177 server_in_two_cells:
178         write_unlock(&cell->servers_lock);
179         kfree(candidate);
180         afs_dec_servers_outstanding(cell->net);
181         printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n",
182                addr);
183         _leave(" = -EEXIST");
184         return ERR_PTR(-EEXIST);
185 }
186
187 /*
188  * look up a server by its IP address
189  */
190 struct afs_server *afs_find_server(struct afs_net *net,
191                                    const struct sockaddr_rxrpc *srx)
192 {
193         struct afs_server *server = NULL;
194         struct rb_node *p;
195         int diff;
196
197         _enter("{%d,%pIS}", srx->transport.family, &srx->transport);
198
199         read_lock(&net->servers_lock);
200
201         p = net->servers.rb_node;
202         while (p) {
203                 server = rb_entry(p, struct afs_server, master_rb);
204
205                 _debug("- consider %p", p);
206
207                 diff = memcmp(srx, &server->addr, sizeof(*srx));
208                 if (diff < 0) {
209                         p = p->rb_left;
210                 } else if (diff > 0) {
211                         p = p->rb_right;
212                 } else {
213                         afs_get_server(server);
214                         goto found;
215                 }
216         }
217
218         server = NULL;
219 found:
220         read_unlock(&net->servers_lock);
221         _leave(" = %p", server);
222         return server;
223 }
224
225 static 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
237 /*
238  * destroy a server record
239  * - removes from the cell list
240  */
241 void afs_put_server(struct afs_net *net, struct afs_server *server)
242 {
243         if (!server)
244                 return;
245
246         _enter("%p{%d}", server, atomic_read(&server->usage));
247
248         _debug("PUT SERVER %d", atomic_read(&server->usage));
249
250         ASSERTCMP(atomic_read(&server->usage), >, 0);
251
252         if (likely(!atomic_dec_and_test(&server->usage))) {
253                 _leave("");
254                 return;
255         }
256
257         spin_lock(&net->server_graveyard_lock);
258         if (atomic_read(&server->usage) == 0) {
259                 list_move_tail(&server->grave, &net->server_graveyard);
260                 server->time_of_death = ktime_get_real_seconds();
261                 afs_set_server_timer(net, afs_server_timeout);
262         }
263         spin_unlock(&net->server_graveyard_lock);
264         _leave(" [dead]");
265 }
266
267 /*
268  * destroy a dead server
269  */
270 static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
271 {
272         _enter("%p", server);
273
274         afs_fs_give_up_all_callbacks(server, NULL, false);
275         afs_put_cell(net, server->cell);
276         kfree(server);
277         afs_dec_servers_outstanding(net);
278 }
279
280 /*
281  * reap dead server records
282  */
283 void afs_reap_server(struct work_struct *work)
284 {
285         LIST_HEAD(corpses);
286         struct afs_server *server;
287         struct afs_net *net = container_of(work, struct afs_net, server_reaper);
288         unsigned long delay, expiry;
289         time64_t now;
290
291         now = ktime_get_real_seconds();
292         spin_lock(&net->server_graveyard_lock);
293
294         while (!list_empty(&net->server_graveyard)) {
295                 server = list_entry(net->server_graveyard.next,
296                                     struct afs_server, grave);
297
298                 /* the queue is ordered most dead first */
299                 if (net->live) {
300                         expiry = server->time_of_death + afs_server_timeout;
301                         if (expiry > now) {
302                                 delay = (expiry - now);
303                                 afs_set_server_timer(net, delay);
304                                 break;
305                         }
306                 }
307
308                 write_lock(&server->cell->servers_lock);
309                 write_lock(&net->servers_lock);
310                 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);
315                         rb_erase(&server->master_rb, &net->servers);
316                 }
317                 write_unlock(&net->servers_lock);
318                 write_unlock(&server->cell->servers_lock);
319         }
320
321         spin_unlock(&net->server_graveyard_lock);
322
323         /* 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);
327                 afs_destroy_server(net, server);
328         }
329
330         afs_dec_servers_outstanding(net);
331 }
332
333 /*
334  * Discard all the server records from a net namespace when it is destroyed or
335  * the afs module is removed.
336  */
337 void __net_exit afs_purge_servers(struct afs_net *net)
338 {
339         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);
348 }
This page took 0.051204 seconds and 4 git commands to generate.