mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 07:02:06 +00:00 
			
		
		
		
	 f8314dc60c
			
		
	
	
		f8314dc60c
		
	
	
	
	
		
			
			Conversions from kmalloc+memset to kzalloc. Signed-off-by: Panagiotis Issaris <takis@issaris.org> Jffs2-bit-acked-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
		
			
				
	
	
		
			952 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			952 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* vlocation.c: volume location management
 | |
|  *
 | |
|  * Copyright (C) 2002 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/slab.h>
 | |
| #include <linux/fs.h>
 | |
| #include <linux/pagemap.h>
 | |
| #include "volume.h"
 | |
| #include "cell.h"
 | |
| #include "cmservice.h"
 | |
| #include "fsclient.h"
 | |
| #include "vlclient.h"
 | |
| #include "kafstimod.h"
 | |
| #include <rxrpc/connection.h>
 | |
| #include "internal.h"
 | |
| 
 | |
| #define AFS_VLDB_TIMEOUT HZ*1000
 | |
| 
 | |
| static void afs_vlocation_update_timer(struct afs_timer *timer);
 | |
| static void afs_vlocation_update_attend(struct afs_async_op *op);
 | |
| static void afs_vlocation_update_discard(struct afs_async_op *op);
 | |
| static void __afs_put_vlocation(struct afs_vlocation *vlocation);
 | |
| 
 | |
| static void __afs_vlocation_timeout(struct afs_timer *timer)
 | |
| {
 | |
| 	struct afs_vlocation *vlocation =
 | |
| 		list_entry(timer, struct afs_vlocation, timeout);
 | |
| 
 | |
| 	_debug("VL TIMEOUT [%s{u=%d}]",
 | |
| 	       vlocation->vldb.name, atomic_read(&vlocation->usage));
 | |
| 
 | |
| 	afs_vlocation_do_timeout(vlocation);
 | |
| }
 | |
| 
 | |
| static const struct afs_timer_ops afs_vlocation_timer_ops = {
 | |
| 	.timed_out	= __afs_vlocation_timeout,
 | |
| };
 | |
| 
 | |
| static const struct afs_timer_ops afs_vlocation_update_timer_ops = {
 | |
| 	.timed_out	= afs_vlocation_update_timer,
 | |
| };
 | |
| 
 | |
| static const struct afs_async_op_ops afs_vlocation_update_op_ops = {
 | |
| 	.attend		= afs_vlocation_update_attend,
 | |
| 	.discard	= afs_vlocation_update_discard,
 | |
| };
 | |
| 
 | |
| static LIST_HEAD(afs_vlocation_update_pendq);	/* queue of VLs awaiting update */
 | |
| static struct afs_vlocation *afs_vlocation_update;	/* VL currently being updated */
 | |
| static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */
 | |
| 
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
| static cachefs_match_val_t afs_vlocation_cache_match(void *target,
 | |
| 						     const void *entry);
 | |
| static void afs_vlocation_cache_update(void *source, void *entry);
 | |
| 
 | |
| struct cachefs_index_def afs_vlocation_cache_index_def = {
 | |
| 	.name		= "vldb",
 | |
| 	.data_size	= sizeof(struct afs_cache_vlocation),
 | |
| 	.keys[0]	= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
 | |
| 	.match		= afs_vlocation_cache_match,
 | |
| 	.update		= afs_vlocation_cache_update,
 | |
| };
 | |
| #endif
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * iterate through the VL servers in a cell until one of them admits knowing
 | |
|  * about the volume in question
 | |
|  * - caller must have cell->vl_sem write-locked
 | |
|  */
 | |
| static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation,
 | |
| 					   const char *name,
 | |
| 					   unsigned namesz,
 | |
| 					   struct afs_cache_vlocation *vldb)
 | |
| {
 | |
| 	struct afs_server *server = NULL;
 | |
| 	struct afs_cell *cell = vlocation->cell;
 | |
| 	int count, ret;
 | |
| 
 | |
| 	_enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz);
 | |
| 
 | |
| 	ret = -ENOMEDIUM;
 | |
| 	for (count = cell->vl_naddrs; count > 0; count--) {
 | |
| 		_debug("CellServ[%hu]: %08x",
 | |
| 		       cell->vl_curr_svix,
 | |
| 		       cell->vl_addrs[cell->vl_curr_svix].s_addr);
 | |
| 
 | |
| 		/* try and create a server */
 | |
| 		ret = afs_server_lookup(cell,
 | |
| 					&cell->vl_addrs[cell->vl_curr_svix],
 | |
| 					&server);
 | |
| 		switch (ret) {
 | |
| 		case 0:
 | |
| 			break;
 | |
| 		case -ENOMEM:
 | |
| 		case -ENONET:
 | |
| 			goto out;
 | |
| 		default:
 | |
| 			goto rotate;
 | |
| 		}
 | |
| 
 | |
| 		/* attempt to access the VL server */
 | |
| 		ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb);
 | |
