diff options
Diffstat (limited to 'fs/nfs/pnfs_dev.c')
| -rw-r--r-- | fs/nfs/pnfs_dev.c | 156 | 
1 files changed, 156 insertions, 0 deletions
diff --git a/fs/nfs/pnfs_dev.c b/fs/nfs/pnfs_dev.c new file mode 100644 index 00000000000..bf05189a7cb --- /dev/null +++ b/fs/nfs/pnfs_dev.c @@ -0,0 +1,156 @@ +/* + *  Device operations for the pnfs client. + * + *  Copyright (c) 2002 + *  The Regents of the University of Michigan + *  All Rights Reserved + * + *  Dean Hildebrand <dhildebz@umich.edu> + *  Garth Goodson   <Garth.Goodson@netapp.com> + * + *  Permission is granted to use, copy, create derivative works, and + *  redistribute this software and such derivative works for any purpose, + *  so long as the name of the University of Michigan is not used in + *  any advertising or publicity pertaining to the use or distribution + *  of this software without specific, written prior authorization. If + *  the above copyright notice or any other identification of the + *  University of Michigan is included in any copy of any portion of + *  this software, then the disclaimer below must also be included. + * + *  This software is provided as is, without representation or warranty + *  of any kind either express or implied, including without limitation + *  the implied warranties of merchantability, fitness for a particular + *  purpose, or noninfringement.  The Regents of the University of + *  Michigan shall not be liable for any damages, including special, + *  indirect, incidental, or consequential damages, with respect to any + *  claim arising out of or in connection with the use of the software, + *  even if it has been or is hereafter advised of the possibility of + *  such damages. + */ + +#include "pnfs.h" + +#define NFSDBG_FACILITY		NFSDBG_PNFS + +/* + * Device ID RCU cache. A device ID is unique per server and layout type. + */ +#define NFS4_DEVICE_ID_HASH_BITS	5 +#define NFS4_DEVICE_ID_HASH_SIZE	(1 << NFS4_DEVICE_ID_HASH_BITS) +#define NFS4_DEVICE_ID_HASH_MASK	(NFS4_DEVICE_ID_HASH_SIZE - 1) + +static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE]; +static DEFINE_SPINLOCK(nfs4_deviceid_lock); + +void +nfs4_print_deviceid(const struct nfs4_deviceid *id) +{ +	u32 *p = (u32 *)id; + +	dprintk("%s: device id= [%x%x%x%x]\n", __func__, +		p[0], p[1], p[2], p[3]); +} +EXPORT_SYMBOL_GPL(nfs4_print_deviceid); + +static inline u32 +nfs4_deviceid_hash(const struct nfs4_deviceid *id) +{ +	unsigned char *cptr = (unsigned char *)id->data; +	unsigned int nbytes = NFS4_DEVICEID4_SIZE; +	u32 x = 0; + +	while (nbytes--) { +		x *= 37; +		x += *cptr++; +	} +	return x & NFS4_DEVICE_ID_HASH_MASK; +} + +/* + * Lookup a deviceid in cache and get a reference count on it if found + * + * @clp nfs_client associated with deviceid + * @id deviceid to look up + */ +struct nfs4_deviceid_node * +nfs4_find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) +{ +	struct nfs4_deviceid_node *d; +	struct hlist_node *n; +	long hash = nfs4_deviceid_hash(id); + +	rcu_read_lock(); +	hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) { +		if (d->nfs_client == clp && !memcmp(&d->deviceid, id, sizeof(*id))) { +			if (!atomic_inc_not_zero(&d->ref)) +				goto fail; +			rcu_read_unlock(); +			return d; +		} +	} +fail: +	rcu_read_unlock(); +	return NULL; +} +EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); + +void +nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, +			const struct nfs_client *nfs_client, +			const struct nfs4_deviceid *id) +{ +	d->nfs_client = nfs_client; +	d->deviceid = *id; +} +EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node); + +/* + * Uniquely initialize and insert a deviceid node into cache + * + * @new new deviceid node + *      Note that the caller must set up new->nfs_client and new->deviceid + * + * @ret the inserted node, if none found, otherwise, the found entry. + */ +struct nfs4_deviceid_node * +nfs4_insert_deviceid_node(struct nfs4_deviceid_node *new) +{ +	struct nfs4_deviceid_node *d; +	long hash; + +	spin_lock(&nfs4_deviceid_lock); +	d = nfs4_find_get_deviceid(new->nfs_client, &new->deviceid); +	if (d) { +		spin_unlock(&nfs4_deviceid_lock); +		return d; +	} + +	INIT_HLIST_NODE(&new->node); +	atomic_set(&new->ref, 1); +	hash = nfs4_deviceid_hash(&new->deviceid); +	hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); +	spin_unlock(&nfs4_deviceid_lock); + +	return new; +} +EXPORT_SYMBOL_GPL(nfs4_insert_deviceid_node); + +/* + * Dereference a deviceid node and delete it when its reference count drops + * to zero. + * + * @d deviceid node to put + * + * @ret true iff the node was deleted + */ +bool +nfs4_put_deviceid_node(struct nfs4_deviceid_node *d) +{ +	if (!atomic_dec_and_lock(&d->ref, &nfs4_deviceid_lock)) +		return false; +	hlist_del_init_rcu(&d->node); +	spin_unlock(&nfs4_deviceid_lock); +	synchronize_rcu(); +	return true; +} +EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node);  |