Skip to content

Commit

Permalink
Merge tag 'nfs-for-3.9-1' of git://git.linux-nfs.org/projects/trondmy…
Browse files Browse the repository at this point in the history
…/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:

 - Fix an Oops in the pNFS layoutget code

 - Fix a number of NFSv4 and v4.1 state recovery deadlocks and hangs due
   to the interaction of the session drain lock and state management
   locks.

 - Remove task->tk_xprt, which was hiding a lot of RCU dereferencing
   bugs

 - Fix a long standing NFSv3 posix lock recovery bug.

 - Revert commit 324d003 ("NFS: add nfs_sb_deactive_async to avoid
   deadlock").  It turned out that the root cause of the deadlock was
   due to interactions with the workqueues that have now been resolved.

* tag 'nfs-for-3.9-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (22 commits)
  NLM: Ensure that we resend all pending blocking locks after a reclaim
  umount oops when remove blocklayoutdriver first
  sunrpc: silence build warning in gss_fill_context
  nfs: remove kfree() redundant null checks
  NFSv4.1: Don't decode skipped layoutgets
  NFSv4.1: Fix bulk recall and destroy of layouts
  NFSv4.1: Fix an ABBA locking issue with session and state serialisation
  NFSv4: Fix a reboot recovery race when opening a file
  NFSv4: Ensure delegation recall and byte range lock removal don't conflict
  NFSv4: Fix up the return values of nfs4_open_delegation_recall
  NFSv4.1: Don't lose locks when a server reboots during delegation return
  NFSv4.1: Prevent deadlocks between state recovery and file locking
  NFSv4: Allow the state manager to mark an open_owner as being recovered
  SUNRPC: Add missing static declaration to _gss_mech_get_by_name
  Revert "NFS: add nfs_sb_deactive_async to avoid deadlock"
  SUNRPC: Nuke the tk_xprt macro
  SUNRPC: Avoid RCU dereferences in the transport bind and connect code
  SUNRPC: Fix an RCU dereference in xprt_reserve
  SUNRPC: Pass pointers to struct rpc_xprt to the congestion window
  SUNRPC: Fix an RCU dereference in xs_local_rpcbind
  ...
  • Loading branch information
torvalds committed Feb 21, 2013
2 parents 9b9a72a + 666b3d8 commit 2171ee8
Show file tree
Hide file tree
Showing 26 changed files with 415 additions and 260 deletions.
3 changes: 3 additions & 0 deletions fs/lockd/clntproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,9 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
if (status < 0)
break;
/* Resend the blocking lock request after a server reboot */
if (resp->status == nlm_lck_denied_grace_period)
continue;
if (resp->status != nlm_lck_blocked)
break;
}
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/blocklayout/blocklayout.c
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,7 @@ static const struct nfs_pageio_ops bl_pg_write_ops = {
static struct pnfs_layoutdriver_type blocklayout_type = {
.id = LAYOUT_BLOCK_VOLUME,
.name = "LAYOUT_BLOCK_VOLUME",
.owner = THIS_MODULE,
.read_pagelist = bl_read_pagelist,
.write_pagelist = bl_write_pagelist,
.alloc_layout_hdr = bl_alloc_layout_hdr,
Expand Down
61 changes: 8 additions & 53 deletions fs/nfs/callback_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,60 +183,15 @@ static u32 initiate_file_draining(struct nfs_client *clp,
static u32 initiate_bulk_draining(struct nfs_client *clp,
struct cb_layoutrecallargs *args)
{
struct nfs_server *server;
struct pnfs_layout_hdr *lo;
struct inode *ino;
u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
struct pnfs_layout_hdr *tmp;
LIST_HEAD(recall_list);
LIST_HEAD(free_me_list);
struct pnfs_layout_range range = {
.iomode = IOMODE_ANY,
.offset = 0,
.length = NFS4_MAX_UINT64,
};

spin_lock(&clp->cl_lock);
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if ((args->cbl_recall_type == RETURN_FSID) &&
memcmp(&server->fsid, &args->cbl_fsid,
sizeof(struct nfs_fsid)))
continue;
int stat;

list_for_each_entry(lo, &server->layouts, plh_layouts) {
ino = igrab(lo->plh_inode);
if (!ino)
continue;
spin_lock(&ino->i_lock);
/* Is this layout in the process of being freed? */
if (NFS_I(ino)->layout != lo) {
spin_unlock(&ino->i_lock);
iput(ino);
continue;
}
pnfs_get_layout_hdr(lo);
spin_unlock(&ino->i_lock);
list_add(&lo->plh_bulk_recall, &recall_list);
}
}
rcu_read_unlock();
spin_unlock(&clp->cl_lock);

list_for_each_entry_safe(lo, tmp,
&recall_list, plh_bulk_recall) {
ino = lo->plh_inode;
spin_lock(&ino->i_lock);
set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
if (pnfs_mark_matching_lsegs_invalid(lo, &free_me_list, &range))
rv = NFS4ERR_DELAY;
list_del_init(&lo->plh_bulk_recall);
spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&free_me_list);
pnfs_put_layout_hdr(lo);
iput(ino);
}
return rv;
if (args->cbl_recall_type == RETURN_FSID)
stat = pnfs_destroy_layouts_byfsid(clp, &args->cbl_fsid, true);
else
stat = pnfs_destroy_layouts_byclid(clp, true);
if (stat != 0)
return NFS4ERR_DELAY;
return NFS4ERR_NOMATCHING_LAYOUT;
}

static u32 do_callback_layoutrecall(struct nfs_client *clp,
Expand Down
154 changes: 110 additions & 44 deletions fs/nfs/delegation.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags)
flags &= FMODE_READ|FMODE_WRITE;
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation != NULL && (delegation->type & flags) == flags) {
if (delegation != NULL && (delegation->type & flags) == flags &&
!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
nfs_mark_delegation_referenced(delegation);
ret = 1;
}
Expand All @@ -70,8 +71,10 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
int status = 0;

if (inode->i_flock == NULL)
goto out;
return 0;

if (inode->i_flock == NULL)
goto out;
/* Protect inode->i_flock using the file locks lock */
lock_flocks();
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
Expand All @@ -94,7 +97,9 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
struct nfs4_state_owner *sp;
struct nfs4_state *state;
unsigned int seq;
int err;

again:
Expand All @@ -109,9 +114,16 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
continue;
get_nfs_open_context(ctx);
spin_unlock(&inode->i_lock);
sp = state->owner;
/* Block nfs4_proc_unlck */
mutex_lock(&sp->so_delegreturn_mutex);
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
err = nfs4_open_delegation_recall(ctx, state, stateid);
if (err >= 0)
if (!err)
err = nfs_delegation_claim_locks(ctx, state);
if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
err = -EAGAIN;
mutex_unlock(&sp->so_delegreturn_mutex);
put_nfs_open_context(ctx);
if (err != 0)
return err;
Expand Down Expand Up @@ -181,40 +193,92 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
return inode;
}

static struct nfs_delegation *
nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
{
struct nfs_delegation *ret = NULL;
struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);

if (delegation == NULL)
goto out;
spin_lock(&delegation->lock);
if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
ret = delegation;
spin_unlock(&delegation->lock);
out:
return ret;
}

static struct nfs_delegation *
nfs_start_delegation_return(struct nfs_inode *nfsi)
{
struct nfs_delegation *delegation;

rcu_read_lock();
delegation = nfs_start_delegation_return_locked(nfsi);
rcu_read_unlock();
return delegation;
}

static void
nfs_abort_delegation_return(struct nfs_delegation *delegation,
struct nfs_client *clp)
{

spin_lock(&delegation->lock);
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
spin_unlock(&delegation->lock);
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}

static struct nfs_delegation *
nfs_detach_delegation_locked(struct nfs_inode *nfsi,
struct nfs_server *server)
struct nfs_delegation *delegation,
struct nfs_client *clp)
{
struct nfs_delegation *delegation =
struct nfs_delegation *deleg_cur =
rcu_dereference_protected(nfsi->delegation,
lockdep_is_held(&server->nfs_client->cl_lock));
lockdep_is_held(&clp->cl_lock));

if (delegation == NULL)
goto nomatch;
if (deleg_cur == NULL || delegation != deleg_cur)
return NULL;

spin_lock(&delegation->lock);
set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
list_del_rcu(&delegation->super_list);
delegation->inode = NULL;
nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock);
return delegation;
nomatch:
return NULL;
}

static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
struct nfs_server *server)
struct nfs_delegation *delegation,
struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client;
struct nfs_delegation *delegation;

spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(nfsi, server);
delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
spin_unlock(&clp->cl_lock);
return delegation;
}

