Commit 9e3bd4e2 authored by Weston Andros Adamson's avatar Weston Andros Adamson Committed by Trond Myklebust

NFS: fix umount of pnfs filesystems

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: default avatarWeston Andros Adamson <dros@netapp.com>
Signed-off-by: default avatarAndy Adamson <andros@netapp.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 3f303103
...@@ -186,6 +186,7 @@ int pnfs_ld_read_done(struct nfs_read_data *); ...@@ -186,6 +186,7 @@ int pnfs_ld_read_done(struct nfs_read_data *);
/* pnfs_dev.c */ /* pnfs_dev.c */
struct nfs4_deviceid_node { struct nfs4_deviceid_node {
struct hlist_node node; struct hlist_node node;
struct hlist_node tmpnode;
const struct pnfs_layoutdriver_type *ld; const struct pnfs_layoutdriver_type *ld;
const struct nfs_client *nfs_client; const struct nfs_client *nfs_client;
struct nfs4_deviceid deviceid; struct nfs4_deviceid deviceid;
......
...@@ -174,6 +174,7 @@ nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, ...@@ -174,6 +174,7 @@ nfs4_init_deviceid_node(struct nfs4_deviceid_node *d,
const struct nfs4_deviceid *id) const struct nfs4_deviceid *id)
{ {
INIT_HLIST_NODE(&d->node); INIT_HLIST_NODE(&d->node);
INIT_HLIST_NODE(&d->tmpnode);
d->ld = ld; d->ld = ld;
d->nfs_client = nfs_client; d->nfs_client = nfs_client;
d->deviceid = *id; d->deviceid = *id;
...@@ -238,24 +239,29 @@ static void ...@@ -238,24 +239,29 @@ static void
_deviceid_purge_client(const struct nfs_client *clp, long hash) _deviceid_purge_client(const struct nfs_client *clp, long hash)
{ {
struct nfs4_deviceid_node *d; struct nfs4_deviceid_node *d;
struct hlist_node *n, *next; struct hlist_node *n;
HLIST_HEAD(tmp); HLIST_HEAD(tmp);
spin_lock(&nfs4_deviceid_lock);
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node)
if (d->nfs_client == clp && atomic_read(&d->ref)) { if (d->nfs_client == clp && atomic_read(&d->ref)) {
hlist_del_init_rcu(&d->node); hlist_del_init_rcu(&d->node);
hlist_add_head(&d->node, &tmp); hlist_add_head(&d->tmpnode, &tmp);
} }
rcu_read_unlock(); rcu_read_unlock();
spin_unlock(&nfs4_deviceid_lock);
if (hlist_empty(&tmp)) if (hlist_empty(&tmp))
return; return;
synchronize_rcu(); 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)) if (atomic_dec_and_test(&d->ref))
d->ld->free_deviceid_node(d); d->ld->free_deviceid_node(d);
}
} }
void void
...@@ -263,8 +269,8 @@ nfs4_deviceid_purge_client(const struct nfs_client *clp) ...@@ -263,8 +269,8 @@ nfs4_deviceid_purge_client(const struct nfs_client *clp)
{ {
long h; 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++) for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++)
_deviceid_purge_client(clp, h); _deviceid_purge_client(clp, h);
spin_unlock(&nfs4_deviceid_lock);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment