Newer
Older
int ret = -ENOENT;
if (lockowner == NULL)
goto out;
if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
fl_owner = lockowner->l_owner;
fl_pid = lockowner->l_pid;
spin_lock(&state->state_lock);

Trond Myklebust
committed
lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE);
if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
nfs4_stateid_copy(dst, &lsp->ls_stateid);
ret = 0;
smp_rmb();
if (!list_empty(&lsp->ls_seqid.list))
ret = -EWOULDBLOCK;
spin_unlock(&state->state_lock);
out:
return ret;
}
static int nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
const nfs4_stateid *src;
int ret;
src = &zero_stateid;
seq = read_seqbegin(&state->seqlock);
if (test_bit(NFS_OPEN_STATE, &state->flags))
src = &state->open_stateid;
nfs4_stateid_copy(dst, src);
ret = 0;
smp_rmb();
if (!list_empty(&state->owner->so_seqid.list))
ret = -EWOULDBLOCK;
} while (read_seqretry(&state->seqlock, seq));
return ret;
}
/*
* Byte-range lock aware utility to initialize the stateid of read/write
* requests.
*/
int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
fmode_t fmode, const struct nfs_lockowner *lockowner)
int ret = 0;
if (nfs4_copy_delegation_stateid(dst, state->inode, fmode))
goto out;
ret = nfs4_copy_lock_stateid(dst, state, lockowner);
if (ret != -ENOENT)
goto out;
ret = nfs4_copy_open_stateid(dst, state);
out:

Trond Myklebust
committed
if (nfs_server_capable(state->inode, NFS_CAP_STATEID_NFSV41))
dst->seqid = 0;
return ret;
struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask)
{
struct nfs_seqid *new;
new = kmalloc(sizeof(*new), gfp_mask);
if (new != NULL) {
new->sequence = counter;
INIT_LIST_HEAD(&new->list);
new->task = NULL;
}
return new;
}
void nfs_release_seqid(struct nfs_seqid *seqid)
struct nfs_seqid_counter *sequence;
if (list_empty(&seqid->list))
return;
sequence = seqid->sequence;
spin_lock(&sequence->lock);
list_del_init(&seqid->list);
if (!list_empty(&sequence->list)) {
struct nfs_seqid *next;
next = list_first_entry(&sequence->list,
struct nfs_seqid, list);
rpc_wake_up_queued_task(&sequence->wait, next->task);
spin_unlock(&sequence->lock);
}
void nfs_free_seqid(struct nfs_seqid *seqid)
{
nfs_release_seqid(seqid);
* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
* failed with a seqid incrementing error -
* see comments nfs_fs.h:seqid_mutating_error()
*/
static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
{
switch (status) {
case 0:
break;
case -NFS4ERR_BAD_SEQID:
if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
return;
pr_warn_ratelimited("NFS: v4 server returned a bad"
" sequence-id error on an"
" unconfirmed sequence %p!\n",
seqid->sequence);
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_BADXDR:
case -NFS4ERR_RESOURCE:
case -NFS4ERR_NOFILEHANDLE:
/* Non-seqid mutating errors */
return;
};
/*
* Note: no locking needed as we are guaranteed to be first
* on the sequence list
*/
seqid->sequence->counter++;
}
void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
{
struct nfs4_state_owner *sp = container_of(seqid->sequence,
struct nfs4_state_owner, so_seqid);
struct nfs_server *server = sp->so_server;
if (status == -NFS4ERR_BAD_SEQID)
if (!nfs4_has_session(server->nfs_client))
nfs_increment_seqid(status, seqid);
}
/*
* Increment the seqid if the LOCK/LOCKU succeeded, or
* failed with a seqid incrementing error -
* see comments nfs_fs.h:seqid_mutating_error()
*/
void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid)
{
nfs_increment_seqid(status, seqid);
}
int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
{
struct nfs_seqid_counter *sequence = seqid->sequence;
int status = 0;
spin_lock(&sequence->lock);
seqid->task = task;
if (list_empty(&seqid->list))
list_add_tail(&seqid->list, &sequence->list);
if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid)
goto unlock;
rpc_sleep_on(&sequence->wait, task, NULL);
status = -EAGAIN;
unlock:
spin_unlock(&sequence->lock);
return status;
static int nfs4_run_state_manager(void *);
static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING);
rpc_wake_up(&clp->cl_rpcwaitq);
}
* Schedule the nfs_client asynchronous state management routine
void nfs4_schedule_state_manager(struct nfs_client *clp)
struct task_struct *task;
char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
return;
__module_get(THIS_MODULE);
atomic_inc(&clp->cl_count);
/* The rcu_read_lock() is not strictly necessary, as the state
* manager is the only thread that ever changes the rpc_xprt
* after it's initialized. At this point, we're single threaded. */
rcu_read_lock();
snprintf(buf, sizeof(buf), "%s-manager",
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
rcu_read_unlock();
task = kthread_run(nfs4_run_state_manager, clp, "%s", buf);
if (IS_ERR(task)) {
printk(KERN_ERR "%s: kthread_run: %ld\n",
__func__, PTR_ERR(task));
nfs4_clear_state_manager_bit(clp);
nfs_put_client(clp);
module_put(THIS_MODULE);
}
* Schedule a lease recovery attempt
void nfs4_schedule_lease_recovery(struct nfs_client *clp)
if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
dprintk("%s: scheduling lease recovery for server %s\n", __func__,
clp->cl_hostname);
nfs4_schedule_state_manager(clp);
EXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery);
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
int nfs4_wait_clnt_recover(struct nfs_client *clp)
{
int res;
might_sleep();
res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
nfs_wait_bit_killable, TASK_KILLABLE);
if (res)
return res;
if (clp->cl_cons_state < 0)
return clp->cl_cons_state;
return 0;
}
int nfs4_client_recover_expired_lease(struct nfs_client *clp)
{
unsigned int loop;
int ret;
for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
ret = nfs4_wait_clnt_recover(clp);
if (ret != 0)
break;
if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
!test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
break;
nfs4_schedule_state_manager(clp);
ret = -EIO;
}
return ret;
}
/*
* nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
* @clp: client to process
*
* Set the NFS4CLNT_LEASE_EXPIRED state in order to force a
* resend of the SETCLIENTID and hence re-establish the
* callback channel. Then return all existing delegations.
*/
static void nfs40_handle_cb_pathdown(struct nfs_client *clp)
{
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs_expire_all_delegations(clp);
dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__,
clp->cl_hostname);
}
void nfs4_schedule_path_down_recovery(struct nfs_client *clp)
{
nfs40_handle_cb_pathdown(clp);
nfs4_schedule_state_manager(clp);
}
static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
{
set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
/* Don't recover state that expired before the reboot */
if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
return 0;
}
set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
return 1;
}
static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
{
set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
return 1;
}
int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
{
struct nfs_client *clp = server->nfs_client;
if (!nfs4_valid_open_stateid(state))
return -EBADF;
nfs4_state_mark_reclaim_nograce(clp, state);
dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
clp->cl_hostname);
nfs4_schedule_state_manager(clp);
EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
void nfs_inode_find_state_and_recover(struct inode *inode,
const nfs4_stateid *stateid)
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
struct nfs4_state *state;
bool found = false;
spin_lock(&inode->i_lock);
list_for_each_entry(ctx, &nfsi->open_files, list) {
state = ctx->state;
if (state == NULL)
continue;
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
continue;
if (!nfs4_stateid_match(&state->stateid, stateid))
continue;
nfs4_state_mark_reclaim_nograce(clp, state);
found = true;
}
spin_unlock(&inode->i_lock);
if (found)
nfs4_schedule_state_manager(clp);
}
static void nfs4_state_mark_open_context_bad(struct nfs4_state *state)
{
struct inode *inode = state->inode;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
spin_lock(&inode->i_lock);
list_for_each_entry(ctx, &nfsi->open_files, list) {
if (ctx->state != state)
continue;
set_bit(NFS_CONTEXT_BAD, &ctx->flags);
}
spin_unlock(&inode->i_lock);
}
static void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error)
{
set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags);
nfs4_state_mark_open_context_bad(state);
static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)

Trond Myklebust
committed
struct nfs_inode *nfsi = NFS_I(inode);
if (inode->i_flock == NULL)
return 0;
/* Guard against delegation returns and new lock/unlock calls */