| 		switch (ret) {
 | |
| 		case 0:
 | |
| 			afs_put_server(server);
 | |
| 			goto out;
 | |
| 		case -ENOMEM:
 | |
| 		case -ENONET:
 | |
| 		case -ENETUNREACH:
 | |
| 		case -EHOSTUNREACH:
 | |
| 		case -ECONNREFUSED:
 | |
| 			down_write(&server->sem);
 | |
| 			if (server->vlserver) {
 | |
| 				rxrpc_put_connection(server->vlserver);
 | |
| 				server->vlserver = NULL;
 | |
| 			}
 | |
| 			up_write(&server->sem);
 | |
| 			afs_put_server(server);
 | |
| 			if (ret == -ENOMEM || ret == -ENONET)
 | |
| 				goto out;
 | |
| 			goto rotate;
 | |
| 		case -ENOMEDIUM:
 | |
| 			afs_put_server(server);
 | |
| 			goto out;
 | |
| 		default:
 | |
| 			afs_put_server(server);
 | |
| 			ret = -ENOMEDIUM;
 | |
| 			goto rotate;
 | |
| 		}
 | |
| 
 | |
| 		/* rotate the server records upon lookup failure */
 | |
| 	rotate:
 | |
| 		cell->vl_curr_svix++;
 | |
| 		cell->vl_curr_svix %= cell->vl_naddrs;
 | |
| 	}
 | |
| 
 | |
|  out:
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret;
 | |
| 
 | |
| } /* end afs_vlocation_access_vl_by_name() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * iterate through the VL servers in a cell until one of them admits knowing
 | |
|  * about the volume in question
 | |
|  * - caller must have cell->vl_sem write-locked
 | |
|  */
 | |
| static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation,
 | |
| 					 afs_volid_t volid,
 | |
| 					 afs_voltype_t voltype,
 | |
| 					 struct afs_cache_vlocation *vldb)
 | |
| {
 | |
| 	struct afs_server *server = NULL;
 | |
| 	struct afs_cell *cell = vlocation->cell;
 | |
| 	int count, ret;
 | |
| 
 | |
| 	_enter("%s,%x,%d,", cell->name, volid, voltype);
 | |
| 
 | |
| 	ret = -ENOMEDIUM;
 | |
| 	for (count = cell->vl_naddrs; count > 0; count--) {
 | |
| 		_debug("CellServ[%hu]: %08x",
 | |
| 		       cell->vl_curr_svix,
 | |
| 		       cell->vl_addrs[cell->vl_curr_svix].s_addr);
 | |
| 
 | |
| 		/* try and create a server */
 | |
| 		ret = afs_server_lookup(cell,
 | |
| 					&cell->vl_addrs[cell->vl_curr_svix],
 | |
| 					&server);
 | |
| 		switch (ret) {
 | |
| 		case 0:
 | |
| 			break;
 | |
| 		case -ENOMEM:
 | |
| 		case -ENONET:
 | |
| 			goto out;
 | |
| 		default:
 | |
| 			goto rotate;
 | |
| 		}
 | |
| 
 | |
| 		/* attempt to access the VL server */
 | |
| 		ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb);
 | |
| 		switch (ret) {
 | |
| 		case 0:
 | |
| 			afs_put_server(server);
 | |
| 			goto out;
 | |
| 		case -ENOMEM:
 | |
| 		case -ENONET:
 | |
| 		case -ENETUNREACH:
 | |
| 		case -EHOSTUNREACH:
 | |
| 		case -ECONNREFUSED:
 | |
| 			down_write(&server->sem);
 | |
| 			if (server->vlserver) {
 | |
| 				rxrpc_put_connection(server->vlserver);
 | |
| 				server->vlserver = NULL;
 | |
| 			}
 | |
| 			up_write(&server->sem);
 | |
| 			afs_put_server(server);
 | |
| 			if (ret == -ENOMEM || ret == -ENONET)
 | |
| 				goto out;
 | |
| 			goto rotate;
 | |
| 		case -ENOMEDIUM:
 | |
| 			afs_put_server(server);
 | |
| 			goto out;
 | |
| 		default:
 | |
| 			afs_put_server(server);
 | |
| 			ret = -ENOMEDIUM;
 | |
| 			goto rotate;
 | |
| 		}
 | |
| 
 | |
| 		/* rotate the server records upon lookup failure */
 | |
| 	rotate:
 | |
| 		cell->vl_curr_svix++;
 | |
| 		cell->vl_curr_svix %= cell->vl_naddrs;
 | |
| 	}
 | |
| 
 | |
|  out:
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret;
 | |
| 
 | |
| } /* end afs_vlocation_access_vl_by_id() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * lookup volume location
 | |
|  * - caller must have cell->vol_sem write-locked
 | |
|  * - iterate through the VL servers in a cell until one of them admits knowing
 | |
|  *   about the volume in question
 | |
|  * - lookup in the local cache if not able to find on the VL server
 | |
|  * - insert/update in the local cache if did get a VL response
 | |
|  */
 | |
| int afs_vlocation_lookup(struct afs_cell *cell,
 | |
| 			 const char *name,
 | |
| 			 unsigned namesz,
 | |
| 			 struct afs_vlocation **_vlocation)
 | |
| {
 | |
| 	struct afs_cache_vlocation vldb;
 | |
| 	struct afs_vlocation *vlocation;
 | |
| 	afs_voltype_t voltype;
 | |
| 	afs_volid_t vid;
 | |
| 	int active = 0, ret;
 | |
| 
 | |
| 	_enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz);
 | |
| 
 | |
| 	if (namesz > sizeof(vlocation->vldb.name)) {
 | |
| 		_leave(" = -ENAMETOOLONG");
 | |
| 		return -ENAMETOOLONG;
 | |
| 	}
 | |
| 
 | |
| 	/* search the cell's active list first */
 | |
| 	list_for_each_entry(vlocation, &cell->vl_list, link) {
 | |
| 		if (namesz < sizeof(vlocation->vldb.name) &&
 | |
| 		    vlocation->vldb.name[namesz] != '\0')
 | |
| 			continue;
 | |
| 
 | |
| 		if (memcmp(vlocation->vldb.name, name, namesz) == 0)
 | |
| 			goto found_in_memory;
 | |
| 	}
 | |
| 
 | |
| 	/* search the cell's graveyard list second */
 | |
| 	spin_lock(&cell->vl_gylock);
 | |
| 	list_for_each_entry(vlocation, &cell->vl_graveyard, link) {
 | |
| 		if (namesz < sizeof(vlocation->vldb.name) &&
 | |
| 		    vlocation->vldb.name[namesz] != '\0')
 | |
| 			continue;
 | |
| 
 | |
| 		if (memcmp(vlocation->vldb.name, name, namesz) == 0)
 | |
| 			goto found_in_graveyard;
 | |
| 	}
 | |
| 	spin_unlock(&cell->vl_gylock);
 | |
| 
 | |
| 	/* not in the cell's in-memory lists - create a new record */
 | |
| 	vlocation = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL);
 | |
| 	if (!vlocation)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	atomic_set(&vlocation->usage, 1);
 | |
| 	INIT_LIST_HEAD(&vlocation->link);
 | |
| 	rwlock_init(&vlocation->lock);
 | |
| 	memcpy(vlocation->vldb.name, name, namesz);
 | |
| 
 | |
| 	afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops);
 | |
| 	afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops);
 | |
| 	afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops);
 | |
| 
 | |
| 	afs_get_cell(cell);
 | |
| 	vlocation->cell = cell;
 | |
| 
 | |
| 	list_add_tail(&vlocation->link, &cell->vl_list);
 | |
| 
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
| 	/* we want to store it in the cache, plus it might already be
 | |
| 	 * encached */
 | |
| 	cachefs_acquire_cookie(cell->cache,
 | |
| 			       &afs_volume_cache_index_def,
 | |
| 			       vlocation,
 | |
| 			       &vlocation->cache);
 | |
| 
 | |
| 	if (vlocation->valid)
 | |
| 		goto found_in_cache;
 | |
| #endif
 | |
| 
 | |
| 	/* try to look up an unknown volume in the cell VL databases by name */
 | |
| 	ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb);
 | |
| 	if (ret < 0) {
 | |
| 		printk("kAFS: failed to locate '%*.*s' in cell '%s'\n",
 | |
| 		       namesz, namesz, name, cell->name);
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
| 	goto found_on_vlserver;
 | |
| 
 | |
|  found_in_graveyard:
 | |
| 	/* found in the graveyard - resurrect */
 | |
| 	_debug("found in graveyard");
 | |
| 	atomic_inc(&vlocation->usage);
 | |
| 	list_move_tail(&vlocation->link, &cell->vl_list);
 | |
| 	spin_unlock(&cell->vl_gylock);
 | |
| 
 | |
| 	afs_kafstimod_del_timer(&vlocation->timeout);
 | |
| 	goto active;
 | |
| 
 | |
|  found_in_memory:
 | |
| 	/* found in memory - check to see if it's active */
 | |
| 	_debug("found in memory");
 | |
| 	atomic_inc(&vlocation->usage);
 | |
| 
 | |
|  active:
 | |
| 	active = 1;
 | |
| 
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
|  found_in_cache:
 | |
| #endif
 | |
| 	/* try to look up a cached volume in the cell VL databases by ID */
 | |
| 	_debug("found in cache");
 | |
| 
 | |
| 	_debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
 | |
| 	       vlocation->vldb.name,
 | |
| 	       vlocation->vldb.vidmask,
 | |
| 	       ntohl(vlocation->vldb.servers[0].s_addr),
 | |
| 	       vlocation->vldb.srvtmask[0],
 | |
| 	       ntohl(vlocation->vldb.servers[1].s_addr),
 | |
| 	       vlocation->vldb.srvtmask[1],
 | |
| 	       ntohl(vlocation->vldb.servers[2].s_addr),
 | |
| 	       vlocation->vldb.srvtmask[2]
 | |
| 	       );
 | |
