pnfs_dev.c (10332B)
1/* 2 * Device operations for the pnfs client. 3 * 4 * Copyright (c) 2002 5 * The Regents of the University of Michigan 6 * All Rights Reserved 7 * 8 * Dean Hildebrand <dhildebz@umich.edu> 9 * Garth Goodson <Garth.Goodson@netapp.com> 10 * 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. 19 * 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 28 * such damages. 29 */ 30 31#include <linux/export.h> 32#include <linux/nfs_fs.h> 33#include "nfs4session.h" 34#include "internal.h" 35#include "pnfs.h" 36 37#include "nfs4trace.h" 38 39#define NFSDBG_FACILITY NFSDBG_PNFS 40 41/* 42 * Device ID RCU cache. A device ID is unique per server and layout type. 43 */ 44#define NFS4_DEVICE_ID_HASH_BITS 5 45#define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS) 46#define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1) 47 48 49static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE]; 50static DEFINE_SPINLOCK(nfs4_deviceid_lock); 51 52#ifdef NFS_DEBUG 53void 54nfs4_print_deviceid(const struct nfs4_deviceid *id) 55{ 56 u32 *p = (u32 *)id; 57 58 dprintk("%s: device id= [%x%x%x%x]\n", __func__, 59 p[0], p[1], p[2], p[3]); 60} 61EXPORT_SYMBOL_GPL(nfs4_print_deviceid); 62#endif 63 64static inline u32 65nfs4_deviceid_hash(const struct nfs4_deviceid *id) 66{ 67 unsigned char *cptr = (unsigned char *)id->data; 68 unsigned int nbytes = NFS4_DEVICEID4_SIZE; 69 u32 x = 0; 70 71 while (nbytes--) { 72 x *= 37; 73 x += *cptr++; 74 } 75 return x & NFS4_DEVICE_ID_HASH_MASK; 76} 77 78static struct nfs4_deviceid_node * 79_lookup_deviceid(const struct pnfs_layoutdriver_type *ld, 80 const struct nfs_client *clp, const struct nfs4_deviceid *id, 81 long hash) 82{ 83 struct nfs4_deviceid_node *d; 84 85 hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[hash], node) 86 if (d->ld == ld && d->nfs_client == clp && 87 !memcmp(&d->deviceid, id, sizeof(*id))) { 88 if (atomic_read(&d->ref)) 89 return d; 90 else 91 continue; 92 } 93 return NULL; 94} 95 96static struct nfs4_deviceid_node * 97nfs4_get_device_info(struct nfs_server *server, 98 const struct nfs4_deviceid *dev_id, 99 const struct cred *cred, gfp_t gfp_flags) 100{ 101 struct nfs4_deviceid_node *d = NULL; 102 struct pnfs_device *pdev = NULL; 103 struct page **pages = NULL; 104 u32 max_resp_sz; 105 int max_pages; 106 int rc, i; 107 108 /* 109 * Use the session max response size as the basis for setting 110 * GETDEVICEINFO's maxcount 111 */ 112 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; 113 if (server->pnfs_curr_ld->max_deviceinfo_size && 114 server->pnfs_curr_ld->max_deviceinfo_size < max_resp_sz) 115 max_resp_sz = server->pnfs_curr_ld->max_deviceinfo_size; 116 max_pages = nfs_page_array_len(0, max_resp_sz); 117 dprintk("%s: server %p max_resp_sz %u max_pages %d\n", 118 __func__, server, max_resp_sz, max_pages); 119 120 pdev = kzalloc(sizeof(*pdev), gfp_flags); 121 if (!pdev) 122 return NULL; 123 124 pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags); 125 if (!pages) 126 goto out_free_pdev; 127 128 for (i = 0; i < max_pages; i++) { 129 pages[i] = alloc_page(gfp_flags); 130 if (!pages[i]) 131 goto out_free_pages; 132 } 133 134 memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id)); 135 pdev->layout_type = server->pnfs_curr_ld->id; 136 pdev->pages = pages; 137 pdev->pgbase = 0; 138 pdev->pglen = max_resp_sz; 139 pdev->mincount = 0; 140 pdev->maxcount = max_resp_sz - nfs41_maxgetdevinfo_overhead; 141 142 rc = nfs4_proc_getdeviceinfo(server, pdev, cred); 143 dprintk("%s getdevice info returns %d\n", __func__, rc); 144 if (rc) 145 goto out_free_pages; 146 147 /* 148 * Found new device, need to decode it and then add it to the 149 * list of known devices for this mountpoint. 150 */ 151 d = server->pnfs_curr_ld->alloc_deviceid_node(server, pdev, 152 gfp_flags); 153 if (d && pdev->nocache) 154 set_bit(NFS_DEVICEID_NOCACHE, &d->flags); 155 156out_free_pages: 157 for (i = 0; i < max_pages; i++) 158 __free_page(pages[i]); 159 kfree(pages); 160out_free_pdev: 161 kfree(pdev); 162 dprintk("<-- %s d %p\n", __func__, d); 163 return d; 164} 165 166/* 167 * Lookup a deviceid in cache and get a reference count on it if found 168 * 169 * @clp nfs_client associated with deviceid 170 * @id deviceid to look up 171 */ 172static struct nfs4_deviceid_node * 173__nfs4_find_get_deviceid(struct nfs_server *server, 174 const struct nfs4_deviceid *id, long hash) 175{ 176 struct nfs4_deviceid_node *d; 177 178 rcu_read_lock(); 179 d = _lookup_deviceid(server->pnfs_curr_ld, server->nfs_client, id, 180 hash); 181 if (d != NULL && !atomic_inc_not_zero(&d->ref)) 182 d = NULL; 183 rcu_read_unlock(); 184 return d; 185} 186 187struct nfs4_deviceid_node * 188nfs4_find_get_deviceid(struct nfs_server *server, 189 const struct nfs4_deviceid *id, const struct cred *cred, 190 gfp_t gfp_mask) 191{ 192 long hash = nfs4_deviceid_hash(id); 193 struct nfs4_deviceid_node *d, *new; 194 195 d = __nfs4_find_get_deviceid(server, id, hash); 196 if (d) 197 goto found; 198 199 new = nfs4_get_device_info(server, id, cred, gfp_mask); 200 if (!new) { 201 trace_nfs4_find_deviceid(server, id, -ENOENT); 202 return new; 203 } 204 205 spin_lock(&nfs4_deviceid_lock); 206 d = __nfs4_find_get_deviceid(server, id, hash); 207 if (d) { 208 spin_unlock(&nfs4_deviceid_lock); 209 server->pnfs_curr_ld->free_deviceid_node(new); 210 } else { 211 atomic_inc(&new->ref); 212 hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); 213 spin_unlock(&nfs4_deviceid_lock); 214 d = new; 215 } 216found: 217 trace_nfs4_find_deviceid(server, id, 0); 218 return d; 219} 220EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); 221 222/* 223 * Remove a deviceid from cache 224 * 225 * @clp nfs_client associated with deviceid 226 * @id the deviceid to unhash 227 * 228 * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise. 229 */ 230void 231nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld, 232 const struct nfs_client *clp, const struct nfs4_deviceid *id) 233{ 234 struct nfs4_deviceid_node *d; 235 236 spin_lock(&nfs4_deviceid_lock); 237 rcu_read_lock(); 238 d = _lookup_deviceid(ld, clp, id, nfs4_deviceid_hash(id)); 239 rcu_read_unlock(); 240 if (!d) { 241 spin_unlock(&nfs4_deviceid_lock); 242 return; 243 } 244 hlist_del_init_rcu(&d->node); 245 clear_bit(NFS_DEVICEID_NOCACHE, &d->flags); 246 spin_unlock(&nfs4_deviceid_lock); 247 248 /* balance the initial ref set in pnfs_insert_deviceid */ 249 nfs4_put_deviceid_node(d); 250} 251EXPORT_SYMBOL_GPL(nfs4_delete_deviceid); 252 253void 254nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, struct nfs_server *server, 255 const struct nfs4_deviceid *id) 256{ 257 INIT_HLIST_NODE(&d->node); 258 INIT_HLIST_NODE(&d->tmpnode); 259 d->ld = server->pnfs_curr_ld; 260 d->nfs_client = server->nfs_client; 261 d->flags = 0; 262 d->deviceid = *id; 263 atomic_set(&d->ref, 1); 264} 265EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node); 266 267/* 268 * Dereference a deviceid node and delete it when its reference count drops 269 * to zero. 270 * 271 * @d deviceid node to put 272 * 273 * return true iff the node was deleted 274 * Note that since the test for d->ref == 0 is sufficient to establish 275 * that the node is no longer hashed in the global device id cache. 276 */ 277bool 278nfs4_put_deviceid_node(struct nfs4_deviceid_node *d) 279{ 280 if (test_bit(NFS_DEVICEID_NOCACHE, &d->flags)) { 281 if (atomic_add_unless(&d->ref, -1, 2)) 282 return false; 283 nfs4_delete_deviceid(d->ld, d->nfs_client, &d->deviceid); 284 } 285 if (!atomic_dec_and_test(&d->ref)) 286 return false; 287 trace_nfs4_deviceid_free(d->nfs_client, &d->deviceid); 288 d->ld->free_deviceid_node(d); 289 return true; 290} 291EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node); 292 293void 294nfs4_mark_deviceid_available(struct nfs4_deviceid_node *node) 295{ 296 if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) { 297 clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags); 298 smp_mb__after_atomic(); 299 } 300} 301EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_available); 302 303void 304nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node) 305{ 306 node->timestamp_unavailable = jiffies; 307 smp_mb__before_atomic(); 308 set_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags); 309 smp_mb__after_atomic(); 310} 311EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_unavailable); 312 313bool 314nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node) 315{ 316 if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) { 317 unsigned long start, end; 318 319 end = jiffies; 320 start = end - PNFS_DEVICE_RETRY_TIMEOUT; 321 if (time_in_range(node->timestamp_unavailable, start, end)) 322 return true; 323 clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags); 324 smp_mb__after_atomic(); 325 } 326 return false; 327} 328EXPORT_SYMBOL_GPL(nfs4_test_deviceid_unavailable); 329 330static void 331_deviceid_purge_client(const struct nfs_client *clp, long hash) 332{ 333 struct nfs4_deviceid_node *d; 334 HLIST_HEAD(tmp); 335 336 spin_lock(&nfs4_deviceid_lock); 337 rcu_read_lock(); 338 hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[hash], node) 339 if (d->nfs_client == clp && atomic_read(&d->ref)) { 340 hlist_del_init_rcu(&d->node); 341 hlist_add_head(&d->tmpnode, &tmp); 342 clear_bit(NFS_DEVICEID_NOCACHE, &d->flags); 343 } 344 rcu_read_unlock(); 345 spin_unlock(&nfs4_deviceid_lock); 346 347 if (hlist_empty(&tmp)) 348 return; 349 350 while (!hlist_empty(&tmp)) { 351 d = hlist_entry(tmp.first, struct nfs4_deviceid_node, tmpnode); 352 hlist_del(&d->tmpnode); 353 nfs4_put_deviceid_node(d); 354 } 355} 356 357void 358nfs4_deviceid_purge_client(const struct nfs_client *clp) 359{ 360 long h; 361 362 if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_MDS)) 363 return; 364 for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++) 365 _deviceid_purge_client(clp, h); 366} 367 368/* 369 * Stop use of all deviceids associated with an nfs_client 370 */ 371void 372nfs4_deviceid_mark_client_invalid(struct nfs_client *clp) 373{ 374 struct nfs4_deviceid_node *d; 375 int i; 376 377 rcu_read_lock(); 378 for (i = 0; i < NFS4_DEVICE_ID_HASH_SIZE; i ++){ 379 hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[i], node) 380 if (d->nfs_client == clp) 381 set_bit(NFS_DEVICEID_INVALID, &d->flags); 382 } 383 rcu_read_unlock(); 384}