Commit ed373838 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: On server reboot we need to recover byte-range locks.

Signed-off-by: default avatarTrond Myklebust <trond.myklebust@fys.uio.no>
parent 191f7912
...@@ -2192,16 +2192,19 @@ static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock ...@@ -2192,16 +2192,19 @@ static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock
lsp = nfs4_find_lock_state(state, request->fl_owner); lsp = nfs4_find_lock_state(state, request->fl_owner);
if (!lsp) if (!lsp)
goto out; goto out;
/* We might have lost the locks! */
if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) {
luargs.seqid = lsp->ls_seqid; luargs.seqid = lsp->ls_seqid;
memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid)); memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid));
arg.u.locku = &luargs; arg.u.locku = &luargs;
status = rpc_call_sync(server->client, &msg, 0); status = rpc_call_sync(server->client, &msg, 0);
nfs4_increment_lock_seqid(status, lsp); nfs4_increment_lock_seqid(status, lsp);
}
if (status == 0) { if (status == 0) {
memcpy(&lsp->ls_stateid, &res.u.stateid, memcpy(&lsp->ls_stateid, &res.u.stateid,
sizeof(lsp->ls_stateid)); sizeof(lsp->ls_stateid));
nfs4_notify_unlck(inode, request, lsp); nfs4_notify_unlck(state, request, lsp);
} }
nfs4_put_lock_state(lsp); nfs4_put_lock_state(lsp);
out: out:
...@@ -2225,11 +2228,10 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * ...@@ -2225,11 +2228,10 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
return err; return err;
} }
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *request, int reclaim)
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_client *clp = server->nfs4_state;
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
struct nfs_lockargs arg = { struct nfs_lockargs arg = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
...@@ -2247,24 +2249,22 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -2247,24 +2249,22 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
.rpc_cred = state->owner->so_cred, .rpc_cred = state->owner->so_cred,
}; };
struct nfs_lock_opargs largs = { struct nfs_lock_opargs largs = {
.reclaim = reclaim,
.new_lock_owner = 0, .new_lock_owner = 0,
}; };
int status; int status;
down_read(&clp->cl_sem); lsp = nfs4_get_lock_state(state, request->fl_owner);
down(&state->lock_sema); if (lsp == NULL)
lsp = nfs4_find_lock_state(state, request->fl_owner); return -ENOMEM;
if (lsp == NULL) { if (!(lsp->ls_flags & NFS_LOCK_INITIALIZED)) {
struct nfs4_state_owner *owner = state->owner; struct nfs4_state_owner *owner = state->owner;
struct nfs_open_to_lock otl = { struct nfs_open_to_lock otl = {
.lock_owner = { .lock_owner = {
.clientid = server->nfs4_state->cl_clientid, .clientid = server->nfs4_state->cl_clientid,
}, },
}; };
status = -ENOMEM;
lsp = nfs4_alloc_lock_state(state, request->fl_owner);
if (!lsp)
goto out;
otl.lock_seqid = lsp->ls_seqid; otl.lock_seqid = lsp->ls_seqid;
otl.lock_owner.id = lsp->ls_id; otl.lock_owner.id = lsp->ls_id;
memcpy(&otl.open_stateid, &state->stateid, sizeof(otl.open_stateid)); memcpy(&otl.open_stateid, &state->stateid, sizeof(otl.open_stateid));
...@@ -2293,11 +2293,28 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -2293,11 +2293,28 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
/* save the returned stateid. */ /* save the returned stateid. */
if (status == 0) { if (status == 0) {
memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid)); memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid));
nfs4_notify_setlk(inode, request, lsp); lsp->ls_flags |= NFS_LOCK_INITIALIZED;
if (!reclaim)
nfs4_notify_setlk(state, request, lsp);
} else if (status == -NFS4ERR_DENIED) } else if (status == -NFS4ERR_DENIED)
status = -EAGAIN; status = -EAGAIN;
nfs4_put_lock_state(lsp); nfs4_put_lock_state(lsp);
out: return status;
}
int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
{
return _nfs4_do_setlk(state, F_SETLK64, request, 1);
}
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs4_client *clp = state->owner->so_client;
int status;
down_read(&clp->cl_sem);
down(&state->lock_sema);
status = _nfs4_do_setlk(state, cmd, request, 0);
up(&state->lock_sema); up(&state->lock_sema);
if (status == 0) { if (status == 0) {
/* Note: we always want to sleep here! */ /* Note: we always want to sleep here! */
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h> #include <linux/nfs_idmap.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
...@@ -516,8 +517,7 @@ nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) ...@@ -516,8 +517,7 @@ nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
* *
* The caller must be holding state->lock_sema * The caller must be holding state->lock_sema
*/ */
struct nfs4_lock_state * static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
{ {
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
struct nfs4_client *clp = state->owner->so_client; struct nfs4_client *clp = state->owner->so_client;
...@@ -525,12 +525,12 @@ nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) ...@@ -525,12 +525,12 @@ nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
lsp = kmalloc(sizeof(*lsp), GFP_KERNEL); lsp = kmalloc(sizeof(*lsp), GFP_KERNEL);
if (lsp == NULL) if (lsp == NULL)
return NULL; return NULL;
lsp->ls_flags = 0;
lsp->ls_seqid = 0; /* arbitrary */ lsp->ls_seqid = 0; /* arbitrary */
lsp->ls_id = -1; lsp->ls_id = -1;
memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data)); memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data));
atomic_set(&lsp->ls_count, 1); atomic_set(&lsp->ls_count, 1);
lsp->ls_owner = fl_owner; lsp->ls_owner = fl_owner;
lsp->ls_parent = state;
INIT_LIST_HEAD(&lsp->ls_locks); INIT_LIST_HEAD(&lsp->ls_locks);
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
lsp->ls_id = nfs4_alloc_lockowner_id(clp); lsp->ls_id = nfs4_alloc_lockowner_id(clp);
...@@ -538,6 +538,22 @@ nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) ...@@ -538,6 +538,22 @@ nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
return lsp; return lsp;
} }
/*
* Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one.
*
* The caller must be holding state->lock_sema and clp->cl_sem
*/
struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
{
struct nfs4_lock_state * lsp;
lsp = nfs4_find_lock_state(state, owner);
if (lsp == NULL)
lsp = nfs4_alloc_lock_state(state, owner);
return lsp;
}
/* /*
* Byte-range lock aware utility to initialize the stateid of read/write * Byte-range lock aware utility to initialize the stateid of read/write
* requests. * requests.
...@@ -588,13 +604,11 @@ nfs4_check_unlock(struct file_lock *fl, struct file_lock *request) ...@@ -588,13 +604,11 @@ nfs4_check_unlock(struct file_lock *fl, struct file_lock *request)
/* /*
* Post an initialized lock_state on the state->lock_states list. * Post an initialized lock_state on the state->lock_states list.
*/ */
void void nfs4_notify_setlk(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp)
nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp)
{ {
struct nfs4_state *state = lsp->ls_parent;
if (!list_empty(&lsp->ls_locks)) if (!list_empty(&lsp->ls_locks))
return; return;
atomic_inc(&lsp->ls_count);
write_lock(&state->state_lock); write_lock(&state->state_lock);
list_add(&lsp->ls_locks, &state->lock_states); list_add(&lsp->ls_locks, &state->lock_states);
set_bit(LK_STATE_IN_USE, &state->flags); set_bit(LK_STATE_IN_USE, &state->flags);
...@@ -611,9 +625,9 @@ nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lo ...@@ -611,9 +625,9 @@ nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lo
* *
*/ */
void void
nfs4_notify_unlck(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp) nfs4_notify_unlck(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp)
{ {
struct nfs4_state *state = lsp->ls_parent; struct inode *inode = state->inode;
struct file_lock *fl; struct file_lock *fl;
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
...@@ -631,6 +645,7 @@ nfs4_notify_unlck(struct inode *inode, struct file_lock *request, struct nfs4_lo ...@@ -631,6 +645,7 @@ nfs4_notify_unlck(struct inode *inode, struct file_lock *request, struct nfs4_lo
if (list_empty(&state->lock_states)) if (list_empty(&state->lock_states))
clear_bit(LK_STATE_IN_USE, &state->flags); clear_bit(LK_STATE_IN_USE, &state->flags);
write_unlock(&state->state_lock); write_unlock(&state->state_lock);
nfs4_put_lock_state(lsp);
} }
/* /*
...@@ -642,8 +657,7 @@ nfs4_put_lock_state(struct nfs4_lock_state *lsp) ...@@ -642,8 +657,7 @@ nfs4_put_lock_state(struct nfs4_lock_state *lsp)
{ {
if (!atomic_dec_and_test(&lsp->ls_count)) if (!atomic_dec_and_test(&lsp->ls_count))
return; return;
if (!list_empty(&lsp->ls_locks)) BUG_ON (!list_empty(&lsp->ls_locks));
return;
kfree(lsp); kfree(lsp);
} }
...@@ -705,18 +719,62 @@ nfs4_schedule_state_recovery(struct nfs4_client *clp) ...@@ -705,18 +719,62 @@ nfs4_schedule_state_recovery(struct nfs4_client *clp)
schedule_work(&clp->cl_recoverd); schedule_work(&clp->cl_recoverd);
} }
static int static int nfs4_reclaim_locks(struct nfs4_state *state)
nfs4_reclaim_open_state(struct nfs4_state_owner *sp) {
struct inode *inode = state->inode;
struct file_lock *fl;
int status = 0;
for (fl = inode->i_flock; fl != 0; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
if ((struct nfs4_state *)fl->fl_file->private_data != state)
continue;
status = nfs4_lock_reclaim(state, fl);
if (status >= 0)
continue;
switch (status) {
default:
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
__FUNCTION__, status);
case -NFS4ERR_EXPIRED:
case -NFS4ERR_NO_GRACE:
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
/* kill_proc(fl->fl_owner, SIGLOST, 1); */
break;
case -NFS4ERR_STALE_CLIENTID:
goto out_err;
}
}
return 0;
out_err:
return status;
}
static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
{ {
struct nfs4_state *state; struct nfs4_state *state;
struct nfs4_lock_state *lock;
int status = 0; int status = 0;
list_for_each_entry(state, &sp->so_states, open_states) { list_for_each_entry(state, &sp->so_states, open_states) {
if (state->state == 0) if (state->state == 0)
continue; continue;
status = nfs4_open_reclaim(sp, state); status = nfs4_open_reclaim(sp, state);
if (status >= 0) list_for_each_entry(lock, &state->lock_states, ls_locks)
lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
if (status >= 0) {
status = nfs4_reclaim_locks(state);
if (status < 0)
goto out_err;
list_for_each_entry(lock, &state->lock_states, ls_locks) {
if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
printk("%s: Lock reclaim failed!\n",
__FUNCTION__);
}
continue; continue;
}
switch (status) { switch (status) {
default: default:
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
...@@ -757,6 +815,7 @@ static int reclaimer(void *ptr) ...@@ -757,6 +815,7 @@ static int reclaimer(void *ptr)
complete(&args->complete); complete(&args->complete);
/* Ensure exclusive access to NFSv4 state */ /* Ensure exclusive access to NFSv4 state */
lock_kernel();
down_write(&clp->cl_sem); down_write(&clp->cl_sem);
/* Are there any NFS mounts out there? */ /* Are there any NFS mounts out there? */
if (list_empty(&clp->cl_superblocks)) if (list_empty(&clp->cl_superblocks))
...@@ -780,6 +839,7 @@ static int reclaimer(void *ptr) ...@@ -780,6 +839,7 @@ static int reclaimer(void *ptr)
out: out:
set_bit(NFS4CLNT_OK, &clp->cl_state); set_bit(NFS4CLNT_OK, &clp->cl_state);
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
unlock_kernel();
wake_up_all(&clp->cl_waitq); wake_up_all(&clp->cl_waitq);
rpc_wake_up(&clp->cl_rpcwaitq); rpc_wake_up(&clp->cl_rpcwaitq);
nfs4_put_client(clp); nfs4_put_client(clp);
......
...@@ -591,7 +591,8 @@ struct nfs4_state_owner { ...@@ -591,7 +591,8 @@ struct nfs4_state_owner {
struct nfs4_lock_state { struct nfs4_lock_state {
struct list_head ls_locks; /* Other lock stateids */ struct list_head ls_locks; /* Other lock stateids */
fl_owner_t ls_owner; /* POSIX lock owner */ fl_owner_t ls_owner; /* POSIX lock owner */
struct nfs4_state * ls_parent; /* Parent nfs4_state */ #define NFS_LOCK_INITIALIZED 1
int ls_flags;
u32 ls_seqid; u32 ls_seqid;
u32 ls_id; u32 ls_id;
nfs4_stateid ls_stateid; nfs4_stateid ls_stateid;
...@@ -644,6 +645,7 @@ extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); ...@@ -644,6 +645,7 @@ extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *);
extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception *); extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception *);
extern int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request);
/* nfs4renewd.c */ /* nfs4renewd.c */
extern void nfs4_schedule_state_renewal(struct nfs4_client *); extern void nfs4_schedule_state_renewal(struct nfs4_client *);
...@@ -667,11 +669,11 @@ extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mod ...@@ -667,11 +669,11 @@ extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mod
extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp);
extern void nfs4_schedule_state_recovery(struct nfs4_client *); extern void nfs4_schedule_state_recovery(struct nfs4_client *);
extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t); extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t);
extern struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t); extern struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t);
extern void nfs4_put_lock_state(struct nfs4_lock_state *state); extern void nfs4_put_lock_state(struct nfs4_lock_state *state);
extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls); extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls);
extern void nfs4_notify_setlk(struct inode *, struct file_lock *, struct nfs4_lock_state *); extern void nfs4_notify_setlk(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *);
extern void nfs4_notify_unlck(struct inode *, struct file_lock *, struct nfs4_lock_state *); extern void nfs4_notify_unlck(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *);
extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
......
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