| 
 | |
| 	_debug("Vids: %08x %08x %08x",
 | |
| 	       vlocation->vldb.vid[0],
 | |
| 	       vlocation->vldb.vid[1],
 | |
| 	       vlocation->vldb.vid[2]);
 | |
| 
 | |
| 	if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
 | |
| 		vid = vlocation->vldb.vid[0];
 | |
| 		voltype = AFSVL_RWVOL;
 | |
| 	}
 | |
| 	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
 | |
| 		vid = vlocation->vldb.vid[1];
 | |
| 		voltype = AFSVL_ROVOL;
 | |
| 	}
 | |
| 	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
 | |
| 		vid = vlocation->vldb.vid[2];
 | |
| 		voltype = AFSVL_BACKVOL;
 | |
| 	}
 | |
| 	else {
 | |
| 		BUG();
 | |
| 		vid = 0;
 | |
| 		voltype = 0;
 | |
| 	}
 | |
| 
 | |
| 	ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb);
 | |
| 	switch (ret) {
 | |
| 		/* net error */
 | |
| 	default:
 | |
| 		printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n",
 | |
| 		       namesz, namesz, name, vid, cell->name, ret);
 | |
| 		goto error;
 | |
| 
 | |
| 		/* pulled from local cache into memory */
 | |
| 	case 0:
 | |
| 		goto found_on_vlserver;
 | |
| 
 | |
| 		/* uh oh... looks like the volume got deleted */
 | |
| 	case -ENOMEDIUM:
 | |
| 		printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n",
 | |
| 		       namesz, namesz, name, vid, cell->name);
 | |
| 
 | |
| 		/* TODO: make existing record unavailable */
 | |
| 		goto error;
 | |
| 	}
 | |
| 
 | |
|  found_on_vlserver:
 | |
| 	_debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }",
 | |
| 	       namesz, namesz, 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 ((namesz < sizeof(vlocation->vldb.name) &&
 | |
| 	     vlocation->vldb.name[namesz] != '\0') ||
 | |
| 	    memcmp(vldb.name, name, namesz) != 0)
 | |
| 		printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n",
 | |
| 		       namesz, namesz, name, vldb.name);
 | |
| 
 | |
| 	memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
 | |
| 
 | |
| 	afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ);
 | |
| 
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
| 	/* update volume entry in local cache */
 | |
| 	cachefs_update_cookie(vlocation->cache);
 | |
| #endif
 | |
| 
 | |
| 	*_vlocation = vlocation;
 | |
| 	_leave(" = 0 (%p)",vlocation);
 | |
| 	return 0;
 | |
| 
 | |
|  error:
 | |
