From b6b42cecacc461da1adba0138660f83bb0877da5 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Aug 2004 11:14:18 -0400 Subject: NFSv4: Fix up the exception handling. Ensure we always handle NFS4ERR_DELAY properly. Signed-off-by: Trond Myklebust --- include/linux/nfs_fs.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 1b9154d67afa..7bab91858ee2 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -629,6 +629,11 @@ struct nfs4_state { }; +struct nfs4_exception { + long timeout; + int retry; +}; + extern struct dentry_operations nfs4_dentry_operations; extern struct inode_operations nfs4_dir_inode_operations; @@ -638,11 +643,12 @@ extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_proc_renew(struct nfs4_client *); -extern int nfs4_do_close(struct inode *, struct nfs4_state *); -int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode); +extern int _nfs4_do_close(struct inode *, struct nfs4_state *); +extern int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode); 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 int nfs4_open_revalidate(struct inode *, struct dentry *, int); +extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception *); /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); @@ -663,7 +669,6 @@ extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_close_state(struct nfs4_state *, mode_t); extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); -extern int nfs4_handle_error(struct nfs_server *, int); 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); -- cgit v1.2.3 From 191f7912ab4ad8d992f27d1c47828157ac22648c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Aug 2004 11:16:50 -0400 Subject: NFSv4: Clean up the reboot recovery. Ensure that we exclude stateful operations by using a per-server read/write semaphore. Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 9 ++- fs/nfs/nfs4proc.c | 86 ++++++++++++++++----- fs/nfs/nfs4state.c | 200 +++++++++++++++++++------------------------------ include/linux/nfs_fs.h | 10 +-- 4 files changed, 153 insertions(+), 152 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index daaa0680d737..98f04ecec859 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1510,8 +1510,13 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); nfs_idmap_new(clp); } - if (list_empty(&clp->cl_superblocks)) - clear_bit(NFS4CLNT_OK, &clp->cl_state); + if (list_empty(&clp->cl_superblocks)) { + err = nfs4_init_client(clp); + if (err != 0) { + up_write(&clp->cl_sem); + goto out_fail; + } + } list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); if (!IS_ERR(clnt)) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9f8632c35aa2..906cde77d088 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -254,6 +254,7 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct struct nfs4_state_owner *sp; struct nfs4_state *state = NULL; struct nfs_server *server = NFS_SERVER(dir); + struct nfs4_client *clp = server->nfs4_state; struct inode *inode = NULL; int status; struct nfs_fattr f_attr = { @@ -279,6 +280,8 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct .rpc_cred = cred, }; + /* 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"); @@ -342,15 +345,18 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct up(&sp->so_sema); nfs4_put_state_owner(sp); + up_read(&clp->cl_sem); *res = state; return 0; out_err: if (sp != NULL) { + if (state != NULL) + nfs4_put_open_state(state); up(&sp->so_sema); nfs4_put_state_owner(sp); } - if (state != NULL) - nfs4_put_open_state(state); + /* Note: clp->cl_sem must be released before nfs4_put_open_state()! */ + up_read(&clp->cl_sem); if (inode != NULL) iput(inode); *res = NULL; @@ -446,7 +452,7 @@ int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, * * NOTE: Caller must be holding the sp->so_owner semaphore! */ -int _nfs4_do_close(struct inode *inode, struct nfs4_state *state) +static int _nfs4_do_close(struct inode *inode, struct nfs4_state *state) { struct nfs4_state_owner *sp = state->owner; int status = 0; @@ -475,7 +481,27 @@ int _nfs4_do_close(struct inode *inode, struct nfs4_state *state) return status; } -int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) +int nfs4_do_close(struct inode *inode, struct nfs4_state *state) +{ + struct nfs_server *server = NFS_SERVER(state->inode); + struct nfs4_exception exception = { }; + int err; + do { + err = _nfs4_do_close(inode, state); + switch (err) { + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_EXPIRED: + nfs4_schedule_state_recovery(server->nfs4_state); + err = 0; + default: + state->state = 0; + } + err = nfs4_handle_exception(server, err, &exception); + } while (exception.retry); + return err; +} + +static int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) { struct nfs4_state_owner *sp = state->owner; int status = 0; @@ -500,6 +526,26 @@ int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mod return status; } +int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) +{ + struct nfs_server *server = NFS_SERVER(state->inode); + struct nfs4_exception exception = { }; + int err; + do { + err = _nfs4_do_downgrade(inode, state, mode); + switch (err) { + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_EXPIRED: + nfs4_schedule_state_recovery(server->nfs4_state); + err = 0; + default: + state->state = mode; + } + err = nfs4_handle_exception(server, err, &exception); + } while (exception.retry); + return err; +} + struct inode * nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { @@ -1829,6 +1875,8 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) case -NFS4ERR_EXPIRED: rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL); nfs4_schedule_state_recovery(clp); + if (test_bit(NFS4CLNT_OK, &clp->cl_state)) + rpc_wake_up_task(task); task->tk_status = 0; return -EAGAIN; case -NFS4ERR_GRACE: @@ -1844,12 +1892,11 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) return 0; } -int -nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) +int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) { DEFINE_WAIT(wait); sigset_t oldset; - int interruptible, res; + int interruptible, res = 0; might_sleep(); @@ -1857,19 +1904,12 @@ nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) interruptible = TASK_UNINTERRUPTIBLE; if (clnt->cl_intr) interruptible = TASK_INTERRUPTIBLE; - do { - res = 0; - prepare_to_wait(&clp->cl_waitq, &wait, interruptible); - nfs4_schedule_state_recovery(clp); - if (test_bit(NFS4CLNT_OK, &clp->cl_state) && - !test_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state)) - break; - if (clnt->cl_intr && signalled()) { - res = -ERESTARTSYS; - break; - } + prepare_to_wait(&clp->cl_waitq, &wait, interruptible); + nfs4_schedule_state_recovery(clp); + if (clnt->cl_intr && signalled()) + res = -ERESTARTSYS; + else if (!test_bit(NFS4CLNT_OK, &clp->cl_state)) schedule(); - } while(!test_bit(NFS4CLNT_OK, &clp->cl_state)); finish_wait(&clp->cl_waitq, &wait); rpc_clnt_sigunmask(clnt, &oldset); return res; @@ -2072,6 +2112,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock struct nfs4_lock_state *lsp; int status; + down_read(&clp->cl_sem); nlo.clientid = clp->cl_clientid; down(&state->lock_sema); lsp = nfs4_find_lock_state(state, request->fl_owner); @@ -2105,6 +2146,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock if (lsp) nfs4_put_lock_state(lsp); up(&state->lock_sema); + up_read(&clp->cl_sem); return status; } @@ -2125,6 +2167,7 @@ static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock { 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), @@ -2144,6 +2187,7 @@ static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock 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) @@ -2164,6 +2208,7 @@ out: up(&state->lock_sema); if (status == 0) posix_lock_file(request->fl_file, request); + up_read(&clp->cl_sem); return status; } @@ -2184,6 +2229,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock { 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), @@ -2205,6 +2251,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock }; int status; + down_read(&clp->cl_sem); down(&state->lock_sema); lsp = nfs4_find_lock_state(state, request->fl_owner); if (lsp == NULL) { @@ -2258,6 +2305,7 @@ out: if (posix_lock_file_wait(request->fl_file, request) < 0) printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); } + up_read(&clp->cl_sem); return status; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index c5fa64cb2294..d2326b7cf9bc 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -106,7 +106,7 @@ nfs4_alloc_client(struct in_addr *addr) INIT_LIST_HEAD(&clp->cl_superblocks); init_waitqueue_head(&clp->cl_waitq); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client"); - clp->cl_state = 1 << NFS4CLNT_NEW; + clp->cl_state = 1 << NFS4CLNT_OK; } return clp; } @@ -169,6 +169,16 @@ nfs4_put_client(struct nfs4_client *clp) nfs4_free_client(clp); } +int nfs4_init_client(struct nfs4_client *clp) +{ + int status = nfs4_proc_setclientid(clp, 0, 0); + if (status == 0) + status = nfs4_proc_setclientid_confirm(clp); + if (status == 0) + nfs4_schedule_state_renewal(clp); + return status; +} + u32 nfs4_alloc_lockowner_id(struct nfs4_client *clp) { @@ -185,7 +195,6 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) atomic_inc(&sp->so_count); sp->so_cred = cred; list_move(&sp->so_list, &clp->cl_state_owners); - sp->so_generation = clp->cl_generation; clp->cl_nunused--; } return sp; @@ -237,8 +246,11 @@ nfs4_unhash_state_owner(struct nfs4_state_owner *sp) spin_unlock(&clp->cl_lock); } -struct nfs4_state_owner * -nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) +/* + * Note: must be called with clp->cl_sem held in order to prevent races + * with reboot recovery! + */ +struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) { struct nfs4_client *clp = server->nfs4_state; struct nfs4_state_owner *sp, *new; @@ -254,23 +266,23 @@ nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) new->so_client = clp; new->so_id = nfs4_alloc_lockowner_id(clp); new->so_cred = cred; - new->so_generation = clp->cl_generation; sp = new; new = NULL; } spin_unlock(&clp->cl_lock); if (new) kfree(new); - if (sp) { - if (!test_bit(NFS4CLNT_OK, &clp->cl_state)) - nfs4_wait_clnt_recover(server->client, clp); - } else - put_rpccred(cred); - return sp; + if (sp != NULL) + return sp; + put_rpccred(cred); + return NULL; } -void -nfs4_put_state_owner(struct nfs4_state_owner *sp) +/* + * Must be called with clp->cl_sem held in order to avoid races + * with state recovery... + */ +void nfs4_put_state_owner(struct nfs4_state_owner *sp) { struct nfs4_client *clp = sp->so_client; struct rpc_cred *cred = sp->so_cred; @@ -330,8 +342,6 @@ __nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) continue; if ((state->state & mode) != mode) continue; - /* Add the state to the head of the inode's list */ - list_move(&state->inode_states, &nfsi->open_states); atomic_inc(&state->count); if (mode & FMODE_READ) state->nreaders++; @@ -353,8 +363,6 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) if (state->nreaders == 0 && state->nwriters == 0) continue; if (state->owner == owner) { - /* Add the state to the head of the inode's list */ - list_move(&state->inode_states, &nfsi->open_states); atomic_inc(&state->count); return state; } @@ -411,55 +419,40 @@ out: return state; } -static void -__nfs4_put_open_state(struct nfs4_state *state) +/* + * Beware! Caller must be holding exactly one + * reference to clp->cl_sem and owner->so_sema! + */ +void nfs4_put_open_state(struct nfs4_state *state) { struct inode *inode = state->inode; struct nfs4_state_owner *owner = state->owner; - struct nfs4_exception exception = { }; - int status = 0; - if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) { - up(&owner->so_sema); + if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) return; - } if (!list_empty(&state->inode_states)) list_del(&state->inode_states); spin_unlock(&inode->i_lock); list_del(&state->open_states); - if (state->state != 0) { - for (;;) { - status = _nfs4_do_close(inode, state); - up(&owner->so_sema); - if (!status) - break; - status = nfs4_handle_exception(NFS_SERVER(inode), status, &exception); - if (!exception.retry) - break; - down(&owner->so_sema); - } - } else - up(&owner->so_sema); + BUG_ON (state->state != 0); nfs4_free_open_state(state); nfs4_put_state_owner(owner); } -void -nfs4_put_open_state(struct nfs4_state *state) -{ - down(&state->owner->so_sema); - __nfs4_put_open_state(state); -} - -void -nfs4_close_state(struct nfs4_state *state, mode_t mode) +/* + * Beware! Caller must be holding no references to clp->cl_sem! + * of owner->so_sema! + */ +void nfs4_close_state(struct nfs4_state *state, mode_t mode) { struct inode *inode = state->inode; struct nfs4_state_owner *owner = state->owner; - struct nfs4_exception exception = { }; + struct nfs4_client *clp = owner->so_client; int newstate; int status = 0; + atomic_inc(&owner->so_count); + down_read(&clp->cl_sem); down(&owner->so_sema); /* Protect against nfs4_find_state() */ spin_lock(&inode->i_lock); @@ -470,29 +463,24 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode) if (state->nwriters == 0 && state->nreaders == 0) list_del_init(&state->inode_states); spin_unlock(&inode->i_lock); - do { - newstate = 0; - if (state->state == 0) - break; + newstate = 0; + if (state->state != 0) { if (state->nreaders) newstate |= FMODE_READ; if (state->nwriters) newstate |= FMODE_WRITE; if (state->state == newstate) - break; + goto out; if (newstate != 0) - status = _nfs4_do_downgrade(inode, state, newstate); + status = nfs4_do_downgrade(inode, state, newstate); else - status = _nfs4_do_close(inode, state); - if (!status) { - state->state = newstate; - break; - } - up(&owner->so_sema); - status = nfs4_handle_exception(NFS_SERVER(inode), status, &exception); - down(&owner->so_sema); - } while (exception.retry); - __nfs4_put_open_state(state); + status = nfs4_do_close(inode, state); + } +out: + nfs4_put_open_state(state); + up(&owner->so_sema); + nfs4_put_state_owner(owner); + up_read(&clp->cl_sem); } /* @@ -571,10 +559,9 @@ nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_own } /* -* Called with state->lock_sema held. +* Called with state->lock_sema and clp->cl_sem held. */ -void -nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) +void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) { if (status == NFS_OK || seqid_mutating_err(-status)) lsp->ls_seqid++; @@ -661,14 +648,13 @@ nfs4_put_lock_state(struct nfs4_lock_state *lsp) } /* -* Called with sp->so_sema held. +* Called with sp->so_sema and clp->cl_sem held. * * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or * failed with a seqid incrementing error - * see comments nfs_fs.h:seqid_mutating_error() */ -void -nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) +void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) { if (status == NFS_OK || seqid_mutating_err(-status)) sp->so_seqid++; @@ -697,21 +683,14 @@ nfs4_recover_state(void *data) init_completion(&args.complete); - down_read(&clp->cl_sem); - if (test_and_set_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state)) - goto out_failed; if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) goto out_failed_clear; wait_for_completion(&args.complete); return; out_failed_clear: - smp_mb__before_clear_bit(); - clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state); - smp_mb__after_clear_bit(); + set_bit(NFS4CLNT_OK, &clp->cl_state); wake_up_all(&clp->cl_waitq); rpc_wake_up(&clp->cl_rpcwaitq); -out_failed: - up_read(&clp->cl_sem); } /* @@ -722,10 +701,8 @@ nfs4_schedule_state_recovery(struct nfs4_client *clp) { if (!clp) return; - smp_mb__before_clear_bit(); - clear_bit(NFS4CLNT_OK, &clp->cl_state); - smp_mb__after_clear_bit(); - schedule_work(&clp->cl_recoverd); + if (test_and_clear_bit(NFS4CLNT_OK, &clp->cl_state)) + schedule_work(&clp->cl_recoverd); } static int @@ -766,75 +743,50 @@ out_err: return status; } -static int -reclaimer(void *ptr) +static int reclaimer(void *ptr) { struct reclaimer_args *args = (struct reclaimer_args *)ptr; struct nfs4_client *clp = args->clp; struct nfs4_state_owner *sp; - int generation; int status; daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); allow_signal(SIGKILL); + atomic_inc(&clp->cl_count); complete(&args->complete); + /* Ensure exclusive access to NFSv4 state */ + down_write(&clp->cl_sem); /* Are there any NFS mounts out there? */ if (list_empty(&clp->cl_superblocks)) goto out; - if (!test_bit(NFS4CLNT_NEW, &clp->cl_state)) { - status = nfs4_proc_renew(clp); - if (status == 0) { - set_bit(NFS4CLNT_OK, &clp->cl_state); - goto out; - } - } - status = nfs4_proc_setclientid(clp, 0, 0); - if (status) - goto out_error; - status = nfs4_proc_setclientid_confirm(clp); +restart_loop: + status = nfs4_proc_renew(clp); + if (status == 0) + goto out; + status = nfs4_init_client(clp); if (status) goto out_error; - generation = ++(clp->cl_generation); - clear_bit(NFS4CLNT_NEW, &clp->cl_state); - set_bit(NFS4CLNT_OK, &clp->cl_state); - up_read(&clp->cl_sem); - nfs4_schedule_state_renewal(clp); -restart_loop: - spin_lock(&clp->cl_lock); + /* Note: list is protected by exclusive lock on cl->cl_sem */ list_for_each_entry(sp, &clp->cl_state_owners, so_list) { - if (sp->so_generation - generation >= 0) - continue; - atomic_inc(&sp->so_count); - spin_unlock(&clp->cl_lock); - down(&sp->so_sema); - if (sp->so_generation - generation < 0) { - smp_rmb(); - sp->so_generation = clp->cl_generation; - status = nfs4_reclaim_open_state(sp); - } - up(&sp->so_sema); - nfs4_put_state_owner(sp); + status = nfs4_reclaim_open_state(sp); if (status < 0) { if (status == -NFS4ERR_STALE_CLIENTID) - nfs4_schedule_state_recovery(clp); - goto out; + goto restart_loop; + goto out_error; } - goto restart_loop; } - spin_unlock(&clp->cl_lock); out: - smp_mb__before_clear_bit(); - clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state); - smp_mb__after_clear_bit(); + set_bit(NFS4CLNT_OK, &clp->cl_state); + up_write(&clp->cl_sem); wake_up_all(&clp->cl_waitq); rpc_wake_up(&clp->cl_rpcwaitq); + nfs4_put_client(clp); return 0; out_error: - printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n", - NIPQUAD(clp->cl_addr.s_addr)); - up_read(&clp->cl_sem); + printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", + NIPQUAD(clp->cl_addr.s_addr), -status); goto out; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 7bab91858ee2..2dc6d786385c 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -507,8 +507,6 @@ struct idmap; enum nfs4_client_state { NFS4CLNT_OK = 0, - NFS4CLNT_NEW, - NFS4CLNT_SETUP_STATE, }; /* @@ -520,7 +518,6 @@ struct nfs4_client { u64 cl_clientid; /* constant */ nfs4_verifier cl_confirm; unsigned long cl_state; - long cl_generation; u32 cl_lockowner_id; @@ -573,9 +570,7 @@ struct nfs4_state_owner { u32 so_id; /* 32-bit identifier, unique */ struct semaphore so_sema; u32 so_seqid; /* protected by so_sema */ - unsigned int so_flags; /* protected by so_sema */ atomic_t so_count; - long so_generation; struct rpc_cred *so_cred; /* Associated cred */ struct list_head so_states; @@ -643,8 +638,8 @@ extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_proc_renew(struct nfs4_client *); -extern int _nfs4_do_close(struct inode *, struct nfs4_state *); -extern int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode); +extern int nfs4_do_close(struct inode *, struct nfs4_state *); +extern int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode); 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 int nfs4_open_revalidate(struct inode *, struct dentry *, int); @@ -660,6 +655,7 @@ 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 void nfs4_put_client(struct nfs4_client *clp); +extern int nfs4_init_client(struct nfs4_client *clp); extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); -- cgit v1.2.3 From ed373838fc745d62890844aeffa8275e0f75e90f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Aug 2004 11:17:43 -0400 Subject: NFSv4: On server reboot we need to recover byte-range locks. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 53 +++++++++++++++++++----------- fs/nfs/nfs4state.c | 88 ++++++++++++++++++++++++++++++++++++++++++-------- include/linux/nfs_fs.h | 10 +++--- 3 files changed, 115 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 906cde77d088..941347732930 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -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); 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_state *state, int cmd, struct file_lock * 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_state *state, int cmd, struct file_lock .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_state *state, int cmd, struct file_lock /* 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 --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index d2326b7cf9bc..60a52170a07f 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -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 */ -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 *state, fl_owner_t fl_owner) 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); @@ -538,6 +538,22 @@ nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) 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 * requests. @@ -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. */ -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, struct file_lock *request, struct nfs4_lo * */ 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, struct file_lock *request, struct nfs4_lo 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_state *lsp) { 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_client *clp) 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 --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 2dc6d786385c..f72279a6a36c 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -591,7 +591,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; @@ -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 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 *); @@ -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_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); -- cgit v1.2.3 From af0ccd94c660c7236d44ed3b2cda6c395b6e3113 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Aug 2004 11:18:15 -0400 Subject: NFSv4: Prime SETCLIENTID call for the delegation callback info. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 41 ++++++++++++++++++++++++++--------------- fs/nfs/nfs4xdr.c | 33 +++++++++++++++++---------------- include/linux/nfs_xdr.h | 8 +++++--- 3 files changed, 48 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 941347732930..257f3c6597d1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1996,13 +1996,15 @@ nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa return 1; } -int -nfs4_proc_setclientid(struct nfs4_client *clp, - u32 program, unsigned short port) +int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port) { - u32 *p; - struct nfs4_setclientid setclientid; - struct timespec tv; + static nfs4_verifier sc_verifier; + static int initialized; + + struct nfs4_setclientid setclientid = { + .sc_verifier = &sc_verifier, + .sc_prog = program, + }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], .rpc_argp = &setclientid, @@ -2010,15 +2012,24 @@ nfs4_proc_setclientid(struct nfs4_client *clp, .rpc_cred = clp->cl_cred, }; - tv = CURRENT_TIME; - p = (u32*)setclientid.sc_verifier.data; - *p++ = (u32)tv.tv_sec; - *p = (u32)tv.tv_nsec; - setclientid.sc_name = clp->cl_ipaddr; - sprintf(setclientid.sc_netid, "tcp"); - sprintf(setclientid.sc_uaddr, "%s.%d.%d", clp->cl_ipaddr, port >> 8, port & 255); - setclientid.sc_prog = htonl(program); - setclientid.sc_cb_ident = 0; + if (!initialized) { + struct timespec boot_time; + u32 *p; + + initialized = 1; + boot_time = CURRENT_TIME; + p = (u32*)sc_verifier.data; + *p++ = htonl((u32)boot_time.tv_sec); + *p = htonl((u32)boot_time.tv_nsec); + } + setclientid.sc_name_len = scnprintf(setclientid.sc_name, + sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u", + clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr)); + setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, + sizeof(setclientid.sc_netid), "tcp"); + setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr, + sizeof(setclientid.sc_uaddr), "%s.%d.%d", + clp->cl_ipaddr, port >> 8, port & 255); return rpc_call_sync(clp->cl_rpcclient, &msg, 0); } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index a8d4e4c5224e..4988e501603a 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -404,6 +404,15 @@ struct compound_hdr { BUG_ON(!p); \ } while (0) +static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) +{ + uint32_t *p; + + p = xdr_reserve_space(xdr, 4 + len); + BUG_ON(p == NULL); + xdr_encode_opaque(p, str, len); +} + static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) { uint32_t *p; @@ -1047,26 +1056,18 @@ static int encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs * static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid) { - uint32_t total_len; - uint32_t len1, len2, len3; uint32_t *p; - len1 = strlen(setclientid->sc_name); - len2 = strlen(setclientid->sc_netid); - len3 = strlen(setclientid->sc_uaddr); - total_len = XDR_QUADLEN(len1) + XDR_QUADLEN(len2) + XDR_QUADLEN(len3); - total_len = (total_len << 2) + 24 + sizeof(setclientid->sc_verifier.data); - - RESERVE_SPACE(total_len); + RESERVE_SPACE(4 + sizeof(setclientid->sc_verifier->data)); WRITE32(OP_SETCLIENTID); - WRITEMEM(setclientid->sc_verifier.data, sizeof(setclientid->sc_verifier.data)); - WRITE32(len1); - WRITEMEM(setclientid->sc_name, len1); + WRITEMEM(setclientid->sc_verifier->data, sizeof(setclientid->sc_verifier->data)); + + encode_string(xdr, setclientid->sc_name_len, setclientid->sc_name); + RESERVE_SPACE(4); WRITE32(setclientid->sc_prog); - WRITE32(len2); - WRITEMEM(setclientid->sc_netid, len2); - WRITE32(len3); - WRITEMEM(setclientid->sc_uaddr, len3); + encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid); + encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr); + RESERVE_SPACE(4); WRITE32(setclientid->sc_cb_ident); return 0; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 2c7f4617e650..427ec6708d20 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -597,13 +597,15 @@ struct nfs4_rename_res { }; struct nfs4_setclientid { - nfs4_verifier sc_verifier; /* request */ - char * sc_name; /* request */ + const nfs4_verifier * sc_verifier; /* request */ + unsigned int sc_name_len; + char sc_name[32]; /* request */ u32 sc_prog; /* request */ + unsigned int sc_netid_len; char sc_netid[4]; /* request */ + unsigned int sc_uaddr_len; char sc_uaddr[24]; /* request */ u32 sc_cb_ident; /* request */ - struct nfs4_client * sc_state; /* response */ }; struct nfs4_statfs_arg { -- cgit v1.2.3 From 6caf69feb23a522921b5b12f572a94334183bc9f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Aug 2004 11:19:03 -0400 Subject: NFSv2/v3/v4: Place NFS nfs_page shared data into a single structure that hangs off filp->private_data. As a side effect, this also cleans up the NFSv4 private file state info. Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 49 +++++++++------- fs/nfs/file.c | 10 ++-- fs/nfs/inode.c | 127 ++++++++++++++++++++++++++++++----------- fs/nfs/nfs3proc.c | 50 ++-------------- fs/nfs/nfs4proc.c | 146 ++++++++++++----------------------------------- fs/nfs/nfs4state.c | 2 +- fs/nfs/pagelist.c | 47 +++++++++------ fs/nfs/proc.c | 46 ++------------- fs/nfs/read.c | 64 +++++++++++++-------- fs/nfs/write.c | 102 +++++++++++++++++---------------- include/linux/nfs_fs.h | 43 ++++++++++---- include/linux/nfs_page.h | 29 +++------- include/linux/nfs_xdr.h | 8 +-- 13 files changed, 340 insertions(+), 383 deletions(-) (limited to 'include') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index e4e228ea968a..080e3e7b6c07 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -110,7 +110,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty) * nfs_direct_read_seg - Read in one iov segment. Generate separate * read RPCs for each "rsize" bytes. * @inode: target inode - * @file: target file (may be NULL) + * @ctx: target file open context * user_addr: starting address of this segment of user's buffer * count: size of this segment * file_offset: offset in file to begin the operation @@ -118,7 +118,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty) * nr_pages: size of pages array */ static int -nfs_direct_read_seg(struct inode *inode, struct file *file, +nfs_direct_read_seg(struct inode *inode, struct nfs_open_context *ctx, unsigned long user_addr, size_t count, loff_t file_offset, struct page **pages, int nr_pages) { @@ -127,9 +127,11 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, int curpage = 0; struct nfs_read_data rdata = { .inode = inode, + .cred = ctx->cred, .args = { .fh = NFS_FH(inode), - .lockowner = current->files, + .lockowner = ctx->lockowner, + .state = ctx->state, }, .res = { .fattr = &rdata.fattr, @@ -151,7 +153,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, user_addr + tot_bytes, rdata.args.pgbase, curpage); lock_kernel(); - result = NFS_PROTO(inode)->read(&rdata, file); + result = NFS_PROTO(inode)->read(&rdata); unlock_kernel(); if (result <= 0) { @@ -183,7 +185,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, * nfs_direct_read - For each iov segment, map the user's buffer * then generate read RPCs. * @inode: target inode - * @file: target file (may be NULL) + * @ctx: target file open context * @iov: array of vectors that define I/O buffer * file_offset: offset in file to begin the operation * nr_segs: size of iovec array @@ -193,7 +195,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, * server. */ static ssize_t -nfs_direct_read(struct inode *inode, struct file *file, +nfs_direct_read(struct inode *inode, struct nfs_open_context *ctx, const struct iovec *iov, loff_t file_offset, unsigned long nr_segs) { @@ -216,7 +218,7 @@ nfs_direct_read(struct inode *inode, struct file *file, return page_count; } - result = nfs_direct_read_seg(inode, file, user_addr, size, + result = nfs_direct_read_seg(inode, ctx, user_addr, size, file_offset, pages, page_count); nfs_free_user_pages(pages, page_count, 1); @@ -239,7 +241,7 @@ nfs_direct_read(struct inode *inode, struct file *file, * nfs_direct_write_seg - Write out one iov segment. Generate separate * write RPCs for each "wsize" bytes, then commit. * @inode: target inode - * @file: target file (may be NULL) + * @ctx: target file open context * user_addr: starting address of this segment of user's buffer * count: size of this segment * file_offset: offset in file to begin the operation @@ -247,7 +249,7 @@ nfs_direct_read(struct inode *inode, struct file *file, * nr_pages: size of pages array */ static int -nfs_direct_write_seg(struct inode *inode, struct file *file, +nfs_direct_write_seg(struct inode *inode, struct nfs_open_context *ctx, unsigned long user_addr, size_t count, loff_t file_offset, struct page **pages, int nr_pages) { @@ -257,9 +259,11 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, struct nfs_writeverf first_verf; struct nfs_write_data wdata = { .inode = inode, + .cred = ctx->cred, .args = { .fh = NFS_FH(inode), - .lockowner = current->files, + .lockowner = ctx->lockowner, + .state = ctx->state, }, .res = { .fattr = &wdata.fattr, @@ -290,7 +294,7 @@ retry: user_addr + tot_bytes, wdata.args.pgbase, curpage); lock_kernel(); - result = NFS_PROTO(inode)->write(&wdata, file); + result = NFS_PROTO(inode)->write(&wdata); unlock_kernel(); if (result <= 0) { @@ -325,7 +329,7 @@ retry: wdata.args.offset = file_offset; lock_kernel(); - result = NFS_PROTO(inode)->commit(&wdata, file); + result = NFS_PROTO(inode)->commit(&wdata); unlock_kernel(); if (result < 0 || memcmp(&first_verf.verifier, @@ -349,7 +353,7 @@ sync_retry: * nfs_direct_write - For each iov segment, map the user's buffer * then generate write and commit RPCs. * @inode: target inode - * @file: target file (may be NULL) + * @ctx: target file open context * @iov: array of vectors that define I/O buffer * file_offset: offset in file to begin the operation * nr_segs: size of iovec array @@ -358,8 +362,7 @@ sync_retry: * that non-direct readers might access, so they will pick up these * writes immediately. */ -static ssize_t -nfs_direct_write(struct inode *inode, struct file *file, +static int nfs_direct_write(struct inode *inode, struct nfs_open_context *ctx, const struct iovec *iov, loff_t file_offset, unsigned long nr_segs) { @@ -382,7 +385,7 @@ nfs_direct_write(struct inode *inode, struct file *file, return page_count; } - result = nfs_direct_write_seg(inode, file, user_addr, size, + result = nfs_direct_write_seg(inode, ctx, user_addr, size, file_offset, pages, page_count); nfs_free_user_pages(pages, page_count, 0); @@ -414,6 +417,7 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, { ssize_t result = -EINVAL; struct file *file = iocb->ki_filp; + struct nfs_open_context *ctx; struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; @@ -423,19 +427,20 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, if (!is_sync_kiocb(iocb)) return result; + ctx = (struct nfs_open_context *)file->private_data; switch (rw) { case READ: dprintk("NFS: direct_IO(read) (%s) off/no(%Lu/%lu)\n", dentry->d_name.name, file_offset, nr_segs); - result = nfs_direct_read(inode, file, iov, + result = nfs_direct_read(inode, ctx, iov, file_offset, nr_segs); break; case WRITE: dprintk("NFS: direct_IO(write) (%s) off/no(%Lu/%lu)\n", dentry->d_name.name, file_offset, nr_segs); - result = nfs_direct_write(inode, file, iov, + result = nfs_direct_write(inode, ctx, iov, file_offset, nr_segs); break; default: @@ -471,6 +476,8 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t ssize_t retval = -EINVAL; loff_t *ppos = &iocb->ki_pos; struct file *file = iocb->ki_filp; + struct nfs_open_context *ctx = + (struct nfs_open_context *) file->private_data; struct dentry *dentry = file->f_dentry; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; @@ -502,7 +509,7 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t goto out; } - retval = nfs_direct_read(inode, file, &iov, pos, 1); + retval = nfs_direct_read(inode, ctx, &iov, pos, 1); if (retval > 0) *ppos = pos + retval; @@ -542,6 +549,8 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t *ppos = &iocb->ki_pos; unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; struct file *file = iocb->ki_filp; + struct nfs_open_context *ctx = + (struct nfs_open_context *) file->private_data; struct dentry *dentry = file->f_dentry; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; @@ -589,7 +598,7 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, goto out; } - retval = nfs_direct_write(inode, file, &iov, pos, 1); + retval = nfs_direct_write(inode, ctx, &iov, pos, 1); if (mapping->nrpages) invalidate_inode_pages2(mapping); if (retval > 0) diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 1b0c4ae4556e..0bd5913f690b 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -113,6 +113,7 @@ nfs_file_release(struct inode *inode, struct file *filp) static int nfs_file_flush(struct file *file) { + struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct inode *inode = file->f_dentry->d_inode; int status; @@ -124,8 +125,8 @@ nfs_file_flush(struct file *file) /* Ensure that data+attribute caches are up to date after close() */ status = nfs_wb_all(inode); if (!status) { - status = file->f_error; - file->f_error = 0; + status = ctx->error; + ctx->error = 0; if (!status) __nfs_revalidate_inode(NFS_SERVER(inode), inode); } @@ -197,6 +198,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) static int nfs_fsync(struct file *file, struct dentry *dentry, int datasync) { + struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct inode *inode = dentry->d_inode; int status; @@ -205,8 +207,8 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync) lock_kernel(); status = nfs_wb_all(inode); if (!status) { - status = file->f_error; - file->f_error = 0; + status = ctx->error; + ctx->error = 0; } unlock_kernel(); return status; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 98f04ecec859..6bc28939de68 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -121,8 +121,9 @@ nfs_delete_inode(struct inode * inode) { dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); + nfs_wb_all(inode); /* - * The following can never actually happen... + * The following should never happen... */ if (nfs_have_writebacks(inode)) { printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); @@ -139,10 +140,10 @@ static void nfs_clear_inode(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); - struct rpc_cred *cred = nfsi->mm_cred; + struct rpc_cred *cred; - if (cred) - put_rpccred(cred); + nfs_wb_all(inode); + BUG_ON (!list_empty(&nfsi->open_files)); cred = nfsi->cache_access.cred; if (cred) put_rpccred(cred); @@ -812,53 +813,114 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) return err; } +struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred) +{ + struct nfs_open_context *ctx; + + ctx = (struct nfs_open_context *)kmalloc(sizeof(*ctx), GFP_KERNEL); + if (ctx != NULL) { + atomic_set(&ctx->count, 1); + ctx->dentry = dget(dentry); + ctx->cred = get_rpccred(cred); + ctx->state = NULL; + ctx->lockowner = current->files; + ctx->error = 0; + init_waitqueue_head(&ctx->waitq); + } + return ctx; +} + +struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) +{ + if (ctx != NULL) + atomic_inc(&ctx->count); + return ctx; +} + +void put_nfs_open_context(struct nfs_open_context *ctx) +{ + if (atomic_dec_and_test(&ctx->count)) { + if (ctx->state != NULL) + nfs4_close_state(ctx->state, ctx->mode); + if (ctx->cred != NULL) + put_rpccred(ctx->cred); + dput(ctx->dentry); + kfree(ctx); + } +} + /* * Ensure that mmap has a recent RPC credential for use when writing out * shared pages */ -void -nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred) +void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) { - struct rpc_cred **p = &NFS_I(inode)->mm_cred, - *oldcred = *p; + struct inode *inode = filp->f_dentry->d_inode; + struct nfs_inode *nfsi = NFS_I(inode); - *p = get_rpccred(cred); - if (oldcred) - put_rpccred(oldcred); + filp->private_data = get_nfs_open_context(ctx); + spin_lock(&inode->i_lock); + list_add(&ctx->list, &nfsi->open_files); + spin_unlock(&inode->i_lock); +} + +struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_open_context *pos, *ctx = NULL; + + spin_lock(&inode->i_lock); + list_for_each_entry(pos, &nfsi->open_files, list) { + if ((pos->mode & mode) == mode) { + ctx = get_nfs_open_context(pos); + break; + } + } + spin_unlock(&inode->i_lock); + return ctx; +} + +void nfs_file_clear_open_context(struct file *filp) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct nfs_open_context *ctx = (struct nfs_open_context *)filp->private_data; + + if (ctx) { + filp->private_data = NULL; + spin_lock(&inode->i_lock); + list_del(&ctx->list); + spin_unlock(&inode->i_lock); + put_nfs_open_context(ctx); + } } /* - * These are probably going to contain hooks for - * allocating and releasing RPC credentials for - * the file. I'll have to think about Tronds patch - * a bit more.. + * These allocate and release file read/write context information. */ int nfs_open(struct inode *inode, struct file *filp) { - struct rpc_auth *auth; + struct nfs_open_context *ctx; struct rpc_cred *cred; - auth = NFS_CLIENT(inode)->cl_auth; - cred = rpcauth_lookupcred(auth, 0); - filp->private_data = cred; - if ((filp->f_mode & FMODE_WRITE) != 0) { - nfs_set_mmcred(inode, cred); + if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL) + return -ENOMEM; + ctx = alloc_nfs_open_context(filp->f_dentry, cred); + put_rpccred(cred); + if (ctx == NULL) + return -ENOMEM; + ctx->mode = filp->f_mode; + nfs_file_set_open_context(filp, ctx); + put_nfs_open_context(ctx); + if ((filp->f_mode & FMODE_WRITE) != 0) nfs_begin_data_update(inode); - } return 0; } int nfs_release(struct inode *inode, struct file *filp) { - struct rpc_cred *cred; - - lock_kernel(); if ((filp->f_mode & FMODE_WRITE) != 0) nfs_end_data_update(inode); - cred = nfs_file_cred(filp); - if (cred) - put_rpccred(cred); - unlock_kernel(); + nfs_file_clear_open_context(filp); return 0; } @@ -1397,6 +1459,9 @@ static void nfs4_clear_inode(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); + /* First call standard NFS clear_inode() code */ + nfs_clear_inode(inode); + /* Now clear out any remaining state */ while (!list_empty(&nfsi->open_states)) { struct nfs4_state *state; @@ -1411,8 +1476,6 @@ static void nfs4_clear_inode(struct inode *inode) BUG_ON(atomic_read(&state->count) != 1); nfs4_close_state(state, state->state); } - /* Now call standard NFS clear_inode() code */ - nfs_clear_inode(inode); } @@ -1717,7 +1780,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb) if (!nfsi) return NULL; nfsi->flags = 0; - nfsi->mm_cred = NULL; nfs4_zero_state(nfsi); return &nfsi->vfs_inode; } @@ -1737,6 +1799,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) spin_lock_init(&nfsi->req_lock); INIT_LIST_HEAD(&nfsi->dirty); INIT_LIST_HEAD(&nfsi->commit); + INIT_LIST_HEAD(&nfsi->open_files); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); atomic_set(&nfsi->data_updates, 0); nfsi->ndirty = 0; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 0bf9af7678b7..bbceb6d3f2ce 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -68,18 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task) return 1; } -static struct rpc_cred * -nfs_cred(struct inode *inode, struct file *filp) -{ - struct rpc_cred *cred = NULL; - - if (filp) - cred = (struct rpc_cred *)filp->private_data; - if (!cred) - cred = NFS_I(inode)->mm_cred; - return cred; -} - /* * Bare-bones access to getattr: this is for nfs_read_super. */ @@ -233,8 +221,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page) return status; } -static int -nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp) +static int nfs3_proc_read(struct nfs_read_data *rdata) { int flags = rdata->flags; struct inode * inode = rdata->inode; @@ -243,13 +230,13 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp) .rpc_proc = &nfs3_procedures[NFS3PROC_READ], .rpc_argp = &rdata->args, .rpc_resp = &rdata->res, + .rpc_cred = rdata->cred, }; int status; dprintk("NFS call read %d @ %Ld\n", rdata->args.count, (long long) rdata->args.offset); fattr->valid = 0; - msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); if (status >= 0) nfs_refresh_inode(inode, fattr); @@ -257,8 +244,7 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp) return status; } -static int -nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) +static int nfs3_proc_write(struct nfs_write_data *wdata) { int rpcflags = wdata->flags; struct inode * inode = wdata->inode; @@ -267,13 +253,13 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) .rpc_proc = &nfs3_procedures[NFS3PROC_WRITE], .rpc_argp = &wdata->args, .rpc_resp = &wdata->res, + .rpc_cred = wdata->cred, }; int status; dprintk("NFS call write %d @ %Ld\n", wdata->args.count, (long long) wdata->args.offset); fattr->valid = 0; - msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags); if (status >= 0) nfs_refresh_inode(inode, fattr); @@ -281,8 +267,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) return status < 0? status : wdata->res.count; } -static int -nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp) +static int nfs3_proc_commit(struct nfs_write_data *cdata) { struct inode * inode = cdata->inode; struct nfs_fattr * fattr = cdata->res.fattr; @@ -290,13 +275,13 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp) .rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT], .rpc_argp = &cdata->args, .rpc_resp = &cdata->res, + .rpc_cred = cdata->cred, }; int status; dprintk("NFS call commit %d @ %Ld\n", cdata->args.count, (long long) cdata->args.offset); fattr->valid = 0; - msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); if (status >= 0) nfs_refresh_inode(inode, fattr); @@ -840,27 +825,6 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, int how) rpc_call_setup(task, &msg, 0); } -/* - * Set up the nfspage struct with the right credentials - */ -void -nfs3_request_init(struct nfs_page *req, struct file *filp) -{ - req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp)); -} - -static int -nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) -{ - if (req->wb_file != filp) - return 0; - if (req->wb_page != page) - return 0; - if (req->wb_cred != nfs_file_cred(filp)) - return 0; - return 1; -} - static int nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) { @@ -900,7 +864,5 @@ struct nfs_rpc_ops nfs_v3_clientops = { .commit_setup = nfs3_proc_commit_setup, .file_open = nfs_open, .file_release = nfs_release, - .request_init = nfs3_request_init, - .request_compatible = nfs3_request_compatible, .lock = nfs3_proc_lock, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 257f3c6597d1..7b92c0711015 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -975,7 +975,7 @@ static int nfs4_proc_readlink(struct inode *inode, struct page *page) return err; } -static int _nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) +static int _nfs4_proc_read(struct nfs_read_data *rdata) { int flags = rdata->flags; struct inode *inode = rdata->inode; @@ -985,6 +985,7 @@ static int _nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ], .rpc_argp = &rdata->args, .rpc_resp = &rdata->res, + .rpc_cred = rdata->cred, }; unsigned long timestamp = jiffies; int status; @@ -992,19 +993,6 @@ static int _nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) dprintk("NFS call read %d @ %Ld\n", rdata->args.count, (long long) rdata->args.offset); - /* - * Try first to use O_RDONLY, then O_RDWR stateid. - */ - if (filp) { - struct nfs4_state *state; - state = (struct nfs4_state *)filp->private_data; - rdata->args.state = state; - msg.rpc_cred = state->owner->so_cred; - } else { - rdata->args.state = NULL; - msg.rpc_cred = NFS_I(inode)->mm_cred; - } - fattr->valid = 0; status = rpc_call_sync(server->client, &msg, flags); if (!status) @@ -1013,19 +1001,19 @@ static int _nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) return status; } -static int nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) +static int nfs4_proc_read(struct nfs_read_data *rdata) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(NFS_SERVER(rdata->inode), - _nfs4_proc_read(rdata, filp), + _nfs4_proc_read(rdata), &exception); } while (exception.retry); return err; } -static int _nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) +static int _nfs4_proc_write(struct nfs_write_data *wdata) { int rpcflags = wdata->flags; struct inode *inode = wdata->inode; @@ -1035,44 +1023,32 @@ static int _nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE], .rpc_argp = &wdata->args, .rpc_resp = &wdata->res, + .rpc_cred = wdata->cred, }; int status; dprintk("NFS call write %d @ %Ld\n", wdata->args.count, (long long) wdata->args.offset); - /* - * Try first to use O_WRONLY, then O_RDWR stateid. - */ - if (filp) { - struct nfs4_state *state; - state = (struct nfs4_state *)filp->private_data; - wdata->args.state = state; - msg.rpc_cred = state->owner->so_cred; - } else { - wdata->args.state = NULL; - msg.rpc_cred = NFS_I(inode)->mm_cred; - } - fattr->valid = 0; status = rpc_call_sync(server->client, &msg, rpcflags); dprintk("NFS reply write: %d\n", status); return status; } -static int nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) +static int nfs4_proc_write(struct nfs_write_data *wdata) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(NFS_SERVER(wdata->inode), - _nfs4_proc_write(wdata, filp), + _nfs4_proc_write(wdata), &exception); } while (exception.retry); return err; } -static int _nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp) +static int _nfs4_proc_commit(struct nfs_write_data *cdata) { struct inode *inode = cdata->inode; struct nfs_fattr *fattr = cdata->res.fattr; @@ -1081,33 +1057,26 @@ static int _nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp) .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT], .rpc_argp = &cdata->args, .rpc_resp = &cdata->res, + .rpc_cred = cdata->cred, }; int status; dprintk("NFS call commit %d @ %Ld\n", cdata->args.count, (long long) cdata->args.offset); - /* - * Try first to use O_WRONLY, then O_RDWR stateid. - */ - if (filp) - msg.rpc_cred = ((struct nfs4_state *)filp->private_data)->owner->so_cred; - else - msg.rpc_cred = NFS_I(inode)->mm_cred; - fattr->valid = 0; status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply commit: %d\n", status); return status; } -static int nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp) +static int nfs4_proc_commit(struct nfs_write_data *cdata) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(NFS_SERVER(cdata->inode), - _nfs4_proc_commit(cdata, filp), + _nfs4_proc_commit(cdata), &exception); } while (exception.retry); return err; @@ -1797,8 +1766,10 @@ static int nfs4_proc_file_open(struct inode *inode, struct file *filp) { struct dentry *dentry = filp->f_dentry; - struct nfs4_state *state; + struct nfs_open_context *ctx; + struct nfs4_state *state = NULL; struct rpc_cred *cred; + int status = -ENOMEM; dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n", (int)dentry->d_parent->d_name.len, @@ -1808,21 +1779,28 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) /* Find our open stateid */ cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - state = nfs4_find_state(inode, cred, filp->f_mode); + if (unlikely(cred == NULL)) + return -ENOMEM; + ctx = alloc_nfs_open_context(dentry, cred); put_rpccred(cred); - if (state == NULL) { - printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); - return -EIO; /* ERACE actually */ - } + if (unlikely(ctx == NULL)) + return -ENOMEM; + status = -EIO; /* ERACE actually */ + state = nfs4_find_state(inode, cred, filp->f_mode); + if (unlikely(state == NULL)) + goto no_state; + ctx->state = state; nfs4_close_state(state, filp->f_mode); - if (filp->f_mode & FMODE_WRITE) { - lock_kernel(); - nfs_set_mmcred(inode, state->owner->so_cred); + ctx->mode = filp->f_mode; + nfs_file_set_open_context(filp, ctx); + put_nfs_open_context(ctx); + if (filp->f_mode & FMODE_WRITE) nfs_begin_data_update(inode); - unlock_kernel(); - } - filp->private_data = state; return 0; +no_state: + printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); + put_nfs_open_context(ctx); + return status; } /* @@ -1831,37 +1809,12 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) static int nfs4_proc_file_release(struct inode *inode, struct file *filp) { - struct nfs4_state *state = (struct nfs4_state *)filp->private_data; - - if (state) - nfs4_close_state(state, filp->f_mode); - if (filp->f_mode & FMODE_WRITE) { - lock_kernel(); + if (filp->f_mode & FMODE_WRITE) nfs_end_data_update(inode); - unlock_kernel(); - } + nfs_file_clear_open_context(filp); return 0; } -/* - * Set up the nfspage struct with the right state info and credentials - */ -static void -nfs4_request_init(struct nfs_page *req, struct file *filp) -{ - struct nfs4_state *state; - - if (!filp) { - req->wb_cred = get_rpccred(NFS_I(req->wb_inode)->mm_cred); - req->wb_state = NULL; - return; - } - state = (struct nfs4_state *)filp->private_data; - req->wb_state = state; - req->wb_cred = get_rpccred(state->owner->so_cred); - req->wb_lockowner = current->files; -} - static int nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) { @@ -1974,28 +1927,6 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_ return nfs4_map_errors(ret); } - -static int -nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) -{ - struct nfs4_state *state = NULL; - struct rpc_cred *cred = NULL; - - if (req->wb_file != filp) - return 0; - if (req->wb_page != page) - return 0; - state = (struct nfs4_state *)filp->private_data; - if (req->wb_state != state) - return 0; - if (req->wb_lockowner != current->files) - return 0; - cred = state->owner->so_cred; - if (req->wb_cred != cred) - return 0; - return 1; -} - int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port) { static nfs4_verifier sc_verifier; @@ -2353,13 +2284,14 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock * static int nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) { + struct nfs_open_context *ctx; struct nfs4_state *state; unsigned long timeout = NFS4_LOCK_MINTIMEOUT; int status; /* verify open state */ - state = (struct nfs4_state *)filp->private_data; - BUG_ON(!state); + ctx = (struct nfs_open_context *)filp->private_data; + state = ctx->state; if (request->fl_start < 0 || request->fl_end < 0) return -EINVAL; @@ -2419,8 +2351,6 @@ struct nfs_rpc_ops nfs_v4_clientops = { .commit_setup = nfs4_proc_commit_setup, .file_open = nfs4_proc_file_open, .file_release = nfs4_proc_file_release, - .request_init = nfs4_request_init, - .request_compatible = nfs4_request_compatible, .lock = nfs4_proc_lock, }; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 60a52170a07f..0b67691034fc 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -728,7 +728,7 @@ static int nfs4_reclaim_locks(struct nfs4_state *state) 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) + if (((struct nfs_open_context *)fl->fl_file->private_data)->state != state) continue; status = nfs4_lock_reclaim(state, fl); if (status >= 0) diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index dc496624345f..4f1ba723848d 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -31,7 +31,6 @@ nfs_page_alloc(void) if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->wb_list); - init_waitqueue_head(&p->wb_wait); } return p; } @@ -57,7 +56,7 @@ nfs_page_free(struct nfs_page *p) * User should ensure it is safe to sleep in this function. */ struct nfs_page * -nfs_create_request(struct file *file, struct inode *inode, +nfs_create_request(struct nfs_open_context *ctx, struct inode *inode, struct page *page, unsigned int offset, unsigned int count) { @@ -89,33 +88,38 @@ nfs_create_request(struct file *file, struct inode *inode, req->wb_offset = offset; req->wb_pgbase = offset; req->wb_bytes = count; - req->wb_inode = inode; atomic_set(&req->wb_count, 1); - server->rpc_ops->request_init(req, file); + req->wb_context = get_nfs_open_context(ctx); return req; } +/** + * nfs_unlock_request - Unlock request and wake up sleepers. + * @req: + */ +void nfs_unlock_request(struct nfs_page *req) +{ + if (!NFS_WBACK_BUSY(req)) { + printk(KERN_ERR "NFS: Invalid unlock attempted\n"); + BUG(); + } + smp_mb__before_clear_bit(); + clear_bit(PG_BUSY, &req->wb_flags); + smp_mb__after_clear_bit(); + wake_up_all(&req->wb_context->waitq); + nfs_release_request(req); +} + /** * nfs_clear_request - Free up all resources allocated to the request * @req: * - * Release all resources associated with a write request after it + * Release page resources associated with a write request after it * has completed. */ void nfs_clear_request(struct nfs_page *req) { - if (req->wb_state) - req->wb_state = NULL; - /* Release struct file or cached credential */ - if (req->wb_file) { - fput(req->wb_file); - req->wb_file = NULL; - } - if (req->wb_cred) { - put_rpccred(req->wb_cred); - req->wb_cred = NULL; - } if (req->wb_page) { page_cache_release(req->wb_page); req->wb_page = NULL; @@ -142,6 +146,7 @@ nfs_release_request(struct nfs_page *req) /* Release struct file or cached credential */ nfs_clear_request(req); + put_nfs_open_context(req->wb_context); nfs_page_free(req); } @@ -185,12 +190,12 @@ nfs_list_add_request(struct nfs_page *req, struct list_head *head) int nfs_wait_on_request(struct nfs_page *req) { - struct inode *inode = req->wb_inode; + struct inode *inode = req->wb_context->dentry->d_inode; struct rpc_clnt *clnt = NFS_CLIENT(inode); if (!NFS_WBACK_BUSY(req)) return 0; - return nfs_wait_event(clnt, req->wb_wait, !NFS_WBACK_BUSY(req)); + return nfs_wait_event(clnt, req->wb_context->waitq, !NFS_WBACK_BUSY(req)); } /** @@ -215,7 +220,11 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, req = nfs_list_entry(head->next); if (prev) { - if (req->wb_cred != prev->wb_cred) + if (req->wb_context->cred != prev->wb_context->cred) + break; + if (req->wb_context->lockowner != prev->wb_context->lockowner) + break; + if (req->wb_context->state != prev->wb_context->state) break; if (req->wb_index != (prev->wb_index + 1)) break; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 279ddad6ee4e..2e2fff50f25b 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -49,18 +49,6 @@ extern struct rpc_procinfo nfs_procedures[]; -static struct rpc_cred * -nfs_cred(struct inode *inode, struct file *filp) -{ - struct rpc_cred *cred = NULL; - - if (filp) - cred = (struct rpc_cred *)filp->private_data; - if (!cred) - cred = NFS_I(inode)->mm_cred; - return cred; -} - /* * Bare-bones access to getattr: this is for nfs_read_super. */ @@ -167,8 +155,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page) return status; } -static int -nfs_proc_read(struct nfs_read_data *rdata, struct file *filp) +static int nfs_proc_read(struct nfs_read_data *rdata) { int flags = rdata->flags; struct inode * inode = rdata->inode; @@ -177,15 +164,14 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp) .rpc_proc = &nfs_procedures[NFSPROC_READ], .rpc_argp = &rdata->args, .rpc_resp = &rdata->res, + .rpc_cred = rdata->cred, }; int status; dprintk("NFS call read %d @ %Ld\n", rdata->args.count, (long long) rdata->args.offset); fattr->valid = 0; - msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); - if (status >= 0) { nfs_refresh_inode(inode, fattr); /* Emulate the eof flag, which isn't normally needed in NFSv2 @@ -198,8 +184,7 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp) return status; } -static int -nfs_proc_write(struct nfs_write_data *wdata, struct file *filp) +static int nfs_proc_write(struct nfs_write_data *wdata) { int flags = wdata->flags; struct inode * inode = wdata->inode; @@ -208,13 +193,13 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp) .rpc_proc = &nfs_procedures[NFSPROC_WRITE], .rpc_argp = &wdata->args, .rpc_resp = &wdata->res, + .rpc_cred = wdata->cred, }; int status; dprintk("NFS call write %d @ %Ld\n", wdata->args.count, (long long) wdata->args.offset); fattr->valid = 0; - msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); if (status >= 0) { nfs_refresh_inode(inode, fattr); @@ -621,27 +606,6 @@ nfs_proc_commit_setup(struct nfs_write_data *data, int how) BUG(); } -/* - * Set up the nfspage struct with the right credentials - */ -static void -nfs_request_init(struct nfs_page *req, struct file *filp) -{ - req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp)); -} - -static int -nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) -{ - if (req->wb_file != filp) - return 0; - if (req->wb_page != page) - return 0; - if (req->wb_cred != nfs_file_cred(filp)) - return 0; - return 1; -} - static int nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) { @@ -682,7 +646,5 @@ struct nfs_rpc_ops nfs_v2_clientops = { .commit_setup = nfs_proc_commit_setup, .file_open = nfs_open, .file_release = nfs_release, - .request_init = nfs_request_init, - .request_compatible = nfs_request_compatible, .lock = nfs_proc_lock, }; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 2d32aba43fbb..54df1a48843d 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -91,8 +91,8 @@ int nfs_return_empty_page(struct page *page) /* * Read a page synchronously. */ -static int -nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) +static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode, + struct page *page) { unsigned int rsize = NFS_SERVER(inode)->rsize; unsigned int count = PAGE_CACHE_SIZE; @@ -105,10 +105,12 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) memset(rdata, 0, sizeof(*rdata)); rdata->flags = (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); + rdata->cred = ctx->cred; rdata->inode = inode; INIT_LIST_HEAD(&rdata->pages); rdata->args.fh = NFS_FH(inode); - rdata->args.lockowner = current->files; + rdata->args.lockowner = ctx->lockowner; + rdata->args.state = ctx->state; rdata->args.pages = &page; rdata->args.pgbase = 0UL; rdata->args.count = rsize; @@ -134,7 +136,7 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) rdata->args.count); lock_kernel(); - result = NFS_PROTO(inode)->read(rdata, file); + result = NFS_PROTO(inode)->read(rdata); unlock_kernel(); /* @@ -169,8 +171,8 @@ io_error: return result; } -static int -nfs_readpage_async(struct file *file, struct inode *inode, struct page *page) +static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, + struct page *page) { LIST_HEAD(one_request); struct nfs_page *new; @@ -179,7 +181,7 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page) len = nfs_page_length(inode, page); if (len == 0) return nfs_return_empty_page(page); - new = nfs_create_request(file, inode, page, 0, len); + new = nfs_create_request(ctx, inode, page, 0, len); if (IS_ERR(new)) { unlock_page(page); return PTR_ERR(new); @@ -202,8 +204,8 @@ static void nfs_readpage_release(struct nfs_page *req) nfs_unlock_request(req); dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", - req->wb_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_inode), + req->wb_context->dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); } @@ -217,16 +219,16 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, struct inode *inode; data->req = req; - data->inode = inode = req->wb_inode; - data->cred = req->wb_cred; + data->inode = inode = req->wb_context->dentry->d_inode; + data->cred = req->wb_context->cred; data->args.fh = NFS_FH(inode); data->args.offset = req_offset(req) + offset; data->args.pgbase = req->wb_pgbase + offset; data->args.pages = data->pagevec; data->args.count = count; - data->args.lockowner = req->wb_lockowner; - data->args.state = req->wb_state; + data->args.lockowner = req->wb_context->lockowner; + data->args.state = req->wb_context->state; data->res.fattr = &data->fattr; data->res.count = count; @@ -396,7 +398,7 @@ nfs_pagein_list(struct list_head *head, int rpages) while (!list_empty(head)) { pages += nfs_coalesce_requests(head, &one_request, rpages); req = nfs_list_entry(one_request.next); - error = nfs_pagein_one(&one_request, req->wb_inode); + error = nfs_pagein_one(&one_request, req->wb_context->dentry->d_inode); if (error < 0) break; } @@ -500,9 +502,9 @@ void nfs_readpage_result(struct rpc_task *task) * - The error flag is set for this page. This happens only when a * previous async read operation failed. */ -int -nfs_readpage(struct file *file, struct page *page) +int nfs_readpage(struct file *file, struct page *page) { + struct nfs_open_context *ctx; struct inode *inode = page->mapping->host; int error; @@ -519,25 +521,33 @@ nfs_readpage(struct file *file, struct page *page) if (error) goto out_error; + if (file == NULL) { + ctx = nfs_find_open_context(inode, FMODE_READ); + if (ctx == NULL) + return -EBADF; + } else + ctx = get_nfs_open_context((struct nfs_open_context *) + file->private_data); if (!IS_SYNC(inode)) { - error = nfs_readpage_async(file, inode, page); + error = nfs_readpage_async(ctx, inode, page); goto out; } - error = nfs_readpage_sync(file, inode, page); + error = nfs_readpage_sync(ctx, inode, page); if (error < 0 && IS_SWAPFILE(inode)) printk("Aiee.. nfs swap-in of page failed!\n"); out: + put_nfs_open_context(ctx); return error; out_error: unlock_page(page); - goto out; + return error; } struct nfs_readdesc { struct list_head *head; - struct file *filp; + struct nfs_open_context *ctx; }; static int @@ -552,7 +562,7 @@ readpage_async_filler(void *data, struct page *page) len = nfs_page_length(inode, page); if (len == 0) return nfs_return_empty_page(page); - new = nfs_create_request(desc->filp, inode, page, 0, len); + new = nfs_create_request(desc->ctx, inode, page, 0, len); if (IS_ERR(new)) { SetPageError(page); unlock_page(page); @@ -565,13 +575,11 @@ readpage_async_filler(void *data, struct page *page) return 0; } -int -nfs_readpages(struct file *filp, struct address_space *mapping, +int nfs_readpages(struct file *filp, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { LIST_HEAD(head); struct nfs_readdesc desc = { - .filp = filp, .head = &head, }; struct inode *inode = mapping->host; @@ -583,12 +591,20 @@ nfs_readpages(struct file *filp, struct address_space *mapping, (long long)NFS_FILEID(inode), nr_pages); + if (filp == NULL) { + desc.ctx = nfs_find_open_context(inode, FMODE_READ); + if (desc.ctx == NULL) + return -EBADF; + } else + desc.ctx = get_nfs_open_context((struct nfs_open_context *) + filp->private_data); ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); if (!list_empty(&head)) { int err = nfs_pagein_list(&head, server->rpages); if (!ret) ret = err; } + put_nfs_open_context(desc.ctx); return ret; } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index fe99d21dd3dd..c956b6f9b1d0 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -71,7 +71,8 @@ /* * Local function declarations */ -static struct nfs_page * nfs_update_request(struct file*, struct inode *, +static struct nfs_page * nfs_update_request(struct nfs_open_context*, + struct inode *, struct page *, unsigned int, unsigned int); static void nfs_writeback_done_partial(struct nfs_write_data *, int); @@ -173,7 +174,7 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int * Write a page synchronously. * Offset is the data offset within the page. */ -static int nfs_writepage_sync(struct file *file, struct inode *inode, +static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode, struct page *page, unsigned int offset, unsigned int count, int how) { @@ -187,9 +188,11 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode, memset(wdata, 0, sizeof(*wdata)); wdata->flags = how; + wdata->cred = ctx->cred; wdata->inode = inode; wdata->args.fh = NFS_FH(inode); - wdata->args.lockowner = current->files; + wdata->args.lockowner = ctx->lockowner; + wdata->args.state = ctx->state; wdata->args.pages = &page; wdata->args.stable = NFS_FILE_SYNC; wdata->args.pgbase = offset; @@ -208,7 +211,7 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode, wdata->args.count = count; wdata->args.offset = page_offset(page) + wdata->args.pgbase; - result = NFS_PROTO(inode)->write(wdata, file); + result = NFS_PROTO(inode)->write(wdata); if (result < 0) { /* Must mark the page invalid after I/O error */ @@ -234,20 +237,19 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode, io_error: nfs_end_data_update_defer(inode); - if (wdata->cred) - put_rpccred(wdata->cred); kfree(wdata); return written ? written : result; } -static int nfs_writepage_async(struct file *file, struct inode *inode, - struct page *page, unsigned int offset, unsigned int count) +static int nfs_writepage_async(struct nfs_open_context *ctx, + struct inode *inode, struct page *page, + unsigned int offset, unsigned int count) { struct nfs_page *req; int status; - req = nfs_update_request(file, inode, page, offset, count); + req = nfs_update_request(ctx, inode, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status < 0) goto out; @@ -274,6 +276,7 @@ static int wb_priority(struct writeback_control *wbc) */ int nfs_writepage(struct page *page, struct writeback_control *wbc) { + struct nfs_open_context *ctx; struct inode *inode = page->mapping->host; unsigned long end_index; unsigned offset = PAGE_CACHE_SIZE; @@ -308,16 +311,21 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc) if (page->index >= end_index+1 || !offset) goto out; do_it: + ctx = nfs_find_open_context(inode, FMODE_WRITE); + if (ctx == NULL) { + err = -EBADF; + goto out; + } lock_kernel(); if (!IS_SYNC(inode) && inode_referenced) { - err = nfs_writepage_async(NULL, inode, page, 0, offset); + err = nfs_writepage_async(ctx, inode, page, 0, offset); if (err >= 0) { err = 0; if (wbc->for_reclaim) nfs_flush_inode(inode, 0, 0, FLUSH_STABLE); } } else { - err = nfs_writepage_sync(NULL, inode, page, 0, + err = nfs_writepage_sync(ctx, inode, page, 0, offset, priority); if (err >= 0) { if (err != offset) @@ -326,6 +334,7 @@ do_it: } } unlock_kernel(); + put_nfs_open_context(ctx); out: unlock_page(page); if (inode_referenced) @@ -396,10 +405,9 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req) /* * Insert a write request into an inode */ -static void -nfs_inode_remove_request(struct nfs_page *req) +static void nfs_inode_remove_request(struct nfs_page *req) { - struct inode *inode = req->wb_inode; + struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); BUG_ON (!NFS_WBACK_BUSY(req)); @@ -450,7 +458,7 @@ nfs_find_request(struct inode *inode, unsigned long index) static void nfs_mark_request_dirty(struct nfs_page *req) { - struct inode *inode = req->wb_inode; + struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); spin_lock(&nfsi->req_lock); @@ -467,7 +475,7 @@ nfs_mark_request_dirty(struct nfs_page *req) static inline int nfs_dirty_request(struct nfs_page *req) { - struct nfs_inode *nfsi = NFS_I(req->wb_inode); + struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode); return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty; } @@ -478,7 +486,7 @@ nfs_dirty_request(struct nfs_page *req) static void nfs_mark_request_commit(struct nfs_page *req) { - struct inode *inode = req->wb_inode; + struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); spin_lock(&nfsi->req_lock); @@ -619,9 +627,9 @@ static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr) * * Note: Should always be called with the Page Lock held! */ -static struct nfs_page * -nfs_update_request(struct file* file, struct inode *inode, struct page *page, - unsigned int offset, unsigned int bytes) +static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, + struct inode *inode, struct page *page, + unsigned int offset, unsigned int bytes) { struct nfs_server *server = NFS_SERVER(inode); struct nfs_inode *nfsi = NFS_I(inode); @@ -669,13 +677,9 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, } spin_unlock(&nfsi->req_lock); - new = nfs_create_request(file, inode, page, offset, bytes); + new = nfs_create_request(ctx, inode, page, offset, bytes); if (IS_ERR(new)) return new; - if (file) { - new->wb_file = file; - get_file(file); - } } /* We have a request for our page. @@ -685,7 +689,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, * request. */ rqend = req->wb_offset + req->wb_bytes; - if (req->wb_file != file + if (req->wb_context != ctx || req->wb_page != page || !nfs_dirty_request(req) || offset > rqend || end < req->wb_offset) { @@ -706,9 +710,9 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, return req; } -int -nfs_flush_incompatible(struct file *file, struct page *page) +int nfs_flush_incompatible(struct file *file, struct page *page) { + struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct inode *inode = page->mapping->host; struct nfs_page *req; int status = 0; @@ -722,7 +726,7 @@ nfs_flush_incompatible(struct file *file, struct page *page) */ req = nfs_find_request(inode, page->index); if (req) { - if (!NFS_PROTO(inode)->request_compatible(req, file, page)) + if (req->wb_page != page || ctx != req->wb_context) status = nfs_wb_page(inode, page); nfs_release_request(req); } @@ -738,6 +742,7 @@ nfs_flush_incompatible(struct file *file, struct page *page) int nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsigned int count) { + struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct dentry *dentry = file->f_dentry; struct inode *inode = page->mapping->host; struct nfs_page *req; @@ -748,7 +753,7 @@ int nfs_updatepage(struct file *file, struct page *page, count, (long long)(page_offset(page) +offset)); if (IS_SYNC(inode)) { - status = nfs_writepage_sync(file, inode, page, offset, count, 0); + status = nfs_writepage_sync(ctx, inode, page, offset, count, 0); if (status > 0) { if (offset == 0 && status == PAGE_CACHE_SIZE) SetPageUptodate(page); @@ -785,7 +790,7 @@ int nfs_updatepage(struct file *file, struct page *page, * it out now. */ do { - req = nfs_update_request(file, inode, page, offset, count); + req = nfs_update_request(ctx, inode, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status != -EBUSY) break; @@ -861,16 +866,16 @@ static void nfs_write_rpcsetup(struct nfs_page *req, * NB: take care not to mess about with data->commit et al. */ data->req = req; - data->inode = inode = req->wb_inode; - data->cred = req->wb_cred; + data->inode = inode = req->wb_context->dentry->d_inode; + data->cred = req->wb_context->cred; data->args.fh = NFS_FH(inode); data->args.offset = req_offset(req) + offset; data->args.pgbase = req->wb_pgbase + offset; data->args.pages = data->pagevec; data->args.count = count; - data->args.lockowner = req->wb_lockowner; - data->args.state = req->wb_state; + data->args.lockowner = req->wb_context->lockowner; + data->args.state = req->wb_context->state; data->res.fattr = &data->fattr; data->res.count = count; @@ -1030,7 +1035,7 @@ nfs_flush_list(struct list_head *head, int wpages, int how) while (!list_empty(head)) { pages += nfs_coalesce_requests(head, &one_request, wpages); req = nfs_list_entry(one_request.next); - error = nfs_flush_one(&one_request, req->wb_inode, how); + error = nfs_flush_one(&one_request, req->wb_context->dentry->d_inode, how); if (error < 0) break; } @@ -1055,16 +1060,15 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status) struct page *page = req->wb_page; dprintk("NFS: write (%s/%Ld %d@%Ld)", - req->wb_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_inode), + req->wb_context->dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); if (status < 0) { ClearPageUptodate(page); SetPageError(page); - if (req->wb_file) - req->wb_file->f_error = status; + req->wb_context->error = status; dprintk(", error = %d\n", status); } else { #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) @@ -1105,16 +1109,15 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status) page = req->wb_page; dprintk("NFS: write (%s/%Ld %d@%Ld)", - req->wb_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_inode), + req->wb_context->dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); if (status < 0) { ClearPageUptodate(page); SetPageError(page); - if (req->wb_file) - req->wb_file->f_error = status; + req->wb_context->error = status; end_page_writeback(page); nfs_inode_remove_request(req); dprintk(", error = %d\n", status); @@ -1233,7 +1236,7 @@ static void nfs_commit_rpcsetup(struct list_head *head, list_splice_init(head, &data->pages); first = nfs_list_entry(data->pages.next); last = nfs_list_entry(data->pages.prev); - inode = first->wb_inode; + inode = first->wb_context->dentry->d_inode; /* * Determine the offset range of requests in the COMMIT call. @@ -1247,7 +1250,7 @@ static void nfs_commit_rpcsetup(struct list_head *head, len = 0; data->inode = inode; - data->cred = first->wb_cred; + data->cred = first->wb_context->cred; data->args.fh = NFS_FH(data->inode); data->args.offset = start; @@ -1314,13 +1317,12 @@ nfs_commit_done(struct rpc_task *task) nfs_list_remove_request(req); dprintk("NFS: commit (%s/%Ld %d@%Ld)", - req->wb_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_inode), + req->wb_context->dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); if (task->tk_status < 0) { - if (req->wb_file) - req->wb_file->f_error = task->tk_status; + req->wb_context->error = task->tk_status; nfs_inode_remove_request(req); dprintk(", error = %d\n", task->tk_status); goto next; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index f72279a6a36c..0b9fb290b1b2 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -83,6 +83,20 @@ struct nfs_access_entry { int mask; }; +struct nfs4_state; +struct nfs_open_context { + atomic_t count; + struct dentry *dentry; + struct rpc_cred *cred; + struct nfs4_state *state; + fl_owner_t lockowner; + int mode; + int error; + + struct list_head list; + wait_queue_head_t waitq; +}; + /* * nfs fs inode data in memory */ @@ -156,8 +170,8 @@ struct nfs_inode { ncommit, npages; - /* Credentials for shared mmap */ - struct rpc_cred *mm_cred; + /* Open contexts for shared mmap writes */ + struct list_head open_files; wait_queue_head_t nfs_i_wait; @@ -268,7 +282,6 @@ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int nfs_permission(struct inode *, int, struct nameidata *); -extern void nfs_set_mmcred(struct inode *, struct rpc_cred *); extern int nfs_open(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); @@ -278,6 +291,12 @@ extern void nfs_end_attr_update(struct inode *); extern void nfs_begin_data_update(struct inode *); extern void nfs_end_data_update(struct inode *); extern void nfs_end_data_update_defer(struct inode *); +extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred); +extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); +extern void put_nfs_open_context(struct nfs_open_context *ctx); +extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx); +extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode); +extern void nfs_file_clear_open_context(struct file *filp); /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ extern u32 root_nfs_parse_addr(char *name); /*__init*/ @@ -289,16 +308,15 @@ extern struct inode_operations nfs_file_inode_operations; extern struct file_operations nfs_file_operations; extern struct address_space_operations nfs_file_aops; -static __inline__ struct rpc_cred * -nfs_file_cred(struct file *file) +static inline struct rpc_cred *nfs_file_cred(struct file *file) { - struct rpc_cred *cred = NULL; - if (file) - cred = (struct rpc_cred *)file->private_data; -#ifdef RPC_DEBUG - BUG_ON(cred && cred->cr_magic != RPCAUTH_CRED_MAGIC); -#endif - return cred; + if (file != NULL) { + struct nfs_open_context *ctx; + + ctx = (struct nfs_open_context*)file->private_data; + return ctx->cred; + } + return NULL; } /* @@ -684,6 +702,7 @@ struct nfs4_mount_data; #define destroy_nfsv4_state(server) do { } while (0) #define nfs4_put_state_owner(inode, owner) do { } while (0) #define nfs4_put_open_state(state) do { } while (0) +#define nfs4_close_state(a, b) do { } while (0) #define nfs4_renewd_prepare_shutdown(server) do { } while (0) #endif diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 12a6758cc859..39e4895bcdb4 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -29,14 +29,9 @@ struct nfs_page { struct list_head wb_list, /* Defines state of page: */ *wb_list_head; /* read/write/commit */ - struct file *wb_file; - fl_owner_t wb_lockowner; - struct inode *wb_inode; - struct rpc_cred *wb_cred; - struct nfs4_state *wb_state; struct page *wb_page; /* page to read in/write out */ + struct nfs_open_context *wb_context; /* File state context info */ atomic_t wb_complete; /* i/os we're waiting for */ - wait_queue_head_t wb_wait; /* wait queue */ unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */ unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */ wb_pgbase, /* Start of page data */ @@ -50,9 +45,11 @@ struct nfs_page { #define NFS_NEED_COMMIT(req) (test_bit(PG_NEED_COMMIT,&(req)->wb_flags)) #define NFS_NEED_RESCHED(req) (test_bit(PG_NEED_RESCHED,&(req)->wb_flags)) -extern struct nfs_page *nfs_create_request(struct file *, struct inode *, - struct page *, - unsigned int, unsigned int); +extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx, + struct inode *inode, + struct page *page, + unsigned int offset, + unsigned int count); extern void nfs_clear_request(struct nfs_page *req); extern void nfs_release_request(struct nfs_page *req); @@ -64,6 +61,7 @@ extern int nfs_scan_list(struct list_head *, struct list_head *, extern int nfs_coalesce_requests(struct list_head *, struct list_head *, unsigned int); extern int nfs_wait_on_request(struct nfs_page *); +extern void nfs_unlock_request(struct nfs_page *req); /* * Lock the page of an asynchronous request without incrementing the wb_count @@ -88,19 +86,6 @@ nfs_lock_request(struct nfs_page *req) return 1; } -static inline void -nfs_unlock_request(struct nfs_page *req) -{ - if (!NFS_WBACK_BUSY(req)) { - printk(KERN_ERR "NFS: Invalid unlock attempted\n"); - BUG(); - } - smp_mb__before_clear_bit(); - clear_bit(PG_BUSY, &req->wb_flags); - smp_mb__after_clear_bit(); - wake_up_all(&req->wb_wait); - nfs_release_request(req); -} /** * nfs_list_remove_request - Remove a request from its wb_list diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 427ec6708d20..340ff5c8f3df 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -678,9 +678,9 @@ struct nfs_rpc_ops { struct nfs_fh *, struct nfs_fattr *); int (*access) (struct inode *, struct nfs_access_entry *); int (*readlink)(struct inode *, struct page *); - int (*read) (struct nfs_read_data *, struct file *); - int (*write) (struct nfs_write_data *, struct file *); - int (*commit) (struct nfs_write_data *, struct file *); + int (*read) (struct nfs_read_data *); + int (*write) (struct nfs_write_data *); + int (*commit) (struct nfs_write_data *); struct inode * (*create) (struct inode *, struct qstr *, struct iattr *, int); int (*remove) (struct inode *, struct qstr *); @@ -712,8 +712,6 @@ struct nfs_rpc_ops { void (*commit_setup) (struct nfs_write_data *, int how); int (*file_open) (struct inode *, struct file *); int (*file_release) (struct inode *, struct file *); - void (*request_init)(struct nfs_page *, struct file *); - int (*request_compatible)(struct nfs_page *, struct file *, struct page *); int (*lock)(struct file *, int, struct file_lock *); }; -- cgit v1.2.3 From 90c1f6b281e6eebffa6a43b899a585d79cac61b3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Aug 2004 11:19:39 -0400 Subject: NFSv4: More cleanups of the NFSv4 state. Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 6 ++---- fs/nfs/nfs4xdr.c | 10 +++++----- fs/nfs/read.c | 6 ++---- fs/nfs/write.c | 6 ++---- include/linux/nfs_xdr.h | 6 ++---- 5 files changed, 13 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 080e3e7b6c07..21c3c4d396be 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -130,8 +130,7 @@ nfs_direct_read_seg(struct inode *inode, struct nfs_open_context *ctx, .cred = ctx->cred, .args = { .fh = NFS_FH(inode), - .lockowner = ctx->lockowner, - .state = ctx->state, + .context = ctx, }, .res = { .fattr = &rdata.fattr, @@ -262,8 +261,7 @@ nfs_direct_write_seg(struct inode *inode, struct nfs_open_context *ctx, .cred = ctx->cred, .args = { .fh = NFS_FH(inode), - .lockowner = ctx->lockowner, - .state = ctx->state, + .context = ctx, }, .res = { .fattr = &wdata.fattr, diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 4988e501603a..8eb1b2faa327 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -912,15 +912,15 @@ static int encode_putrootfh(struct xdr_stream *xdr) return 0; } -static void encode_stateid(struct xdr_stream *xdr, struct nfs4_state *state, fl_owner_t lockowner) +static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx) { extern nfs4_stateid zero_stateid; nfs4_stateid stateid; uint32_t *p; RESERVE_SPACE(16); - if (state != NULL) { - nfs4_copy_stateid(&stateid, state, lockowner); + if (ctx->state != NULL) { + nfs4_copy_stateid(&stateid, ctx->state, ctx->lockowner); WRITEMEM(stateid.data, sizeof(stateid.data)); } else WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data)); @@ -933,7 +933,7 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args) RESERVE_SPACE(4); WRITE32(OP_READ); - encode_stateid(xdr, args->state, args->lockowner); + encode_stateid(xdr, args->context); RESERVE_SPACE(12); WRITE64(args->offset); @@ -1092,7 +1092,7 @@ static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args RESERVE_SPACE(4); WRITE32(OP_WRITE); - encode_stateid(xdr, args->state, args->lockowner); + encode_stateid(xdr, args->context); RESERVE_SPACE(16); WRITE64(args->offset); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 54df1a48843d..0de2e2d12015 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -109,8 +109,7 @@ static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode, rdata->inode = inode; INIT_LIST_HEAD(&rdata->pages); rdata->args.fh = NFS_FH(inode); - rdata->args.lockowner = ctx->lockowner; - rdata->args.state = ctx->state; + rdata->args.context = ctx; rdata->args.pages = &page; rdata->args.pgbase = 0UL; rdata->args.count = rsize; @@ -227,8 +226,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, data->args.pgbase = req->wb_pgbase + offset; data->args.pages = data->pagevec; data->args.count = count; - data->args.lockowner = req->wb_context->lockowner; - data->args.state = req->wb_context->state; + data->args.context = req->wb_context; data->res.fattr = &data->fattr; data->res.count = count; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index c956b6f9b1d0..daea97848193 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -191,8 +191,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode, wdata->cred = ctx->cred; wdata->inode = inode; wdata->args.fh = NFS_FH(inode); - wdata->args.lockowner = ctx->lockowner; - wdata->args.state = ctx->state; + wdata->args.context = ctx; wdata->args.pages = &page; wdata->args.stable = NFS_FILE_SYNC; wdata->args.pgbase = offset; @@ -874,8 +873,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req, data->args.pgbase = req->wb_pgbase + offset; data->args.pages = data->pagevec; data->args.count = count; - data->args.lockowner = req->wb_context->lockowner; - data->args.state = req->wb_context->state; + data->args.context = req->wb_context; data->res.fattr = &data->fattr; data->res.count = count; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 340ff5c8f3df..3f19c949074f 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -235,8 +235,7 @@ struct nfs_lockres { struct nfs_readargs { struct nfs_fh * fh; - fl_owner_t lockowner; - struct nfs4_state * state; + struct nfs_open_context *context; __u64 offset; __u32 count; unsigned int pgbase; @@ -259,8 +258,7 @@ struct nfs_readres { struct nfs_writeargs { struct nfs_fh * fh; - fl_owner_t lockowner; - struct nfs4_state * state; + struct nfs_open_context *context; __u64 offset; __u32 count; enum nfs3_stable_how stable; -- cgit v1.2.3 From 60fa4cfb6897d8adf096c667905a53d848e57e72 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Aug 2004 11:21:20 -0400 Subject: NFSv2/v3/v4: Make the rpc_ops->getattr method take a filehandle rather than an inode argument. Fix up nfs_instantiate() and _nfs4_do_open to use this since doing a new lookup might be racy. --- fs/nfs/dir.c | 8 +++++++- fs/nfs/inode.c | 2 +- fs/nfs/nfs3proc.c | 7 ++++--- fs/nfs/nfs4proc.c | 18 +++++++++++------- fs/nfs/proc.c | 7 ++++--- include/linux/nfs_xdr.h | 3 ++- 6 files changed, 29 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 59f88f6ee2fb..a9d54bea05c9 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -982,12 +982,18 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, /* We may have been initialized further down */ if (dentry->d_inode) return 0; - if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) { + if (fhandle->size == 0) { struct inode *dir = dentry->d_parent->d_inode; error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); if (error) goto out_err; } + if (!(fattr->valid & NFS_ATTR_FATTR)) { + struct nfs_server *server = NFS_SB(dentry->d_sb); + error = server->rpc_ops->getattr(server, fhandle, fattr); + if (error < 0) + goto out_err; + } inode = nfs_fhget(dentry->d_sb, fhandle, fattr); if (inode) { d_instantiate(dentry, inode); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 6bc28939de68..9cc5215a1d27 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -961,7 +961,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) /* Protect against RPC races by saving the change attribute */ verifier = nfs_save_change_attribute(inode); - status = NFS_PROTO(inode)->getattr(inode, &fattr); + status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr); if (status) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", inode->i_sb->s_id, diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index bbceb6d3f2ce..d47ad1107c9e 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -92,14 +92,15 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * One function for each procedure in the NFS protocol. */ static int -nfs3_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) +nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { int status; dprintk("NFS call getattr\n"); fattr->valid = 0; - status = rpc_call(NFS_CLIENT(inode), NFS3PROC_GETATTR, - NFS_FH(inode), fattr, 0); + status = rpc_call(server->client, NFS3PROC_GETATTR, + fhandle, fattr, 0); dprintk("NFS reply getattr\n"); return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7b92c0711015..25abdef04d32 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -305,6 +305,11 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct if (status) goto out_err; update_changeattr(dir, &o_res.cinfo); + if (!(f_attr.valid & NFS_ATTR_FATTR)) { + status = server->rpc_ops->getattr(server, &o_res.fh, &f_attr); + if (status < 0) + goto out_err; + } status = -ENOMEM; inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr); @@ -734,11 +739,10 @@ out: return status; } -static int _nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) +static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { - struct nfs_server *server = NFS_SERVER(inode); struct nfs4_getattr_arg args = { - .fh = NFS_FH(inode), + .fh = fhandle, .bitmask = server->attr_bitmask, }; struct nfs4_getattr_res res = { @@ -752,16 +756,16 @@ static int _nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) }; fattr->valid = 0; - return rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + return rpc_call_sync(server->client, &msg, 0); } -static int nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) +static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { struct nfs4_exception exception = { }; int err; do { - err = nfs4_handle_exception(NFS_SERVER(inode), - _nfs4_proc_getattr(inode, fattr), + err = nfs4_handle_exception(server, + _nfs4_proc_getattr(server, fhandle, fattr), &exception); } while (exception.retry); return err; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 2e2fff50f25b..d1670384e6f3 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -87,14 +87,15 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * One function for each procedure in the NFS protocol. */ static int -nfs_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) +nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { int status; dprintk("NFS call getattr\n"); fattr->valid = 0; - status = rpc_call(NFS_CLIENT(inode), NFSPROC_GETATTR, - NFS_FH(inode), fattr, 0); + status = rpc_call(server->client, NFSPROC_GETATTR, + fhandle, fattr, 0); dprintk("NFS reply getattr\n"); return status; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 3f19c949074f..15f1c41b4f7a 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -669,7 +669,8 @@ struct nfs_rpc_ops { int (*getroot) (struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); - int (*getattr) (struct inode *, struct nfs_fattr *); + int (*getattr) (struct nfs_server *, struct nfs_fh *, + struct nfs_fattr *); int (*setattr) (struct dentry *, struct nfs_fattr *, struct iattr *); int (*lookup) (struct inode *, struct qstr *, -- cgit v1.2.3