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,%pI4", cell
, &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: Server %pI4 appears to be in two cells\n",
173 _leave(" = -EEXIST");
174 return ERR_PTR(-EEXIST
);
178 * look up a server by its IP address
180 struct afs_server
*afs_find_server(const struct in_addr
*_addr
)
182 struct afs_server
*server
= NULL
;
184 struct in_addr addr
= *_addr
;
186 _enter("%pI4", &addr
.s_addr
);
188 read_lock(&afs_servers_lock
);
190 p
= afs_servers
.rb_node
;
192 server
= rb_entry(p
, struct afs_server
, master_rb
);
194 _debug("- consider %p", p
);
196 if (addr
.s_addr
< server
->addr
.s_addr
) {
198 } else if (addr
.s_addr
> server
->addr
.s_addr
) {
201 afs_get_server(server
);
208 read_unlock(&afs_servers_lock
);
209 ASSERTIFCMP(server
, server
->addr
.s_addr
, ==, addr
.s_addr
);
210 _leave(" = %p", server
);
215 * destroy a server record
216 * - removes from the cell list
218 void afs_put_server(struct afs_server
*server
)
223 _enter("%p{%d}", server
, atomic_read(&server
->usage
));
225 _debug("PUT SERVER %d", atomic_read(&server
->usage
));
227 ASSERTCMP(atomic_read(&server
->usage
), >, 0);
229 if (likely(!atomic_dec_and_test(&server
->usage
))) {
234 afs_flush_callback_breaks(server
);
236 spin_lock(&afs_server_graveyard_lock
);
237 if (atomic_read(&server
->usage
) == 0) {
238 list_move_tail(&server
->grave
, &afs_server_graveyard
);
239 server
->time_of_death
= get_seconds();
240 schedule_delayed_work(&afs_server_reaper
,
241 afs_server_timeout
* HZ
);
243 spin_unlock(&afs_server_graveyard_lock
);
248 * destroy a dead server
250 static void afs_destroy_server(struct afs_server
*server
)
252 _enter("%p", server
);
254 ASSERTIF(server
->cb_break_head
!= server
->cb_break_tail
,
255 delayed_work_pending(&server
->cb_break_work
));
257 ASSERTCMP(server
->fs_vnodes
.rb_node
, ==, NULL
);
258 ASSERTCMP(server
->cb_promises
.rb_node
, ==, NULL
);
259 ASSERTCMP(server
->cb_break_head
, ==, server
->cb_break_tail
);
260 ASSERTCMP(atomic_read(&server
->cb_break_n
), ==, 0);
262 afs_put_cell(server
->cell
);
267 * reap dead server records
269 static void afs_reap_server(struct work_struct
*work
)
272 struct afs_server
*server
;
273 unsigned long delay
, expiry
;
277 spin_lock(&afs_server_graveyard_lock
);
279 while (!list_empty(&afs_server_graveyard
)) {
280 server
= list_entry(afs_server_graveyard
.next
,
281 struct afs_server
, grave
);
283 /* the queue is ordered most dead first */
284 expiry
= server
->time_of_death
+ afs_server_timeout
;
286 delay
= (expiry
- now
) * HZ
;
287 if (!schedule_delayed_work(&afs_server_reaper
, delay
)) {
288 cancel_delayed_work(&afs_server_reaper
);
289 schedule_delayed_work(&afs_server_reaper
,
295 write_lock(&server
->cell
->servers_lock
);
296 write_lock(&afs_servers_lock
);
297 if (atomic_read(&server
->usage
) > 0) {
298 list_del_init(&server
->grave
);
300 list_move_tail(&server
->grave
, &corpses
);
301 list_del_init(&server
->link
);
302 rb_erase(&server
->master_rb
, &afs_servers
);
304 write_unlock(&afs_servers_lock
);
305 write_unlock(&server
->cell
->servers_lock
);
308 spin_unlock(&afs_server_graveyard_lock
);
310 /* now reap the corpses we've extracted */
311 while (!list_empty(&corpses
)) {
312 server
= list_entry(corpses
.next
, struct afs_server
, grave
);
313 list_del(&server
->grave
);
314 afs_destroy_server(server
);
319 * discard all the server records for rmmod
321 void __exit
afs_purge_servers(void)
323 afs_server_timeout
= 0;
324 cancel_delayed_work(&afs_server_reaper
);
325 schedule_delayed_work(&afs_server_reaper
, 0);