| 	if (vlocation) {
 | |
| 		if (active) {
 | |
| 			__afs_put_vlocation(vlocation);
 | |
| 		}
 | |
| 		else {
 | |
| 			list_del(&vlocation->link);
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
| 			cachefs_relinquish_cookie(vlocation->cache, 0);
 | |
| #endif
 | |
| 			afs_put_cell(vlocation->cell);
 | |
| 			kfree(vlocation);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret;
 | |
| } /* end afs_vlocation_lookup() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * finish using a volume location record
 | |
|  * - caller must have cell->vol_sem write-locked
 | |
|  */
 | |
| static void __afs_put_vlocation(struct afs_vlocation *vlocation)
 | |
| {
 | |
| 	struct afs_cell *cell;
 | |
| 
 | |
| 	if (!vlocation)
 | |
| 		return;
 | |
| 
 | |
| 	_enter("%s", vlocation->vldb.name);
 | |
| 
 | |
| 	cell = vlocation->cell;
 | |
| 
 | |
| 	/* sanity check */
 | |
| 	BUG_ON(atomic_read(&vlocation->usage) <= 0);
 | |
| 
 | |
| 	spin_lock(&cell->vl_gylock);
 | |
| 	if (likely(!atomic_dec_and_test(&vlocation->usage))) {
 | |
| 		spin_unlock(&cell->vl_gylock);
 | |
| 		_leave("");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* move to graveyard queue */
 | |
| 	list_move_tail(&vlocation->link,&cell->vl_graveyard);
 | |
| 
 | |
| 	/* remove from pending timeout queue (refcounted if actually being
 | |
| 	 * updated) */
 | |
| 	list_del_init(&vlocation->upd_op.link);
 | |
| 
 | |
| 	/* time out in 10 secs */
 | |
| 	afs_kafstimod_del_timer(&vlocation->upd_timer);
 | |
| 	afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ);
 | |
| 
 | |
| 	spin_unlock(&cell->vl_gylock);
 | |
| 
 | |
| 	_leave(" [killed]");
 | |
| } /* end __afs_put_vlocation() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * finish using a volume location record
 | |
|  */
 | |
| void afs_put_vlocation(struct afs_vlocation *vlocation)
 | |
| {
 | |
| 	if (vlocation) {
 | |
| 		struct afs_cell *cell = vlocation->cell;
 | |
| 
 | |
| 		down_write(&cell->vl_sem);
 | |
| 		__afs_put_vlocation(vlocation);
 | |
| 		up_write(&cell->vl_sem);
 | |
| 	}
 | |
| } /* end afs_put_vlocation() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * timeout vlocation record
 | |
|  * - removes from the cell's graveyard if the usage count is zero
 | |
|  */
 | |
| void afs_vlocation_do_timeout(struct afs_vlocation *vlocation)
 | |
| {
 | |
| 	struct afs_cell *cell;
 | |
| 
 | |
| 	_enter("%s", vlocation->vldb.name);
 | |
| 
 | |
| 	cell = vlocation->cell;
 | |
| 
 | |
| 	BUG_ON(atomic_read(&vlocation->usage) < 0);
 | |
| 
 | |
| 	/* remove from graveyard if still dead */
 | |
| 	spin_lock(&cell->vl_gylock);
 | |
| 	if (atomic_read(&vlocation->usage) == 0)
 | |
| 		list_del_init(&vlocation->link);
 | |
| 	else
 | |
| 		vlocation = NULL;
 | |
| 	spin_unlock(&cell->vl_gylock);
 | |
| 
 | |
| 	if (!vlocation) {
 | |
| 		_leave("");
 | |
| 		return; /* resurrected */
 | |
| 	}
 | |
| 
 | |
| 	/* we can now destroy it properly */
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
| 	cachefs_relinquish_cookie(vlocation->cache, 0);
 | |
| #endif
 | |
| 	afs_put_cell(cell);
 | |
| 
 | |
| 	kfree(vlocation);
 | |
| 
 | |
| 	_leave(" [destroyed]");
 | |
| } /* end afs_vlocation_do_timeout() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * send an update operation to the currently selected server
 | |
|  */
 | |
| static int afs_vlocation_update_begin(struct afs_vlocation *vlocation)
 | |
| {
 | |
| 	afs_voltype_t voltype;
 | |
| 	afs_volid_t vid;
 | |
| 	int ret;
 | |
| 
 | |
| 	_enter("%s{ufs=%u ucs=%u}",
 | |
| 	       vlocation->vldb.name,
 | |
| 	       vlocation->upd_first_svix,
 | |
| 	       vlocation->upd_curr_svix);
 | |
| 
 | |
| 	/* try to look up a cached volume in the cell VL databases by ID */
 | |
| 	if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
 | |
| 		vid = vlocation->vldb.vid[0];
 | |
| 		voltype = AFSVL_RWVOL;
 | |
| 	}
 | |
| 	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
 | |
| 		vid = vlocation->vldb.vid[1];
 | |
| 		voltype = AFSVL_ROVOL;
 | |
| 	}
 | |
| 	else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
 | |
| 		vid = vlocation->vldb.vid[2];
 | |
| 		voltype = AFSVL_BACKVOL;
 | |
| 	}
 | |
| 	else {
 | |
| 		BUG();
 | |
| 		vid = 0;
 | |
| 		voltype = 0;
 | |
| 	}
 | |
| 
 | |
| 	/* contact the chosen server */
 | |
| 	ret = afs_server_lookup(
 | |
| 		vlocation->cell,
 | |
| 		&vlocation->cell->vl_addrs[vlocation->upd_curr_svix],
 | |
| 		&vlocation->upd_op.server);
 | |
| 
 | |
| 	switch (ret) {
 | |
| 	case 0:
 | |
| 		break;
 | |
| 	case -ENOMEM:
 | |
| 	case -ENONET:
 | |
| 	default:
 | |
| 		_leave(" = %d", ret);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	/* initiate the update operation */
 | |
| 	ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype);
 | |
