NFSv4: Make NFS clean up byte range locks asynchronously Currently we fail to do so if the process was signalled. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 2 fs/nfs/nfs4proc.c | 140 ++++++++++++++++++++++++++++++++++------------------- fs/nfs/nfs4state.c | 8 +-- 3 files changed, 97 insertions(+), 53 deletions(-) Index: linux-2.6.12-rc1/fs/nfs/nfs4proc.c =================================================================== --- linux-2.6.12-rc1.orig/fs/nfs/nfs4proc.c +++ linux-2.6.12-rc1/fs/nfs/nfs4proc.c @@ -2636,7 +2636,7 @@ static int _nfs4_proc_getlk(struct nfs4_ down_read(&clp->cl_sem); nlo.clientid = clp->cl_clientid; - down(&state->lock_sema); + iosem_lock(&state->lock_iosem); lsp = nfs4_find_lock_state(state, request->fl_owner); if (lsp) nlo.id = lsp->ls_id; @@ -2667,7 +2667,7 @@ static int _nfs4_proc_getlk(struct nfs4_ } if (lsp) nfs4_put_lock_state(lsp); - up(&state->lock_sema); + iosem_unlock(&state->lock_iosem); up_read(&clp->cl_sem); return status; } @@ -2704,56 +2704,97 @@ static int do_vfs_lock(struct file *file return res; } -static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) +struct nfs4_unlockdata { + struct nfs_lockargs arg; + struct nfs_lockres res; + struct nfs_locku_opargs luargs; + struct nfs4_lock_state *lsp; + struct nfs4_state *state; + struct nfs_open_context *ctx; + struct file_lock fl; + struct iosem_work worker; +}; + +static void release_calldata(struct nfs4_unlockdata *calldata) { - struct inode *inode = state->inode; - struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_client *clp = server->nfs4_state; - struct nfs_lockargs arg = { - .fh = NFS_FH(inode), - .type = nfs4_lck_type(cmd, request), - .offset = request->fl_start, - .length = nfs4_lck_length(request), - }; - struct nfs_lockres res = { - .server = server, - }; + if (calldata->lsp != NULL) { + nfs4_notify_unlck(calldata->state, &calldata->fl, + calldata->lsp); + nfs4_put_lock_state(calldata->lsp); + } + iosem_unlock(&calldata->state->lock_iosem); + up_read(&calldata->state->owner->so_client->cl_sem); + put_nfs_open_context(calldata->ctx); + kfree(calldata); +} + +static void nfs4_locku_done(struct rpc_task *task) +{ + struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)task->tk_calldata; + + + nfs4_increment_lock_seqid(task->tk_status, calldata->lsp); + if (task->tk_status == 0) + memcpy(&calldata->lsp->ls_stateid, &calldata->res.u.stateid, + sizeof(calldata->lsp->ls_stateid)); + release_calldata(calldata); +} + +static void nfs4_do_unlock_func(void *data) +{ + struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)data; struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = state->owner->so_cred, + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU], + .rpc_argp = &calldata->arg, + .rpc_resp = &calldata->res, + .rpc_cred = calldata->state->owner->so_cred, }; struct nfs4_lock_state *lsp; - struct nfs_locku_opargs luargs; - int status = 0; - - down_read(&clp->cl_sem); - down(&state->lock_sema); - lsp = nfs4_find_lock_state(state, request->fl_owner); - if (!lsp) - goto out; - /* 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, RPC_TASK_NOINTR); - nfs4_increment_lock_seqid(status, lsp); - } - if (status == 0) { - memcpy(&lsp->ls_stateid, &res.u.stateid, - sizeof(lsp->ls_stateid)); - nfs4_notify_unlck(state, request, lsp); - } - nfs4_put_lock_state(lsp); + lsp = nfs4_find_lock_state(calldata->state, calldata->fl.fl_owner); + if (lsp == NULL) + goto out_free; + if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) + goto out_free; + + calldata->lsp = lsp; + calldata->luargs.seqid = lsp->ls_seqid; + memcpy(&calldata->luargs.stateid, &lsp->ls_stateid, sizeof(calldata->luargs.stateid)); + + if (rpc_call_async(NFS_SERVER(calldata->state->inode)->client, + &msg, 0, nfs4_locku_done, calldata) != 0) + goto out_free; + return; +out_free: + release_calldata(calldata); +} + +static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *fl) +{ + struct nfs4_unlockdata *calldata; + + if (!test_bit(LK_STATE_IN_USE, &state->flags)) + goto out; + calldata = (struct nfs4_unlockdata *)kmalloc(sizeof(*calldata), GFP_KERNEL); + if (calldata == NULL) + return -ENOMEM; + calldata->arg.fh = NFS_FH(state->inode); + calldata->arg.type = nfs4_lck_type(cmd, fl); + calldata->arg.offset = fl->fl_start; + calldata->arg.length = nfs4_lck_length(fl); + calldata->res.server = NFS_SERVER(state->inode); + calldata->arg.u.locku = &calldata->luargs; + calldata->lsp = NULL; + calldata->state = state; + calldata->ctx = get_nfs_open_context((struct nfs_open_context*)fl->fl_file->private_data); + memcpy(&calldata->fl, fl, sizeof(calldata->fl)); + iosem_work_init(&calldata->worker, nfs4_do_unlock_func, calldata); + down_read(&state->owner->so_client->cl_sem); + iosem_lock_and_schedule_work(&state->lock_iosem, &calldata->worker); + /* Note: We do the VFS unlock now! */ + do_vfs_lock(fl->fl_file, fl); out: - up(&state->lock_sema); - if (status == 0) - do_vfs_lock(request->fl_file, request); - up_read(&clp->cl_sem); - return status; + return 0; } static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) @@ -2859,9 +2900,9 @@ static int _nfs4_proc_setlk(struct nfs4_ int status; down_read(&clp->cl_sem); - down(&state->lock_sema); + iosem_lock(&state->lock_iosem); status = _nfs4_do_setlk(state, cmd, request, 0); - up(&state->lock_sema); + iosem_unlock(&state->lock_iosem); if (status == 0) { /* Note: we always want to sleep here! */ request->fl_flags |= FL_SLEEP; @@ -2897,6 +2938,9 @@ nfs4_proc_lock(struct file *filp, int cm ctx = (struct nfs_open_context *)filp->private_data; state = ctx->state; + if (state == NULL) + return -ENOLCK; + if (request->fl_start < 0 || request->fl_end < 0) return -EINVAL; Index: linux-2.6.12-rc1/fs/nfs/nfs4_fs.h =================================================================== --- linux-2.6.12-rc1.orig/fs/nfs/nfs4_fs.h +++ linux-2.6.12-rc1/fs/nfs/nfs4_fs.h @@ -154,7 +154,7 @@ struct nfs4_state { struct inode *inode; /* Pointer to the inode */ unsigned long flags; /* Do we hold any locks? */ - struct semaphore lock_sema; /* Serializes file locking operations */ + struct iosem lock_iosem; /* Serializes file locking operations */ rwlock_t state_lock; /* Protects the lock_states list */ nfs4_stateid stateid; Index: linux-2.6.12-rc1/fs/nfs/nfs4state.c =================================================================== --- linux-2.6.12-rc1.orig/fs/nfs/nfs4state.c +++ linux-2.6.12-rc1/fs/nfs/nfs4state.c @@ -358,7 +358,7 @@ nfs4_alloc_open_state(void) memset(state->stateid.data, 0, sizeof(state->stateid.data)); atomic_set(&state->count, 1); INIT_LIST_HEAD(&state->lock_states); - init_MUTEX(&state->lock_sema); + iosem_init(&state->lock_iosem); rwlock_init(&state->state_lock); return state; } @@ -585,7 +585,7 @@ nfs4_find_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 + * The caller must be holding state->lock_iosem */ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) { @@ -612,7 +612,7 @@ static struct nfs4_lock_state *nfs4_allo * 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 + * The caller must be holding state->lock_iosem and clp->cl_sem */ struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) { @@ -645,7 +645,7 @@ nfs4_copy_stateid(nfs4_stateid *dst, str } /* -* Called with state->lock_sema and clp->cl_sem held. +* Called with state->lock_iosem and clp->cl_sem held. */ void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) {