xref: /openbmc/linux/fs/nfs/pnfs_dev.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1a1eaecbcSBenny Halevy /*
2a1eaecbcSBenny Halevy  *  Device operations for the pnfs client.
3a1eaecbcSBenny Halevy  *
4a1eaecbcSBenny Halevy  *  Copyright (c) 2002
5a1eaecbcSBenny Halevy  *  The Regents of the University of Michigan
6a1eaecbcSBenny Halevy  *  All Rights Reserved
7a1eaecbcSBenny Halevy  *
8a1eaecbcSBenny Halevy  *  Dean Hildebrand <dhildebz@umich.edu>
9a1eaecbcSBenny Halevy  *  Garth Goodson   <Garth.Goodson@netapp.com>
10a1eaecbcSBenny Halevy  *
11a1eaecbcSBenny Halevy  *  Permission is granted to use, copy, create derivative works, and
12a1eaecbcSBenny Halevy  *  redistribute this software and such derivative works for any purpose,
13a1eaecbcSBenny Halevy  *  so long as the name of the University of Michigan is not used in
14a1eaecbcSBenny Halevy  *  any advertising or publicity pertaining to the use or distribution
15a1eaecbcSBenny Halevy  *  of this software without specific, written prior authorization. If
16a1eaecbcSBenny Halevy  *  the above copyright notice or any other identification of the
17a1eaecbcSBenny Halevy  *  University of Michigan is included in any copy of any portion of
18a1eaecbcSBenny Halevy  *  this software, then the disclaimer below must also be included.
19a1eaecbcSBenny Halevy  *
20a1eaecbcSBenny Halevy  *  This software is provided as is, without representation or warranty
21a1eaecbcSBenny Halevy  *  of any kind either express or implied, including without limitation
22a1eaecbcSBenny Halevy  *  the implied warranties of merchantability, fitness for a particular
23a1eaecbcSBenny Halevy  *  purpose, or noninfringement.  The Regents of the University of
24a1eaecbcSBenny Halevy  *  Michigan shall not be liable for any damages, including special,
25a1eaecbcSBenny Halevy  *  indirect, incidental, or consequential damages, with respect to any
26a1eaecbcSBenny Halevy  *  claim arising out of or in connection with the use of the software,
27a1eaecbcSBenny Halevy  *  even if it has been or is hereafter advised of the possibility of
28a1eaecbcSBenny Halevy  *  such damages.
29a1eaecbcSBenny Halevy  */
30a1eaecbcSBenny Halevy 
31afeacc8cSPaul Gortmaker #include <linux/export.h>
32661373b1SChristoph Hellwig #include <linux/nfs_fs.h>
33661373b1SChristoph Hellwig #include "nfs4session.h"
34661373b1SChristoph Hellwig #include "internal.h"
35a1eaecbcSBenny Halevy #include "pnfs.h"
36a1eaecbcSBenny Halevy 
37cac1d3a2STrond Myklebust #include "nfs4trace.h"
38cac1d3a2STrond Myklebust 
39a1eaecbcSBenny Halevy #define NFSDBG_FACILITY		NFSDBG_PNFS
40a1eaecbcSBenny Halevy 
41a1eaecbcSBenny Halevy /*
42a1eaecbcSBenny Halevy  * Device ID RCU cache. A device ID is unique per server and layout type.
43a1eaecbcSBenny Halevy  */
44a1eaecbcSBenny Halevy #define NFS4_DEVICE_ID_HASH_BITS	5
45a1eaecbcSBenny Halevy #define NFS4_DEVICE_ID_HASH_SIZE	(1 << NFS4_DEVICE_ID_HASH_BITS)
46a1eaecbcSBenny Halevy #define NFS4_DEVICE_ID_HASH_MASK	(NFS4_DEVICE_ID_HASH_SIZE - 1)
47a1eaecbcSBenny Halevy 
481dfed273STrond Myklebust 
49a1eaecbcSBenny Halevy static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE];
50a1eaecbcSBenny Halevy static DEFINE_SPINLOCK(nfs4_deviceid_lock);
51a1eaecbcSBenny Halevy 
526f00866dSTrond Myklebust #ifdef NFS_DEBUG
53a1eaecbcSBenny Halevy void
nfs4_print_deviceid(const struct nfs4_deviceid * id)54a1eaecbcSBenny Halevy nfs4_print_deviceid(const struct nfs4_deviceid *id)
55a1eaecbcSBenny Halevy {
56a1eaecbcSBenny Halevy 	u32 *p = (u32 *)id;
57a1eaecbcSBenny Halevy 
58a1eaecbcSBenny Halevy 	dprintk("%s: device id= [%x%x%x%x]\n", __func__,
59a1eaecbcSBenny Halevy 		p[0], p[1], p[2], p[3]);
60a1eaecbcSBenny Halevy }
61a1eaecbcSBenny Halevy EXPORT_SYMBOL_GPL(nfs4_print_deviceid);
626f00866dSTrond Myklebust #endif
63a1eaecbcSBenny Halevy 
64a1eaecbcSBenny Halevy static inline u32
nfs4_deviceid_hash(const struct nfs4_deviceid * id)65a1eaecbcSBenny Halevy nfs4_deviceid_hash(const struct nfs4_deviceid *id)
66a1eaecbcSBenny Halevy {
67a1eaecbcSBenny Halevy 	unsigned char *cptr = (unsigned char *)id->data;
68a1eaecbcSBenny Halevy 	unsigned int nbytes = NFS4_DEVICEID4_SIZE;
69a1eaecbcSBenny Halevy 	u32 x = 0;
70a1eaecbcSBenny Halevy 
71a1eaecbcSBenny Halevy 	while (nbytes--) {
72a1eaecbcSBenny Halevy 		x *= 37;
73a1eaecbcSBenny Halevy 		x += *cptr++;
74a1eaecbcSBenny Halevy 	}
75a1eaecbcSBenny Halevy 	return x & NFS4_DEVICE_ID_HASH_MASK;
76a1eaecbcSBenny Halevy }
77a1eaecbcSBenny Halevy 
781be5683bSMarc Eshel static struct nfs4_deviceid_node *
_lookup_deviceid(const struct pnfs_layoutdriver_type * ld,const struct nfs_client * clp,const struct nfs4_deviceid * id,long hash)7935c8bb54SBenny Halevy _lookup_deviceid(const struct pnfs_layoutdriver_type *ld,
8035c8bb54SBenny Halevy 		 const struct nfs_client *clp, const struct nfs4_deviceid *id,
811be5683bSMarc Eshel 		 long hash)
821be5683bSMarc Eshel {
831be5683bSMarc Eshel 	struct nfs4_deviceid_node *d;
841be5683bSMarc Eshel 
85b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[hash], node)
8635c8bb54SBenny Halevy 		if (d->ld == ld && d->nfs_client == clp &&
8735c8bb54SBenny Halevy 		    !memcmp(&d->deviceid, id, sizeof(*id))) {
881be5683bSMarc Eshel 			if (atomic_read(&d->ref))
891be5683bSMarc Eshel 				return d;
901be5683bSMarc Eshel 			else
911be5683bSMarc Eshel 				continue;
921be5683bSMarc Eshel 		}
931be5683bSMarc Eshel 	return NULL;
941be5683bSMarc Eshel }
951be5683bSMarc Eshel 
96661373b1SChristoph Hellwig static struct nfs4_deviceid_node *
nfs4_get_device_info(struct nfs_server * server,const struct nfs4_deviceid * dev_id,const struct cred * cred,gfp_t gfp_flags)97661373b1SChristoph Hellwig nfs4_get_device_info(struct nfs_server *server,
98661373b1SChristoph Hellwig 		const struct nfs4_deviceid *dev_id,
99a52458b4SNeilBrown 		const struct cred *cred, gfp_t gfp_flags)
100661373b1SChristoph Hellwig {
101661373b1SChristoph Hellwig 	struct nfs4_deviceid_node *d = NULL;
102661373b1SChristoph Hellwig 	struct pnfs_device *pdev = NULL;
103661373b1SChristoph Hellwig 	struct page **pages = NULL;
104661373b1SChristoph Hellwig 	u32 max_resp_sz;
105661373b1SChristoph Hellwig 	int max_pages;
106661373b1SChristoph Hellwig 	int rc, i;
107661373b1SChristoph Hellwig 
108661373b1SChristoph Hellwig 	/*
109661373b1SChristoph Hellwig 	 * Use the session max response size as the basis for setting
110661373b1SChristoph Hellwig 	 * GETDEVICEINFO's maxcount
111661373b1SChristoph Hellwig 	 */
112661373b1SChristoph Hellwig 	max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
113661373b1SChristoph Hellwig 	if (server->pnfs_curr_ld->max_deviceinfo_size &&
114661373b1SChristoph Hellwig 	    server->pnfs_curr_ld->max_deviceinfo_size < max_resp_sz)
115661373b1SChristoph Hellwig 		max_resp_sz = server->pnfs_curr_ld->max_deviceinfo_size;
116661373b1SChristoph Hellwig 	max_pages = nfs_page_array_len(0, max_resp_sz);
117661373b1SChristoph Hellwig 	dprintk("%s: server %p max_resp_sz %u max_pages %d\n",
118661373b1SChristoph Hellwig 		__func__, server, max_resp_sz, max_pages);
119661373b1SChristoph Hellwig 
120661373b1SChristoph Hellwig 	pdev = kzalloc(sizeof(*pdev), gfp_flags);
121661373b1SChristoph Hellwig 	if (!pdev)
122661373b1SChristoph Hellwig 		return NULL;
123661373b1SChristoph Hellwig 
124661373b1SChristoph Hellwig 	pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags);
125661373b1SChristoph Hellwig 	if (!pages)
126661373b1SChristoph Hellwig 		goto out_free_pdev;
127661373b1SChristoph Hellwig 
128661373b1SChristoph Hellwig 	for (i = 0; i < max_pages; i++) {
129661373b1SChristoph Hellwig 		pages[i] = alloc_page(gfp_flags);
130661373b1SChristoph Hellwig 		if (!pages[i])
131661373b1SChristoph Hellwig 			goto out_free_pages;
132661373b1SChristoph Hellwig 	}
133661373b1SChristoph Hellwig 
134661373b1SChristoph Hellwig 	memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id));
135661373b1SChristoph Hellwig 	pdev->layout_type = server->pnfs_curr_ld->id;
136661373b1SChristoph Hellwig 	pdev->pages = pages;
137661373b1SChristoph Hellwig 	pdev->pgbase = 0;
138661373b1SChristoph Hellwig 	pdev->pglen = max_resp_sz;
139661373b1SChristoph Hellwig 	pdev->mincount = 0;
140661373b1SChristoph Hellwig 	pdev->maxcount = max_resp_sz - nfs41_maxgetdevinfo_overhead;
141661373b1SChristoph Hellwig 
142661373b1SChristoph Hellwig 	rc = nfs4_proc_getdeviceinfo(server, pdev, cred);
143661373b1SChristoph Hellwig 	dprintk("%s getdevice info returns %d\n", __func__, rc);
144661373b1SChristoph Hellwig 	if (rc)
145661373b1SChristoph Hellwig 		goto out_free_pages;
146661373b1SChristoph Hellwig 
147661373b1SChristoph Hellwig 	/*
148661373b1SChristoph Hellwig 	 * Found new device, need to decode it and then add it to the
149661373b1SChristoph Hellwig 	 * list of known devices for this mountpoint.
150661373b1SChristoph Hellwig 	 */
151661373b1SChristoph Hellwig 	d = server->pnfs_curr_ld->alloc_deviceid_node(server, pdev,
152661373b1SChristoph Hellwig 			gfp_flags);
153df52699eSTrond Myklebust 	if (d && pdev->nocache)
154df52699eSTrond Myklebust 		set_bit(NFS_DEVICEID_NOCACHE, &d->flags);
155661373b1SChristoph Hellwig 
156661373b1SChristoph Hellwig out_free_pages:
157*96562c45SFedor Pchelkin 	while (--i >= 0)
158661373b1SChristoph Hellwig 		__free_page(pages[i]);
159661373b1SChristoph Hellwig 	kfree(pages);
160661373b1SChristoph Hellwig out_free_pdev:
161661373b1SChristoph Hellwig 	kfree(pdev);
162661373b1SChristoph Hellwig 	dprintk("<-- %s d %p\n", __func__, d);
163661373b1SChristoph Hellwig 	return d;
164661373b1SChristoph Hellwig }
165661373b1SChristoph Hellwig 
166a1eaecbcSBenny Halevy /*
167a1eaecbcSBenny Halevy  * Lookup a deviceid in cache and get a reference count on it if found
168a1eaecbcSBenny Halevy  *
169a1eaecbcSBenny Halevy  * @clp nfs_client associated with deviceid
170a1eaecbcSBenny Halevy  * @id deviceid to look up
171a1eaecbcSBenny Halevy  */
17217280175STrond Myklebust static struct nfs4_deviceid_node *
__nfs4_find_get_deviceid(struct nfs_server * server,const struct nfs4_deviceid * id,long hash)173661373b1SChristoph Hellwig __nfs4_find_get_deviceid(struct nfs_server *server,
174661373b1SChristoph Hellwig 		const struct nfs4_deviceid *id, long hash)
175a1eaecbcSBenny Halevy {
176a1eaecbcSBenny Halevy 	struct nfs4_deviceid_node *d;
177a1eaecbcSBenny Halevy 
178a1eaecbcSBenny Halevy 	rcu_read_lock();
179661373b1SChristoph Hellwig 	d = _lookup_deviceid(server->pnfs_curr_ld, server->nfs_client, id,
180661373b1SChristoph Hellwig 			hash);
18184a80f62STrond Myklebust 	if (d != NULL && !atomic_inc_not_zero(&d->ref))
18284a80f62STrond Myklebust 		d = NULL;
183a1eaecbcSBenny Halevy 	rcu_read_unlock();
184a1eaecbcSBenny Halevy 	return d;
185a1eaecbcSBenny Halevy }
1861be5683bSMarc Eshel 
1871be5683bSMarc Eshel struct nfs4_deviceid_node *
nfs4_find_get_deviceid(struct nfs_server * server,const struct nfs4_deviceid * id,const struct cred * cred,gfp_t gfp_mask)188661373b1SChristoph Hellwig nfs4_find_get_deviceid(struct nfs_server *server,
189a52458b4SNeilBrown 		const struct nfs4_deviceid *id, const struct cred *cred,
190661373b1SChristoph Hellwig 		gfp_t gfp_mask)
1911be5683bSMarc Eshel {
192661373b1SChristoph Hellwig 	long hash = nfs4_deviceid_hash(id);
193661373b1SChristoph Hellwig 	struct nfs4_deviceid_node *d, *new;
194661373b1SChristoph Hellwig 
195661373b1SChristoph Hellwig 	d = __nfs4_find_get_deviceid(server, id, hash);
196661373b1SChristoph Hellwig 	if (d)
197cac1d3a2STrond Myklebust 		goto found;
198661373b1SChristoph Hellwig 
199661373b1SChristoph Hellwig 	new = nfs4_get_device_info(server, id, cred, gfp_mask);
200cac1d3a2STrond Myklebust 	if (!new) {
201cac1d3a2STrond Myklebust 		trace_nfs4_find_deviceid(server, id, -ENOENT);
202661373b1SChristoph Hellwig 		return new;
203cac1d3a2STrond Myklebust 	}
204661373b1SChristoph Hellwig 
205661373b1SChristoph Hellwig 	spin_lock(&nfs4_deviceid_lock);
206661373b1SChristoph Hellwig 	d = __nfs4_find_get_deviceid(server, id, hash);
207661373b1SChristoph Hellwig 	if (d) {
208661373b1SChristoph Hellwig 		spin_unlock(&nfs4_deviceid_lock);
209661373b1SChristoph Hellwig 		server->pnfs_curr_ld->free_deviceid_node(new);
210cac1d3a2STrond Myklebust 	} else {
211661373b1SChristoph Hellwig 		atomic_inc(&new->ref);
212cac1d3a2STrond Myklebust 		hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]);
213661373b1SChristoph Hellwig 		spin_unlock(&nfs4_deviceid_lock);
214cac1d3a2STrond Myklebust 		d = new;
215cac1d3a2STrond Myklebust 	}
216cac1d3a2STrond Myklebust found:
217cac1d3a2STrond Myklebust 	trace_nfs4_find_deviceid(server, id, 0);
218cac1d3a2STrond Myklebust 	return d;
219a1eaecbcSBenny Halevy }
220a1eaecbcSBenny Halevy EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid);
221a1eaecbcSBenny Halevy 
2221be5683bSMarc Eshel /*
22347cb498eSTrond Myklebust  * Remove a deviceid from cache
2241be5683bSMarc Eshel  *
2251be5683bSMarc Eshel  * @clp nfs_client associated with deviceid
2261be5683bSMarc Eshel  * @id the deviceid to unhash
2271be5683bSMarc Eshel  *
2281be5683bSMarc Eshel  * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise.
2291be5683bSMarc Eshel  */
23047cb498eSTrond Myklebust void
nfs4_delete_deviceid(const struct pnfs_layoutdriver_type * ld,const struct nfs_client * clp,const struct nfs4_deviceid * id)23147cb498eSTrond Myklebust nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld,
23235c8bb54SBenny Halevy 			 const struct nfs_client *clp, const struct nfs4_deviceid *id)
2331be5683bSMarc Eshel {
2341be5683bSMarc Eshel 	struct nfs4_deviceid_node *d;
2351be5683bSMarc Eshel 
2361be5683bSMarc Eshel 	spin_lock(&nfs4_deviceid_lock);
2371be5683bSMarc Eshel 	rcu_read_lock();
23835c8bb54SBenny Halevy 	d = _lookup_deviceid(ld, clp, id, nfs4_deviceid_hash(id));
2391be5683bSMarc Eshel 	rcu_read_unlock();
2401be5683bSMarc Eshel 	if (!d) {
2411be5683bSMarc Eshel 		spin_unlock(&nfs4_deviceid_lock);
24247cb498eSTrond Myklebust 		return;
2431be5683bSMarc Eshel 	}
2441be5683bSMarc Eshel 	hlist_del_init_rcu(&d->node);
245df52699eSTrond Myklebust 	clear_bit(NFS_DEVICEID_NOCACHE, &d->flags);
2461be5683bSMarc Eshel 	spin_unlock(&nfs4_deviceid_lock);
2471be5683bSMarc Eshel 
2481be5683bSMarc Eshel 	/* balance the initial ref set in pnfs_insert_deviceid */
249fb1458f4STrond Myklebust 	nfs4_put_deviceid_node(d);
2501be5683bSMarc Eshel }
2511be5683bSMarc Eshel EXPORT_SYMBOL_GPL(nfs4_delete_deviceid);
2521be5683bSMarc Eshel 
253a1eaecbcSBenny Halevy void
nfs4_init_deviceid_node(struct nfs4_deviceid_node * d,struct nfs_server * server,const struct nfs4_deviceid * id)254661373b1SChristoph Hellwig nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, struct nfs_server *server,
255a1eaecbcSBenny Halevy 			const struct nfs4_deviceid *id)
256a1eaecbcSBenny Halevy {
2571775bc34SBenny Halevy 	INIT_HLIST_NODE(&d->node);
2589e3bd4e2SWeston Andros Adamson 	INIT_HLIST_NODE(&d->tmpnode);
259661373b1SChristoph Hellwig 	d->ld = server->pnfs_curr_ld;
260661373b1SChristoph Hellwig 	d->nfs_client = server->nfs_client;
261c47abcf8SAndy Adamson 	d->flags = 0;
262a1eaecbcSBenny Halevy 	d->deviceid = *id;
2631775bc34SBenny Halevy 	atomic_set(&d->ref, 1);
264a1eaecbcSBenny Halevy }
265a1eaecbcSBenny Halevy EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node);
266a1eaecbcSBenny Halevy 
267a1eaecbcSBenny Halevy /*
268a1eaecbcSBenny Halevy  * Dereference a deviceid node and delete it when its reference count drops
269a1eaecbcSBenny Halevy  * to zero.
270a1eaecbcSBenny Halevy  *
271a1eaecbcSBenny Halevy  * @d deviceid node to put
272a1eaecbcSBenny Halevy  *
27347cb498eSTrond Myklebust  * return true iff the node was deleted
27447cb498eSTrond Myklebust  * Note that since the test for d->ref == 0 is sufficient to establish
27547cb498eSTrond Myklebust  * that the node is no longer hashed in the global device id cache.
276a1eaecbcSBenny Halevy  */
277a1eaecbcSBenny Halevy bool
nfs4_put_deviceid_node(struct nfs4_deviceid_node * d)278a1eaecbcSBenny Halevy nfs4_put_deviceid_node(struct nfs4_deviceid_node *d)
279a1eaecbcSBenny Halevy {
280df52699eSTrond Myklebust 	if (test_bit(NFS_DEVICEID_NOCACHE, &d->flags)) {
281df52699eSTrond Myklebust 		if (atomic_add_unless(&d->ref, -1, 2))
282df52699eSTrond Myklebust 			return false;
283df52699eSTrond Myklebust 		nfs4_delete_deviceid(d->ld, d->nfs_client, &d->deviceid);
284df52699eSTrond Myklebust 	}
28547cb498eSTrond Myklebust 	if (!atomic_dec_and_test(&d->ref))
286a1eaecbcSBenny Halevy 		return false;
287cac1d3a2STrond Myklebust 	trace_nfs4_deviceid_free(d->nfs_client, &d->deviceid);
2881775bc34SBenny Halevy 	d->ld->free_deviceid_node(d);
289a1eaecbcSBenny Halevy 	return true;
290a1eaecbcSBenny Halevy }
291a1eaecbcSBenny Halevy EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node);
2921775bc34SBenny Halevy 
2931dfed273STrond Myklebust void
nfs4_mark_deviceid_available(struct nfs4_deviceid_node * node)29476c66905STrond Myklebust nfs4_mark_deviceid_available(struct nfs4_deviceid_node *node)
29576c66905STrond Myklebust {
29676c66905STrond Myklebust 	if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) {
29776c66905STrond Myklebust 		clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
29876c66905STrond Myklebust 		smp_mb__after_atomic();
29976c66905STrond Myklebust 	}
30076c66905STrond Myklebust }
30176c66905STrond Myklebust EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_available);
30276c66905STrond Myklebust 
30376c66905STrond Myklebust void
nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node * node)3041dfed273STrond Myklebust nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node)
3051dfed273STrond Myklebust {
3061dfed273STrond Myklebust 	node->timestamp_unavailable = jiffies;
30739a5201aSTrond Myklebust 	smp_mb__before_atomic();
3081dfed273STrond Myklebust 	set_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
30939a5201aSTrond Myklebust 	smp_mb__after_atomic();
3101dfed273STrond Myklebust }
3111dfed273STrond Myklebust EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_unavailable);
3121dfed273STrond Myklebust 
3131dfed273STrond Myklebust bool
nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node * node)3141dfed273STrond Myklebust nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node)
3151dfed273STrond Myklebust {
3161dfed273STrond Myklebust 	if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) {
3171dfed273STrond Myklebust 		unsigned long start, end;
3181dfed273STrond Myklebust 
3191dfed273STrond Myklebust 		end = jiffies;
3201dfed273STrond Myklebust 		start = end - PNFS_DEVICE_RETRY_TIMEOUT;
3211dfed273STrond Myklebust 		if (time_in_range(node->timestamp_unavailable, start, end))
3221dfed273STrond Myklebust 			return true;
3231dfed273STrond Myklebust 		clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
32439a5201aSTrond Myklebust 		smp_mb__after_atomic();
3251dfed273STrond Myklebust 	}
3261dfed273STrond Myklebust 	return false;
3271dfed273STrond Myklebust }
3281dfed273STrond Myklebust EXPORT_SYMBOL_GPL(nfs4_test_deviceid_unavailable);
3291dfed273STrond Myklebust 
3301775bc34SBenny Halevy static void
_deviceid_purge_client(const struct nfs_client * clp,long hash)3311775bc34SBenny Halevy _deviceid_purge_client(const struct nfs_client *clp, long hash)
3321775bc34SBenny Halevy {
3331775bc34SBenny Halevy 	struct nfs4_deviceid_node *d;
3341775bc34SBenny Halevy 	HLIST_HEAD(tmp);
3351775bc34SBenny Halevy 
3369e3bd4e2SWeston Andros Adamson 	spin_lock(&nfs4_deviceid_lock);
3371775bc34SBenny Halevy 	rcu_read_lock();
338b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[hash], node)
3391775bc34SBenny Halevy 		if (d->nfs_client == clp && atomic_read(&d->ref)) {
3401775bc34SBenny Halevy 			hlist_del_init_rcu(&d->node);
3419e3bd4e2SWeston Andros Adamson 			hlist_add_head(&d->tmpnode, &tmp);
342df52699eSTrond Myklebust 			clear_bit(NFS_DEVICEID_NOCACHE, &d->flags);
3431775bc34SBenny Halevy 		}
3441775bc34SBenny Halevy 	rcu_read_unlock();
3459e3bd4e2SWeston Andros Adamson 	spin_unlock(&nfs4_deviceid_lock);
3461775bc34SBenny Halevy 
3471775bc34SBenny Halevy 	if (hlist_empty(&tmp))
3481775bc34SBenny Halevy 		return;
3491775bc34SBenny Halevy 
3509e3bd4e2SWeston Andros Adamson 	while (!hlist_empty(&tmp)) {
3519e3bd4e2SWeston Andros Adamson 		d = hlist_entry(tmp.first, struct nfs4_deviceid_node, tmpnode);
3529e3bd4e2SWeston Andros Adamson 		hlist_del(&d->tmpnode);
353fb1458f4STrond Myklebust 		nfs4_put_deviceid_node(d);
3541775bc34SBenny Halevy 	}
3559e3bd4e2SWeston Andros Adamson }
3561775bc34SBenny Halevy 
3571775bc34SBenny Halevy void
nfs4_deviceid_purge_client(const struct nfs_client * clp)3581775bc34SBenny Halevy nfs4_deviceid_purge_client(const struct nfs_client *clp)
3591775bc34SBenny Halevy {
3601775bc34SBenny Halevy 	long h;
3611775bc34SBenny Halevy 
3629e3bd4e2SWeston Andros Adamson 	if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_MDS))
3639e3bd4e2SWeston Andros Adamson 		return;
3641775bc34SBenny Halevy 	for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++)
3651775bc34SBenny Halevy 		_deviceid_purge_client(clp, h);
3661775bc34SBenny Halevy }
367c47abcf8SAndy Adamson 
368c47abcf8SAndy Adamson /*
369c47abcf8SAndy Adamson  * Stop use of all deviceids associated with an nfs_client
370c47abcf8SAndy Adamson  */
371c47abcf8SAndy Adamson void
nfs4_deviceid_mark_client_invalid(struct nfs_client * clp)372c47abcf8SAndy Adamson nfs4_deviceid_mark_client_invalid(struct nfs_client *clp)
373c47abcf8SAndy Adamson {
374c47abcf8SAndy Adamson 	struct nfs4_deviceid_node *d;
375c47abcf8SAndy Adamson 	int i;
376c47abcf8SAndy Adamson 
377c47abcf8SAndy Adamson 	rcu_read_lock();
378c47abcf8SAndy Adamson 	for (i = 0; i < NFS4_DEVICE_ID_HASH_SIZE; i ++){
379b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[i], node)
380c47abcf8SAndy Adamson 			if (d->nfs_client == clp)
381c47abcf8SAndy Adamson 				set_bit(NFS_DEVICEID_INVALID, &d->flags);
382c47abcf8SAndy Adamson 	}
383c47abcf8SAndy Adamson 	rcu_read_unlock();
384c47abcf8SAndy Adamson }
385