NFSv4.1: make deviceid cache global
[linux-2.6-microblaze.git] / fs / nfs / nfs4filelayoutdev.c
index de5350f..eda4527 100644 (file)
 
 #define NFSDBG_FACILITY                NFSDBG_PNFS_LD
 
-/*
- * Device ID RCU cache. A device ID is unique per client ID and layout type.
- */
-#define NFS4_FL_DEVICE_ID_HASH_BITS    5
-#define NFS4_FL_DEVICE_ID_HASH_SIZE    (1 << NFS4_FL_DEVICE_ID_HASH_BITS)
-#define NFS4_FL_DEVICE_ID_HASH_MASK    (NFS4_FL_DEVICE_ID_HASH_SIZE - 1)
-
-static inline u32
-nfs4_fl_deviceid_hash(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_FL_DEVICE_ID_HASH_MASK;
-}
-
-static struct hlist_head filelayout_deviceid_cache[NFS4_FL_DEVICE_ID_HASH_SIZE];
-static DEFINE_SPINLOCK(filelayout_deviceid_lock);
-
 /*
  * Data server cache
  *
@@ -89,27 +65,6 @@ print_ds(struct nfs4_pnfs_ds *ds)
                ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0);
 }
 
-void
-print_ds_list(struct nfs4_file_layout_dsaddr *dsaddr)
-{
-       int i;
-
-       ifdebug(FACILITY) {
-               printk("%s dsaddr->ds_num %d\n", __func__,
-                      dsaddr->ds_num);
-               for (i = 0; i < dsaddr->ds_num; i++)
-                       print_ds(dsaddr->ds_list[i]);
-       }
-}
-
-void print_deviceid(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]);
-}
-
 /* nfs4_ds_cache_lock is held */
 static struct nfs4_pnfs_ds *
 _data_server_lookup_locked(u32 ip_addr, u32 port)
@@ -207,7 +162,7 @@ nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr)
        struct nfs4_pnfs_ds *ds;
        int i;
 
-       print_deviceid(&dsaddr->deviceid);
+       nfs4_print_deviceid(&dsaddr->id_node.deviceid);
 
        for (i = 0; i < dsaddr->ds_num; i++) {
                ds = dsaddr->ds_list[i];
@@ -225,11 +180,11 @@ nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr)
 }
 
 static struct nfs4_pnfs_ds *
-nfs4_pnfs_ds_add(struct inode *inode, u32 ip_addr, u32 port)
+nfs4_pnfs_ds_add(struct inode *inode, u32 ip_addr, u32 port, gfp_t gfp_flags)
 {
        struct nfs4_pnfs_ds *tmp_ds, *ds;
 
-       ds = kzalloc(sizeof(*tmp_ds), GFP_KERNEL);
+       ds = kzalloc(sizeof(*tmp_ds), gfp_flags);
        if (!ds)
                goto out;
 
@@ -261,7 +216,7 @@ out:
  * Currently only support ipv4, and one multi-path address.
  */
 static struct nfs4_pnfs_ds *
-decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode)
+decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode, gfp_t gfp_flags)
 {
        struct nfs4_pnfs_ds *ds = NULL;
        char *buf;
@@ -303,7 +258,7 @@ decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode)
                        rlen);
                goto out_err;
        }
-       buf = kmalloc(rlen + 1, GFP_KERNEL);
+       buf = kmalloc(rlen + 1, gfp_flags);
        if (!buf) {
                dprintk("%s: Not enough memory\n", __func__);
                goto out_err;
@@ -333,7 +288,7 @@ decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode)
        sscanf(pstr, "-%d-%d", &tmp[0], &tmp[1]);
        port = htons((tmp[0] << 8) | (tmp[1]));
 
-       ds = nfs4_pnfs_ds_add(inode, ip_addr, port);
+       ds = nfs4_pnfs_ds_add(inode, ip_addr, port, gfp_flags);
        dprintk("%s: Decoded address and port %s\n", __func__, buf);
 out_free:
        kfree(buf);
@@ -343,7 +298,7 @@ out_err:
 
 /* Decode opaque device data and return the result */
 static struct nfs4_file_layout_dsaddr*
-decode_device(struct inode *ino, struct pnfs_device *pdev)
+decode_device(struct inode *ino, struct pnfs_device *pdev, gfp_t gfp_flags)
 {
        int i;
        u32 cnt, num;
@@ -362,7 +317,7 @@ decode_device(struct inode *ino, struct pnfs_device *pdev)
        struct page *scratch;
 
        /* set up xdr stream */
-       scratch = alloc_page(GFP_KERNEL);
+       scratch = alloc_page(gfp_flags);
        if (!scratch)
                goto out_err;
 
@@ -384,7 +339,7 @@ decode_device(struct inode *ino, struct pnfs_device *pdev)
        }
 
        /* read stripe indices */
-       stripe_indices = kcalloc(cnt, sizeof(u8), GFP_KERNEL);
+       stripe_indices = kcalloc(cnt, sizeof(u8), gfp_flags);
        if (!stripe_indices)
                goto out_err_free_scratch;
 
@@ -423,7 +378,7 @@ decode_device(struct inode *ino, struct pnfs_device *pdev)
 
        dsaddr = kzalloc(sizeof(*dsaddr) +
                        (sizeof(struct nfs4_pnfs_ds *) * (num - 1)),
-                       GFP_KERNEL);
+                       gfp_flags);
        if (!dsaddr)
                goto out_err_free_stripe_indices;
 
@@ -431,8 +386,8 @@ decode_device(struct inode *ino, struct pnfs_device *pdev)
        dsaddr->stripe_indices = stripe_indices;
        stripe_indices = NULL;
        dsaddr->ds_num = num;
