NFS: fix umount of pnfs filesystems
authorWeston Andros Adamson <dros@netapp.com>
Wed, 1 Jun 2011 01:46:50 +0000 (21:46 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 15 Jun 2011 15:23:02 +0000 (11:23 -0400)
Unmounting a pnfs filesystem hangs using filelayout and possibly others.
This fixes the use of the rcu protected node by making use of a new 'tmpnode'
for the temporary purge list. Also, the spinlock shouldn't be held when calling
synchronize_rcu().

Signed-off-by: Weston Andros Adamson <dros@netapp.com>
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/pnfs.h
fs/nfs/pnfs_dev.c

index 48d0a8e4d06212264469df6b4c21acba110597f0..96bf4e6f45beda6d9646c5b19a48d13ef1ec3f5d 100644 (file)
@@ -186,6 +186,7 @@ int pnfs_ld_read_done(struct nfs_read_data *);
 /* pnfs_dev.c */
 struct nfs4_deviceid_node {
        struct hlist_node               node;
+       struct hlist_node               tmpnode;
        const struct pnfs_layoutdriver_type *ld;
        const struct nfs_client         *nfs_client;
        struct nfs4_deviceid            deviceid;
index c65e133ce9c071d17e26729fe767b23f4271b445..5944d4b369a2f5707e98acd01252178bd69c1ebf 100644 (file)
@@ -174,6 +174,7 @@ nfs4_init_deviceid_node(struct nfs4_deviceid_node *d,
                        const struct nfs4_deviceid *id)
 {
        INIT_HLIST_NODE(&d->node);
+       INIT_HLIST_NODE(&d->tmpnode);
        d->ld = ld;
        d->nfs_client = nfs_client;
        d->deviceid = *id;
@@ -238,24 +239,29 @@ static void
 _deviceid_purge_client(const struct nfs_client *clp, long hash)
 {
        struct nfs4_deviceid_node *d;
-       struct hlist_node *n, *next;
+       struct hlist_node *n;
        HLIST_HEAD(tmp);
 
+       spin_lock(&nfs4_deviceid_lock);
        rcu_read_lock();
        hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node)
                if (d->nfs_client == clp && atomic_read(&d->ref)) {
                        hlist_del_init_rcu(&d->node);
-                       hlist_add_head(&d->node, &tmp);
+                       hlist_add_head(&d->tmpnode, &tmp);
                }
        rcu_read_unlock();
+       spin_unlock(&nfs4_deviceid_lock);
 
        if (hlist_empty(&tmp))
                return;
 
        synchronize_rcu();
-       hlist_for_each_entry_safe(d, n, next, &tmp, node)
+       while (!hlist_empty(&tmp)) {
+               d = hlist_entry(tmp.first, struct nfs4_deviceid_node, tmpnode);
+               hlist_del(&d->tmpnode);
                if (atomic_dec_and_test(&d->ref))
                        d->ld->free_deviceid_node(d);
+       }
 }
 
 void
@@ -263,8 +269,8 @@ nfs4_deviceid_purge_client(const struct nfs_client *clp)
 {
        long h;
 
-       spin_lock(&nfs4_deviceid_lock);
+       if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_MDS))
+               return;
        for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++)
                _deviceid_purge_client(clp, h);
-       spin_unlock(&nfs4_deviceid_lock);
 }