static struct nfs_delegation *
nfs_inode_detach_delegation(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_delegation *delegation;

delegation = nfs_start_delegation_return(nfsi);
if (delegation == NULL)
return NULL;
return nfs_detach_delegation(nfsi, delegation, server);
}

/**
* nfs_inode_set_delegation - set up a delegation on an inode
* @inode: inode to which delegation applies
Expand Down Expand Up @@ -268,7 +332,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation = NULL;
goto out;
}
freeme = nfs_detach_delegation_locked(nfsi, server);
freeme = nfs_detach_delegation_locked(nfsi,
old_delegation, clp);
if (freeme == NULL)
goto out;
}
list_add_rcu(&delegation->super_list, &server->delegations);
nfsi->delegation_state = delegation->type;
Expand All @@ -292,19 +359,29 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
/*
* Basic procedure for returning a delegation to the server
*/
static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
int err;

/*
* Guard against new delegated open/lock/unlock calls and against
* state recovery
*/
down_write(&nfsi->rwsem);
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
up_write(&nfsi->rwsem);
if (err)
if (delegation == NULL)
return 0;
do {
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
if (!issync || err != -EAGAIN)
break;
/*
* Guard against state recovery
*/
err = nfs4_wait_clnt_recover(clp);
} while (err == 0);

if (err) {
nfs_abort_delegation_return(delegation, clp);
goto out;
}
if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode)))
goto out;

err = nfs_do_return_delegation(inode, delegation, issync);
Expand Down Expand Up @@ -340,13 +417,10 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
continue;
delegation = nfs_detach_delegation(NFS_I(inode),
server);
delegation = nfs_start_delegation_return_locked(NFS_I(inode));
rcu_read_unlock();

if (delegation != NULL)
err = __nfs_inode_return_delegation(inode,
delegation, 0);
err = nfs_end_delegation_return(inode, delegation, 0);
iput(inode);
if (!err)
goto restart;
Expand All @@ -367,15 +441,11 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
*/
void nfs_inode_return_delegation_noreclaim(struct inode *inode)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;

if (rcu_access_pointer(nfsi->delegation) != NULL) {
delegation = nfs_detach_delegation(nfsi, server);
if (delegation != NULL)
nfs_do_return_delegation(inode, delegation, 0);
}
delegation = nfs_inode_detach_delegation(inode);
if (delegation != NULL)
nfs_do_return_delegation(inode, delegation, 0);
}

/**
Expand All @@ -390,18 +460,14 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode)
*/
int nfs4_inode_return_delegation(struct inode *inode)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int err = 0;

nfs_wb_all(inode);
if (rcu_access_pointer(nfsi->delegation) != NULL) {
delegation = nfs_detach_delegation(nfsi, server);
if (delegation != NULL) {
err = __nfs_inode_return_delegation(inode, delegation, 1);
}
}
delegation = nfs_start_delegation_return(nfsi);
if (delegation != NULL)
err = nfs_end_delegation_return(inode, delegation, 1);
return err;
}

Expand Down Expand Up @@ -471,7 +537,7 @@ void nfs_remove_bad_delegation(struct inode *inode)
{
struct nfs_delegation *delegation;

delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode));
delegation = nfs_inode_detach_delegation(inode);
if (delegation) {
nfs_inode_find_state_and_recover(inode, &delegation->stateid);
nfs_free_delegation(delegation);
Expand Down Expand Up @@ -649,7 +715,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
if (inode == NULL)
continue;
delegation = nfs_detach_delegation(NFS_I(inode),
server);
delegation, server);
rcu_read_unlock();

if (delegation != NULL)
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/delegation.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum {
NFS_DELEGATION_NEED_RECLAIM = 0,
NFS_DELEGATION_RETURN,
NFS_DELEGATION_REFERENCED,
NFS_DELEGATION_RETURNING,
};

int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
Expand Down
3 changes: 1 addition & 2 deletions fs/nfs/getroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh,
}
spin_unlock(&ret->d_lock);
out:
if (name)
kfree(name);
kfree(name);
nfs_free_fattr(fsinfo.fattr);
return ret;
}
5 changes: 1 addition & 4 deletions fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -694,10 +694,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
if (ctx->cred != NULL)
put_rpccred(ctx->cred);
dput(ctx->dentry);
if (is_sync)
nfs_sb_deactive(sb);
else
nfs_sb_deactive_async(sb);
nfs_sb_deactive(sb);
kfree(ctx->mdsthreshold);
kfree(ctx);
}
Expand Down
Loading

0 comments on commit 2171ee8

Please sign in to comment.