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