1/* AFS server record management 2 * 3 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. 4 * 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> 14#include "internal.h" 15 16static unsigned afs_server_timeout = 10; /* server timeout in seconds */ 17 18static void afs_reap_server(struct work_struct *); 19 20/* tree of all the servers, indexed by IP address */ 21static struct rb_root afs_servers = RB_ROOT; 22static DEFINE_RWLOCK(afs_servers_lock); 23 24/* LRU list of all the servers not currently in use */ 25static LIST_HEAD(afs_server_graveyard); 26static DEFINE_SPINLOCK(afs_server_graveyard_lock); 27static DECLARE_DELAYED_WORK(afs_server_reaper, afs_reap_server); 28 29/* 30 * install a server record in the master tree 31 */ 32static int afs_install_server(struct afs_server *server) 33{ 34 struct afs_server *xserver; 35 struct rb_node **pp, *p; 36 int ret; 37 38 _enter("%p", server); 39 40 write_lock(&afs_servers_lock); 41 42 ret = -EEXIST; 43 pp = &afs_servers.rb_node; 44 p = NULL; 45 while (*pp) { 46 p = *pp; 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) 50 pp = &(*pp)->rb_left; 51 else if (server->addr.s_addr > xserver->addr.s_addr) 52 pp = &(*pp)->rb_right; 53 else 54 goto error; 55 } 56 57 rb_link_node(&server->master_rb, p, pp); 58 rb_insert_color(&server->master_rb, &afs_servers); 59 ret = 0; 60 61error: 62 write_unlock(&afs_servers_lock); 63 return ret; 64} 65 66/* 67 * allocate a new server record 68 */ 69static struct afs_server *afs_alloc_server(struct afs_cell *cell, 70 const struct in_addr *addr) 71{ 72 struct afs_server *server; 73 74 _enter(""); 75 76 server = kzalloc(sizeof(struct afs_server), GFP_KERNEL); 77 if (server) { 78 atomic_set(&server->usage, 1); 79 server->cell = cell; 80 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); 91 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)); 95 } else { 96 _leave(" = NULL [nomem]"); 97 } 98 return server; 99} 100 101/* 102 * get an FS-server record for a cell 103 */ 104struct afs_server *afs_lookup_server(struct afs_cell *cell, 105 const struct in_addr *addr) 106{ 107 struct afs_server *server, *candidate; 108 109 _enter("%p,%pI4", cell, &addr->s_addr); 110 111 /* quick scan of the list to see if we already have the server */ 112 read_lock(&cell->servers_lock); 113 114 list_for_each_entry(server, &cell->servers, link) { 115 if (server->addr.s_addr == addr->s_addr) 116 goto found_server_quickly; 117 } 118 read_unlock(&cell->servers_lock); 119 120 candidate = afs_alloc_server(cell, addr); 121 if (!candidate) { 122 _leave(" = -ENOMEM"); 123 return ERR_PTR(-ENOMEM); 124 } 125 126 write_lock(&cell->servers_lock); 127 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) 131 goto found_server; 132 } 133 134 _debug("new"); 135 server = candidate; 136 if (afs_install_server(server) < 0) 137 goto server_in_two_cells; 138 139 afs_get_cell(cell); 140 list_add_tail(&server->link, &cell->servers); 141 142 write_unlock(&cell->servers_lock); 143 _leave(" = %p{%d}", server, atomic_read(&server->usage)); 144 return server; 145 146 /* found a matching server quickly */ 147found_server_quickly: 148 _debug("found quickly"); 149 afs_get_server(server); 150 read_unlock(&cell->servers_lock); 151no_longer_unused: 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); 156 } 157 _leave(" = %p{%d}", server, atomic_read(&server->usage)); 158 return server; 159 160 /* found a matching server on the second pass */ 161found_server: 162 _debug("found"); 163 afs_get_server(server); 164 write_unlock(&cell->servers_lock); 165 kfree(candidate); 166 goto no_longer_unused; 167 168 /* found a server that seems to be in two cells */ 169server_in_two_cells: 170 write_unlock(&cell->servers_lock); 171 kfree(candidate); 172 printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n", 173 addr); 174 _leave(" = -EEXIST"); 175 return ERR_PTR(-EEXIST); 176} 177 178/* 179 * look up a server by its IP address 180 */ 181struct afs_server *afs_find_server(const struct in_addr *_addr) 182{ 183 struct afs_server *server = NULL; 184 struct rb_node *p; 185 struct in_addr addr = *_addr; 186 187 _enter("%pI4", &addr.s_addr); 188 189 read_lock(&afs_servers_lock); 190 191 p = afs_servers.rb_node; 192 while (p) { 193 server = rb_entry(p, struct afs_server, master_rb); 194 195 _debug("- consider %p", p); 196 197 if (addr.s_addr < server->addr.s_addr) { 198 p = p->rb_left; 199 } else if (addr.s_addr > server->addr.s_addr) { 200 p = p->rb_right; 201 } else { 202 afs_get_server(server); 203 goto found; 204 } 205 } 206 207 server = NULL; 208found: 209 read_unlock(&afs_servers_lock); 210 ASSERTIFCMP(server, server->addr.s_addr, ==, addr.s_addr); 211 _leave(" = %p", server); 212 return server; 213} 214 215/* 216 * destroy a server record 217 * - removes from the cell list 218 */ 219void afs_put_server(struct afs_server *server) 220{ 221 if (!server) 222 return; 223 224 _enter("%p{%d}", server, atomic_read(&server->usage)); 225 226 _debug("PUT SERVER %d", atomic_read(&server->usage)); 227 228 ASSERTCMP(atomic_read(&server->usage), >, 0); 229 230 if (likely(!atomic_dec_and_test(&server->usage))) { 231 _leave(""); 232 return; 233 } 234 235 afs_flush_callback_breaks(server); 236 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); 243 } 244 spin_unlock(&afs_server_graveyard_lock); 245 _leave(" [dead]"); 246} 247 248/* 249 * destroy a dead server 250 */ 251static void afs_destroy_server(struct afs_server *server) 252{ 253 _enter("%p", server); 254 255 ASSERTIF(server->cb_break_head != server->cb_break_tail, 256 delayed_work_pending(&server->cb_break_work)); 257 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); 262 263 afs_put_cell(server->cell); 264 kfree(server); 265} 266 267/* 268 * reap dead server records 269 */ 270static void afs_reap_server(struct work_struct *work) 271{ 272 LIST_HEAD(corpses); 273 struct afs_server *server; 274 unsigned long delay, expiry; 275 time_t now; 276 277 now = get_seconds(); 278 spin_lock(&afs_server_graveyard_lock); 279 280 while (!list_empty(&afs_server_graveyard)) { 281 server = list_entry(afs_server_graveyard.next, 282 struct afs_server, grave); 283 284 /* the queue is ordered most dead first */ 285 expiry = server->time_of_death + afs_server_timeout; 286 if (expiry > now) { 287 delay = (expiry - now) * HZ; 288 mod_delayed_work(afs_wq, &afs_server_reaper, delay); 289 break; 290 } 291 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); 296 } else { 297 list_move_tail(&server->grave, &corpses); 298 list_del_init(&server->link); 299 rb_erase(&server->master_rb, &afs_servers); 300 } 301 write_unlock(&afs_servers_lock); 302 write_unlock(&server->cell->servers_lock); 303 } 304 305 spin_unlock(&afs_server_graveyard_lock); 306 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); 312 } 313} 314 315/* 316 * discard all the server records for rmmod 317 */ 318void __exit afs_purge_servers(void) 319{ 320 afs_server_timeout = 0; 321 mod_delayed_work(afs_wq, &afs_server_reaper, 0); 322} 323