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 324d003b0c
("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
...
This commit is contained in:
@@ -550,6 +550,9 @@ again:
|
||||
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;
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
};
|
||||
int stat;
|
||||
|
||||
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) {
|
||||
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,
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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:
|
||||
@@ -109,9 +114,16 @@ again:
|
||||
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;
|
||||
@@ -182,39 +194,91 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
|
||||
}
|
||||
|
||||
static struct nfs_delegation *
|
||||
nfs_detach_delegation_locked(struct nfs_inode *nfsi,
|
||||
struct nfs_server *server)
|
||||
nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
|
||||
{
|
||||
struct nfs_delegation *delegation =
|
||||
rcu_dereference_protected(nfsi->delegation,
|
||||
lockdep_is_held(&server->nfs_client->cl_lock));
|
||||
struct nfs_delegation *ret = NULL;
|
||||
struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
|
||||
|
||||
if (delegation == NULL)
|
||||
goto nomatch;
|
||||
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_delegation *delegation,
|
||||
struct nfs_client *clp)
|
||||
{
|
||||
struct nfs_delegation *deleg_cur =
|
||||
rcu_dereference_protected(nfsi->delegation,
|
||||
lockdep_is_held(&clp->cl_lock));
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
@@ -292,19 +359,29 @@ out:
|
||||
/*
|
||||
* 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);
|
||||
@@ -340,13 +417,10 @@ restart:
|
||||
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;
|
||||
@@ -367,15 +441,11 @@ restart:
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -649,7 +715,7 @@ restart:
|
||||
if (inode == NULL)
|
||||
continue;
|
||||
delegation = nfs_detach_delegation(NFS_I(inode),
|
||||
server);
|
||||
delegation, server);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (delegation != NULL)
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -329,7 +329,6 @@ extern int __init register_nfs_fs(void);
|
||||
extern void __exit unregister_nfs_fs(void);
|
||||
extern void nfs_sb_active(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 */
|
||||
#define NFS_PATH_CANONICAL 1
|
||||
|
@@ -13,6 +13,8 @@
|
||||
|
||||
#define NFS4_MAX_LOOP_ON_RECOVER (10)
|
||||
|
||||
#include <linux/seqlock.h>
|
||||
|
||||
struct idmap;
|
||||
|
||||
enum nfs4_client_state {
|
||||
@@ -90,6 +92,8 @@ struct nfs4_state_owner {
|
||||
unsigned long so_flags;
|
||||
struct list_head so_states;
|
||||
struct nfs_seqid_counter so_seqid;
|
||||
seqcount_t so_reclaim_seqcount;
|
||||
struct mutex so_delegreturn_mutex;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@@ -896,6 +896,8 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
|
||||
return 0;
|
||||
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
|
||||
return 0;
|
||||
if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
|
||||
return 0;
|
||||
nfs_mark_delegation_referenced(delegation);
|
||||
return 1;
|
||||
}
|
||||
@@ -973,6 +975,7 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat
|
||||
|
||||
spin_lock(&deleg_cur->lock);
|
||||
if (nfsi->delegation != deleg_cur ||
|
||||
test_bit(NFS_DELEGATION_RETURNING, &deleg_cur->flags) ||
|
||||
(deleg_cur->type & fmode) != fmode)
|
||||
goto no_delegation_unlock;
|
||||
|
||||
@@ -1352,19 +1355,18 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
|
||||
case -NFS4ERR_BAD_HIGH_SLOT:
|
||||
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
||||
case -NFS4ERR_DEADSESSION:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
case -NFS4ERR_EXPIRED:
|
||||
/* Don't recall a delegation if it was lost */
|
||||
nfs4_schedule_lease_recovery(server->nfs_client);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -ERESTARTSYS:
|
||||
/*
|
||||
* The show must go on: exit, but mark the
|
||||
* stateid as needing recovery.
|
||||
*/
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
@@ -1375,6 +1377,7 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
err = nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
out:
|
||||
@@ -1463,7 +1466,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
|
||||
struct nfs4_state_owner *sp = data->owner;
|
||||
|
||||
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
|
||||
* a delegation instead.
|
||||
@@ -1498,6 +1501,7 @@ unlock_no_action:
|
||||
rcu_read_unlock();
|
||||
out_no_action:
|
||||
task->tk_action = NULL;
|
||||
out_wait:
|
||||
nfs4_sequence_done(task, &data->o_res.seq_res);
|
||||
}
|
||||
|
||||
@@ -1845,6 +1849,43 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
|
||||
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
|
||||
*/
|
||||
@@ -1889,18 +1930,7 @@ static int _nfs4_do_open(struct inode *dir,
|
||||
if (dentry->d_inode != NULL)
|
||||
opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
|
||||
|
||||
status = _nfs4_proc_open(opendata);
|
||||
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);
|
||||
status = _nfs4_open_and_get_state(opendata, fmode, flags, &state);
|
||||
if (status != 0)
|
||||
goto err_opendata_put;
|
||||
|
||||
@@ -2088,7 +2118,7 @@ static void nfs4_free_closedata(void *data)
|
||||
nfs4_put_open_state(calldata->state);
|
||||
nfs_free_seqid(calldata->arg.seqid);
|
||||
nfs4_put_state_owner(sp);
|
||||
nfs_sb_deactive_async(sb);
|
||||
nfs_sb_deactive(sb);
|
||||
kfree(calldata);
|
||||
}
|
||||
|
||||
@@ -2150,7 +2180,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
||||
|
||||
dprintk("%s: begin!\n", __func__);
|
||||
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];
|
||||
calldata->arg.fmode = FMODE_READ|FMODE_WRITE;
|
||||
@@ -2172,16 +2202,14 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
||||
|
||||
if (!call_close) {
|
||||
/* Note: exit _without_ calling nfs4_close_done */
|
||||
task->tk_action = NULL;
|
||||
nfs4_sequence_done(task, &calldata->res.seq_res);
|
||||
goto out;
|
||||
goto out_no_action;
|
||||
}
|
||||
|
||||
if (calldata->arg.fmode == 0) {
|
||||
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
|
||||
if (calldata->roc &&
|
||||
pnfs_roc_drain(inode, &calldata->roc_barrier, task))
|
||||
goto out;
|
||||
goto out_wait;
|
||||
}
|
||||
|
||||
nfs_fattr_init(calldata->res.fattr);
|
||||
@@ -2191,8 +2219,12 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
||||
&calldata->res.seq_res,
|
||||
task) != 0)
|
||||
nfs_release_seqid(calldata->arg.seqid);
|
||||
out:
|
||||
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 = {
|
||||
@@ -4423,12 +4455,10 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
|
||||
struct nfs4_unlockdata *calldata = data;
|
||||
|
||||
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) {
|
||||
/* Note: exit _without_ running nfs4_locku_done */
|
||||
task->tk_action = NULL;
|
||||
nfs4_sequence_done(task, &calldata->res.seq_res);
|
||||
return;
|
||||
goto out_no_action;
|
||||
}
|
||||
calldata->timestamp = jiffies;
|
||||
if (nfs4_setup_sequence(calldata->server,
|
||||
@@ -4436,6 +4466,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
|
||||
&calldata->res.seq_res,
|
||||
task) != 0)
|
||||
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 = {
|
||||
@@ -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)
|
||||
{
|
||||
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 nfs4_lock_state *lsp;
|
||||
struct rpc_task *task;
|
||||
@@ -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);
|
||||
/* Unlock _before_ we do the RPC call */
|
||||
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);
|
||||
if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
|
||||
up_read(&nfsi->rwsem);
|
||||
mutex_unlock(&sp->so_delegreturn_mutex);
|
||||
goto out;
|
||||
}
|
||||
up_read(&nfsi->rwsem);
|
||||
mutex_unlock(&sp->so_delegreturn_mutex);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
/* Is this a delegated lock? */
|
||||
@@ -4576,7 +4618,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
|
||||
|
||||
dprintk("%s: begin!\n", __func__);
|
||||
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? */
|
||||
if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
|
||||
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)
|
||||
nfs_release_seqid(data->arg.open_seqid);
|
||||
out_release_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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
struct nfs4_state_owner *sp = state->owner;
|
||||
struct nfs_inode *nfsi = NFS_I(state->inode);
|
||||
unsigned char fl_flags = request->fl_flags;
|
||||
unsigned int seq;
|
||||
int status = -ENOLCK;
|
||||
|
||||
if ((fl_flags & FL_POSIX) &&
|
||||
@@ -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);
|
||||
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);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
down_read(&nfsi->rwsem);
|
||||
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) {
|
||||
status = -NFS4ERR_DELAY;
|
||||
goto out_unlock;
|
||||
}
|
||||
/* Note: we always want to sleep here! */
|
||||
request->fl_flags = fl_flags | FL_SLEEP;
|
||||
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)
|
||||
case 0:
|
||||
case -ESTALE:
|
||||
goto out;
|
||||
case -NFS4ERR_EXPIRED:
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
case -NFS4ERR_EXPIRED:
|
||||
nfs4_schedule_lease_recovery(server->nfs_client);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -NFS4ERR_BADSESSION:
|
||||
case -NFS4ERR_BADSLOT:
|
||||
case -NFS4ERR_BAD_HIGH_SLOT:
|
||||
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
||||
case -NFS4ERR_DEADSESSION:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -ERESTARTSYS:
|
||||
/*
|
||||
* The show must go on: exit, but mark the
|
||||
* stateid as needing recovery.
|
||||
*/
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
@@ -4975,9 +5026,8 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
|
||||
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
|
||||
err = 0;
|
||||
goto out;
|
||||
case -NFS4ERR_DELAY:
|
||||
break;
|
||||
}
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
err = nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
out:
|
||||
@@ -6134,7 +6184,8 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
|
||||
status = nfs4_wait_for_completion_rpc_task(task);
|
||||
if (status == 0)
|
||||
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);
|
||||
rpc_put_task(task);
|
||||
dprintk("<-- %s status=%d\n", __func__, status);
|
||||
|
@@ -518,6 +518,8 @@ nfs4_alloc_state_owner(struct nfs_server *server,
|
||||
nfs4_init_seqid_counter(&sp->so_seqid);
|
||||
atomic_set(&sp->so_count, 1);
|
||||
INIT_LIST_HEAD(&sp->so_lru);
|
||||
seqcount_init(&sp->so_reclaim_seqcount);
|
||||
mutex_init(&sp->so_delegreturn_mutex);
|
||||
return sp;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* server that doesn't support a grace period.
|
||||
*/
|
||||
restart:
|
||||
spin_lock(&sp->so_lock);
|
||||
write_seqcount_begin(&sp->so_reclaim_seqcount);
|
||||
restart:
|
||||
list_for_each_entry(state, &sp->so_states, open_states) {
|
||||
if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
|
||||
continue;
|
||||
@@ -1412,6 +1415,7 @@ restart:
|
||||
}
|
||||
spin_unlock(&state->state_lock);
|
||||
nfs4_put_open_state(state);
|
||||
spin_lock(&sp->so_lock);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
@@ -1449,12 +1453,17 @@ restart:
|
||||
goto out_err;
|
||||
}
|
||||
nfs4_put_open_state(state);
|
||||
spin_lock(&sp->so_lock);
|
||||
goto restart;
|
||||
}
|
||||
write_seqcount_end(&sp->so_reclaim_seqcount);
|
||||
spin_unlock(&sp->so_lock);
|
||||
return 0;
|
||||
out_err:
|
||||
nfs4_put_open_state(state);
|
||||
spin_lock(&sp->so_lock);
|
||||
write_seqcount_end(&sp->so_reclaim_seqcount);
|
||||
spin_unlock(&sp->so_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@@ -647,6 +647,7 @@ static struct pnfs_layoutdriver_type objlayout_type = {
|
||||
.flags = PNFS_LAYOUTRET_ON_SETATTR |
|
||||
PNFS_LAYOUTRET_ON_ERROR,
|
||||
|
||||
.owner = THIS_MODULE,
|
||||
.alloc_layout_hdr = objlayout_alloc_layout_hdr,
|
||||
.free_layout_hdr = objlayout_free_layout_hdr,
|
||||
|
||||
|
154
fs/nfs/pnfs.c
154
fs/nfs/pnfs.c
@@ -505,6 +505,136 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pnfs_destroy_layout);
|
||||
|
||||
static bool
|
||||
pnfs_layout_add_bulk_destroy_list(struct inode *inode,
|
||||
struct list_head *layout_list)
|
||||
{
|
||||
struct pnfs_layout_hdr *lo;
|
||||
bool ret = false;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
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);
|
||||
rcu_read_lock();
|
||||
restart:
|
||||
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
||||
if (memcmp(&server->fsid, fsid, sizeof(*fsid)) != 0)
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
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.
|
||||
@@ -512,30 +642,10 @@ EXPORT_SYMBOL_GPL(pnfs_destroy_layout);
|
||||
void
|
||||
pnfs_destroy_all_layouts(struct nfs_client *clp)
|
||||
{
|
||||
struct nfs_server *server;
|
||||
struct pnfs_layout_hdr *lo;
|
||||
LIST_HEAD(tmp_list);
|
||||
|
||||
nfs4_deviceid_mark_client_invalid(clp);
|
||||
nfs4_deviceid_purge_client(clp);
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
||||
if (!list_empty(&server->layouts))
|
||||
list_splice_init(&server->layouts, &tmp_list);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
spin_unlock(&clp->cl_lock);
|
||||
|
||||
while (!list_empty(&tmp_list)) {
|
||||
lo = list_entry(tmp_list.next, struct pnfs_layout_hdr,
|
||||
plh_layouts);
|
||||
dprintk("%s freeing layout for inode %lu\n", __func__,
|
||||
lo->plh_inode->i_ino);
|
||||
list_del_init(&lo->plh_layouts);
|
||||
pnfs_destroy_layout(NFS_I(lo->plh_inode));
|
||||
}
|
||||
pnfs_destroy_layouts_byclid(clp, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -888,7 +998,7 @@ alloc_init_layout_hdr(struct inode *ino,
|
||||
atomic_set(&lo->plh_refcount, 1);
|
||||
INIT_LIST_HEAD(&lo->plh_layouts);
|
||||
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_lc_cred = get_rpccred(ctx->state->owner->so_cred);
|
||||
return lo;
|
||||
|
@@ -132,7 +132,7 @@ struct pnfs_layoutdriver_type {
|
||||
struct pnfs_layout_hdr {
|
||||
atomic_t plh_refcount;
|
||||
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 */
|
||||
nfs4_stateid plh_stateid;
|
||||
atomic_t plh_outstanding; /* number of RPCs out */
|
||||
@@ -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_destroy_layout(struct nfs_inode *);
|
||||
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_set_layout_stateid(struct pnfs_layout_hdr *lo,
|
||||
const nfs4_stateid *new,
|
||||
|
@@ -54,7 +54,6 @@
|
||||
#include <linux/parser.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@@ -418,54 +417,6 @@ void nfs_sb_deactive(struct super_block *sb)
|
||||
}
|
||||
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
|
||||
*/
|
||||
|
@@ -95,7 +95,7 @@ static void nfs_async_unlink_release(void *calldata)
|
||||
|
||||
nfs_dec_sillycount(data->dir);
|
||||
nfs_free_unlinkdata(data);
|
||||
nfs_sb_deactive_async(sb);
|
||||
nfs_sb_deactive(sb);
|
||||
}
|
||||
|
||||
static void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
|
||||
@@ -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
|
||||
* that anymore.
|
||||
*/
|
||||
if (devname_garbage)
|
||||
kfree(devname_garbage);
|
||||
kfree(devname_garbage);
|
||||
return 0;
|
||||
out_unlock:
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
Reference in New Issue
Block a user