| 	if (ret < 0) {
 | |
| 		_leave(" = %d", ret);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	_leave(" = %d", ret);
 | |
| 	return ret;
 | |
| } /* end afs_vlocation_update_begin() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * abandon updating a VL record
 | |
|  * - does not restart the update timer
 | |
|  */
 | |
| static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation,
 | |
| 					 afs_vlocation_upd_t state,
 | |
| 					 int ret)
 | |
| {
 | |
| 	_enter("%s,%u", vlocation->vldb.name, state);
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		printk("kAFS: Abandoning VL update '%s': %d\n",
 | |
| 		       vlocation->vldb.name, ret);
 | |
| 
 | |
| 	/* discard the server record */
 | |
| 	afs_put_server(vlocation->upd_op.server);
 | |
| 	vlocation->upd_op.server = NULL;
 | |
| 
 | |
| 	spin_lock(&afs_vlocation_update_lock);
 | |
| 	afs_vlocation_update = NULL;
 | |
| 	vlocation->upd_state = state;
 | |
| 
 | |
| 	/* TODO: start updating next VL record on pending list */
 | |
| 
 | |
| 	spin_unlock(&afs_vlocation_update_lock);
 | |
| 
 | |
| 	_leave("");
 | |
| } /* end afs_vlocation_update_abandon() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * handle periodic update timeouts and busy retry timeouts
 | |
|  * - called from kafstimod
 | |
|  */
 | |
| static void afs_vlocation_update_timer(struct afs_timer *timer)
 | |
| {
 | |
| 	struct afs_vlocation *vlocation =
 | |
| 		list_entry(timer, struct afs_vlocation, upd_timer);
 | |
| 	int ret;
 | |
| 
 | |
| 	_enter("%s", vlocation->vldb.name);
 | |
| 
 | |
| 	/* only update if not in the graveyard (defend against putting too) */
 | |
| 	spin_lock(&vlocation->cell->vl_gylock);
 | |
| 
 | |
| 	if (!atomic_read(&vlocation->usage))
 | |
| 		goto out_unlock1;
 | |
| 
 | |
| 	spin_lock(&afs_vlocation_update_lock);
 | |
| 
 | |
| 	/* if we were woken up due to EBUSY sleep then restart immediately if
 | |
| 	 * possible or else jump to front of pending queue */
 | |
| 	if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) {
 | |
| 		if (afs_vlocation_update) {
 | |
| 			list_add(&vlocation->upd_op.link,
 | |
| 				 &afs_vlocation_update_pendq);
 | |
| 		}
 | |
| 		else {
 | |
| 			afs_get_vlocation(vlocation);
 | |
| 			afs_vlocation_update = vlocation;
 | |
| 			vlocation->upd_state = AFS_VLUPD_INPROGRESS;
 | |
| 		}
 | |
| 		goto out_unlock2;
 | |
| 	}
 | |
| 
 | |
| 	/* put on pending queue if there's already another update in progress */
 | |
| 	if (afs_vlocation_update) {
 | |
| 		vlocation->upd_state = AFS_VLUPD_PENDING;
 | |
| 		list_add_tail(&vlocation->upd_op.link,
 | |
| 			      &afs_vlocation_update_pendq);
 | |
| 		goto out_unlock2;
 | |
| 	}
 | |
| 
 | |
| 	/* hold a ref on it while actually updating */
 | |
| 	afs_get_vlocation(vlocation);
 | |
| 	afs_vlocation_update = vlocation;
 | |
| 	vlocation->upd_state = AFS_VLUPD_INPROGRESS;
 | |
| 
 | |
| 	spin_unlock(&afs_vlocation_update_lock);
 | |
| 	spin_unlock(&vlocation->cell->vl_gylock);
 | |
| 
 | |
| 	/* okay... we can start the update */
 | |
| 	_debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name);
 | |
| 	vlocation->upd_first_svix = vlocation->cell->vl_curr_svix;
 | |
| 	vlocation->upd_curr_svix = vlocation->upd_first_svix;
 | |
| 	vlocation->upd_rej_cnt = 0;
 | |
| 	vlocation->upd_busy_cnt = 0;
 | |
| 
 | |
| 	ret = afs_vlocation_update_begin(vlocation);
 | |
| 	if (ret < 0) {
 | |
| 		afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
 | |
| 		afs_kafstimod_add_timer(&vlocation->upd_timer,
 | |
| 					AFS_VLDB_TIMEOUT);
 | |
| 		afs_put_vlocation(vlocation);
 | |
| 	}
 | |
| 
 | |
| 	_leave("");
 | |
| 	return;
 | |
| 
 | |
|  out_unlock2:
 | |
| 	spin_unlock(&afs_vlocation_update_lock);
 | |
|  out_unlock1:
 | |
