From f4ce1299b329e96bb247c95c4fee8809827d6931 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 19 Aug 2013 18:59:33 -0400 Subject: NFS: Add event tracing for generic NFS events Add tracepoints for inode attribute updates, attribute revalidation, writeback start/end fsync start/end, attribute change start/end, permission check start/end. The intention is to enable performance tracing using 'perf'as well as improving debugging. Signed-off-by: Trond Myklebust --- fs/nfs/write.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'fs/nfs/write.c') diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f1bdb7254776..d37e8ca9ab86 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -31,6 +31,8 @@ #include "fscache.h" #include "pnfs.h" +#include "nfstrace.h" + #define NFSDBG_FACILITY NFSDBG_PAGECACHE #define MIN_POOL_WRITE (32) @@ -1732,8 +1734,14 @@ int nfs_wb_all(struct inode *inode) .range_start = 0, .range_end = LLONG_MAX, }; + int ret; + + trace_nfs_writeback_inode_enter(inode); + + ret = sync_inode(inode, &wbc); - return sync_inode(inode, &wbc); + trace_nfs_writeback_inode_exit(inode, ret); + return ret; } EXPORT_SYMBOL_GPL(nfs_wb_all); @@ -1781,6 +1789,8 @@ int nfs_wb_page(struct inode *inode, struct page *page) }; int ret; + trace_nfs_writeback_page_enter(inode); + for (;;) { wait_on_page_writeback(page); if (clear_page_dirty_for_io(page)) { @@ -1789,14 +1799,15 @@ int nfs_wb_page(struct inode *inode, struct page *page) goto out_error; continue; } + ret = 0; if (!PagePrivate(page)) break; ret = nfs_commit_inode(inode, FLUSH_SYNC); if (ret < 0) goto out_error; } - return 0; out_error: + trace_nfs_writeback_page_exit(inode, ret); return ret; } -- cgit v1.2.3 From dc24826bfca8d788d05f625208f06d90be5560b3 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 14 Aug 2013 11:59:16 -0400 Subject: NFS avoid expired credential keys for buffered writes We must avoid buffering a WRITE that is using a credential key (e.g. a GSS context key) that is about to expire or has expired. We currently will paint ourselves into a corner by returning success to the applciation for such a buffered WRITE, only to discover that we do not have permission when we attempt to flush the WRITE (and potentially associated COMMIT) to disk. Use the RPC layer credential key timeout and expire routines which use a a watermark, gss_key_expire_timeo. We test the key in nfs_file_write. If a WRITE is using a credential with a key that will expire within watermark seconds, flush the inode in nfs_write_end and send only NFS_FILE_SYNC WRITEs by adding nfs_ctx_key_to_expire to nfs_need_sync_write. Note that this results in single page NFS_FILE_SYNC WRITEs. Signed-off-by: Andy Adamson [Trond: removed a pr_warn_ratelimited() for now] Signed-off-by: Trond Myklebust --- fs/nfs/file.c | 15 ++++++++++++++- fs/nfs/internal.h | 2 ++ fs/nfs/write.c | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) (limited to 'fs/nfs/write.c') diff --git a/fs/nfs/file.c b/fs/nfs/file.c index d6a9db0a8545..1e6bfdbc1aff 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -411,6 +411,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping, struct page *page, void *fsdata) { unsigned offset = pos & (PAGE_CACHE_SIZE - 1); + struct nfs_open_context *ctx = nfs_file_open_context(file); int status; dfprintk(PAGECACHE, "NFS: write_end(%s/%s(%ld), %u@%lld)\n", @@ -446,6 +447,13 @@ static int nfs_write_end(struct file *file, struct address_space *mapping, if (status < 0) return status; NFS_I(mapping->host)->write_io += copied; + + if (nfs_ctx_key_to_expire(ctx)) { + status = nfs_wb_all(mapping->host); + if (status < 0) + return status; + } + return copied; } @@ -642,7 +650,8 @@ static int nfs_need_sync_write(struct file *filp, struct inode *inode) if (IS_SYNC(inode) || (filp->f_flags & O_DSYNC)) return 1; ctx = nfs_file_open_context(filp); - if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags)) + if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags) || + nfs_ctx_key_to_expire(ctx)) return 1; return 0; } @@ -656,6 +665,10 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, ssize_t result; size_t count = iov_length(iov, nr_segs); + result = nfs_key_timeout_notify(iocb->ki_filp, inode); + if (result) + return result; + if (iocb->ki_filp->f_flags & O_DIRECT) return nfs_file_direct_write(iocb, iov, nr_segs, pos, true); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 50f7068903b9..2415198d29ad 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -431,6 +431,8 @@ void nfs_request_remove_commit_list(struct nfs_page *req, void nfs_init_cinfo(struct nfs_commit_info *cinfo, struct inode *inode, struct nfs_direct_req *dreq); +int nfs_key_timeout_notify(struct file *filp, struct inode *inode); +bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx); #ifdef CONFIG_MIGRATION extern int nfs_migrate_page(struct address_space *, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index d37e8ca9ab86..94eb4504731a 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -875,6 +875,33 @@ int nfs_flush_incompatible(struct file *file, struct page *page) return status; } +/* + * Avoid buffered writes when a open context credential's key would + * expire soon. + * + * Returns -EACCES if the key will expire within RPC_KEY_EXPIRE_FAIL. + * + * Return 0 and set a credential flag which triggers the inode to flush + * and performs NFS_FILE_SYNC writes if the key will expired within + * RPC_KEY_EXPIRE_TIMEO. + */ +int +nfs_key_timeout_notify(struct file *filp, struct inode *inode) +{ + struct nfs_open_context *ctx = nfs_file_open_context(filp); + struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth; + + return rpcauth_key_timeout_notify(auth, ctx->cred); +} + +/* + * Test if the open context credential key is marked to expire soon. + */ +bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx) +{ + return rpcauth_cred_key_to_expire(ctx->cred); +} + /* * If the page cache is marked as unsafe or invalid, then we can't rely on * the PageUptodate() flag. In this case, we will need to turn off -- cgit v1.2.3 From ef1820f9be27b6ad158f433ab38002ab8131db4d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 4 Sep 2013 17:04:49 +1000 Subject: NFSv4: Don't try to recover NFSv4 locks when they are lost. When an NFSv4 client loses contact with the server it can lose any locks that it holds. Currently when it reconnects to the server it simply tries to reclaim those locks. This might succeed even though some other client has held and released a lock in the mean time. So the first client might think the file is unchanged, but it isn't. This isn't good. If, when recovery happens, the locks cannot be claimed because some other client still holds the lock, then we get a message in the kernel logs, but the client can still write. So two clients can both think they have a lock and can both write at the same time. This is equally not good. There was a patch a while ago http://comments.gmane.org/gmane.linux.nfs/41917 which tried to address some of this, but it didn't seem to go anywhere. That patch would also send a signal to the process. That might be useful but for now this patch just causes writes to fail. For NFSv4 (unlike v2/v3) there is a strong link between the lock and the write request so we can fairly easily fail any IO of the lock is gone. While some applications might not expect this, it is still safer than allowing the write to succeed. Because this is a fairly big change in behaviour a module parameter, "recover_locks", is introduced which defaults to true (the current behaviour) but can be set to "false" to tell the client not to try to recover things that were lost. Signed-off-by: NeilBrown Signed-off-by: Trond Myklebust --- fs/nfs/nfs3proc.c | 6 ++++-- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 34 ++++++++++++++++++++++++++-------- fs/nfs/nfs4state.c | 14 +++++++++++--- fs/nfs/proc.c | 6 ++++-- fs/nfs/read.c | 7 ++++--- fs/nfs/write.c | 7 ++++--- include/linux/nfs_xdr.h | 4 ++-- 8 files changed, 56 insertions(+), 23 deletions(-) (limited to 'fs/nfs/write.c') diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 1db588a3f08b..90cb10d7b693 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -826,9 +826,10 @@ static void nfs3_proc_read_setup(struct nfs_read_data *data, struct rpc_message msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ]; } -static void nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +static int nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) { rpc_call_start(task); + return 0; } static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -847,9 +848,10 @@ static void nfs3_proc_write_setup(struct nfs_write_data *data, struct rpc_messag msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE]; } -static void nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +static int nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) { rpc_call_start(task); + return 0; } static void nfs3_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index af2d5bf043f0..64118316a407 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -133,6 +133,7 @@ struct nfs4_lock_state { struct list_head ls_locks; /* Other lock stateids */ struct nfs4_state * ls_state; /* Pointer to open state */ #define NFS_LOCK_INITIALIZED 0 +#define NFS_LOCK_LOST 1 unsigned long ls_flags; struct nfs_seqid_counter ls_seqid; nfs4_stateid ls_stateid; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0122919a311a..1eb694e0f305 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4028,15 +4028,19 @@ static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); } -static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +static int nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) { if (nfs4_setup_sequence(NFS_SERVER(data->header->inode), &data->args.seq_args, &data->res.seq_res, task)) - return; - nfs4_set_rw_stateid(&data->args.stateid, data->args.context, - data->args.lock_context, FMODE_READ); + return 0; + if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context, + data->args.lock_context, FMODE_READ) == -EIO) + return -EIO; + if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) + return -EIO; + return 0; } static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data) @@ -4112,15 +4116,19 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); } -static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +static int nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) { if (nfs4_setup_sequence(NFS_SERVER(data->header->inode), &data->args.seq_args, &data->res.seq_res, task)) - return; - nfs4_set_rw_stateid(&data->args.stateid, data->args.context, - data->args.lock_context, FMODE_WRITE); + return 0; + if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context, + data->args.lock_context, FMODE_WRITE) == -EIO) + return -EIO; + if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) + return -EIO; + return 0; } static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) @@ -5515,6 +5523,12 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request return err; } +bool recover_locks = true; +module_param(recover_locks, bool, 0644); +MODULE_PARM_DESC(recover_locks, + "If the server reports that a lock might be lost, " + "try to recovery it risking corruption."); + static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) { struct nfs_server *server = NFS_SERVER(state->inode); @@ -5526,6 +5540,10 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request err = nfs4_set_lock_state(state, request); if (err != 0) return err; + if (!recover_locks) { + set_bit(NFS_LOCK_LOST, &request->fl_u.nfs4_fl.owner->ls_flags); + return 0; + } do { if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) return 0; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index da608ee8d5ff..cc14cbb78b73 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -969,7 +969,9 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst, fl_pid = lockowner->l_pid; spin_lock(&state->state_lock); lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); - if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { + if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags)) + ret = -EIO; + else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { nfs4_stateid_copy(dst, &lsp->ls_stateid); ret = 0; smp_rmb(); @@ -1009,11 +1011,17 @@ static int nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state) int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, fmode_t fmode, const struct nfs_lockowner *lockowner) { - int ret = 0; + int ret = nfs4_copy_lock_stateid(dst, state, lockowner); + if (ret == -EIO) + /* A lost lock - don't even consider delegations */ + goto out; if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) goto out; - ret = nfs4_copy_lock_stateid(dst, state, lockowner); if (ret != -ENOENT) + /* nfs4_copy_delegation_stateid() didn't over-write + * dst, so it still has the lock stateid which we now + * choose to use. + */ goto out; ret = nfs4_copy_open_stateid(dst, state); out: diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index c041c41f7a52..a8f57c728df5 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -623,9 +623,10 @@ static void nfs_proc_read_setup(struct nfs_read_data *data, struct rpc_message * msg->rpc_proc = &nfs_procedures[NFSPROC_READ]; } -static void nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +static int nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) { rpc_call_start(task); + return 0; } static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -644,9 +645,10 @@ static void nfs_proc_write_setup(struct nfs_write_data *data, struct rpc_message msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE]; } -static void nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +static int nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) { rpc_call_start(task); + return 0; } static void nfs_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 70a26c651f09..31db5c366b81 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -513,9 +513,10 @@ static void nfs_readpage_release_common(void *calldata) void nfs_read_prepare(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; - NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data); - if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) - rpc_exit(task, -EIO); + int err; + err = NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data); + if (err) + rpc_exit(task, err); } static const struct rpc_call_ops nfs_read_common_ops = { diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 94eb4504731a..379450c8d04b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1294,9 +1294,10 @@ EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds); void nfs_write_prepare(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; - NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data); - if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) - rpc_exit(task, -EIO); + int err; + err = NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data); + if (err) + rpc_exit(task, err); } void nfs_commit_prepare(struct rpc_task *task, void *calldata) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ddc3e32178c3..7c3956d65b3e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1419,12 +1419,12 @@ struct nfs_rpc_ops { void (*read_setup) (struct nfs_read_data *, struct rpc_message *); void (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, const struct nfs_pgio_completion_ops *); - void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); + int (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int, const struct nfs_pgio_completion_ops *); - void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); + int (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *); void (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *); -- cgit v1.2.3 From 8c21c62c4452f4e66c3dac9b3f6b74474fad3e08 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 13 Aug 2013 16:37:37 -0400 Subject: nfs4.1: Add SP4_MACH_CRED write and commit support WRITE and COMMIT can use the machine credential. If WRITE is supported and COMMIT is not, make all (mach cred) writes FILE_SYNC4. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 50 ++++++++++++++++++++++++++++++++++++++--------- fs/nfs/nfs4proc.c | 10 ++++++++++ fs/nfs/write.c | 6 ++++++ include/linux/nfs_fs_sb.h | 2 ++ 4 files changed, 59 insertions(+), 9 deletions(-) (limited to 'fs/nfs/write.c') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 3559e899666e..d2db3ce07d3a 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -269,15 +269,9 @@ is_ds_client(struct nfs_client *clp) return clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_DS; } -/* - * Function responsible for determining if an rpc_message should use the - * machine cred under SP4_MACH_CRED and if so switching the credential and - * authflavor (using the nfs_client's rpc_clnt which will be krb5i/p). - * Should be called before rpc_call_sync/rpc_call_async. - */ -static inline void -nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode, - struct rpc_clnt **clntp, struct rpc_message *msg) +static inline bool +_nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode, + struct rpc_clnt **clntp, struct rpc_message *msg) { struct rpc_cred *newcred = NULL; rpc_authflavor_t flavor; @@ -295,7 +289,37 @@ nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode, WARN_ON(flavor != RPC_AUTH_GSS_KRB5I && flavor != RPC_AUTH_GSS_KRB5P); *clntp = clp->cl_rpcclient; + + return true; } + return false; +} + +/* + * Function responsible for determining if an rpc_message should use the + * machine cred under SP4_MACH_CRED and if so switching the credential and + * authflavor (using the nfs_client's rpc_clnt which will be krb5i/p). + * Should be called before rpc_call_sync/rpc_call_async. + */ +static inline void +nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode, + struct rpc_clnt **clntp, struct rpc_message *msg) +{ + _nfs4_state_protect(clp, sp4_mode, clntp, msg); +} + +/* + * Special wrapper to nfs4_state_protect for write. + * If WRITE can use machine cred but COMMIT cannot, make sure all writes + * that use machine cred use NFS_FILE_SYNC. + */ +static inline void +nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp, + struct rpc_message *msg, struct nfs_write_data *wdata) +{ + if (_nfs4_state_protect(clp, NFS_SP4_MACH_CRED_WRITE, clntp, msg) && + !test_bit(NFS_SP4_MACH_CRED_COMMIT, &clp->cl_sp4_flags)) + wdata->args.stable = NFS_FILE_SYNC; } #else /* CONFIG_NFS_v4_1 */ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) @@ -320,6 +344,12 @@ nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_flags, struct rpc_clnt **clntp, struct rpc_message *msg) { } + +static inline void +nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp, + struct rpc_message *msg, struct nfs_write_data *wdata) +{ +} #endif /* CONFIG_NFS_V4_1 */ extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; @@ -455,6 +485,8 @@ static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state) #define nfs4_close_state(a, b) do { } while (0) #define nfs4_close_sync(a, b) do { } while (0) +#define nfs4_state_protect(a, b, c, d) do { } while (0) +#define nfs4_state_protect_write(a, b, c, d) do { } while (0) #endif /* CONFIG_NFS_V4 */ #endif /* __LINUX_FS_NFS_NFS4_FS.H */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4818a38f469b..e3cdfe346ebc 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6223,6 +6223,16 @@ static int nfs4_sp4_select_mode(struct nfs_client *clp, dfprintk(MOUNT, " stateid mode enabled\n"); set_bit(NFS_SP4_MACH_CRED_STATEID, &clp->cl_sp4_flags); } + + if (test_bit(OP_WRITE, sp->allow.u.longs)) { + dfprintk(MOUNT, " write mode enabled\n"); + set_bit(NFS_SP4_MACH_CRED_WRITE, &clp->cl_sp4_flags); + } + + if (test_bit(OP_COMMIT, sp->allow.u.longs)) { + dfprintk(MOUNT, " commit mode enabled\n"); + set_bit(NFS_SP4_MACH_CRED_COMMIT, &clp->cl_sp4_flags); + } } return 0; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 379450c8d04b..40979e815434 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1022,6 +1022,9 @@ int nfs_initiate_write(struct rpc_clnt *clnt, data->args.count, (unsigned long long)data->args.offset); + nfs4_state_protect_write(NFS_SERVER(inode)->nfs_client, + &task_setup_data.rpc_client, &msg, data); + task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) { ret = PTR_ERR(task); @@ -1488,6 +1491,9 @@ int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data, dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); + nfs4_state_protect(NFS_SERVER(data->inode)->nfs_client, + NFS_SP4_MACH_CRED_COMMIT, &task_setup_data.rpc_client, &msg); + task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 179586555bbe..e8ff178c3d33 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -97,6 +97,8 @@ struct nfs_client { #define NFS_SP4_MACH_CRED_CLEANUP 2 /* CLOSE and LOCKU */ #define NFS_SP4_MACH_CRED_SECINFO 3 /* SECINFO and SECINFO_NO_NAME */ #define NFS_SP4_MACH_CRED_STATEID 4 /* TEST_STATEID and FREE_STATEID */ +#define NFS_SP4_MACH_CRED_WRITE 5 /* WRITE */ +#define NFS_SP4_MACH_CRED_COMMIT 6 /* COMMIT */ #endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_NFS_FSCACHE -- cgit v1.2.3 From 0f1d26055068bbc66751d1974ecc6f0398b3ac67 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 5 Sep 2013 15:52:51 -0400 Subject: NFS: Don't check lock owner compatibility in writes unless file is locked If we're doing buffered writes, and there is no file locking involved, then we don't have to worry about whether or not the lock owner information is identical. By relaxing this check, we ensure that fork()ed child processes can write to a page without having to first sync dirty data that was written by the parent to disk. Reported-by: Quentin Barnes Signed-off-by: Trond Myklebust Tested-by: Quentin Barnes --- fs/nfs/write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfs/write.c') diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 40979e815434..ac1dc331ba31 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -863,7 +863,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page) return 0; l_ctx = req->wb_lock_context; do_flush = req->wb_page != page || req->wb_context != ctx; - if (l_ctx) { + if (l_ctx && ctx->dentry->d_inode->i_flock != NULL) { do_flush |= l_ctx->lockowner.l_owner != current->files || l_ctx->lockowner.l_pid != current->tgid; } -- cgit v1.2.3