-
-       memcpy(&dsaddr->deviceid, &pdev->dev_id, sizeof(pdev->dev_id));
+       nfs4_init_deviceid_node(&dsaddr->id_node, NFS_SERVER(ino)->nfs_client,
+                               &pdev->dev_id);
 
        for (i = 0; i < dsaddr->ds_num; i++) {
                int j;
@@ -452,7 +407,7 @@ decode_device(struct inode *ino, struct pnfs_device *pdev)
                for (j = 0; j < mp_count; j++) {
                        if (j == 0) {
                                dsaddr->ds_list[i] = decode_and_add_ds(&stream,
-                                       ino);
+                                       ino, gfp_flags);
                                if (dsaddr->ds_list[i] == NULL)
                                        goto out_err_free_deviceid;
                        } else {
@@ -503,32 +458,25 @@ out_err:
  * available devices.
  */
 static struct nfs4_file_layout_dsaddr *
-decode_and_add_device(struct inode *inode, struct pnfs_device *dev)
+decode_and_add_device(struct inode *inode, struct pnfs_device *dev, gfp_t gfp_flags)
 {
-       struct nfs4_file_layout_dsaddr *d, *new;
-       long hash;
+       struct nfs4_deviceid_node *d;
+       struct nfs4_file_layout_dsaddr *n, *new;
 
-       new = decode_device(inode, dev);
+       new = decode_device(inode, dev, gfp_flags);
        if (!new) {
                printk(KERN_WARNING "%s: Could not decode or add device\n",
                        __func__);
                return NULL;
        }
 
-       spin_lock(&filelayout_deviceid_lock);
-       d = nfs4_fl_find_get_deviceid(&new->deviceid);
-       if (d) {
-               spin_unlock(&filelayout_deviceid_lock);
+       d = nfs4_insert_deviceid_node(&new->id_node);
+       n = container_of(d, struct nfs4_file_layout_dsaddr, id_node);
+       if (n != new) {
                nfs4_fl_free_deviceid(new);
-               return d;
+               return n;
        }
 
-       INIT_HLIST_NODE(&new->node);
-       atomic_set(&new->ref, 1);
-       hash = nfs4_fl_deviceid_hash(&new->deviceid);
-       hlist_add_head_rcu(&new->node, &filelayout_deviceid_cache[hash]);
-       spin_unlock(&filelayout_deviceid_lock);
-
        return new;
 }
 
@@ -537,7 +485,7 @@ decode_and_add_device(struct inode *inode, struct pnfs_device *dev)
  * of available devices, and return it.
  */
 struct nfs4_file_layout_dsaddr *
-get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id)
+get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags)
 {
        struct pnfs_device *pdev = NULL;
        u32 max_resp_sz;
@@ -556,17 +504,17 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id)
        dprintk("%s inode %p max_resp_sz %u max_pages %d\n",
                __func__, inode, max_resp_sz, max_pages);
 
-       pdev = kzalloc(sizeof(struct pnfs_device), GFP_KERNEL);
+       pdev = kzalloc(sizeof(struct pnfs_device), gfp_flags);
        if (pdev == NULL)
                return NULL;
 
-       pages = kzalloc(max_pages * sizeof(struct page *), GFP_KERNEL);
+       pages = kzalloc(max_pages * sizeof(struct page *), gfp_flags);
        if (pages == NULL) {
                kfree(pdev);
                return NULL;
        }
        for (i = 0; i < max_pages; i++) {
-               pages[i] = alloc_page(GFP_KERNEL);
+               pages[i] = alloc_page(gfp_flags);
                if (!pages[i])
                        goto out_free;
        }
@@ -587,7 +535,7 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id)
         * Found new device, need to decode it and then add it to the
         * list of known devices for this mountpoint.
         */
-       dsaddr = decode_and_add_device(inode, pdev);
+       dsaddr = decode_and_add_device(inode, pdev, gfp_flags);
 out_free:
        for (i = 0; i < max_pages; i++)
                __free_page(pages[i]);
@@ -600,35 +548,8 @@ out_free:
 void
 nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr)
 {
-       if (atomic_dec_and_lock(&dsaddr->ref, &filelayout_deviceid_lock)) {
-               hlist_del_rcu(&dsaddr->node);
-               spin_unlock(&filelayout_deviceid_lock);
-
-               synchronize_rcu();
+       if (nfs4_put_deviceid_node(&dsaddr->id_node))
                nfs4_fl_free_deviceid(dsaddr);
-       }
-}
-
-struct nfs4_file_layout_dsaddr *
-nfs4_fl_find_get_deviceid(struct nfs4_deviceid *id)
-{
-       struct nfs4_file_layout_dsaddr *d;
-       struct hlist_node *n;
-       long hash = nfs4_fl_deviceid_hash(id);
-
-
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(d, n, &filelayout_deviceid_cache[hash], node) {
-               if (!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;
 }
 
 /*
@@ -676,15 +597,15 @@ static void
 filelayout_mark_devid_negative(struct nfs4_file_layout_dsaddr *dsaddr,
                               int err, u32 ds_addr)
 {
-       u32 *p = (u32 *)&dsaddr->deviceid;
+       u32 *p = (u32 *)&dsaddr->id_node.deviceid;
 
        printk(KERN_ERR "NFS: data server %x connection error %d."
                " Deviceid [%x%x%x%x] marked out of use.\n",
                ds_addr, err, p[0], p[1], p[2], p[3]);
 
-       spin_lock(&filelayout_deviceid_lock);
+       spin_lock(&nfs4_ds_cache_lock);
        dsaddr->flags |= NFS4_DEVICE_ID_NEG_ENTRY;
-       spin_unlock(&filelayout_deviceid_lock);
+       spin_unlock(&nfs4_ds_cache_lock);
 }
 
 struct nfs4_pnfs_ds *