| 	spin_unlock(&vlocation->cell->vl_gylock);
 | |
| 	_leave("");
 | |
| 	return;
 | |
| 
 | |
| } /* end afs_vlocation_update_timer() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * attend to an update operation upon which an event happened
 | |
|  * - called in kafsasyncd context
 | |
|  */
 | |
| static void afs_vlocation_update_attend(struct afs_async_op *op)
 | |
| {
 | |
| 	struct afs_cache_vlocation vldb;
 | |
| 	struct afs_vlocation *vlocation =
 | |
| 		list_entry(op, struct afs_vlocation, upd_op);
 | |
| 	unsigned tmp;
 | |
| 	int ret;
 | |
| 
 | |
| 	_enter("%s", vlocation->vldb.name);
 | |
| 
 | |
| 	ret = afs_rxvl_get_entry_by_id_async2(op, &vldb);
 | |
| 	switch (ret) {
 | |
| 	case -EAGAIN:
 | |
| 		_leave(" [unfinished]");
 | |
| 		return;
 | |
| 
 | |
| 	case 0:
 | |
| 		_debug("END VL UPDATE: %d\n", ret);
 | |
| 		vlocation->valid = 1;
 | |
| 
 | |
| 		_debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }",
 | |
| 		       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]);
 | |
| 
 | |
| 		afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
 | |
| 
 | |
| 		down_write(&vlocation->cell->vl_sem);
 | |
| 
 | |
| 		/* actually update the cache */
 | |
| 		if (strncmp(vldb.name, vlocation->vldb.name,
 | |
| 			    sizeof(vlocation->vldb.name)) != 0)
 | |
| 			printk("kAFS: name of volume '%s'"
 | |
| 			       " changed to '%s' on server\n",
 | |
| 			       vlocation->vldb.name, vldb.name);
 | |
| 
 | |
| 		memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
 | |
| 
 | |
| #if 0
 | |
| 		/* TODO update volume entry in local cache */
 | |
| #endif
 | |
| 
 | |
| 		up_write(&vlocation->cell->vl_sem);
 | |
| 
 | |
| 		if (ret < 0)
 | |
| 			printk("kAFS: failed to update local cache: %d\n", ret);
 | |
| 
 | |
| 		afs_kafstimod_add_timer(&vlocation->upd_timer,
 | |
| 					AFS_VLDB_TIMEOUT);
 | |
| 		afs_put_vlocation(vlocation);
 | |
| 		_leave(" [found]");
 | |
| 		return;
 | |
| 
 | |
| 	case -ENOMEDIUM:
 | |
| 		vlocation->upd_rej_cnt++;
 | |
| 		goto try_next;
 | |
| 
 | |
| 		/* the server is locked - retry in a very short while */
 | |
| 	case -EBUSY:
 | |
| 		vlocation->upd_busy_cnt++;
 | |
| 		if (vlocation->upd_busy_cnt > 3)
 | |
| 			goto try_next; /* too many retries */
 | |
| 
 | |
| 		afs_vlocation_update_abandon(vlocation,
 | |
| 					     AFS_VLUPD_BUSYSLEEP, 0);
 | |
| 		afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2);
 | |
| 		afs_put_vlocation(vlocation);
 | |
| 		_leave(" [busy]");
 | |
| 		return;
 | |
| 
 | |
| 	case -ENETUNREACH:
 | |
| 	case -EHOSTUNREACH:
 | |
| 	case -ECONNREFUSED:
 | |
| 	case -EREMOTEIO:
 | |
| 		/* record bad vlserver info in the cell too
 | |
| 		 * - TODO: use down_write_trylock() if available
 | |
| 		 */
 | |
| 		if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix)
 | |
| 			vlocation->cell->vl_curr_svix =
 | |
| 				vlocation->cell->vl_curr_svix %
 | |
| 				vlocation->cell->vl_naddrs;
 | |
| 
 | |
| 	case -EBADRQC:
 | |
| 	case -EINVAL:
 | |
| 	case -EACCES:
 | |
| 	case -EBADMSG:
 | |
| 		goto try_next;
 | |
| 
 | |
| 	default:
 | |
| 		goto abandon;
 | |
| 	}
 | |
| 
 | |
| 	/* try contacting the next server */
 | |
|  try_next:
 | |
| 	vlocation->upd_busy_cnt = 0;
 | |
| 
 | |
| 	/* discard the server record */
 | |
| 	afs_put_server(vlocation->upd_op.server);
 | |
| 	vlocation->upd_op.server = NULL;
 | |
| 
 | |
| 	tmp = vlocation->cell->vl_naddrs;
 | |
| 	if (tmp == 0)
 | |
| 		goto abandon;
 | |
| 
 | |
| 	vlocation->upd_curr_svix++;
 | |
