fs/nfs/nfs4proc.c | 53 +++++++++++++++++++---------- fs/nfs/nfs4state.c | 88 +++++++++++++++++++++++++++++++++++++++++-------- include/linux/nfs_fs.h | 10 +++-- 3 files changed, 115 insertions(+), 36 deletions(-) diff -u --recursive --new-file --show-c-function linux-2.6.8-27-reboot_recovery/fs/nfs/nfs4proc.c linux-2.6.8-28-recover_locks/fs/nfs/nfs4proc.c --- linux-2.6.8-27-reboot_recovery/fs/nfs/nfs4proc.c 2004-08-06 16:01:01.000000000 -0700 +++ linux-2.6.8-28-recover_locks/fs/nfs/nfs4proc.c 2004-08-06 17:38:20.000000000 -0700 @@ -2192,16 +2192,19 @@ static int _nfs4_proc_unlck(struct nfs4_ lsp = nfs4_find_lock_state(state, request->fl_owner); if (!lsp) goto out; - luargs.seqid = lsp->ls_seqid; - memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid)); - arg.u.locku = &luargs; - status = rpc_call_sync(server->client, &msg, 0); - nfs4_increment_lock_seqid(status, lsp); + /* We might have lost the locks! */ + if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) { + luargs.seqid = lsp->ls_seqid; + memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid)); + arg.u.locku = &luargs; + status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_lock_seqid(status, lsp); + } if (status == 0) { memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(lsp->ls_stateid)); - nfs4_notify_unlck(inode, request, lsp); + nfs4_notify_unlck(state, request, lsp); } nfs4_put_lock_state(lsp); out: @@ -2225,11 +2228,10 @@ static int nfs4_proc_unlck(struct nfs4_s 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 nfs_server *server = NFS_SERVER(inode); - struct nfs4_client *clp = server->nfs4_state; struct nfs4_lock_state *lsp; struct nfs_lockargs arg = { .fh = NFS_FH(inode), @@ -2247,24 +2249,22 @@ static int _nfs4_proc_setlk(struct nfs4_ .rpc_cred = state->owner->so_cred, }; struct nfs_lock_opargs largs = { + .reclaim = reclaim, .new_lock_owner = 0, }; int status; - down_read(&clp->cl_sem); - down(&state->lock_sema); - lsp = nfs4_find_lock_state(state, request->fl_owner); - if (lsp == NULL) { + lsp = nfs4_get_lock_state(state, request->fl_owner); + if (lsp == NULL) + return -ENOMEM; + if (!(lsp->ls_flags & NFS_LOCK_INITIALIZED)) { struct nfs4_state_owner *owner = state->owner; struct nfs_open_to_lock otl = { .lock_owner = { .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_owner.id = lsp->ls_id; memcpy(&otl.open_stateid, &state->stateid, sizeof(otl.open_stateid)); @@ -2293,11 +2293,28 @@ static int _nfs4_proc_setlk(struct nfs4_ /* save the returned stateid. */ if (status == 0) { 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) status = -EAGAIN; 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); if (status == 0) { /* Note: we always want to sleep here! */ diff -u --recursive --new-file --show-c-function linux-2.6.8-27-reboot_recovery/fs/nfs/nfs4state.c linux-2.6.8-28-recover_locks/fs/nfs/nfs4state.c --- linux-2.6.8-27-reboot_recovery/fs/nfs/nfs4state.c 2004-08-06 16:01:01.000000000 -0700 +++ linux-2.6.8-28-recover_locks/fs/nfs/nfs4state.c 2004-08-06 17:36:08.000000000 -0700 @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -516,8 +517,7 @@ nfs4_find_lock_state(struct nfs4_state * * * The caller must be holding state->lock_sema */ -struct nfs4_lock_state * -nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) { struct nfs4_lock_state *lsp; struct nfs4_client *clp = state->owner->so_client; @@ -525,12 +525,12 @@ nfs4_alloc_lock_state(struct nfs4_state lsp = kmalloc(sizeof(*lsp), GFP_KERNEL); if (lsp == NULL) return NULL; + lsp->ls_flags = 0; lsp->ls_seqid = 0; /* arbitrary */ lsp->ls_id = -1; memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data)); atomic_set(&lsp->ls_count, 1); lsp->ls_owner = fl_owner; - lsp->ls_parent = state; INIT_LIST_HEAD(&lsp->ls_locks); spin_lock(&clp->cl_lock); lsp->ls_id = nfs4_alloc_lockowner_id(clp); @@ -539,6 +539,22 @@ nfs4_alloc_lock_state(struct nfs4_state } /* + * 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 * requests. */ @@ -588,13 +604,11 @@ nfs4_check_unlock(struct file_lock *fl, /* * Post an initialized lock_state on the state->lock_states list. */ -void -nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp) +void nfs4_notify_setlk(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp) { - struct nfs4_state *state = lsp->ls_parent; - if (!list_empty(&lsp->ls_locks)) return; + atomic_inc(&lsp->ls_count); write_lock(&state->state_lock); list_add(&lsp->ls_locks, &state->lock_states); set_bit(LK_STATE_IN_USE, &state->flags); @@ -611,9 +625,9 @@ nfs4_notify_setlk(struct inode *inode, s * */ 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; for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { @@ -631,6 +645,7 @@ nfs4_notify_unlck(struct inode *inode, s if (list_empty(&state->lock_states)) clear_bit(LK_STATE_IN_USE, &state->flags); write_unlock(&state->state_lock); + nfs4_put_lock_state(lsp); } /* @@ -642,8 +657,7 @@ nfs4_put_lock_state(struct nfs4_lock_sta { if (!atomic_dec_and_test(&lsp->ls_count)) return; - if (!list_empty(&lsp->ls_locks)) - return; + BUG_ON (!list_empty(&lsp->ls_locks)); kfree(lsp); } @@ -705,18 +719,62 @@ nfs4_schedule_state_recovery(struct nfs4 schedule_work(&clp->cl_recoverd); } -static int -nfs4_reclaim_open_state(struct nfs4_state_owner *sp) +static int nfs4_reclaim_locks(struct nfs4_state *state) +{ + 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_lock_state *lock; int status = 0; list_for_each_entry(state, &sp->so_states, open_states) { if (state->state == 0) continue; 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; + } switch (status) { default: printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", @@ -757,6 +815,7 @@ static int reclaimer(void *ptr) complete(&args->complete); /* Ensure exclusive access to NFSv4 state */ + lock_kernel(); down_write(&clp->cl_sem); /* Are there any NFS mounts out there? */ if (list_empty(&clp->cl_superblocks)) @@ -780,6 +839,7 @@ restart_loop: out: set_bit(NFS4CLNT_OK, &clp->cl_state); up_write(&clp->cl_sem); + unlock_kernel(); wake_up_all(&clp->cl_waitq); rpc_wake_up(&clp->cl_rpcwaitq); nfs4_put_client(clp); diff -u --recursive --new-file --show-c-function linux-2.6.8-27-reboot_recovery/include/linux/nfs_fs.h linux-2.6.8-28-recover_locks/include/linux/nfs_fs.h --- linux-2.6.8-27-reboot_recovery/include/linux/nfs_fs.h 2004-08-06 16:01:01.000000000 -0700 +++ linux-2.6.8-28-recover_locks/include/linux/nfs_fs.h 2004-08-06 16:01:10.000000000 -0700 @@ -593,7 +593,8 @@ struct nfs4_state_owner { struct nfs4_lock_state { struct list_head ls_locks; /* Other lock stateids */ 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_id; nfs4_stateid ls_stateid; @@ -646,6 +647,7 @@ extern int nfs4_wait_clnt_recover(struct 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_handle_exception(struct nfs_server *, int, struct nfs4_exception *); +extern int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request); /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); @@ -669,11 +671,11 @@ extern struct nfs4_state *nfs4_find_stat extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); 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_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_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_unlck(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 nfs4_state *, struct file_lock *, struct nfs4_lock_state *); extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);