2 * Device operations for the pnfs client.
5 * The Regents of the University of Michigan
8 * Dean Hildebrand <dhildebz@umich.edu>
9 * Garth Goodson <Garth.Goodson@netapp.com>
11 * Permission is granted to use, copy, create derivative works, and
12 * redistribute this software and such derivative works for any purpose,
13 * so long as the name of the University of Michigan is not used in
14 * any advertising or publicity pertaining to the use or distribution
15 * of this software without specific, written prior authorization. If
16 * the above copyright notice or any other identification of the
17 * University of Michigan is included in any copy of any portion of
18 * this software, then the disclaimer below must also be included.
20 * This software is provided as is, without representation or warranty
21 * of any kind either express or implied, including without limitation
22 * the implied warranties of merchantability, fitness for a particular
23 * purpose, or noninfringement. The Regents of the University of
24 * Michigan shall not be liable for any damages, including special,
25 * indirect, incidental, or consequential damages, with respect to any
26 * claim arising out of or in connection with the use of the software,
27 * even if it has been or is hereafter advised of the possibility of
31 #include <linux/export.h>
32 #include <linux/nfs_fs.h>
33 #include "nfs4session.h"
37 #define NFSDBG_FACILITY NFSDBG_PNFS
40 * Device ID RCU cache. A device ID is unique per server and layout type.
42 #define NFS4_DEVICE_ID_HASH_BITS 5
43 #define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS)
44 #define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1)
47 static struct hlist_head nfs4_deviceid_cache
[NFS4_DEVICE_ID_HASH_SIZE
];
48 static DEFINE_SPINLOCK(nfs4_deviceid_lock
);
52 nfs4_print_deviceid(const struct nfs4_deviceid
*id
)
56 dprintk("%s: device id= [%x%x%x%x]\n", __func__
,
57 p
[0], p
[1], p
[2], p
[3]);
59 EXPORT_SYMBOL_GPL(nfs4_print_deviceid
);
63 nfs4_deviceid_hash(const struct nfs4_deviceid
*id
)
65 unsigned char *cptr
= (unsigned char *)id
->data
;
66 unsigned int nbytes
= NFS4_DEVICEID4_SIZE
;
73 return x
& NFS4_DEVICE_ID_HASH_MASK
;
76 static struct nfs4_deviceid_node
*
77 _lookup_deviceid(const struct pnfs_layoutdriver_type
*ld
,
78 const struct nfs_client
*clp
, const struct nfs4_deviceid
*id
,
81 struct nfs4_deviceid_node
*d
;
83 hlist_for_each_entry_rcu(d
, &nfs4_deviceid_cache
[hash
], node
)
84 if (d
->ld
== ld
&& d
->nfs_client
== clp
&&
85 !memcmp(&d
->deviceid
, id
, sizeof(*id
))) {
86 if (atomic_read(&d
->ref
))
94 static struct nfs4_deviceid_node
*
95 nfs4_get_device_info(struct nfs_server
*server
,
96 const struct nfs4_deviceid
*dev_id
,
97 const struct cred
*cred
, gfp_t gfp_flags
)
99 struct nfs4_deviceid_node
*d
= NULL
;
100 struct pnfs_device
*pdev
= NULL
;
101 struct page
**pages
= NULL
;
107 * Use the session max response size as the basis for setting
108 * GETDEVICEINFO's maxcount
110 max_resp_sz
= server
->nfs_client
->cl_session
->fc_attrs
.max_resp_sz
;
111 if (server
->pnfs_curr_ld
->max_deviceinfo_size
&&
112 server
->pnfs_curr_ld
->max_deviceinfo_size
< max_resp_sz
)
113 max_resp_sz
= server
->pnfs_curr_ld
->max_deviceinfo_size
;
114 max_pages
= nfs_page_array_len(0, max_resp_sz
);
115 dprintk("%s: server %p max_resp_sz %u max_pages %d\n",
116 __func__
, server
, max_resp_sz
, max_pages
);
118 pdev
= kzalloc(sizeof(*pdev
), gfp_flags
);
122 pages
= kcalloc(max_pages
, sizeof(struct page
*), gfp_flags
);
126 for (i
= 0; i
< max_pages
; i
++) {
127 pages
[i
] = alloc_page(gfp_flags
);
132 memcpy(&pdev
->dev_id
, dev_id
, sizeof(*dev_id
));
133 pdev
->layout_type
= server
->pnfs_curr_ld
->id
;
136 pdev
->pglen
= max_resp_sz
;
138 pdev
->maxcount
= max_resp_sz
- nfs41_maxgetdevinfo_overhead
;
140 rc
= nfs4_proc_getdeviceinfo(server
, pdev
, cred
);
141 dprintk("%s getdevice info returns %d\n", __func__
, rc
);
146 * Found new device, need to decode it and then add it to the
147 * list of known devices for this mountpoint.
149 d
= server
->pnfs_curr_ld
->alloc_deviceid_node(server
, pdev
,
151 if (d
&& pdev
->nocache
)
152 set_bit(NFS_DEVICEID_NOCACHE
, &d
->flags
);
155 for (i
= 0; i
< max_pages
; i
++)
156 __free_page(pages
[i
]);
160 dprintk("<-- %s d %p\n", __func__
, d
);
165 * Lookup a deviceid in cache and get a reference count on it if found
167 * @clp nfs_client associated with deviceid
168 * @id deviceid to look up
170 static struct nfs4_deviceid_node
*
171 __nfs4_find_get_deviceid(struct nfs_server
*server
,
172 const struct nfs4_deviceid
*id
, long hash
)
174 struct nfs4_deviceid_node
*d
;
177 d
= _lookup_deviceid(server
->pnfs_curr_ld
, server
->nfs_client
, id
,
179 if (d
!= NULL
&& !atomic_inc_not_zero(&d
->ref
))
185 struct nfs4_deviceid_node
*
186 nfs4_find_get_deviceid(struct nfs_server
*server
,
187 const struct nfs4_deviceid
*id
, const struct cred
*cred
,
190 long hash
= nfs4_deviceid_hash(id
);
191 struct nfs4_deviceid_node
*d
, *new;
193 d
= __nfs4_find_get_deviceid(server
, id
, hash
);
197 new = nfs4_get_device_info(server
, id
, cred
, gfp_mask
);
201 spin_lock(&nfs4_deviceid_lock
);
202 d
= __nfs4_find_get_deviceid(server
, id
, hash
);
204 spin_unlock(&nfs4_deviceid_lock
);
205 server
->pnfs_curr_ld
->free_deviceid_node(new);
208 hlist_add_head_rcu(&new->node
, &nfs4_deviceid_cache
[hash
]);
209 atomic_inc(&new->ref
);
210 spin_unlock(&nfs4_deviceid_lock
);
214 EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid
);
217 * Remove a deviceid from cache
219 * @clp nfs_client associated with deviceid
220 * @id the deviceid to unhash
222 * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise.
225 nfs4_delete_deviceid(const struct pnfs_layoutdriver_type
*ld
,
226 const struct nfs_client
*clp
, const struct nfs4_deviceid
*id
)
228 struct nfs4_deviceid_node
*d
;
230 spin_lock(&nfs4_deviceid_lock
);
232 d
= _lookup_deviceid(ld
, clp
, id
, nfs4_deviceid_hash(id
));
235 spin_unlock(&nfs4_deviceid_lock
);
238 hlist_del_init_rcu(&d
->node
);
239 clear_bit(NFS_DEVICEID_NOCACHE
, &d
->flags
);
240 spin_unlock(&nfs4_deviceid_lock
);
242 /* balance the initial ref set in pnfs_insert_deviceid */
243 nfs4_put_deviceid_node(d
);
245 EXPORT_SYMBOL_GPL(nfs4_delete_deviceid
);
248 nfs4_init_deviceid_node(struct nfs4_deviceid_node
*d
, struct nfs_server
*server
,
249 const struct nfs4_deviceid
*id
)
251 INIT_HLIST_NODE(&d
->node
);
252 INIT_HLIST_NODE(&d
->tmpnode
);
253 d
->ld
= server
->pnfs_curr_ld
;
254 d
->nfs_client
= server
->nfs_client
;
257 atomic_set(&d
->ref
, 1);
259 EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node
);
262 * Dereference a deviceid node and delete it when its reference count drops
265 * @d deviceid node to put
267 * return true iff the node was deleted
268 * Note that since the test for d->ref == 0 is sufficient to establish
269 * that the node is no longer hashed in the global device id cache.
272 nfs4_put_deviceid_node(struct nfs4_deviceid_node
*d
)
274 if (test_bit(NFS_DEVICEID_NOCACHE
, &d
->flags
)) {
275 if (atomic_add_unless(&d
->ref
, -1, 2))
277 nfs4_delete_deviceid(d
->ld
, d
->nfs_client
, &d
->deviceid
);
279 if (!atomic_dec_and_test(&d
->ref
))
281 d
->ld
->free_deviceid_node(d
);
284 EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node
);
287 nfs4_mark_deviceid_available(struct nfs4_deviceid_node
*node
)
289 if (test_bit(NFS_DEVICEID_UNAVAILABLE
, &node
->flags
)) {
290 clear_bit(NFS_DEVICEID_UNAVAILABLE
, &node
->flags
);
291 smp_mb__after_atomic();
294 EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_available
);
297 nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node
*node
)
299 node
->timestamp_unavailable
= jiffies
;
300 smp_mb__before_atomic();
301 set_bit(NFS_DEVICEID_UNAVAILABLE
, &node
->flags
);
302 smp_mb__after_atomic();
304 EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_unavailable
);
307 nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node
*node
)
309 if (test_bit(NFS_DEVICEID_UNAVAILABLE
, &node
->flags
)) {
310 unsigned long start
, end
;
313 start
= end
- PNFS_DEVICE_RETRY_TIMEOUT
;
314 if (time_in_range(node
->timestamp_unavailable
, start
, end
))
316 clear_bit(NFS_DEVICEID_UNAVAILABLE
, &node
->flags
);
317 smp_mb__after_atomic();
321 EXPORT_SYMBOL_GPL(nfs4_test_deviceid_unavailable
);
324 _deviceid_purge_client(const struct nfs_client
*clp
, long hash
)
326 struct nfs4_deviceid_node
*d
;
329 spin_lock(&nfs4_deviceid_lock
);
331 hlist_for_each_entry_rcu(d
, &nfs4_deviceid_cache
[hash
], node
)
332 if (d
->nfs_client
== clp
&& atomic_read(&d
->ref
)) {
333 hlist_del_init_rcu(&d
->node
);
334 hlist_add_head(&d
->tmpnode
, &tmp
);
335 clear_bit(NFS_DEVICEID_NOCACHE
, &d
->flags
);
338 spin_unlock(&nfs4_deviceid_lock
);
340 if (hlist_empty(&tmp
))
343 while (!hlist_empty(&tmp
)) {
344 d
= hlist_entry(tmp
.first
, struct nfs4_deviceid_node
, tmpnode
);
345 hlist_del(&d
->tmpnode
);
346 nfs4_put_deviceid_node(d
);
351 nfs4_deviceid_purge_client(const struct nfs_client
*clp
)
355 if (!(clp
->cl_exchange_flags
& EXCHGID4_FLAG_USE_PNFS_MDS
))
357 for (h
= 0; h
< NFS4_DEVICE_ID_HASH_SIZE
; h
++)
358 _deviceid_purge_client(clp
, h
);
362 * Stop use of all deviceids associated with an nfs_client
365 nfs4_deviceid_mark_client_invalid(struct nfs_client
*clp
)
367 struct nfs4_deviceid_node
*d
;
371 for (i
= 0; i
< NFS4_DEVICE_ID_HASH_SIZE
; i
++){
372 hlist_for_each_entry_rcu(d
, &nfs4_deviceid_cache
[i
], node
)
373 if (d
->nfs_client
== clp
)
374 set_bit(NFS_DEVICEID_INVALID
, &d
->flags
);