summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/delegation.c46
-rw-r--r--fs/nfs/delegation.h1
-rw-r--r--fs/nfs/nfs4_fs.h1
-rw-r--r--fs/nfs/nfs4proc.c20
-rw-r--r--fs/nfs/nfs4renewd.c9
5 files changed, 75 insertions, 2 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 618a327027b3..75dfb1c717a0 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -8,6 +8,7 @@
*/
#include <linux/config.h>
#include <linux/completion.h>
+#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
@@ -231,6 +232,51 @@ restart:
spin_unlock(&clp->cl_lock);
}
+int nfs_do_expire_all_delegations(void *ptr)
+{
+ struct nfs4_client *clp = ptr;
+ struct nfs_delegation *delegation;
+ struct inode *inode;
+ int err = 0;
+
+ allow_signal(SIGKILL);
+restart:
+ spin_lock(&clp->cl_lock);
+ if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0)
+ goto out;
+ if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
+ goto out;
+ list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
+ inode = igrab(delegation->inode);
+ if (inode == NULL)
+ continue;
+ spin_unlock(&clp->cl_lock);
+ err = nfs_inode_return_delegation(inode);
+ iput(inode);
+ if (!err)
+ goto restart;
+ }
+out:
+ spin_unlock(&clp->cl_lock);
+ nfs4_put_client(clp);
+ module_put_and_exit(0);
+}
+
+void nfs_expire_all_delegations(struct nfs4_client *clp)
+{
+ struct task_struct *task;
+
+ __module_get(THIS_MODULE);
+ atomic_inc(&clp->cl_count);
+ task = kthread_run(nfs_do_expire_all_delegations, clp,
+ "%u.%u.%u.%u-delegreturn",
+ NIPQUAD(clp->cl_addr));
+ if (!IS_ERR(task))
+ return;
+ nfs4_put_client(clp);
+ module_put(THIS_MODULE);
+}
+
/*
* Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
*/
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 2fcc30de924b..fbc50ec271c5 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -30,6 +30,7 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle);
void nfs_return_all_delegations(struct super_block *sb);
+void nfs_expire_all_delegations(struct nfs4_client *clp);
void nfs_handle_cb_pathdown(struct nfs4_client *clp);
void nfs_delegation_mark_reclaim(struct nfs4_client *clp);
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 1d4c5b339b4d..75fe646c28ab 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -39,6 +39,7 @@ struct idmap;
enum nfs4_client_state {
NFS4CLNT_STATE_RECOVER = 0,
+ NFS4CLNT_LEASE_EXPIRED,
};
/*
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 46623ac3ce86..cc33a1c32cfb 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -63,6 +63,7 @@ static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinf
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
+static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp);
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[];
@@ -765,6 +766,15 @@ out:
return -EACCES;
}
+int nfs4_recover_expired_lease(struct nfs_server *server)
+{
+ struct nfs4_client *clp = server->nfs4_state;
+
+ if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+ nfs4_schedule_state_recovery(clp);
+ return nfs4_wait_clnt_recover(server->client, clp);
+}
+
/*
* OPEN_EXPIRED:
* reclaim state on the server after a network partition.
@@ -840,6 +850,9 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
int open_flags = flags & (FMODE_READ|FMODE_WRITE);
int err;
+ err = nfs4_recover_expired_lease(server);
+ if (err != 0)
+ return err;
/* Protect against reboot recovery - NOTE ORDER! */
down_read(&clp->cl_sem);
/* Protect against delegation recall */
@@ -921,12 +934,16 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
int status;
/* Protect against reboot recovery conflicts */
- down_read(&clp->cl_sem);
status = -ENOMEM;
if (!(sp = nfs4_get_state_owner(server, cred))) {
dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
goto out_err;
}
+ status = nfs4_recover_expired_lease(server);
+ if (status != 0)
+ goto out_err;
+ down_read(&clp->cl_sem);
+ status = -ENOMEM;
opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr);
if (opendata == NULL)
goto err_put_state_owner;
@@ -2897,6 +2914,7 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp)
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ;
clp->cl_last_renewal = now;
+ clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
spin_unlock(&clp->cl_lock);
}
return status;
diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c
index a3001628ad32..f62c2f7a4ffb 100644
--- a/fs/nfs/nfs4renewd.c
+++ b/fs/nfs/nfs4renewd.c
@@ -54,6 +54,7 @@
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include "nfs4_fs.h"
+#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_PROC
@@ -68,7 +69,7 @@ nfs4_renew_state(void *data)
dprintk("%s: start\n", __FUNCTION__);
/* Are there any active superblocks? */
if (list_empty(&clp->cl_superblocks))
- goto out;
+ goto out;
spin_lock(&clp->cl_lock);
lease = clp->cl_lease_time;
last = clp->cl_last_renewal;
@@ -76,6 +77,12 @@ nfs4_renew_state(void *data)
timeout = (2 * lease) / 3 + (long)last - (long)now;
/* Are we close to a lease timeout? */
if (time_after(now, last + lease/3)) {
+ if (list_empty(&clp->cl_state_owners)) {
+ set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+ spin_unlock(&clp->cl_lock);
+ nfs_expire_all_delegations(clp);
+ goto out;
+ }
spin_unlock(&clp->cl_lock);
/* Queue an asynchronous RENEW. */
nfs4_proc_async_renew(clp);