| 	if (vlocation->upd_curr_svix >= tmp)
 | |
| 		vlocation->upd_curr_svix = 0;
 | |
| 	if (vlocation->upd_first_svix >= tmp)
 | |
| 		vlocation->upd_first_svix = tmp - 1;
 | |
| 
 | |
| 	/* move to the next server */
 | |
| 	if (vlocation->upd_curr_svix != vlocation->upd_first_svix) {
 | |
| 		afs_vlocation_update_begin(vlocation);
 | |
| 		_leave(" [next]");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* run out of servers to try - was the volume rejected? */
 | |
| 	if (vlocation->upd_rej_cnt > 0) {
 | |
| 		printk("kAFS: Active volume no longer valid '%s'\n",
 | |
| 		       vlocation->vldb.name);
 | |
| 		vlocation->valid = 0;
 | |
| 		afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
 | |
| 		afs_kafstimod_add_timer(&vlocation->upd_timer,
 | |
| 					AFS_VLDB_TIMEOUT);
 | |
| 		afs_put_vlocation(vlocation);
 | |
| 		_leave(" [invalidated]");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* abandon the update */
 | |
|  abandon:
 | |
| 	afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
 | |
| 	afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10);
 | |
| 	afs_put_vlocation(vlocation);
 | |
| 	_leave(" [abandoned]");
 | |
| 
 | |
| } /* end afs_vlocation_update_attend() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * deal with an update operation being discarded
 | |
|  * - called in kafsasyncd context when it's dying due to rmmod
 | |
|  * - the call has already been aborted and put()'d
 | |
|  */
 | |
| static void afs_vlocation_update_discard(struct afs_async_op *op)
 | |
| {
 | |
| 	struct afs_vlocation *vlocation =
 | |
| 		list_entry(op, struct afs_vlocation, upd_op);
 | |
| 
 | |
| 	_enter("%s", vlocation->vldb.name);
 | |
| 
 | |
| 	afs_put_server(op->server);
 | |
| 	op->server = NULL;
 | |
| 
 | |
| 	afs_put_vlocation(vlocation);
 | |
| 
 | |
| 	_leave("");
 | |
| } /* end afs_vlocation_update_discard() */
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * match a VLDB record stored in the cache
 | |
|  * - may also load target from entry
 | |
|  */
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
| static cachefs_match_val_t afs_vlocation_cache_match(void *target,
 | |
| 						     const void *entry)
 | |
| {
 | |
| 	const struct afs_cache_vlocation *vldb = entry;
 | |
| 	struct afs_vlocation *vlocation = target;
 | |
| 
 | |
| 	_enter("{%s},{%s}", vlocation->vldb.name, vldb->name);
 | |
| 
 | |
| 	if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0
 | |
| 	    ) {
 | |
| 		if (!vlocation->valid ||
 | |
| 		    vlocation->vldb.rtime == vldb->rtime
 | |
| 		    ) {
 | |
| 			vlocation->vldb = *vldb;
 | |
| 			vlocation->valid = 1;
 | |
| 			_leave(" = SUCCESS [c->m]");
 | |
| 			return CACHEFS_MATCH_SUCCESS;
 | |
| 		}
 | |
| 		/* need to update cache if cached info differs */
 | |
| 		else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) {
 | |
| 			/* delete if VIDs for this name differ */
 | |
| 			if (memcmp(&vlocation->vldb.vid,
 | |
| 				   &vldb->vid,
 | |
| 				   sizeof(vldb->vid)) != 0) {
 | |
| 				_leave(" = DELETE");
 | |
| 				return CACHEFS_MATCH_SUCCESS_DELETE;
 | |
| 			}
 | |
| 
 | |
| 			_leave(" = UPDATE");
 | |
| 			return CACHEFS_MATCH_SUCCESS_UPDATE;
 | |
| 		}
 | |
| 		else {
 | |
| 			_leave(" = SUCCESS");
 | |
| 			return CACHEFS_MATCH_SUCCESS;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	_leave(" = FAILED");
 | |
| 	return CACHEFS_MATCH_FAILED;
 | |
| } /* end afs_vlocation_cache_match() */
 | |
| #endif
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*
 | |
|  * update a VLDB record stored in the cache
 | |
|  */
 | |
| #ifdef AFS_CACHING_SUPPORT
 | |
| static void afs_vlocation_cache_update(void *source, void *entry)
 | |
| {
 | |
| 	struct afs_cache_vlocation *vldb = entry;
 | |
| 	struct afs_vlocation *vlocation = source;
 | |
| 
 | |
| 	_enter("");
 | |
| 
 | |
| 	*vldb = vlocation->vldb;
 | |
| 
 | |
| } /* end afs_vlocation_cache_update() */
 | |
| #endif
 |