Trond Myklebust
committed
down_write(&nfsi->rwsem);
/* Protect inode->i_flock using the BKL */
spin_lock(&inode->i_lock);
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
if (nfs_file_open_context(fl->fl_file)->state != state)
spin_unlock(&inode->i_lock);
status = ops->recover_lock(state, fl);
switch (status) {
case 0:
break;
case -ESTALE:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_EXPIRED:
case -NFS4ERR_NO_GRACE:
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_BADSESSION:
case -NFS4ERR_BADSLOT:
case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
goto out;
printk(KERN_ERR "NFS: %s: unhandled error %d. "
"Zeroing state\n", __func__, status);
case -ENOMEM:
case -NFS4ERR_DENIED:
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
status = 0;
spin_lock(&inode->i_lock);
spin_unlock(&inode->i_lock);
out:

Trond Myklebust
committed
up_write(&nfsi->rwsem);
static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops)
{
struct nfs4_state *state;
struct nfs4_lock_state *lock;
int status = 0;
/* Note: we rely on the sp->so_states list being ordered
* so that we always reclaim open(O_RDWR) and/or open(O_WRITE)
* states first.
* This is needed to ensure that the server won't give us any
* read delegations that we have to return if, say, we are
* recovering after a network partition or a reboot from a
* server that doesn't support a grace period.
*/
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;
if (!nfs4_valid_open_stateid(state))
continue;
atomic_inc(&state->count);
spin_unlock(&sp->so_lock);
status = ops->recover_open(sp, state);
if (status >= 0) {
status = nfs4_reclaim_locks(state, ops);
if (status >= 0) {
spin_lock(&state->state_lock);
list_for_each_entry(lock, &state->lock_states, ls_locks) {
if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags))
pr_warn_ratelimited("NFS: "
"%s: Lock reclaim "
"failed!\n", __func__);
spin_unlock(&state->state_lock);
nfs4_put_open_state(state);
spin_lock(&sp->so_lock);
goto restart;
printk(KERN_ERR "NFS: %s: unhandled error %d. "
"Zeroing state\n", __func__, status);
case -ENOMEM:
case -ESTALE:
/*
* Open state on this file cannot be recovered
* All we can do is revert to using the zero stateid.
*/
nfs4_state_mark_recovery_failed(state, status);
case -EAGAIN:
ssleep(1);
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
break;
nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
case -NFS4ERR_BADSESSION:
case -NFS4ERR_BADSLOT:
case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
nfs4_put_open_state(state);
spin_lock(&sp->so_lock);
goto restart;
write_seqcount_end(&sp->so_reclaim_seqcount);
spin_unlock(&sp->so_lock);
nfs4_put_open_state(state);
spin_lock(&sp->so_lock);
write_seqcount_end(&sp->so_reclaim_seqcount);
spin_unlock(&sp->so_lock);
static void nfs4_clear_open_state(struct nfs4_state *state)
{
struct nfs4_lock_state *lock;
clear_bit(NFS_DELEGATED_STATE, &state->flags);
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDWR_STATE, &state->flags);
spin_lock(&state->state_lock);
list_for_each_entry(lock, &state->lock_states, ls_locks) {
lock->ls_seqid.flags = 0;
clear_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags);
}
spin_unlock(&state->state_lock);
}
static void nfs4_reset_seqids(struct nfs_server *server,
int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
struct nfs_client *clp = server->nfs_client;
struct rb_node *pos;
struct nfs4_state *state;
spin_lock(&clp->cl_lock);
for (pos = rb_first(&server->state_owners);
pos != NULL;
pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
list_for_each_entry(state, &sp->so_states, open_states) {
if (mark_reclaim(clp, state))
nfs4_clear_open_state(state);
spin_unlock(&sp->so_lock);
spin_unlock(&clp->cl_lock);
}
static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp,
int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
{
struct nfs_server *server;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
nfs4_reset_seqids(server, mark_reclaim);
rcu_read_unlock();
static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
{
/* Mark all delegations for reclaim */
nfs_delegation_mark_reclaim(clp);
nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
}
static void nfs4_reclaim_complete(struct nfs_client *clp,
const struct nfs4_state_recovery_ops *ops,
struct rpc_cred *cred)
{
/* Notify the server we're done reclaiming our state */
if (ops->reclaim_complete)
(void)ops->reclaim_complete(clp, cred);
static void nfs4_clear_reclaim_server(struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client;
struct nfs4_state_owner *sp;
struct rb_node *pos;
struct nfs4_state *state;
spin_lock(&clp->cl_lock);
for (pos = rb_first(&server->state_owners);
pos != NULL;
pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) {
if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT,
&state->flags))
continue;
nfs4_state_mark_reclaim_nograce(clp, state);
}
spin_unlock(&sp->so_lock);
}
spin_unlock(&clp->cl_lock);
}
static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
{
struct nfs_server *server;
if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
return 0;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
nfs4_clear_reclaim_server(server);
rcu_read_unlock();
nfs_delegation_reap_unclaimed(clp);

Trond Myklebust
committed
return 1;
}
static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
{
const struct nfs4_state_recovery_ops *ops;
struct rpc_cred *cred;

Trond Myklebust
committed
if (!nfs4_state_clear_reclaim_reboot(clp))
return;
ops = clp->cl_mvops->reboot_recovery_ops;
cred = ops->get_clid_cred(clp);
nfs4_reclaim_complete(clp, ops, cred);
put_rpccred(cred);
}
static void nfs_delegation_clear_all(struct nfs_client *clp)
{
nfs_delegation_mark_reclaim(clp);
nfs_delegation_reap_unclaimed(clp);
}
static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
{
nfs_delegation_clear_all(clp);
nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
}
static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
{
switch (error) {
case 0:
break;
case -NFS4ERR_CB_PATH_DOWN:
nfs40_handle_cb_pathdown(clp);
break;
case -NFS4ERR_NO_GRACE:
nfs4_state_end_reclaim_reboot(clp);
break;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_LEASE_MOVED:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);

Trond Myklebust
committed
nfs4_state_clear_reclaim_reboot(clp);
nfs4_state_start_reclaim_reboot(clp);
break;
case -NFS4ERR_EXPIRED:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_start_reclaim_nograce(clp);
case -NFS4ERR_BADSESSION:
case -NFS4ERR_BADSLOT:
case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_DEADSESSION:
case -NFS4ERR_SEQ_FALSE_RETRY:
case -NFS4ERR_SEQ_MISORDERED:
set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
/* Zero session reset errors */
break;
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
break;
default:
dprintk("%s: failed to handle error %d for server %s\n",
__func__, error, clp->cl_hostname);
return error;
}
dprintk("%s: handled error %d for server %s\n", __func__, error,
clp->cl_hostname);
return 0;
}
static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
struct nfs4_state_owner *sp;
struct nfs_server *server;
struct rb_node *pos;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
nfs4_purge_state_owners(server);
spin_lock(&clp->cl_lock);
for (pos = rb_first(&server->state_owners);
pos != NULL;
pos = rb_next(pos)) {
sp = rb_entry(pos,
struct nfs4_state_owner, so_server_node);
if (!test_and_clear_bit(ops->owner_flag_bit,
&sp->so_flags))
continue;
atomic_inc(&sp->so_count);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
status = nfs4_reclaim_open_state(sp, ops);
if (status < 0) {
set_bit(ops->owner_flag_bit, &sp->so_flags);
nfs4_put_state_owner(sp);
return nfs4_recovery_handle_error(clp, status);
}
nfs4_put_state_owner(sp);
goto restart;
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
return status;
}
static int nfs4_check_lease(struct nfs_client *clp)
{
struct rpc_cred *cred;
const struct nfs4_state_maintenance_ops *ops =
clp->cl_mvops->state_renewal_ops;
int status;
/* Is the client already known to have an expired lease? */
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
return 0;
spin_lock(&clp->cl_lock);
cred = ops->get_state_renewal_cred_locked(clp);
spin_unlock(&clp->cl_lock);
if (cred == NULL) {
cred = nfs4_get_setclientid_cred(clp);
status = -ENOKEY;
if (cred == NULL)
goto out;

Trond Myklebust
committed
}
status = ops->renew_lease(clp, cred);
put_rpccred(cred);
if (status == -ETIMEDOUT) {
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
return 0;
}
return nfs4_recovery_handle_error(clp, status);
/* Set NFS4CLNT_LEASE_EXPIRED and reclaim reboot state for all v4.0 errors
* and for recoverable errors on EXCHANGE_ID for v4.1
*/
static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
{
switch (status) {
case -NFS4ERR_SEQ_MISORDERED:
if (test_and_set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state))
return -ESERVERFAULT;
/* Lease confirmation error: retry after purging the lease */
ssleep(1);
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
break;
case -NFS4ERR_STALE_CLIENTID:
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
nfs4_state_clear_reclaim_reboot(clp);
nfs4_state_start_reclaim_reboot(clp);
case -NFS4ERR_CLID_INUSE:
pr_err("NFS: Server %s reports our clientid is in use\n",
clp->cl_hostname);
nfs_mark_client_ready(clp, -EPERM);
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
return -EPERM;
case -EACCES:
case -NFS4ERR_DELAY:
case -ETIMEDOUT:
case -EAGAIN:
ssleep(1);
break;
case -NFS4ERR_MINOR_VERS_MISMATCH:
if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
nfs_mark_client_ready(clp, -EPROTONOSUPPORT);
dprintk("%s: exit with error %d for server %s\n",
__func__, -EPROTONOSUPPORT, clp->cl_hostname);
return -EPROTONOSUPPORT;
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
* in nfs4_exchange_id */
default:
dprintk("%s: exit with error %d for server %s\n", __func__,
status, clp->cl_hostname);
return status;
}
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
dprintk("%s: handled error %d for server %s\n", __func__, status,
clp->cl_hostname);
return 0;
}
static int nfs4_establish_lease(struct nfs_client *clp)
{
struct rpc_cred *cred;
const struct nfs4_state_recovery_ops *ops =
clp->cl_mvops->reboot_recovery_ops;
int status;
cred = ops->get_clid_cred(clp);
if (cred == NULL)
return -ENOENT;
status = ops->establish_clid(clp, cred);
put_rpccred(cred);
if (status != 0)
return status;
pnfs_destroy_all_layouts(clp);
return 0;
}
/*
* Returns zero or a negative errno. NFS4ERR values are converted
* to local errno values.
*/
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
static int nfs4_reclaim_lease(struct nfs_client *clp)
{
int status;
status = nfs4_establish_lease(clp);
if (status < 0)
return nfs4_handle_reclaim_lease_error(clp, status);
if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state))
nfs4_state_start_reclaim_nograce(clp);
if (!test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
return 0;
}
static int nfs4_purge_lease(struct nfs_client *clp)
{
int status;
status = nfs4_establish_lease(clp);
if (status < 0)
return nfs4_handle_reclaim_lease_error(clp, status);
clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_start_reclaim_nograce(clp);
/**
* nfs4_discover_server_trunking - Detect server IP address trunking
*
* @clp: nfs_client under test
* @result: OUT: found nfs_client, or clp
*
* Returns zero or a negative errno. If zero is returned,
* an nfs_client pointer is planted in "result".
*
* Note: since we are invoked in process context, and
* not from inside the state manager, we cannot use
* nfs4_handle_reclaim_lease_error().
*/
int nfs4_discover_server_trunking(struct nfs_client *clp,
struct nfs_client **result)
{
const struct nfs4_state_recovery_ops *ops =
clp->cl_mvops->reboot_recovery_ops;
struct rpc_clnt *clnt;
struct rpc_cred *cred;
int i, status;
dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
clnt = clp->cl_rpcclient;
i = 0;
mutex_lock(&nfs_clid_init_mutex);
again:
cred = ops->get_clid_cred(clp);
if (cred == NULL)
goto out_unlock;
status = ops->detect_trunking(clp, result, cred);
put_rpccred(cred);
switch (status) {
case 0:
break;
case -NFS4ERR_DELAY:
case -ETIMEDOUT:
case -EAGAIN:
ssleep(1);
dprintk("NFS: %s after status %d, retrying\n",
__func__, status);
goto again;
case -EACCES:
if (i++ == 0) {
nfs4_root_machine_cred(clp);
goto again;
}
if (i > 2)
case -NFS4ERR_CLID_INUSE:
case -NFS4ERR_WRONGSEC:
clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
if (IS_ERR(clnt)) {
status = PTR_ERR(clnt);
break;
}
/* Note: this is safe because we haven't yet marked the
* client as ready, so we are the only user of
* clp->cl_rpcclient
*/
clnt = xchg(&clp->cl_rpcclient, clnt);
rpc_shutdown_client(clnt);
clnt = clp->cl_rpcclient;
goto again;
case -NFS4ERR_MINOR_VERS_MISMATCH:
status = -EPROTONOSUPPORT;
break;
case -EKEYEXPIRED:
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
* in nfs4_exchange_id */
status = -EKEYEXPIRED;
break;
default:
pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n",
__func__, status);
status = -EIO;
}
out_unlock:
mutex_unlock(&nfs_clid_init_mutex);
dprintk("NFS: %s: status = %d\n", __func__, status);
return status;
}
void nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
struct nfs_client *clp = session->clp;
switch (err) {
default:
set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
break;
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
}
nfs4_schedule_lease_recovery(clp);
EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery);

Trond Myklebust
committed
static void nfs41_ping_server(struct nfs_client *clp)
{
/* Use CHECK_LEASE to ping the server with a SEQUENCE */
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
nfs4_schedule_state_manager(clp);
}
void nfs41_server_notify_target_slotid_update(struct nfs_client *clp)
{
nfs41_ping_server(clp);
}
void nfs41_server_notify_highest_slotid_update(struct nfs_client *clp)
{
nfs41_ping_server(clp);
}
static void nfs4_reset_all_state(struct nfs_client *clp)
{
if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
nfs4_state_start_reclaim_nograce(clp);
dprintk("%s: scheduling reset of all state for server %s!\n",
__func__, clp->cl_hostname);
nfs4_schedule_state_manager(clp);
}
}
static void nfs41_handle_server_reboot(struct nfs_client *clp)