📄 vlocation.c
字号:
/* AFS volume location management * * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include "internal.h"static unsigned afs_vlocation_timeout = 10; /* volume location timeout in seconds */static unsigned afs_vlocation_update_timeout = 10 * 60;static void afs_vlocation_reaper(struct work_struct *);static void afs_vlocation_updater(struct work_struct *);static LIST_HEAD(afs_vlocation_updates);static LIST_HEAD(afs_vlocation_graveyard);static DEFINE_SPINLOCK(afs_vlocation_updates_lock);static DEFINE_SPINLOCK(afs_vlocation_graveyard_lock);static DECLARE_DELAYED_WORK(afs_vlocation_reap, afs_vlocation_reaper);static DECLARE_DELAYED_WORK(afs_vlocation_update, afs_vlocation_updater);static struct workqueue_struct *afs_vlocation_update_worker;/* * iterate through the VL servers in a cell until one of them admits knowing * about the volume in question */static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl, struct key *key, struct afs_cache_vlocation *vldb){ struct afs_cell *cell = vl->cell; struct in_addr addr; int count, ret; _enter("%s,%s", cell->name, vl->vldb.name); down_write(&vl->cell->vl_sem); ret = -ENOMEDIUM; for (count = cell->vl_naddrs; count > 0; count--) { addr = cell->vl_addrs[cell->vl_curr_svix]; _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr); /* attempt to access the VL server */ ret = afs_vl_get_entry_by_name(&addr, key, vl->vldb.name, vldb, &afs_sync_call); switch (ret) { case 0: goto out; case -ENOMEM: case -ENONET: case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: if (ret == -ENOMEM || ret == -ENONET) goto out; goto rotate; case -ENOMEDIUM: goto out; default: ret = -EIO; goto rotate; } /* rotate the server records upon lookup failure */ rotate: cell->vl_curr_svix++; cell->vl_curr_svix %= cell->vl_naddrs; }out: up_write(&vl->cell->vl_sem); _leave(" = %d", ret); return ret;}/* * iterate through the VL servers in a cell until one of them admits knowing * about the volume in question */static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl, struct key *key, afs_volid_t volid, afs_voltype_t voltype, struct afs_cache_vlocation *vldb){ struct afs_cell *cell = vl->cell; struct in_addr addr; int count, ret; _enter("%s,%x,%d,", cell->name, volid, voltype); down_write(&vl->cell->vl_sem); ret = -ENOMEDIUM; for (count = cell->vl_naddrs; count > 0; count--) { addr = cell->vl_addrs[cell->vl_curr_svix]; _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr); /* attempt to access the VL server */ ret = afs_vl_get_entry_by_id(&addr, key, volid, voltype, vldb, &afs_sync_call); switch (ret) { case 0: goto out; case -ENOMEM: case -ENONET: case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: if (ret == -ENOMEM || ret == -ENONET) goto out; goto rotate; case -EBUSY: vl->upd_busy_cnt++; if (vl->upd_busy_cnt <= 3) { if (vl->upd_busy_cnt > 1) { /* second+ BUSY - sleep a little bit */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); __set_current_state(TASK_RUNNING); } continue; } break; case -ENOMEDIUM: vl->upd_rej_cnt++; goto rotate; default: ret = -EIO; goto rotate; } /* rotate the server records upon lookup failure */ rotate: cell->vl_curr_svix++; cell->vl_curr_svix %= cell->vl_naddrs; vl->upd_busy_cnt = 0; }out: if (ret < 0 && vl->upd_rej_cnt > 0) { printk(KERN_NOTICE "kAFS:" " Active volume no longer valid '%s'\n", vl->vldb.name); vl->valid = 0; ret = -ENOMEDIUM; } up_write(&vl->cell->vl_sem); _leave(" = %d", ret); return ret;}/* * allocate a volume location record */static struct afs_vlocation *afs_vlocation_alloc(struct afs_cell *cell, const char *name, size_t namesz){ struct afs_vlocation *vl; vl = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL); if (vl) { vl->cell = cell; vl->state = AFS_VL_NEW; atomic_set(&vl->usage, 1); INIT_LIST_HEAD(&vl->link); INIT_LIST_HEAD(&vl->grave); INIT_LIST_HEAD(&vl->update); init_waitqueue_head(&vl->waitq); spin_lock_init(&vl->lock); memcpy(vl->vldb.name, name, namesz); } _leave(" = %p", vl); return vl;}/* * update record if we found it in the cache */static int afs_vlocation_update_record(struct afs_vlocation *vl, struct key *key, struct afs_cache_vlocation *vldb){ afs_voltype_t voltype; afs_volid_t vid; int ret; /* try to look up a cached volume in the cell VL databases by ID */ _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", vl->vldb.name, vl->vldb.vidmask, ntohl(vl->vldb.servers[0].s_addr), vl->vldb.srvtmask[0], ntohl(vl->vldb.servers[1].s_addr), vl->vldb.srvtmask[1], ntohl(vl->vldb.servers[2].s_addr), vl->vldb.srvtmask[2]); _debug("Vids: %08x %08x %08x", vl->vldb.vid[0], vl->vldb.vid[1], vl->vldb.vid[2]); if (vl->vldb.vidmask & AFS_VOL_VTM_RW) { vid = vl->vldb.vid[0]; voltype = AFSVL_RWVOL; } else if (vl->vldb.vidmask & AFS_VOL_VTM_RO) { vid = vl->vldb.vid[1]; voltype = AFSVL_ROVOL; } else if (vl->vldb.vidmask & AFS_VOL_VTM_BAK) { vid = vl->vldb.vid[2]; voltype = AFSVL_BACKVOL; } else { BUG(); vid = 0; voltype = 0; } /* contact the server to make sure the volume is still available * - TODO: need to handle disconnected operation here */ ret = afs_vlocation_access_vl_by_id(vl, key, vid, voltype, vldb); switch (ret) { /* net error */ default: printk(KERN_WARNING "kAFS:" " failed to update volume '%s' (%x) up in '%s': %d\n", vl->vldb.name, vid, vl->cell->name, ret); _leave(" = %d", ret); return ret; /* pulled from local cache into memory */ case 0: _leave(" = 0"); return 0; /* uh oh... looks like the volume got deleted */ case -ENOMEDIUM: printk(KERN_ERR "kAFS:" " volume '%s' (%x) does not exist '%s'\n", vl->vldb.name, vid, vl->cell->name); /* TODO: make existing record unavailable */ _leave(" = %d", ret); return ret; }}/* * apply the update to a VL record */static void afs_vlocation_apply_update(struct afs_vlocation *vl, struct afs_cache_vlocation *vldb){ _debug("Done VL Lookup: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", vldb->name, vldb->vidmask, ntohl(vldb->servers[0].s_addr), vldb->srvtmask[0], ntohl(vldb->servers[1].s_addr), vldb->srvtmask[1], ntohl(vldb->servers[2].s_addr), vldb->srvtmask[2]); _debug("Vids: %08x %08x %08x", vldb->vid[0], vldb->vid[1], vldb->vid[2]); if (strcmp(vldb->name, vl->vldb.name) != 0) printk(KERN_NOTICE "kAFS:" " name of volume '%s' changed to '%s' on server\n", vl->vldb.name, vldb->name); vl->vldb = *vldb;#ifdef AFS_CACHING_SUPPORT /* update volume entry in local cache */ cachefs_update_cookie(vl->cache);#endif}/* * fill in a volume location record, consulting the cache and the VL server * both */static int afs_vlocation_fill_in_record(struct afs_vlocation *vl, struct key *key){ struct afs_cache_vlocation vldb; int ret; _enter(""); ASSERTCMP(vl->valid, ==, 0); memset(&vldb, 0, sizeof(vldb)); /* see if we have an in-cache copy (will set vl->valid if there is) */#ifdef AFS_CACHING_SUPPORT cachefs_acquire_cookie(cell->cache, &afs_volume_cache_index_def, vlocation, &vl->cache);#endif if (vl->valid) { /* try to update a known volume in the cell VL databases by * ID as the name may have changed */ _debug("found in cache"); ret = afs_vlocation_update_record(vl, key, &vldb); } else { /* try to look up an unknown volume in the cell VL databases by * name */ ret = afs_vlocation_access_vl_by_name(vl, key, &vldb); if (ret < 0) { printk("kAFS: failed to locate '%s' in cell '%s'\n", vl->vldb.name, vl->cell->name); return ret; } } afs_vlocation_apply_update(vl, &vldb); _leave(" = 0"); return 0;}/* * queue a vlocation record for updates */static void afs_vlocation_queue_for_updates(struct afs_vlocation *vl){ struct afs_vlocation *xvl; /* wait at least 10 minutes before updating... */ vl->update_at = get_seconds() + afs_vlocation_update_timeout; spin_lock(&afs_vlocation_updates_lock); if (!list_empty(&afs_vlocation_updates)) { /* ... but wait at least 1 second more than the newest record * already queued so that we don't spam the VL server suddenly * with lots of requests */ xvl = list_entry(afs_vlocation_updates.prev, struct afs_vlocation, update); if (vl->update_at <= xvl->update_at) vl->update_at = xvl->update_at + 1; } else { queue_delayed_work(afs_vlocation_update_worker, &afs_vlocation_update, afs_vlocation_update_timeout * HZ); } list_add_tail(&vl->update, &afs_vlocation_updates);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -