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
;
94 _leave(" = %p{%d}", server
, atomic_read(&server
->usage
));
96 _leave(" = NULL [nomem]");
102 * get an FS-server record for a cell
104 struct afs_server
*afs_lookup_server(struct afs_cell
*cell
,
105 const struct in_addr
*addr
)
107 struct afs_server
*server
, *candidate
;
109 _enter("%p,%pI4", cell
, &addr
->s_addr
);
111 /* quick scan of the list to see if we already have the server */
112 read_lock(&cell
->servers_lock
);
114 list_for_each_entry(server
, &cell
->servers
, link
) {
115 if (server
->addr
.s_addr
== addr
->s_addr
)
116 goto found_server_quickly
;
118 read_unlock(&cell
->servers_lock
);
120 candidate
= afs_alloc_server(cell
, addr
);
122 _leave(" = -ENOMEM");
123 return ERR_PTR(-ENOMEM
);
126 write_lock(&cell
->servers_lock
);
128 /* check the cell's server list again */
129 list_for_each_entry(server
, &cell
->servers
, link
) {
130 if (server
->addr
.s_addr
== addr
->s_addr
)
136 if (afs_install_server(server
) < 0)
137 goto server_in_two_cells
;
140 list_add_tail(&server
->link
, &cell
->servers
);
142 write_unlock(&cell
->servers_lock
);
143 _leave(" = %p{%d}", server
, atomic_read(&server
->usage
));
146 /* found a matching server quickly */
147 found_server_quickly
:
148 _debug("found quickly");
149 afs_get_server(server
);
150 read_unlock(&cell
->servers_lock
);
152 if (!list_empty(&server
->grave
)) {
153 spin_lock(&afs_server_graveyard_lock
);
154 list_del_init(&server
->grave
);
155 spin_unlock(&afs_server_graveyard_lock
);
157 _leave(" = %p{%d}", server
, atomic_read(&server
->usage
));
160 /* found a matching server on the second pass */
163 afs_get_server(server
);
164 write_unlock(&cell
->servers_lock
);
166 goto no_longer_unused
;
168 /* found a server that seems to be in two cells */
170 write_unlock(&cell
->servers_lock
);
172 printk(KERN_NOTICE
"kAFS: Server %pI4 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("%pI4", &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 queue_delayed_work(afs_wq
, &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 mod_delayed_work(afs_wq
, &afs_server_reaper
, delay
);
292 write_lock(&server
->cell
->servers_lock
);
293 write_lock(&afs_servers_lock
);
294 if (atomic_read(&server
->usage
) > 0) {
295 list_del_init(&server
->grave
);
297 list_move_tail(&server
->grave
, &corpses
);
298 list_del_init(&server
->link
);
299 rb_erase(&server
->master_rb
, &afs_servers
);
301 write_unlock(&afs_servers_lock
);
302 write_unlock(&server
->cell
->servers_lock
);
305 spin_unlock(&afs_server_graveyard_lock
);
307 /* now reap the corpses we've extracted */
308 while (!list_empty(&corpses
)) {
309 server
= list_entry(corpses
.next
, struct afs_server
, grave
);
310 list_del(&server
->grave
);
311 afs_destroy_server(server
);
316 * discard all the server records for rmmod
318 void __exit
afs_purge_servers(void)
320 afs_server_timeout
= 0;
321 mod_delayed_work(afs_wq
, &afs_server_reaper
, 0);