1 /* AFS server record management
3 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
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.
12 #include <linux/sched.h>
13 #include <linux/slab.h>
16 static unsigned afs_server_timeout
= 10; /* server timeout in seconds */
18 static void afs_reap_server(struct work_struct
*);
20 /* tree of all the servers, indexed by IP address */
21 static struct rb_root afs_servers
= RB_ROOT
;
22 static DEFINE_RWLOCK(afs_servers_lock
);
24 /* LRU list of all the servers not currently in use */
25 static LIST_HEAD(afs_server_graveyard
);
26 static DEFINE_SPINLOCK(afs_server_graveyard_lock
);
27 static DECLARE_DELAYED_WORK(afs_server_reaper
, afs_reap_server
);
30 * install a server record in the master tree
32 static int afs_install_server(struct afs_server
*server
)
34 struct afs_server
*xserver
;
35 struct rb_node
**pp
, *p
;
40 write_lock(&afs_servers_lock
);
43 pp
= &afs_servers
.rb_node
;
47 _debug("- consider %p", p
);
48 xserver
= rb_entry(p
, struct afs_server
, master_rb
);
49 if (server
->addr
.s_addr
< xserver
->addr
.s_addr
)
51 else if (server
->addr
.s_addr
> xserver
->addr
.s_addr
)
52 pp
= &(*pp
)->rb_right
;
57 rb_link_node(&server
->master_rb
, p
, pp
);
58 rb_insert_color(&server
->master_rb
, &afs_servers
);
62 write_unlock(&afs_servers_lock
);
67 * allocate a new server record
69 static struct afs_server
*afs_alloc_server(struct afs_cell
*cell
,
70 const struct in_addr
*addr
)
72 struct afs_server
*server
;
76 server
= kzalloc(sizeof(struct afs_server
), GFP_KERNEL
);
78 atomic_set(&server
->usage
, 1);
81 INIT_LIST_HEAD(&server
->link
);
82 INIT_LIST_HEAD(&server
->grave
);
83 init_rwsem(&server
->sem
);
84 spin_lock_init(&server
->fs_lock
);
85 server
->fs_vnodes
= RB_ROOT
;
86 server
->cb_promises
= RB_ROOT
;
87 spin_lock_init(&server
->cb_lock
);
88 init_waitqueue_head(&server
->cb_break_waitq
);
89 INIT_DELAYED_WORK(&server
->cb_break_work
,
90 afs_dispatch_give_up_callbacks
);
92 memcpy(&server
->addr
, addr
, sizeof(struct in_addr
));
93 server
->addr
.s_addr
= addr
->s_addr
;
96 _leave(" = %p{%d}", server
, atomic_read(&server
->usage
));
101 * get an FS-server record for a cell
103 struct afs_server
*afs_lookup_server(struct afs_cell
*cell
,
104 const struct in_addr
*addr
)
106 struct afs_server
*server
, *candidate
;
108 _enter("%p,"NIPQUAD_FMT
, cell
, NIPQUAD(addr
->s_addr
));
110 /* quick scan of the list to see if we already have the server */
111 read_lock(&cell
->servers_lock
);
113 list_for_each_entry(server
, &cell
->servers
, link
) {
114 if (server
->addr
.s_addr
== addr
->s_addr
)
115 goto found_server_quickly
;
117 read_unlock(&cell
->servers_lock
);
119 candidate
= afs_alloc_server(cell
, addr
);
121 _leave(" = -ENOMEM");
122 return ERR_PTR(-ENOMEM
);
125 write_lock(&cell
->servers_lock
);
127 /* check the cell's server list again */
128 list_for_each_entry(server
, &cell
->servers
, link
) {
129 if (server
->addr
.s_addr
== addr
->s_addr
)
135 if (afs_install_server(server
) < 0)
136 goto server_in_two_cells
;
139 list_add_tail(&server
->link
, &cell
->servers
);
141 write_unlock(&cell
->servers_lock
);
142 _leave(" = %p{%d}", server
, atomic_read(&server
->usage
));
145 /* found a matching server quickly */
146 found_server_quickly
:
147 _debug("found quickly");
148 afs_get_server(server
);
149 read_unlock(&cell
->servers_lock
);
151 if (!list_empty(&server
->grave
)) {
152 spin_lock(&afs_server_graveyard_lock
);
153 list_del_init(&server
->grave
);
154 spin_unlock(&afs_server_graveyard_lock
);
156 _leave(" = %p{%d}", server
, atomic_read(&server
->usage
));
159 /* found a matching server on the second pass */
162 afs_get_server(server
);
163 write_unlock(&cell
->servers_lock
);
165 goto no_longer_unused
;
167 /* found a server that seems to be in two cells */
169 write_unlock(&cell
->servers_lock
);
171 printk(KERN_NOTICE
"kAFS:"
172 " Server "NIPQUAD_FMT
" appears to be in two cells\n",
174 _leave(" = -EEXIST");
175 return ERR_PTR(-EEXIST
);
179 * look up a server by its IP address
181 struct afs_server
*afs_find_server(const struct in_addr
*_addr
)
183 struct afs_server
*server
= NULL
;
185 struct in_addr addr
= *_addr
;
187 _enter(NIPQUAD_FMT
, NIPQUAD(addr
.s_addr
));
189 read_lock(&afs_servers_lock
);
191 p
= afs_servers
.rb_node
;
193 server
= rb_entry(p
, struct afs_server
, master_rb
);
195 _debug("- consider %p", p
);
197 if (addr
.s_addr
< server
->addr
.s_addr
) {
199 } else if (addr
.s_addr
> server
->addr
.s_addr
) {
202 afs_get_server(server
);
209 read_unlock(&afs_servers_lock
);
210 ASSERTIFCMP(server
, server
->addr
.s_addr
, ==, addr
.s_addr
);
211 _leave(" = %p", server
);
216 * destroy a server record
217 * - removes from the cell list
219 void afs_put_server(struct afs_server
*server
)
224 _enter("%p{%d}", server
, atomic_read(&server
->usage
));
226 _debug("PUT SERVER %d", atomic_read(&server
->usage
));
228 ASSERTCMP(atomic_read(&server
->usage
), >, 0);
230 if (likely(!atomic_dec_and_test(&server
->usage
))) {
235 afs_flush_callback_breaks(server
);
237 spin_lock(&afs_server_graveyard_lock
);
238 if (atomic_read(&server
->usage
) == 0) {
239 list_move_tail(&server
->grave
, &afs_server_graveyard
);
240 server
->time_of_death
= get_seconds();
241 schedule_delayed_work(&afs_server_reaper
,
242 afs_server_timeout
* HZ
);
244 spin_unlock(&afs_server_graveyard_lock
);
249 * destroy a dead server
251 static void afs_destroy_server(struct afs_server
*server
)
253 _enter("%p", server
);
255 ASSERTIF(server
->cb_break_head
!= server
->cb_break_tail
,
256 delayed_work_pending(&server
->cb_break_work
));
258 ASSERTCMP(server
->fs_vnodes
.rb_node
, ==, NULL
);
259 ASSERTCMP(server
->cb_promises
.rb_node
, ==, NULL
);
260 ASSERTCMP(server
->cb_break_head
, ==, server
->cb_break_tail
);
261 ASSERTCMP(atomic_read(&server
->cb_break_n
), ==, 0);
263 afs_put_cell(server
->cell
);
268 * reap dead server records
270 static void afs_reap_server(struct work_struct
*work
)
273 struct afs_server
*server
;
274 unsigned long delay
, expiry
;
278 spin_lock(&afs_server_graveyard_lock
);
280 while (!list_empty(&afs_server_graveyard
)) {
281 server
= list_entry(afs_server_graveyard
.next
,
282 struct afs_server
, grave
);
284 /* the queue is ordered most dead first */
285 expiry
= server
->time_of_death
+ afs_server_timeout
;
287 delay
= (expiry
- now
) * HZ
;
288 if (!schedule_delayed_work(&afs_server_reaper
, delay
)) {
289 cancel_delayed_work(&afs_server_reaper
);
290 schedule_delayed_work(&afs_server_reaper
,
296 write_lock(&server
->cell
->servers_lock
);
297 write_lock(&afs_servers_lock
);
298 if (atomic_read(&server
->usage
) > 0) {
299 list_del_init(&server
->grave
);
301 list_move_tail(&server
->grave
, &corpses
);
302 list_del_init(&server
->link
);
303 rb_erase(&server
->master_rb
, &afs_servers
);
305 write_unlock(&afs_servers_lock
);
306 write_unlock(&server
->cell
->servers_lock
);
309 spin_unlock(&afs_server_graveyard_lock
);
311 /* now reap the corpses we've extracted */
312 while (!list_empty(&corpses
)) {
313 server
= list_entry(corpses
.next
, struct afs_server
, grave
);
314 list_del(&server
->grave
);
315 afs_destroy_server(server
);
320 * discard all the server records for rmmod
322 void __exit
afs_purge_servers(void)
324 afs_server_timeout
= 0;
325 cancel_delayed_work(&afs_server_reaper
);
326 schedule_delayed_work(&afs_server_reaper
, 0);