Commit 2171ee8f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-3.9-1' of git://git.linux-nfs.org/projects/trondmy/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 324d003b ("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
  ...
parents 9b9a72a8 666b3d80
...@@ -550,6 +550,9 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) ...@@ -550,6 +550,9 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT); status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
if (status < 0) if (status < 0)
break; 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) if (resp->status != nlm_lck_blocked)
break; break;
} }
......
...@@ -1273,6 +1273,7 @@ static const struct nfs_pageio_ops bl_pg_write_ops = { ...@@ -1273,6 +1273,7 @@ static const struct nfs_pageio_ops bl_pg_write_ops = {
static struct pnfs_layoutdriver_type blocklayout_type = { static struct pnfs_layoutdriver_type blocklayout_type = {
.id = LAYOUT_BLOCK_VOLUME, .id = LAYOUT_BLOCK_VOLUME,
.name = "LAYOUT_BLOCK_VOLUME", .name = "LAYOUT_BLOCK_VOLUME",
.owner = THIS_MODULE,
.read_pagelist = bl_read_pagelist, .read_pagelist = bl_read_pagelist,
.write_pagelist = bl_write_pagelist, .write_pagelist = bl_write_pagelist,
.alloc_layout_hdr = bl_alloc_layout_hdr, .alloc_layout_hdr = bl_alloc_layout_hdr,
......
...@@ -183,60 +183,15 @@ static u32 initiate_file_draining(struct nfs_client *clp, ...@@ -183,60 +183,15 @@ static u32 initiate_file_draining(struct nfs_client *clp,
static u32 initiate_bulk_draining(struct nfs_client *clp, static u32 initiate_bulk_draining(struct nfs_client *clp,
struct cb_layoutrecallargs *args) struct cb_layoutrecallargs *args)
{ {
struct nfs_server *server; int stat;
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;
list_for_each_entry(lo, &server->layouts, plh_layouts) { if (args->cbl_recall_type == RETURN_FSID)
ino = igrab(lo->plh_inode); stat = pnfs_destroy_layouts_byfsid(clp, &args->cbl_fsid, true);
if (!ino) else
continue; stat = pnfs_destroy_layouts_byclid(clp, true);
spin_lock(&ino->i_lock); if (stat != 0)
/* Is this layout in the process of being freed? */ return NFS4ERR_DELAY;
if (NFS_I(ino)->layout != lo) { return NFS4ERR_NOMATCHING_LAYOUT;
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;
} }
static u32 do_callback_layoutrecall(struct nfs_client *clp, static u32 do_callback_layoutrecall(struct nfs_client *clp,
......
...@@ -55,7 +55,8 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags) ...@@ -55,7 +55,8 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags)
flags &= FMODE_READ|FMODE_WRITE; flags &= FMODE_READ|FMODE_WRITE;
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation); 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); nfs_mark_delegation_referenced(delegation);
ret = 1; ret = 1;
} }
...@@ -70,8 +71,10 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ ...@@ -70,8 +71,10 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
int status = 0; int status = 0;
if (inode->i_flock == NULL) 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 */ /* Protect inode->i_flock using the file locks lock */
lock_flocks(); lock_flocks();
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
...@@ -94,7 +97,9 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s ...@@ -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_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
struct nfs4_state_owner *sp;
struct nfs4_state *state; struct nfs4_state *state;
unsigned int seq;
int err; int err;
again: again:
...@@ -109,9 +114,16 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s ...@@ -109,9 +114,16 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
continue; continue;
get_nfs_open_context(ctx); get_nfs_open_context(ctx);
spin_unlock(&inode->i_lock); 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); err = nfs4_open_delegation_recall(ctx, state, stateid);
if (err >= 0) if (!err)
err = nfs_delegation_claim_locks(ctx, state); 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); put_nfs_open_context(ctx);
if (err != 0) if (err != 0)
return err; return err;
...@@ -181,40 +193,92 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation ...@@ -181,40 +193,92 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
return inode; 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 * static struct nfs_delegation *
nfs_detach_delegation_locked(struct nfs_inode *nfsi, 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, rcu_dereference_protected(nfsi->delegation,
lockdep_is_held(&server->nfs_client->cl_lock)); lockdep_is_held(&clp->cl_lock));
if (delegation == NULL) if (deleg_cur == NULL || delegation != deleg_cur)
goto nomatch; return NULL;
spin_lock(&delegation->lock); spin_lock(&delegation->lock);
set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
list_del_rcu(&delegation->super_list); list_del_rcu(&delegation->super_list);
delegation->inode = NULL; delegation->inode = NULL;
nfsi->delegation_state = 0; nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL); rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
return delegation; return delegation;
nomatch:
return NULL;
} }
static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 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_client *clp = server->nfs_client;
struct nfs_delegation *delegation;
spin_lock(&clp->cl_lock); 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); spin_unlock(&clp->cl_lock);
return delegation; 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 * nfs_inode_set_delegation - set up a delegation on an inode
* @inode: inode to which delegation applies * @inode: inode to which delegation applies
...@@ -268,7 +332,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct ...@@ -268,7 +332,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation = NULL; delegation = NULL;
goto out; 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); list_add_rcu(&delegation->super_list, &server->delegations);
nfsi->delegation_state = delegation->type; nfsi->delegation_state = delegation->type;
...@@ -292,19 +359,29 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct ...@@ -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 * 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); struct nfs_inode *nfsi = NFS_I(inode);
int err; int err;
/* if (delegation == NULL)
* Guard against new delegated open/lock/unlock calls and against return 0;
* state recovery do {
*/ err = nfs_delegation_claim_opens(inode, &delegation->stateid);
down_write(&nfsi->rwsem); if (!issync || err != -EAGAIN)
err = nfs_delegation_claim_opens(inode, &delegation->stateid); break;
up_write(&nfsi->rwsem); /*
if (err) * 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; goto out;
err = nfs_do_return_delegation(inode, delegation, issync); err = nfs_do_return_delegation(inode, delegation, issync);
...@@ -340,13 +417,10 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp) ...@@ -340,13 +417,10 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
inode = nfs_delegation_grab_inode(delegation); inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL) if (inode == NULL)
continue; continue;
delegation = nfs_detach_delegation(NFS_I(inode), delegation = nfs_start_delegation_return_locked(NFS_I(inode));
server);
rcu_read_unlock(); rcu_read_unlock();
if (delegation != NULL) err = nfs_end_delegation_return(inode, delegation, 0);
err = __nfs_inode_return_delegation(inode,
delegation, 0);
iput(inode); iput(inode);
if (!err) if (!err)
goto restart; goto restart;
...@@ -367,15 +441,11 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp) ...@@ -367,15 +441,11 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
*/ */
void nfs_inode_return_delegation_noreclaim(struct inode *inode) 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; struct nfs_delegation *delegation;
if (rcu_access_pointer(nfsi->delegation) != NULL) { delegation = nfs_inode_detach_delegation(inode);
delegation = nfs_detach_delegation(nfsi, server); if (delegation != NULL)
if (delegation != NULL) nfs_do_return_delegation(inode, delegation, 0);
nfs_do_return_delegation(inode, delegation, 0);
}
} }
/** /**
...@@ -390,18 +460,14 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode) ...@@ -390,18 +460,14 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode)
*/ */
int nfs4_inode_return_delegation(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_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int err = 0; int err = 0;
nfs_wb_all(inode); nfs_wb_all(inode);
if (rcu_access_pointer(nfsi->delegation) != NULL) { delegation = nfs_start_delegation_return(nfsi);
delegation = nfs_detach_delegation(nfsi, server); if (delegation != NULL)
if (delegation != NULL) { err = nfs_end_delegation_return(inode, delegation, 1);
err = __nfs_inode_return_delegation(inode, delegation, 1);
}
}
return err; return err;
} }
...@@ -471,7 +537,7 @@ void nfs_remove_bad_delegation(struct inode *inode) ...@@ -471,7 +537,7 @@ void nfs_remove_bad_delegation(struct inode *inode)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode)); delegation = nfs_inode_detach_delegation(inode);
if (delegation) { if (delegation) {
nfs_inode_find_state_and_recover(inode, &delegation->stateid); nfs_inode_find_state_and_recover(inode, &delegation->stateid);
nfs_free_delegation(delegation); nfs_free_delegation(delegation);
...@@ -649,7 +715,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp) ...@@ -649,7 +715,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
if (inode == NULL) if (inode == NULL)
continue; continue;
delegation = nfs_detach_delegation(NFS_I(inode), delegation = nfs_detach_delegation(NFS_I(inode),
server); delegation, server);
rcu_read_unlock(); rcu_read_unlock();
if (delegation != NULL) if (delegation != NULL)
......
...@@ -29,6 +29,7 @@ enum { ...@@ -29,6 +29,7 @@ enum {
NFS_DELEGATION_NEED_RECLAIM = 0, NFS_DELEGATION_NEED_RECLAIM = 0,
NFS_DELEGATION_RETURN, NFS_DELEGATION_RETURN,
NFS_DELEGATION_REFERENCED, NFS_DELEGATION_REFERENCED,
NFS_DELEGATION_RETURNING,
}; };
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
......
...@@ -126,8 +126,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh, ...@@ -126,8 +126,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh,
} }
spin_unlock(&ret->d_lock); spin_unlock(&ret->d_lock);
out: out:
if (name) kfree(name);
kfree(name);
nfs_free_fattr(fsinfo.fattr); nfs_free_fattr(fsinfo.fattr);
return ret; return ret;
} }
...@@ -694,10 +694,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) ...@@ -694,10 +694,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
if (ctx->cred != NULL) if (ctx->cred != NULL)
put_rpccred(ctx->cred); put_rpccred(ctx->cred);
dput(ctx->dentry); dput(ctx->dentry);
if (is_sync) nfs_sb_deactive(sb);
nfs_sb_deactive(sb);
else
nfs_sb_deactive_async(sb);
kfree(ctx->mdsthreshold); kfree(ctx->mdsthreshold);
kfree(ctx); kfree(ctx);
} }
......
...@@ -329,7 +329,6 @@ extern int __init register_nfs_fs(void); ...@@ -329,7 +329,6 @@ extern int __init register_nfs_fs(void);
extern void __exit unregister_nfs_fs(void); extern void __exit unregister_nfs_fs(void);
extern void nfs_sb_active(struct super_block *sb); extern void nfs_sb_active(struct super_block *sb);
extern void nfs_sb_deactive(struct super_block *sb); extern void nfs_sb_deactive(struct super_block *sb);
extern void nfs_sb_deactive_async(struct super_block *sb);
/* namespace.c */ /* namespace.c */
#define NFS_PATH_CANONICAL 1 #define NFS_PATH_CANONICAL 1
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#define NFS4_MAX_LOOP_ON_RECOVER (10) #define NFS4_MAX_LOOP_ON_RECOVER (10)
#include <linux/seqlock.h>
struct idmap; struct idmap;
enum nfs4_client_state { enum nfs4_client_state {
...@@ -90,6 +92,8 @@ struct nfs4_state_owner { ...@@ -90,6 +92,8 @@ struct nfs4_state_owner {
unsigned long so_flags; unsigned long so_flags;
struct list_head so_states; struct list_head so_states;
struct nfs_seqid_counter so_seqid; struct nfs_seqid_counter so_seqid;
seqcount_t so_reclaim_seqcount;
struct mutex so_delegreturn_mutex;
}; };
enum { enum {
......
...@@ -896,6 +896,8 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode) ...@@ -896,6 +896,8 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
return 0; return 0;
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags)) if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
return 0; return 0;
if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
return 0;
nfs_mark_delegation_referenced(delegation); nfs_mark_delegation_referenced(delegation);
return 1; return 1;
} }
...@@ -973,6 +975,7 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat ...@@ -973,6 +975,7 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat
spin_lock(&deleg_cur->lock); spin_lock(&deleg_cur->lock);
if (nfsi->delegation != deleg_cur || if (nfsi->delegation != deleg_cur ||
test_bit(NFS_DELEGATION_RETURNING, &deleg_cur->flags) ||
(deleg_cur->type & fmode) != fmode) (deleg_cur->type & fmode) != fmode)
goto no_delegation_unlock; goto no_delegation_unlock;
...@@ -1352,19 +1355,18 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state ...@@ -1352,19 +1355,18 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
case -NFS4ERR_BAD_HIGH_SLOT: case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
case -NFS4ERR_DEADSESSION: case -NFS4ERR_DEADSESSION:
set_bit(NFS_DELEGATED_STATE, &state->flags);
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err); nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
err = -EAGAIN;
goto out; goto out;
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
set_bit(NFS_DELEGATED_STATE, &state->flags);
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
/* Don't recall a delegation if it was lost */ /* Don't recall a delegation if it was lost */
nfs4_schedule_lease_recovery(server->nfs_client); nfs4_schedule_lease_recovery(server->nfs_client);
err = -EAGAIN;
goto out; goto out;
case -ERESTARTSYS:
/*
* The show must go on: exit, but mark the
* stateid as needing recovery.
*/
case -NFS4ERR_DELEG_REVOKED: case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
...@@ -1375,6 +1377,7 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state ...@@ -1375,6 +1377,7 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
err = 0; err = 0;
goto out; goto out;
} }
set_bit(NFS_DELEGATED_STATE, &state->flags);
err = nfs4_handle_exception(server, err, &exception); err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
out: out:
...@@ -1463,7 +1466,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) ...@@ -1463,7 +1466,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
struct nfs4_state_owner *sp = data->owner; struct nfs4_state_owner *sp = data->owner;
if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0) if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
return; goto out_wait;
/* /*
* Check if we still need to send an OPEN call, or if we can use * Check if we still need to send an OPEN call, or if we can use
* a delegation instead. * a delegation instead.
...@@ -1498,6 +1501,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) ...@@ -1498,6 +1501,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
rcu_read_unlock(); rcu_read_unlock();
out_no_action: out_no_action:
task->tk_action = NULL; task->tk_action = NULL;
out_wait:
nfs4_sequence_done(task, &data->o_res.seq_res); nfs4_sequence_done(task, &data->o_res.seq_res);
} }
...@@ -1845,6 +1849,43 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct ...@@ -1845,6 +1849,43 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
sattr->ia_valid |= ATTR_MTIME; sattr->ia_valid |= ATTR_MTIME;
} }
static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
fmode_t fmode,
int flags,
struct nfs4_state **res)
{
struct nfs4_state_owner *sp = opendata->owner;
struct nfs_server *server = sp->so_server;
struct nfs4_state *state;
unsigned int seq;
int ret;
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
ret = _nfs4_proc_open(opendata);
if (ret != 0)
goto out;
state = nfs4_opendata_to_nfs4_state(opendata);
ret = PTR_ERR(state);
if (IS_ERR(state))
goto out;
if (server->caps & NFS_CAP_POSIX_LOCK)
set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
ret = nfs4_opendata_access(sp->so_cred, opendata, state, fmode, flags);
if (ret != 0)
goto out;
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) {
nfs4_schedule_stateid_recovery(server, state);
nfs4_wait_clnt_recover(server->nfs_client);
}
*res = state;
out:
return ret;
}
/* /*
* Returns a referenced nfs4_state * Returns a referenced nfs4_state
*/ */
...@@ -1889,18 +1930,7 @@ static int _nfs4_do_open(struct inode *dir, ...@@ -1889,18 +1930,7 @@ static int _nfs4_do_open(struct inode *dir,
if (dentry->d_inode != NULL) if (dentry->d_inode != NULL)
opendata->state = nfs4_get_open_state(dentry->d_inode, sp); opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
status = _nfs4_proc_open(opendata); status = _nfs4_open_and_get_state(opendata, fmode, flags, &state);
if (status != 0)
goto err_opendata_put;
state = nfs4_opendata_to_nfs4_state(opendata);
status = PTR_ERR(state);
if (IS_ERR(state))
goto err_opendata_put;
if (server->caps & NFS_CAP_POSIX_LOCK)
set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
status = nfs4_opendata_access(cred, opendata, state, fmode, flags);
if (status != 0) if (status != 0)
goto err_opendata_put; goto err_opendata_put;
...@@ -2088,7 +2118,7 @@ static void nfs4_free_closedata(void *data) ...@@ -2088,7 +2118,7 @@ static void nfs4_free_closedata(void *data)
nfs4_put_open_state(calldata->state); nfs4_put_open_state(calldata->state);
nfs_free_seqid(calldata->arg.seqid); nfs_free_seqid(calldata->arg.seqid);
nfs4_put_state_owner(sp); nfs4_put_state_owner(sp);
nfs_sb_deactive_async(sb); nfs_sb_deactive(sb);
kfree(calldata); kfree(calldata);
} }
...@@ -2150,7 +2180,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) ...@@ -2150,7 +2180,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
dprintk("%s: begin!\n", __func__); dprintk("%s: begin!\n", __func__);
if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
return; goto out_wait;
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
calldata->arg.fmode = FMODE_READ|FMODE_WRITE; calldata->arg.fmode = FMODE_READ|FMODE_WRITE;
...@@ -2172,16 +2202,14 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) ...@@ -2172,16 +2202,14 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
if (!call_close) { if (!call_close) {
/* Note: exit _without_ calling nfs4_close_done */ /* Note: exit _without_ calling nfs4_close_done */
task->tk_action = NULL; goto out_no_action;
nfs4_sequence_done(task, &calldata->res.seq_res);
goto out;
} }
if (calldata->arg.fmode == 0) { if (calldata->arg.fmode == 0) {
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE]; task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
if (calldata->roc && if (calldata->roc &&
pnfs_roc_drain(inode, &calldata->roc_barrier, task)) pnfs_roc_drain(inode, &calldata->roc_barrier, task))
goto out; goto out_wait;
} }
nfs_fattr_init(calldata->res.fattr); nfs_fattr_init(calldata->res.fattr);
...@@ -2191,8 +2219,12 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) ...@@ -2191,8 +2219,12 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
&calldata->res.seq_res, &calldata->res.seq_res,
task) != 0) task) != 0)
nfs_release_seqid(calldata->arg.seqid); nfs_release_seqid(calldata->arg.seqid);
out:
dprintk("%s: done!\n", __func__); dprintk("%s: done!\n", __func__);
return;
out_no_action:
task->tk_action = NULL;
out_wait:
nfs4_sequence_done(task, &calldata->res.seq_res);
} }
static const struct rpc_call_ops nfs4_close_ops = { static const struct rpc_call_ops nfs4_close_ops = {
...@@ -4423,12 +4455,10 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data) ...@@ -4423,12 +4455,10 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
struct nfs4_unlockdata *calldata = data; struct nfs4_unlockdata *calldata = data;
if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
return; goto out_wait;
if (test_bit(NFS_LOCK_INITIALIZED, &calldata->lsp->ls_flags) == 0) { if (test_bit(NFS_LOCK_INITIALIZED, &calldata->lsp->ls_flags) == 0) {
/* Note: exit _without_ running nfs4_locku_done */ /* Note: exit _without_ running nfs4_locku_done */
task->tk_action = NULL; goto out_no_action;
nfs4_sequence_done(task, &calldata->res.seq_res);
return;
} }
calldata->timestamp = jiffies; calldata->timestamp = jiffies;
if (nfs4_setup_sequence(calldata->server, if (nfs4_setup_sequence(calldata->server,
...@@ -4436,6 +4466,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data) ...@@ -4436,6 +4466,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
&calldata->res.seq_res, &calldata->res.seq_res,
task) != 0) task) != 0)
nfs_release_seqid(calldata->arg.seqid); nfs_release_seqid(calldata->arg.seqid);
return;
out_no_action:
task->tk_action = NULL;
out_wait:
nfs4_sequence_done(task, &calldata->res.seq_res);
} }
static const struct rpc_call_ops nfs4_locku_ops = { static const struct rpc_call_ops nfs4_locku_ops = {
...@@ -4482,7 +4517,9 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, ...@@ -4482,7 +4517,9 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{ {
struct nfs_inode *nfsi = NFS_I(state->inode); struct inode *inode = state->inode;
struct nfs4_state_owner *sp = state->owner;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_seqid *seqid; struct nfs_seqid *seqid;
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
struct rpc_task *task; struct rpc_task *task;
...@@ -4492,12 +4529,17 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * ...@@ -4492,12 +4529,17 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
status = nfs4_set_lock_state(state, request); status = nfs4_set_lock_state(state, request);
/* Unlock _before_ we do the RPC call */ /* Unlock _before_ we do the RPC call */
request->fl_flags |= FL_EXISTS; request->fl_flags |= FL_EXISTS;
/* Exclude nfs_delegation_claim_locks() */
mutex_lock(&sp->so_delegreturn_mutex);
/* Exclude nfs4_reclaim_open_stateid() - note nesting! */
down_read(&nfsi->rwsem); down_read(&nfsi->rwsem);
if (do_vfs_lock(request->fl_file, request) == -ENOENT) { if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
up_read(&nfsi->rwsem); up_read(&nfsi->rwsem);
mutex_unlock(&sp->so_delegreturn_mutex);
goto out; goto out;
} }
up_read(&nfsi->rwsem); up_read(&nfsi->rwsem);
mutex_unlock(&sp->so_delegreturn_mutex);
if (status != 0) if (status != 0)
goto out; goto out;
/* Is this a delegated lock? */ /* Is this a delegated lock? */
...@@ -4576,7 +4618,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) ...@@ -4576,7 +4618,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
dprintk("%s: begin!\n", __func__); dprintk("%s: begin!\n", __func__);
if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0) if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0)
return; goto out_wait;
/* Do we need to do an open_to_lock_owner? */ /* Do we need to do an open_to_lock_owner? */
if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) { if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) { if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) {
...@@ -4596,6 +4638,8 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) ...@@ -4596,6 +4638,8 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
nfs_release_seqid(data->arg.open_seqid); nfs_release_seqid(data->arg.open_seqid);
out_release_lock_seqid: out_release_lock_seqid:
nfs_release_seqid(data->arg.lock_seqid); nfs_release_seqid(data->arg.lock_seqid);
out_wait:
nfs4_sequence_done(task, &data->res.seq_res);
dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status); dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
} }
...@@ -4813,8 +4857,10 @@ static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *reques ...@@ -4813,8 +4857,10 @@ static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *reques
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{ {
struct nfs4_state_owner *sp = state->owner;
struct nfs_inode *nfsi = NFS_I(state->inode); struct nfs_inode *nfsi = NFS_I(state->inode);
unsigned char fl_flags = request->fl_flags; unsigned char fl_flags = request->fl_flags;
unsigned int seq;
int status = -ENOLCK; int status = -ENOLCK;
if ((fl_flags & FL_POSIX) && if ((fl_flags & FL_POSIX) &&
...@@ -4836,9 +4882,16 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -4836,9 +4882,16 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
status = do_vfs_lock(request->fl_file, request); status = do_vfs_lock(request->fl_file, request);
goto out_unlock; goto out_unlock;
} }
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
up_read(&nfsi->rwsem);
status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW); status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW);
if (status != 0) if (status != 0)
goto out;
down_read(&nfsi->rwsem);
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) {
status = -NFS4ERR_DELAY;
goto out_unlock; goto out_unlock;
}
/* Note: we always want to sleep here! */ /* Note: we always want to sleep here! */
request->fl_flags = fl_flags | FL_SLEEP; request->fl_flags = fl_flags | FL_SLEEP;
if (do_vfs_lock(request->fl_file, request) < 0) if (do_vfs_lock(request->fl_file, request) < 0)
...@@ -4945,24 +4998,22 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) ...@@ -4945,24 +4998,22 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
case 0: case 0:
case -ESTALE: case -ESTALE:
goto out; goto out;
case -NFS4ERR_EXPIRED:
nfs4_schedule_stateid_recovery(server, state);
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
set_bit(NFS_DELEGATED_STATE, &state->flags);
case -NFS4ERR_EXPIRED:
nfs4_schedule_lease_recovery(server->nfs_client); nfs4_schedule_lease_recovery(server->nfs_client);
err = -EAGAIN;
goto out; goto out;
case -NFS4ERR_BADSESSION: case -NFS4ERR_BADSESSION:
case -NFS4ERR_BADSLOT: case -NFS4ERR_BADSLOT:
case -NFS4ERR_BAD_HIGH_SLOT: case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
case -NFS4ERR_DEADSESSION: case -NFS4ERR_DEADSESSION:
set_bit(NFS_DELEGATED_STATE, &state->flags);
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err); nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
err = -EAGAIN;
goto out; goto out;
case -ERESTARTSYS:
/*
* The show must go on: exit, but mark the
* stateid as needing recovery.
*/
case -NFS4ERR_DELEG_REVOKED: case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
...@@ -4975,9 +5026,8 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) ...@@ -4975,9 +5026,8 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
/* kill_proc(fl->fl_pid, SIGLOST, 1); */ /* kill_proc(fl->fl_pid, SIGLOST, 1); */
err = 0; err = 0;
goto out; goto out;
case -NFS4ERR_DELAY:
break;
} }
set_bit(NFS_DELEGATED_STATE, &state->flags);
err = nfs4_handle_exception(server, err, &exception); err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
out: out:
...@@ -6134,7 +6184,8 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags) ...@@ -6134,7 +6184,8 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
status = nfs4_wait_for_completion_rpc_task(task); status = nfs4_wait_for_completion_rpc_task(task);
if (status == 0) if (status == 0)
status = task->tk_status; status = task->tk_status;
if (status == 0) /* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
if (status == 0 && lgp->res.layoutp->len)
lseg = pnfs_layout_process(lgp); lseg = pnfs_layout_process(lgp);
rpc_put_task(task); rpc_put_task(task);
dprintk("<-- %s status=%d\n", __func__, status); dprintk("<-- %s status=%d\n", __func__, status);
......
...@@ -518,6 +518,8 @@ nfs4_alloc_state_owner(struct nfs_server *server, ...@@ -518,6 +518,8 @@ nfs4_alloc_state_owner(struct nfs_server *server,
nfs4_init_seqid_counter(&sp->so_seqid); nfs4_init_seqid_counter(&sp->so_seqid);
atomic_set(&sp->so_count, 1); atomic_set(&sp->so_count, 1);
INIT_LIST_HEAD(&sp->so_lru); INIT_LIST_HEAD(&sp->so_lru);
seqcount_init(&sp->so_reclaim_seqcount);
mutex_init(&sp->so_delegreturn_mutex);
return sp; return sp;
} }
...@@ -1390,8 +1392,9 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs ...@@ -1390,8 +1392,9 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
* recovering after a network partition or a reboot from a * recovering after a network partition or a reboot from a
* server that doesn't support a grace period. * server that doesn't support a grace period.
*/ */
restart:
spin_lock(&sp->so_lock); spin_lock(&sp->so_lock);
write_seqcount_begin(&sp->so_reclaim_seqcount);
restart:
list_for_each_entry(state, &sp->so_states, open_states) { list_for_each_entry(state, &sp->so_states, open_states) {
if (!test_and_clear_bit(ops->state_flag_bit, &state->flags)) if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
continue; continue;
...@@ -1412,6 +1415,7 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs ...@@ -1412,6 +1415,7 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
} }
spin_unlock(&state->state_lock); spin_unlock(&state->state_lock);
nfs4_put_open_state(state); nfs4_put_open_state(state);
spin_lock(&sp->so_lock);
goto restart; goto restart;
} }
} }
...@@ -1449,12 +1453,17 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs ...@@ -1449,12 +1453,17 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
goto out_err; goto out_err;
} }
nfs4_put_open_state(state); nfs4_put_open_state(state);
spin_lock(&sp->so_lock);
goto restart; goto restart;
} }
write_seqcount_end(&sp->so_reclaim_seqcount);
spin_unlock(&sp->so_lock); spin_unlock(&sp->so_lock);
return 0; return 0;
out_err: out_err:
nfs4_put_open_state(state); nfs4_put_open_state(state);
spin_lock(&sp->so_lock);
write_seqcount_end(&sp->so_reclaim_seqcount);
spin_unlock(&sp->so_lock);
return status; return status;
} }
......
...@@ -647,6 +647,7 @@ static struct pnfs_layoutdriver_type objlayout_type = { ...@@ -647,6 +647,7 @@ static struct pnfs_layoutdriver_type objlayout_type = {
.flags = PNFS_LAYOUTRET_ON_SETATTR | .flags = PNFS_LAYOUTRET_ON_SETATTR |
PNFS_LAYOUTRET_ON_ERROR, PNFS_LAYOUTRET_ON_ERROR,
.owner = THIS_MODULE,
.alloc_layout_hdr = objlayout_alloc_layout_hdr, .alloc_layout_hdr = objlayout_alloc_layout_hdr,
.free_layout_hdr = objlayout_free_layout_hdr, .free_layout_hdr = objlayout_free_layout_hdr,
......
...@@ -505,37 +505,147 @@ pnfs_destroy_layout(struct nfs_inode *nfsi) ...@@ -505,37 +505,147 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
} }
EXPORT_SYMBOL_GPL(pnfs_destroy_layout); EXPORT_SYMBOL_GPL(pnfs_destroy_layout);
/* static bool
* Called by the state manger to remove all layouts established under an pnfs_layout_add_bulk_destroy_list(struct inode *inode,
* expired lease. struct list_head *layout_list)
*/
void
pnfs_destroy_all_layouts(struct nfs_client *clp)
{ {
struct nfs_server *server;
struct pnfs_layout_hdr *lo; struct pnfs_layout_hdr *lo;
LIST_HEAD(tmp_list); bool ret = false;
nfs4_deviceid_mark_client_invalid(clp); spin_lock(&inode->i_lock);
nfs4_deviceid_purge_client(clp); lo = NFS_I(inode)->layout;
if (lo != NULL && list_empty(&lo->plh_bulk_destroy)) {
pnfs_get_layout_hdr(lo);
list_add(&lo->plh_bulk_destroy, layout_list);
ret = true;
}
spin_unlock(&inode->i_lock);
return ret;
}
/* Caller must hold rcu_read_lock and clp->cl_lock */
static int
pnfs_layout_bulk_destroy_byserver_locked(struct nfs_client *clp,
struct nfs_server *server,
struct list_head *layout_list)
{
struct pnfs_layout_hdr *lo, *next;
struct inode *inode;
list_for_each_entry_safe(lo, next, &server->layouts, plh_layouts) {
inode = igrab(lo->plh_inode);
if (inode == NULL)
continue;
list_del_init(&lo->plh_layouts);
if (pnfs_layout_add_bulk_destroy_list(inode, layout_list))
continue;
rcu_read_unlock();
spin_unlock(&clp->cl_lock);
iput(inode);
spin_lock(&clp->cl_lock);
rcu_read_lock();
return -EAGAIN;
}
return 0;
}
static int
pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list,
bool is_bulk_recall)
{
struct pnfs_layout_hdr *lo;
struct inode *inode;
struct pnfs_layout_range range = {
.iomode = IOMODE_ANY,
.offset = 0,
.length = NFS4_MAX_UINT64,
};
LIST_HEAD(lseg_list);
int ret = 0;
while (!list_empty(layout_list)) {
lo = list_entry(layout_list->next, struct pnfs_layout_hdr,
plh_bulk_destroy);
dprintk("%s freeing layout for inode %lu\n", __func__,
lo->plh_inode->i_ino);
inode = lo->plh_inode;
spin_lock(&inode->i_lock);
list_del_init(&lo->plh_bulk_destroy);
lo->plh_block_lgets++; /* permanently block new LAYOUTGETs */
if (is_bulk_recall)
set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
if (pnfs_mark_matching_lsegs_invalid(lo, &lseg_list, &range))
ret = -EAGAIN;
spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&lseg_list);
pnfs_put_layout_hdr(lo);
iput(inode);
}
return ret;
}
int
pnfs_destroy_layouts_byfsid(struct nfs_client *clp,
struct nfs_fsid *fsid,
bool is_recall)
{
struct nfs_server *server;
LIST_HEAD(layout_list);
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
rcu_read_lock(); rcu_read_lock();
restart:
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (!list_empty(&server->layouts)) if (memcmp(&server->fsid, fsid, sizeof(*fsid)) != 0)
list_splice_init(&server->layouts, &tmp_list); continue;
if (pnfs_layout_bulk_destroy_byserver_locked(clp,
server,
&layout_list) != 0)
goto restart;
} }
rcu_read_unlock(); rcu_read_unlock();
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
while (!list_empty(&tmp_list)) { if (list_empty(&layout_list))
lo = list_entry(tmp_list.next, struct pnfs_layout_hdr, return 0;
plh_layouts); return pnfs_layout_free_bulk_destroy_list(&layout_list, is_recall);
dprintk("%s freeing layout for inode %lu\n", __func__, }
lo->plh_inode->i_ino);
list_del_init(&lo->plh_layouts); int
pnfs_destroy_layout(NFS_I(lo->plh_inode)); pnfs_destroy_layouts_byclid(struct nfs_client *clp,
bool is_recall)
{
struct nfs_server *server;
LIST_HEAD(layout_list);
spin_lock(&clp->cl_lock);
rcu_read_lock();
restart:
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (pnfs_layout_bulk_destroy_byserver_locked(clp,
server,
&layout_list) != 0)
goto restart;
} }
rcu_read_unlock();
spin_unlock(&clp->cl_lock);
if (list_empty(&layout_list))
return 0;
return pnfs_layout_free_bulk_destroy_list(&layout_list, is_recall);
}
/*
* Called by the state manger to remove all layouts established under an
* expired lease.
*/
void
pnfs_destroy_all_layouts(struct nfs_client *clp)
{
nfs4_deviceid_mark_client_invalid(clp);
nfs4_deviceid_purge_client(clp);
pnfs_destroy_layouts_byclid(clp, false);
} }
/* /*
...@@ -888,7 +998,7 @@ alloc_init_layout_hdr(struct inode *ino, ...@@ -888,7 +998,7 @@ alloc_init_layout_hdr(struct inode *ino,
atomic_set(&lo->plh_refcount, 1); atomic_set(&lo->plh_refcount, 1);
INIT_LIST_HEAD(&lo->plh_layouts); INIT_LIST_HEAD(&lo->plh_layouts);
INIT_LIST_HEAD(&lo->plh_segs); INIT_LIST_HEAD(&lo->plh_segs);
INIT_LIST_HEAD(&lo->plh_bulk_recall); INIT_LIST_HEAD(&lo->plh_bulk_destroy);
lo->plh_inode = ino; lo->plh_inode = ino;
lo->plh_lc_cred = get_rpccred(ctx->state->owner->so_cred); lo->plh_lc_cred = get_rpccred(ctx->state->owner->so_cred);
return lo; return lo;
......
...@@ -132,7 +132,7 @@ struct pnfs_layoutdriver_type { ...@@ -132,7 +132,7 @@ struct pnfs_layoutdriver_type {
struct pnfs_layout_hdr { struct pnfs_layout_hdr {
atomic_t plh_refcount; atomic_t plh_refcount;
struct list_head plh_layouts; /* other client layouts */ struct list_head plh_layouts; /* other client layouts */
struct list_head plh_bulk_recall; /* clnt list of bulk recalls */ struct list_head plh_bulk_destroy;
struct list_head plh_segs; /* layout segments list */ struct list_head plh_segs; /* layout segments list */
nfs4_stateid plh_stateid; nfs4_stateid plh_stateid;
atomic_t plh_outstanding; /* number of RPCs out */ atomic_t plh_outstanding; /* number of RPCs out */
...@@ -196,6 +196,11 @@ struct pnfs_layout_segment *pnfs_layout_process(struct nfs4_layoutget *lgp); ...@@ -196,6 +196,11 @@ struct pnfs_layout_segment *pnfs_layout_process(struct nfs4_layoutget *lgp);
void pnfs_free_lseg_list(struct list_head *tmp_list); void pnfs_free_lseg_list(struct list_head *tmp_list);
void pnfs_destroy_layout(struct nfs_inode *); void pnfs_destroy_layout(struct nfs_inode *);
void pnfs_destroy_all_layouts(struct nfs_client *); void pnfs_destroy_all_layouts(struct nfs_client *);
int pnfs_destroy_layouts_byfsid(struct nfs_client *clp,
struct nfs_fsid *fsid,
bool is_recall);
int pnfs_destroy_layouts_byclid(struct nfs_client *clp,
bool is_recall);
void pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo); void pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo);
void pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, void pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo,
const nfs4_stateid *new, const nfs4_stateid *new,
......
...@@ -54,7 +54,6 @@ ...@@ -54,7 +54,6 @@
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/nsproxy.h> #include <linux/nsproxy.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/kthread.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -418,54 +417,6 @@ void nfs_sb_deactive(struct super_block *sb) ...@@ -418,54 +417,6 @@ void nfs_sb_deactive(struct super_block *sb)
} }
EXPORT_SYMBOL_GPL(nfs_sb_deactive); EXPORT_SYMBOL_GPL(nfs_sb_deactive);
static int nfs_deactivate_super_async_work(void *ptr)
{
struct super_block *sb = ptr;
deactivate_super(sb);
module_put_and_exit(0);
return 0;
}
/*
* same effect as deactivate_super, but will do final unmount in kthread
* context
*/
static void nfs_deactivate_super_async(struct super_block *sb)
{
struct task_struct *task;
char buf[INET6_ADDRSTRLEN + 1];
struct nfs_server *server = NFS_SB(sb);
struct nfs_client *clp = server->nfs_client;
if (!atomic_add_unless(&sb->s_active, -1, 1)) {
rcu_read_lock();
snprintf(buf, sizeof(buf),
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
rcu_read_unlock();
__module_get(THIS_MODULE);
task = kthread_run(nfs_deactivate_super_async_work, sb,
"%s-deactivate-super", buf);
if (IS_ERR(task)) {
pr_err("%s: kthread_run: %ld\n",
__func__, PTR_ERR(task));
/* make synchronous call and hope for the best */
deactivate_super(sb);
module_put(THIS_MODULE);
}
}
}
void nfs_sb_deactive_async(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
if (atomic_dec_and_test(&server->active))
nfs_deactivate_super_async(sb);
}
EXPORT_SYMBOL_GPL(nfs_sb_deactive_async);
/* /*
* Deliver file system statistics to userspace * Deliver file system statistics to userspace
*/ */
......
...@@ -95,7 +95,7 @@ static void nfs_async_unlink_release(void *calldata) ...@@ -95,7 +95,7 @@ static void nfs_async_unlink_release(void *calldata)
nfs_dec_sillycount(data->dir); nfs_dec_sillycount(data->dir);
nfs_free_unlinkdata(data); nfs_free_unlinkdata(data);
nfs_sb_deactive_async(sb); nfs_sb_deactive(sb);
} }
static void nfs_unlink_prepare(struct rpc_task *task, void *calldata) static void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
...@@ -268,8 +268,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) ...@@ -268,8 +268,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
* point dentry is definitely not a root, so we won't need * point dentry is definitely not a root, so we won't need
* that anymore. * that anymore.
*/ */
if (devname_garbage) kfree(devname_garbage);
kfree(devname_garbage);
return 0; return 0;
out_unlock: out_unlock:
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
......
...@@ -87,7 +87,6 @@ struct rpc_task { ...@@ -87,7 +87,6 @@ struct rpc_task {
tk_cred_retry : 2, tk_cred_retry : 2,
tk_rebind_retry : 2; tk_rebind_retry : 2;
}; };
#define tk_xprt tk_client->cl_xprt
/* support walking a list of tasks on a wait queue */ /* support walking a list of tasks on a wait queue */
#define task_for_each(task, pos, head) \ #define task_for_each(task, pos, head) \
......
...@@ -117,12 +117,12 @@ struct rpc_xprt_ops { ...@@ -117,12 +117,12 @@ struct rpc_xprt_ops {
void (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task); void (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task);
void (*rpcbind)(struct rpc_task *task); void (*rpcbind)(struct rpc_task *task);
void (*set_port)(struct rpc_xprt *xprt, unsigned short port); void (*set_port)(struct rpc_xprt *xprt, unsigned short port);
void (*connect)(struct rpc_task *task); void (*connect)(struct rpc_xprt *xprt, struct rpc_task *task);
void * (*buf_alloc)(struct rpc_task *task, size_t size); void * (*buf_alloc)(struct rpc_task *task, size_t size);
void (*buf_free)(void *buffer); void (*buf_free)(void *buffer);
int (*send_request)(struct rpc_task *task); int (*send_request)(struct rpc_task *task);
void (*set_retrans_timeout)(struct rpc_task *task); void (*set_retrans_timeout)(struct rpc_task *task);
void (*timer)(struct rpc_task *task); void (*timer)(struct rpc_xprt *xprt, struct rpc_task *task);
void (*release_request)(struct rpc_task *task); void (*release_request)(struct rpc_task *task);
void (*close)(struct rpc_xprt *xprt); void (*close)(struct rpc_xprt *xprt);
void (*destroy)(struct rpc_xprt *xprt); void (*destroy)(struct rpc_xprt *xprt);
...@@ -313,7 +313,7 @@ void xprt_set_retrans_timeout_rtt(struct rpc_task *task); ...@@ -313,7 +313,7 @@ void xprt_set_retrans_timeout_rtt(struct rpc_task *task);
void xprt_wake_pending_tasks(struct rpc_xprt *xprt, int status); void xprt_wake_pending_tasks(struct rpc_xprt *xprt, int status);
void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action); void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action);
void xprt_write_space(struct rpc_xprt *xprt); void xprt_write_space(struct rpc_xprt *xprt);
void xprt_adjust_cwnd(struct rpc_task *task, int result); void xprt_adjust_cwnd(struct rpc_xprt *xprt, struct rpc_task *task, int result);
struct rpc_rqst * xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid); struct rpc_rqst * xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid);
void xprt_complete_rqst(struct rpc_task *task, int copied); void xprt_complete_rqst(struct rpc_task *task, int copied);
void xprt_release_rqst_cong(struct rpc_task *task); void xprt_release_rqst_cong(struct rpc_task *task);
......
...@@ -247,8 +247,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct ...@@ -247,8 +247,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct
__func__, ctx->gc_expiry, now, timeout); __func__, ctx->gc_expiry, now, timeout);
return q; return q;
err: err:
dprintk("RPC: %s returns %ld gc_expiry %lu now %lu timeout %u\n", dprintk("RPC: %s returns error %ld\n", __func__, -PTR_ERR(p));
__func__, -PTR_ERR(p), ctx->gc_expiry, now, timeout);
return p; return p;
} }
...@@ -1154,7 +1153,7 @@ gss_marshal(struct rpc_task *task, __be32 *p) ...@@ -1154,7 +1153,7 @@ gss_marshal(struct rpc_task *task, __be32 *p)
/* We compute the checksum for the verifier over the xdr-encoded bytes /* We compute the checksum for the verifier over the xdr-encoded bytes
* starting with the xid and ending at the end of the credential: */ * starting with the xid and ending at the end of the credential: */
iov.iov_base = xprt_skip_transport_header(task->tk_xprt, iov.iov_base = xprt_skip_transport_header(req->rq_xprt,
req->rq_snd_buf.head[0].iov_base); req->rq_snd_buf.head[0].iov_base);
iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; iov.iov_len = (u8 *)p - (u8 *)iov.iov_base;
xdr_buf_from_iov(&iov, &verf_buf); xdr_buf_from_iov(&iov, &verf_buf);
......
...@@ -140,7 +140,7 @@ gss_mech_get(struct gss_api_mech *gm) ...@@ -140,7 +140,7 @@ gss_mech_get(struct gss_api_mech *gm)
EXPORT_SYMBOL_GPL(gss_mech_get); EXPORT_SYMBOL_GPL(gss_mech_get);
struct gss_api_mech * static struct gss_api_mech *
_gss_mech_get_by_name(const char *name) _gss_mech_get_by_name(const char *name)
{ {
struct gss_api_mech *pos, *gm = NULL; struct gss_api_mech *pos, *gm = NULL;
...@@ -205,7 +205,7 @@ mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) ...@@ -205,7 +205,7 @@ mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
return 0; return 0;
} }
struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor) static struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
{ {
struct gss_api_mech *gm = NULL, *pos; struct gss_api_mech *gm = NULL, *pos;
......
...@@ -1400,7 +1400,7 @@ call_allocate(struct rpc_task *task) ...@@ -1400,7 +1400,7 @@ call_allocate(struct rpc_task *task)
{ {
unsigned int slack = task->tk_rqstp->rq_cred->cr_auth->au_cslack; unsigned int slack = task->tk_rqstp->rq_cred->cr_auth->au_cslack;
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = req->rq_xprt;
struct rpc_procinfo *proc = task->tk_msg.rpc_proc; struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
dprint_status(task); dprint_status(task);
...@@ -1508,7 +1508,7 @@ rpc_xdr_encode(struct rpc_task *task) ...@@ -1508,7 +1508,7 @@ rpc_xdr_encode(struct rpc_task *task)
static void static void
call_bind(struct rpc_task *task) call_bind(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
dprint_status(task); dprint_status(task);
...@@ -1602,7 +1602,7 @@ call_bind_status(struct rpc_task *task) ...@@ -1602,7 +1602,7 @@ call_bind_status(struct rpc_task *task)
static void static void
call_connect(struct rpc_task *task) call_connect(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
dprintk("RPC: %5u call_connect xprt %p %s connected\n", dprintk("RPC: %5u call_connect xprt %p %s connected\n",
task->tk_pid, xprt, task->tk_pid, xprt,
...@@ -1685,7 +1685,7 @@ call_transmit(struct rpc_task *task) ...@@ -1685,7 +1685,7 @@ call_transmit(struct rpc_task *task)
if (rpc_reply_expected(task)) if (rpc_reply_expected(task))
return; return;
task->tk_action = rpc_exit_task; task->tk_action = rpc_exit_task;
rpc_wake_up_queued_task(&task->tk_xprt->pending, task); rpc_wake_up_queued_task(&task->tk_rqstp->rq_xprt->pending, task);
} }
/* /*
...@@ -1784,7 +1784,7 @@ call_bc_transmit(struct rpc_task *task) ...@@ -1784,7 +1784,7 @@ call_bc_transmit(struct rpc_task *task)
*/ */
printk(KERN_NOTICE "RPC: Could not send backchannel reply " printk(KERN_NOTICE "RPC: Could not send backchannel reply "
"error: %d\n", task->tk_status); "error: %d\n", task->tk_status);
xprt_conditional_disconnect(task->tk_xprt, xprt_conditional_disconnect(req->rq_xprt,
req->rq_connect_cookie); req->rq_connect_cookie);
break; break;
default: default:
...@@ -1836,7 +1836,7 @@ call_status(struct rpc_task *task) ...@@ -1836,7 +1836,7 @@ call_status(struct rpc_task *task)
case -ETIMEDOUT: case -ETIMEDOUT:
task->tk_action = call_timeout; task->tk_action = call_timeout;
if (task->tk_client->cl_discrtry) if (task->tk_client->cl_discrtry)
xprt_conditional_disconnect(task->tk_xprt, xprt_conditional_disconnect(req->rq_xprt,
req->rq_connect_cookie); req->rq_connect_cookie);
break; break;
case -ECONNRESET: case -ECONNRESET:
...@@ -1991,7 +1991,7 @@ call_decode(struct rpc_task *task) ...@@ -1991,7 +1991,7 @@ call_decode(struct rpc_task *task)
if (task->tk_rqstp == req) { if (task->tk_rqstp == req) {
req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0; req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0;
if (task->tk_client->cl_discrtry) if (task->tk_client->cl_discrtry)
xprt_conditional_disconnect(task->tk_xprt, xprt_conditional_disconnect(req->rq_xprt,
req->rq_connect_cookie); req->rq_connect_cookie);
} }
} }
...@@ -2005,7 +2005,7 @@ rpc_encode_header(struct rpc_task *task) ...@@ -2005,7 +2005,7 @@ rpc_encode_header(struct rpc_task *task)
/* FIXME: check buffer size? */ /* FIXME: check buffer size? */
p = xprt_skip_transport_header(task->tk_xprt, p); p = xprt_skip_transport_header(req->rq_xprt, p);
*p++ = req->rq_xid; /* XID */ *p++ = req->rq_xid; /* XID */
*p++ = htonl(RPC_CALL); /* CALL */ *p++ = htonl(RPC_CALL); /* CALL */
*p++ = htonl(RPC_VERSION); /* RPC version */ *p++ = htonl(RPC_VERSION); /* RPC version */
......
...@@ -430,21 +430,23 @@ __xprt_put_cong(struct rpc_xprt *xprt, struct rpc_rqst *req) ...@@ -430,21 +430,23 @@ __xprt_put_cong(struct rpc_xprt *xprt, struct rpc_rqst *req)
*/ */
void xprt_release_rqst_cong(struct rpc_task *task) void xprt_release_rqst_cong(struct rpc_task *task)
{ {
__xprt_put_cong(task->tk_xprt, task->tk_rqstp); struct rpc_rqst *req = task->tk_rqstp;
__xprt_put_cong(req->rq_xprt, req);
} }
EXPORT_SYMBOL_GPL(xprt_release_rqst_cong); EXPORT_SYMBOL_GPL(xprt_release_rqst_cong);
/** /**
* xprt_adjust_cwnd - adjust transport congestion window * xprt_adjust_cwnd - adjust transport congestion window
* @xprt: pointer to xprt
* @task: recently completed RPC request used to adjust window * @task: recently completed RPC request used to adjust window
* @result: result code of completed RPC request * @result: result code of completed RPC request
* *
* We use a time-smoothed congestion estimator to avoid heavy oscillation. * We use a time-smoothed congestion estimator to avoid heavy oscillation.
*/ */
void xprt_adjust_cwnd(struct rpc_task *task, int result) void xprt_adjust_cwnd(struct rpc_xprt *xprt, struct rpc_task *task, int result)
{ {
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct rpc_xprt *xprt = task->tk_xprt;
unsigned long cwnd = xprt->cwnd; unsigned long cwnd = xprt->cwnd;
if (result >= 0 && cwnd <= xprt->cong) { if (result >= 0 && cwnd <= xprt->cong) {
...@@ -695,7 +697,7 @@ xprt_init_autodisconnect(unsigned long data) ...@@ -695,7 +697,7 @@ xprt_init_autodisconnect(unsigned long data)
*/ */
void xprt_connect(struct rpc_task *task) void xprt_connect(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
dprintk("RPC: %5u xprt_connect xprt %p %s connected\n", task->tk_pid, dprintk("RPC: %5u xprt_connect xprt %p %s connected\n", task->tk_pid,
xprt, (xprt_connected(xprt) ? "is" : "is not")); xprt, (xprt_connected(xprt) ? "is" : "is not"));
...@@ -722,13 +724,13 @@ void xprt_connect(struct rpc_task *task) ...@@ -722,13 +724,13 @@ void xprt_connect(struct rpc_task *task)
if (xprt_test_and_set_connecting(xprt)) if (xprt_test_and_set_connecting(xprt))
return; return;
xprt->stat.connect_start = jiffies; xprt->stat.connect_start = jiffies;
xprt->ops->connect(task); xprt->ops->connect(xprt, task);
} }
} }
static void xprt_connect_status(struct rpc_task *task) static void xprt_connect_status(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
if (task->tk_status == 0) { if (task->tk_status == 0) {
xprt->stat.connect_count++; xprt->stat.connect_count++;
...@@ -832,7 +834,7 @@ static void xprt_timer(struct rpc_task *task) ...@@ -832,7 +834,7 @@ static void xprt_timer(struct rpc_task *task)
spin_lock_bh(&xprt->transport_lock); spin_lock_bh(&xprt->transport_lock);
if (!req->rq_reply_bytes_recvd) { if (!req->rq_reply_bytes_recvd) {
if (xprt->ops->timer) if (xprt->ops->timer)
xprt->ops->timer(task); xprt->ops->timer(xprt, task);
} else } else
task->tk_status = 0; task->tk_status = 0;
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
...@@ -1091,7 +1093,7 @@ EXPORT_SYMBOL_GPL(xprt_free); ...@@ -1091,7 +1093,7 @@ EXPORT_SYMBOL_GPL(xprt_free);
*/ */
void xprt_reserve(struct rpc_task *task) void xprt_reserve(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt;
task->tk_status = 0; task->tk_status = 0;
if (task->tk_rqstp != NULL) if (task->tk_rqstp != NULL)
...@@ -1099,7 +1101,10 @@ void xprt_reserve(struct rpc_task *task) ...@@ -1099,7 +1101,10 @@ void xprt_reserve(struct rpc_task *task)
task->tk_timeout = 0; task->tk_timeout = 0;
task->tk_status = -EAGAIN; task->tk_status = -EAGAIN;
rcu_read_lock();
xprt = rcu_dereference(task->tk_client->cl_xprt);
xprt->ops->alloc_slot(xprt, task); xprt->ops->alloc_slot(xprt, task);
rcu_read_unlock();
} }
static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt) static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
......
...@@ -171,7 +171,7 @@ rpcrdma_create_chunks(struct rpc_rqst *rqst, struct xdr_buf *target, ...@@ -171,7 +171,7 @@ rpcrdma_create_chunks(struct rpc_rqst *rqst, struct xdr_buf *target,
struct rpcrdma_msg *headerp, enum rpcrdma_chunktype type) struct rpcrdma_msg *headerp, enum rpcrdma_chunktype type)
{ {
struct rpcrdma_req *req = rpcr_to_rdmar(rqst); struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_task->tk_xprt); struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
int nsegs, nchunks = 0; int nsegs, nchunks = 0;
unsigned int pos; unsigned int pos;
struct rpcrdma_mr_seg *seg = req->rl_segments; struct rpcrdma_mr_seg *seg = req->rl_segments;
...@@ -366,7 +366,7 @@ rpcrdma_inline_pullup(struct rpc_rqst *rqst, int pad) ...@@ -366,7 +366,7 @@ rpcrdma_inline_pullup(struct rpc_rqst *rqst, int pad)
int int
rpcrdma_marshal_req(struct rpc_rqst *rqst) rpcrdma_marshal_req(struct rpc_rqst *rqst)
{ {
struct rpc_xprt *xprt = rqst->rq_task->tk_xprt; struct rpc_xprt *xprt = rqst->rq_xprt;
struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt); struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
struct rpcrdma_req *req = rpcr_to_rdmar(rqst); struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
char *base; char *base;
......
...@@ -426,9 +426,8 @@ xprt_rdma_set_port(struct rpc_xprt *xprt, u16 port) ...@@ -426,9 +426,8 @@ xprt_rdma_set_port(struct rpc_xprt *xprt, u16 port)
} }
static void static void
xprt_rdma_connect(struct rpc_task *task) xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task)
{ {
struct rpc_xprt *xprt = (struct rpc_xprt *)task->tk_xprt;
struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt); struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
if (r_xprt->rx_ep.rep_connected != 0) { if (r_xprt->rx_ep.rep_connected != 0) {
...@@ -475,7 +474,7 @@ xprt_rdma_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task) ...@@ -475,7 +474,7 @@ xprt_rdma_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
static void * static void *
xprt_rdma_allocate(struct rpc_task *task, size_t size) xprt_rdma_allocate(struct rpc_task *task, size_t size)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
struct rpcrdma_req *req, *nreq; struct rpcrdma_req *req, *nreq;
req = rpcrdma_buffer_get(&rpcx_to_rdmax(xprt)->rx_buf); req = rpcrdma_buffer_get(&rpcx_to_rdmax(xprt)->rx_buf);
...@@ -627,7 +626,7 @@ static int ...@@ -627,7 +626,7 @@ static int
xprt_rdma_send_request(struct rpc_task *task) xprt_rdma_send_request(struct rpc_task *task)
{ {
struct rpc_rqst *rqst = task->tk_rqstp; struct rpc_rqst *rqst = task->tk_rqstp;
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = rqst->rq_xprt;
struct rpcrdma_req *req = rpcr_to_rdmar(rqst); struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt); struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
......
...@@ -235,13 +235,13 @@ struct rpcrdma_create_data_internal { ...@@ -235,13 +235,13 @@ struct rpcrdma_create_data_internal {
}; };
#define RPCRDMA_INLINE_READ_THRESHOLD(rq) \ #define RPCRDMA_INLINE_READ_THRESHOLD(rq) \
(rpcx_to_rdmad(rq->rq_task->tk_xprt).inline_rsize) (rpcx_to_rdmad(rq->rq_xprt).inline_rsize)
#define RPCRDMA_INLINE_WRITE_THRESHOLD(rq)\ #define RPCRDMA_INLINE_WRITE_THRESHOLD(rq)\
(rpcx_to_rdmad(rq->rq_task->tk_xprt).inline_wsize) (rpcx_to_rdmad(rq->rq_xprt).inline_wsize)
#define RPCRDMA_INLINE_PAD_VALUE(rq)\ #define RPCRDMA_INLINE_PAD_VALUE(rq)\
rpcx_to_rdmad(rq->rq_task->tk_xprt).padding rpcx_to_rdmad(rq->rq_xprt).padding
/* /*
* Statistics for RPCRDMA * Statistics for RPCRDMA
......
...@@ -770,7 +770,7 @@ static void xs_tcp_release_xprt(struct rpc_xprt *xprt, struct rpc_task *task) ...@@ -770,7 +770,7 @@ static void xs_tcp_release_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
goto out_release; goto out_release;
if (req->rq_bytes_sent == req->rq_snd_buf.len) if (req->rq_bytes_sent == req->rq_snd_buf.len)
goto out_release; goto out_release;
set_bit(XPRT_CLOSE_WAIT, &task->tk_xprt->state); set_bit(XPRT_CLOSE_WAIT, &xprt->state);
out_release: out_release:
xprt_release_xprt(xprt, task); xprt_release_xprt(xprt, task);
} }
...@@ -1005,7 +1005,7 @@ static void xs_udp_data_ready(struct sock *sk, int len) ...@@ -1005,7 +1005,7 @@ static void xs_udp_data_ready(struct sock *sk, int len)
UDPX_INC_STATS_BH(sk, UDP_MIB_INDATAGRAMS); UDPX_INC_STATS_BH(sk, UDP_MIB_INDATAGRAMS);
xprt_adjust_cwnd(task, copied); xprt_adjust_cwnd(xprt, task, copied);
xprt_complete_rqst(task, copied); xprt_complete_rqst(task, copied);
out_unlock: out_unlock:
...@@ -1646,9 +1646,9 @@ static void xs_udp_set_buffer_size(struct rpc_xprt *xprt, size_t sndsize, size_t ...@@ -1646,9 +1646,9 @@ static void xs_udp_set_buffer_size(struct rpc_xprt *xprt, size_t sndsize, size_t
* *
* Adjust the congestion window after a retransmit timeout has occurred. * Adjust the congestion window after a retransmit timeout has occurred.
*/ */
static void xs_udp_timer(struct rpc_task *task) static void xs_udp_timer(struct rpc_xprt *xprt, struct rpc_task *task)
{ {
xprt_adjust_cwnd(task, -ETIMEDOUT); xprt_adjust_cwnd(xprt, task, -ETIMEDOUT);
} }
static unsigned short xs_get_random_port(void) static unsigned short xs_get_random_port(void)
...@@ -1731,7 +1731,9 @@ static int xs_bind(struct sock_xprt *transport, struct socket *sock) ...@@ -1731,7 +1731,9 @@ static int xs_bind(struct sock_xprt *transport, struct socket *sock)
*/ */
static void xs_local_rpcbind(struct rpc_task *task) static void xs_local_rpcbind(struct rpc_task *task)
{ {
xprt_set_bound(task->tk_xprt); rcu_read_lock();
xprt_set_bound(rcu_dereference(task->tk_client->cl_xprt));
rcu_read_unlock();
} }
static void xs_local_set_port(struct rpc_xprt *xprt, unsigned short port) static void xs_local_set_port(struct rpc_xprt *xprt, unsigned short port)
...@@ -2205,6 +2207,7 @@ static void xs_tcp_setup_socket(struct work_struct *work) ...@@ -2205,6 +2207,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
/** /**
* xs_connect - connect a socket to a remote endpoint * xs_connect - connect a socket to a remote endpoint
* @xprt: pointer to transport structure
* @task: address of RPC task that manages state of connect request * @task: address of RPC task that manages state of connect request
* *
* TCP: If the remote end dropped the connection, delay reconnecting. * TCP: If the remote end dropped the connection, delay reconnecting.
...@@ -2216,9 +2219,8 @@ static void xs_tcp_setup_socket(struct work_struct *work) ...@@ -2216,9 +2219,8 @@ static void xs_tcp_setup_socket(struct work_struct *work)
* If a UDP socket connect fails, the delay behavior here prevents * If a UDP socket connect fails, the delay behavior here prevents
* retry floods (hard mounts). * retry floods (hard mounts).
*/ */
static void xs_connect(struct rpc_task *task) static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt;
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
if (transport->sock != NULL && !RPC_IS_SOFTCONN(task)) { if (transport->sock != NULL && !RPC_IS_SOFTCONN(task)) {
......
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