Commit 7cf3d8b7 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Convert the lease renewal daemon from being

per-mountpoint to being per-server. Instead of running it on
top of rpciod, convert it to use keventd. This mean we can use
the struct nfs4_client semaphores for ordering purposes.
parent acac57de
...@@ -163,6 +163,8 @@ nfs_put_super(struct super_block *sb) ...@@ -163,6 +163,8 @@ nfs_put_super(struct super_block *sb)
nfs_idmap_delete(server); nfs_idmap_delete(server);
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL) if (server->client != NULL)
rpc_shutdown_client(server->client); rpc_shutdown_client(server->client);
if (server->client_sys != NULL) if (server->client_sys != NULL)
...@@ -1281,6 +1283,8 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, ...@@ -1281,6 +1283,8 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
if (!server) if (!server)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
memset(server, 0, sizeof(struct nfs_server)); memset(server, 0, sizeof(struct nfs_server));
/* Zero out the NFS state stuff */
init_nfsv4_state(server);
root = &server->fh; root = &server->fh;
memcpy(root, &data->root, sizeof(*root)); memcpy(root, &data->root, sizeof(*root));
...@@ -1444,13 +1448,15 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, ...@@ -1444,13 +1448,15 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0);
memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
} }
list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
clnt = rpc_clone_client(clp->cl_rpcclient); clnt = rpc_clone_client(clp->cl_rpcclient);
server->nfs4_state = clp; server->nfs4_state = clp;
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
clp = NULL;
if (clnt == NULL) { if (clnt == NULL) {
printk(KERN_WARNING "NFS: cannot create RPC client.\n"); printk(KERN_WARNING "NFS: cannot create RPC client.\n");
goto out_fail; goto out_remove_list;
} }
clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0;
...@@ -1476,13 +1482,16 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, ...@@ -1476,13 +1482,16 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
err = nfs_sb_init(sb, authflavour); err = nfs_sb_init(sb, authflavour);
if (err == 0) if (err == 0)
return 0; return 0;
clp = NULL;
rpciod_down(); rpciod_down();
destroy_nfsv4_state(server);
if (server->idmap != NULL) if (server->idmap != NULL)
nfs_idmap_delete(server); nfs_idmap_delete(server);
out_shutdown: out_shutdown:
rpc_shutdown_client(server->client); rpc_shutdown_client(server->client);
out_remove_list:
down_write(&server->nfs4_state->cl_sem);
list_del_init(&server->nfs4_siblings);
up_write(&server->nfs4_state->cl_sem);
destroy_nfsv4_state(server);
out_fail: out_fail:
if (clp) if (clp)
nfs4_put_client(clp); nfs4_put_client(clp);
...@@ -1542,6 +1551,8 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, ...@@ -1542,6 +1551,8 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
if (!server) if (!server)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
memset(server, 0, sizeof(struct nfs_server)); memset(server, 0, sizeof(struct nfs_server));
/* Zero out the NFS state stuff */
init_nfsv4_state(server);
if (data->version != NFS4_MOUNT_VERSION) { if (data->version != NFS4_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n", printk("nfs warning: mount version %s than kernel\n",
......
...@@ -56,8 +56,6 @@ extern struct rpc_procinfo nfs4_procedures[]; ...@@ -56,8 +56,6 @@ extern struct rpc_procinfo nfs4_procedures[];
extern nfs4_stateid zero_stateid; extern nfs4_stateid zero_stateid;
static spinlock_t renew_lock = SPIN_LOCK_UNLOCKED;
static void static void
nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops,
struct nfs_server *server, char *tag) struct nfs_server *server, char *tag)
...@@ -480,10 +478,11 @@ nfs4_setup_setclientid_confirm(struct nfs4_compound *cp) ...@@ -480,10 +478,11 @@ nfs4_setup_setclientid_confirm(struct nfs4_compound *cp)
static void static void
renew_lease(struct nfs_server *server, unsigned long timestamp) renew_lease(struct nfs_server *server, unsigned long timestamp)
{ {
spin_lock(&renew_lock); struct nfs4_client *clp = server->nfs4_state;
if (time_before(server->last_renewal,timestamp)) spin_lock(&clp->cl_lock);
server->last_renewal = timestamp; if (time_before(clp->cl_last_renewal,timestamp))
spin_unlock(&renew_lock); clp->cl_last_renewal = timestamp;
spin_unlock(&clp->cl_lock);
} }
static inline void static inline void
...@@ -748,6 +747,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -748,6 +747,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo fsinfo; struct nfs_fsinfo fsinfo;
unsigned char * p; unsigned char * p;
struct qstr q; struct qstr q;
unsigned long last_renewed;
int status; int status;
clp = server->nfs4_state; clp = server->nfs4_state;
...@@ -773,6 +773,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -773,6 +773,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
*/ */
nfs4_setup_compound(&compound, ops, server, "setclientid"); nfs4_setup_compound(&compound, ops, server, "setclientid");
nfs4_setup_setclientid(&compound, 0, 0); nfs4_setup_setclientid(&compound, 0, 0);
last_renewed = jiffies;
if ((status = nfs4_call_compound(&compound, NULL, 0))) if ((status = nfs4_call_compound(&compound, NULL, 0)))
goto out_unlock; goto out_unlock;
...@@ -786,20 +787,22 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -786,20 +787,22 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
nfs4_setup_putrootfh(&compound); nfs4_setup_putrootfh(&compound);
nfs4_setup_getrootattr(&compound, fattr, &fsinfo); nfs4_setup_getrootattr(&compound, fattr, &fsinfo);
nfs4_setup_getfh(&compound, fhandle); nfs4_setup_getfh(&compound, fhandle);
last_renewed = jiffies;
if ((status = nfs4_call_compound(&compound, NULL, 0))) if ((status = nfs4_call_compound(&compound, NULL, 0)))
goto out_unlock; goto out_unlock;
clp->cl_state = NFS4CLNT_OK;
no_setclientid:
/* /*
* Now that we have instantiated the clientid and determined * Now that we have instantiated the clientid and determined
* the lease time, we can initialize the renew daemon for this * the lease time, we can initialize the renew daemon for this
* server. * server.
* FIXME: we only need one renewd daemon per server. * FIXME: we only need one renewd daemon per server.
*/ */
server->lease_time = fsinfo.lease_time * HZ; clp->cl_lease_time = fsinfo.lease_time * HZ;
if ((status = nfs4_init_renewd(server))) clp->cl_last_renewal = last_renewed;
goto out_unlock; nfs4_schedule_state_renewal(clp);
clp->cl_state = NFS4CLNT_OK;
no_setclientid:
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
/* /*
...@@ -1642,22 +1645,24 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) ...@@ -1642,22 +1645,24 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how)
static void static void
renew_done(struct rpc_task *task) renew_done(struct rpc_task *task)
{ {
struct nfs_server *server = (struct nfs_server *)task->tk_msg.rpc_resp; struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
unsigned long timestamp = (unsigned long)task->tk_calldata; unsigned long timestamp = (unsigned long)task->tk_calldata;
renew_lease(server, timestamp); spin_lock(&clp->cl_lock);
if (time_before(clp->cl_last_renewal,timestamp))
clp->cl_last_renewal = timestamp;
spin_unlock(&clp->cl_lock);
} }
int int
nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *cred) nfs4_proc_async_renew(struct nfs4_client *clp)
{ {
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
.rpc_argp = server->nfs4_state, .rpc_argp = clp,
.rpc_resp = server, .rpc_cred = clp->cl_cred,
.rpc_cred = cred,
}; };
return rpc_call_async(server->client, &msg, 0, renew_done, (void *)jiffies); return rpc_call_async(clp->cl_rpcclient, &msg, 0, renew_done, (void *)jiffies);
} }
/* /*
......
...@@ -54,53 +54,91 @@ ...@@ -54,53 +54,91 @@
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
static RPC_WAITQ(nfs4_renewd_queue, "nfs4_renewd_queue"); #define NFSDBG_FACILITY NFSDBG_PROC
static void void
renewd(struct rpc_task *task) nfs4_renew_state(void *data)
{ {
struct nfs_server *server = (struct nfs_server *)task->tk_calldata; struct nfs4_client *clp = (struct nfs4_client *)data;
unsigned long lease = server->lease_time; long lease, timeout;
unsigned long last = server->last_renewal; unsigned long last, now;
unsigned long timeout;
if (!server->nfs4_state) down_read(&clp->cl_sem);
timeout = (2 * lease) / 3; dprintk("%s: start\n", __FUNCTION__);
else if (jiffies < last + lease/3) /* Are there any active superblocks? */
timeout = (2 * lease) / 3 + last - jiffies; if (list_empty(&clp->cl_superblocks))
else { goto out;
spin_lock(&clp->cl_lock);
lease = clp->cl_lease_time;
last = clp->cl_last_renewal;
now = jiffies;
timeout = (2 * lease) / 3 + (long)last - (long)now;
/* Are we close to a lease timeout? */
if (time_after(now, last + lease/3)) {
spin_unlock(&clp->cl_lock);
/* Queue an asynchronous RENEW. */ /* Queue an asynchronous RENEW. */
nfs4_proc_async_renew(server, NULL); nfs4_proc_async_renew(clp);
timeout = (2 * lease) / 3; timeout = (2 * lease) / 3;
} spin_lock(&clp->cl_lock);
} else
dprintk("%s: failed to call renewd. Reason: lease not expired \n",
__FUNCTION__);
if (timeout < 5 * HZ) /* safeguard */ if (timeout < 5 * HZ) /* safeguard */
timeout = 5 * HZ; timeout = 5 * HZ;
task->tk_timeout = timeout; dprintk("%s: requeueing work. Lease period = %ld\n",
task->tk_action = renewd; __FUNCTION__, (timeout + HZ - 1) / HZ);
task->tk_exit = NULL; cancel_delayed_work(&clp->cl_renewd);
rpc_sleep_on(&nfs4_renewd_queue, task, NULL, NULL); schedule_delayed_work(&clp->cl_renewd, timeout);
return; spin_unlock(&clp->cl_lock);
out:
up_read(&clp->cl_sem);
dprintk("%s: done\n", __FUNCTION__);
} }
int /* Must be called with clp->cl_sem locked for writes */
nfs4_init_renewd(struct nfs_server *server) void
nfs4_schedule_state_renewal(struct nfs4_client *clp)
{ {
struct rpc_task *task; long timeout;
int status;
lock_kernel(); spin_lock(&clp->cl_lock);
status = -ENOMEM; timeout = (2 * clp->cl_lease_time) / 3 + (long)clp->cl_last_renewal
task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC); - (long)jiffies;
if (!task) if (timeout < 5 * HZ)
goto out; timeout = 5 * HZ;
task->tk_calldata = server; dprintk("%s: requeueing work. Lease period = %ld\n",
task->tk_action = renewd; __FUNCTION__, (timeout + HZ - 1) / HZ);
status = rpc_execute(task); cancel_delayed_work(&clp->cl_renewd);
schedule_delayed_work(&clp->cl_renewd, timeout);
spin_unlock(&clp->cl_lock);
}
out: void
unlock_kernel(); nfs4_renewd_prepare_shutdown(struct nfs_server *server)
return status; {
struct nfs4_client *clp = server->nfs4_state;
if (!clp)
return;
flush_scheduled_work();
down_write(&clp->cl_sem);
if (!list_empty(&server->nfs4_siblings))
list_del_init(&server->nfs4_siblings);
up_write(&clp->cl_sem);
}
/* Must be called with clp->cl_sem locked for writes */
void
nfs4_kill_renewd(struct nfs4_client *clp)
{
down_read(&clp->cl_sem);
if (!list_empty(&clp->cl_superblocks)) {
up_read(&clp->cl_sem);
return;
}
cancel_delayed_work(&clp->cl_renewd);
up_read(&clp->cl_sem);
flush_scheduled_work();
} }
/* /*
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/workqueue.h>
#define OPENOWNER_POOL_SIZE 8 #define OPENOWNER_POOL_SIZE 8
...@@ -55,6 +56,28 @@ nfs4_stateid one_stateid = ...@@ -55,6 +56,28 @@ nfs4_stateid one_stateid =
static LIST_HEAD(nfs4_clientid_list); static LIST_HEAD(nfs4_clientid_list);
extern void nfs4_renew_state(void *);
void
init_nfsv4_state(struct nfs_server *server)
{
server->nfs4_state = NULL;
INIT_LIST_HEAD(&server->nfs4_siblings);
}
void
destroy_nfsv4_state(struct nfs_server *server)
{
if (server->mnt_path) {
kfree(server->mnt_path);
server->mnt_path = NULL;
}
if (server->nfs4_state) {
nfs4_put_client(server->nfs4_state);
server->nfs4_state = NULL;
}
}
/* /*
* nfs4_get_client(): returns an empty client structure * nfs4_get_client(): returns an empty client structure
* nfs4_put_client(): drops reference to client structure * nfs4_put_client(): drops reference to client structure
...@@ -75,6 +98,8 @@ nfs4_alloc_client(struct in_addr *addr) ...@@ -75,6 +98,8 @@ nfs4_alloc_client(struct in_addr *addr)
INIT_LIST_HEAD(&clp->cl_unused); INIT_LIST_HEAD(&clp->cl_unused);
spin_lock_init(&clp->cl_lock); spin_lock_init(&clp->cl_lock);
atomic_set(&clp->cl_count, 1); atomic_set(&clp->cl_count, 1);
INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
INIT_LIST_HEAD(&clp->cl_superblocks);
clp->cl_state = NFS4CLNT_NEW; clp->cl_state = NFS4CLNT_NEW;
} }
return clp; return clp;
...@@ -130,6 +155,7 @@ nfs4_put_client(struct nfs4_client *clp) ...@@ -130,6 +155,7 @@ nfs4_put_client(struct nfs4_client *clp)
return; return;
list_del(&clp->cl_servers); list_del(&clp->cl_servers);
spin_unlock(&state_spinlock); spin_unlock(&state_spinlock);
nfs4_kill_renewd(clp);
nfs4_free_client(clp); nfs4_free_client(clp);
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/nfs3.h> #include <linux/nfs3.h>
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/nfs_xdr.h> #include <linux/nfs_xdr.h>
#include <linux/workqueue.h>
/* /*
* Enable debugging support for nfs client. * Enable debugging support for nfs client.
...@@ -493,6 +494,12 @@ struct nfs4_client { ...@@ -493,6 +494,12 @@ struct nfs4_client {
struct rpc_clnt * cl_rpcclient; struct rpc_clnt * cl_rpcclient;
struct rpc_cred * cl_cred; struct rpc_cred * cl_cred;
struct list_head cl_superblocks; /* List of nfs_server structs */
unsigned long cl_lease_time;
unsigned long cl_last_renewal;
struct work_struct cl_renewd;
/* Our own IP address, as a null-terminated string. /* Our own IP address, as a null-terminated string.
* This is used to generate the clientid, and the callback address. * This is used to generate the clientid, and the callback address.
*/ */
...@@ -545,13 +552,17 @@ struct nfs4_state { ...@@ -545,13 +552,17 @@ struct nfs4_state {
/* nfs4proc.c */ /* nfs4proc.c */
extern int nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *); extern int nfs4_proc_async_renew(struct nfs4_client *);
extern int nfs4_do_close(struct inode *, struct nfs4_state *); extern int nfs4_do_close(struct inode *, struct nfs4_state *);
/* nfs4renewd.c */ /* nfs4renewd.c */
extern int nfs4_init_renewd(struct nfs_server *server); extern void nfs4_schedule_state_renewal(struct nfs4_client *);
extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
extern void nfs4_kill_renewd(struct nfs4_client *);
/* nfs4state.c */ /* nfs4state.c */
extern void init_nfsv4_state(struct nfs_server *);
extern void destroy_nfsv4_state(struct nfs_server *);
extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern struct nfs4_client *nfs4_get_client(struct in_addr *);
extern void nfs4_put_client(struct nfs4_client *clp); extern void nfs4_put_client(struct nfs4_client *clp);
extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
...@@ -560,29 +571,13 @@ extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state ...@@ -560,29 +571,13 @@ extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state
extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp);
struct nfs4_mount_data; struct nfs4_mount_data;
static inline void
destroy_nfsv4_state(struct nfs_server *server)
{
if (server->mnt_path) {
kfree(server->mnt_path);
server->mnt_path = NULL;
}
if (server->nfs4_state) {
nfs4_put_client(server->nfs4_state);
server->nfs4_state = NULL;
}
}
#else #else
#define create_nfsv4_state(server, data) 0 #define init_nfsv4_state(server) do { } while (0)
#define destroy_nfsv4_state(server) do { } while (0) #define destroy_nfsv4_state(server) do { } while (0)
#define nfs4_put_state_owner(inode, owner) do { } while (0) #define nfs4_put_state_owner(inode, owner) do { } while (0)
#define nfs4_put_open_state(state) do { } while (0) #define nfs4_put_open_state(state) do { } while (0)
#define nfs4_renewd_prepare_shutdown(server) do { } while (0)
#endif #endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -35,8 +35,9 @@ struct nfs_server { ...@@ -35,8 +35,9 @@ struct nfs_server {
char ip_addr[16]; char ip_addr[16];
char * mnt_path; char * mnt_path;
struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */ struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */
unsigned long lease_time; /* in jiffies */ struct list_head nfs4_siblings; /* List of other nfs_server structs
unsigned long last_renewal; /* in jiffies */ * that share the same clientid
*/
void *idmap; void *idmap;
#endif #endif
}; };
......
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