diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-31 18:39:07 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-31 18:39:07 -0500 |
commit | 8a1f006ad302ea178aefb1f8c67e679c696289e9 (patch) | |
tree | 8ba312b39070556d2062a9f90b1a7fa394041d78 /fs | |
parent | 14864a52cd8189e8567df8351d9fc7e435133abd (diff) | |
parent | a1800acaf7d1c2bf6d68b9a8f4ab8560cc66555a (diff) |
Merge tag 'nfs-for-3.14-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
Pull NFS client bugfixes from Trond Myklebust:
"Highlights:
- Fix several races in nfs_revalidate_mapping
- NFSv4.1 slot leakage in the pNFS files driver
- Stable fix for a slot leak in nfs40_sequence_done
- Don't reject NFSv4 servers that support ACLs with only ALLOW aces"
* tag 'nfs-for-3.14-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
nfs: initialize the ACL support bits to zero.
NFSv4.1: Cleanup
NFSv4.1: Clean up nfs41_sequence_done
NFSv4: Fix a slot leak in nfs40_sequence_done
NFSv4.1 free slot before resending I/O to MDS
nfs: add memory barriers around NFS_INO_INVALID_DATA and NFS_INO_INVALIDATING
NFS: Fix races in nfs_revalidate_mapping
sunrpc: turn warn_gssd() log message into a dprintk()
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
nfs: handle servers that support only ALLOW ACE type.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/dir.c | 13 | ||||
-rw-r--r-- | fs/nfs/inode.c | 51 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 1 | ||||
-rw-r--r-- | fs/nfs/nfs4client.c | 5 | ||||
-rw-r--r-- | fs/nfs/nfs4filelayout.c | 8 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 31 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfstrace.h | 1 | ||||
-rw-r--r-- | fs/nfs/write.c | 7 |
9 files changed, 84 insertions, 35 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index b266f734bd53..be38b573495a 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -274,6 +274,15 @@ out_eof: | |||
274 | return -EBADCOOKIE; | 274 | return -EBADCOOKIE; |
275 | } | 275 | } |
276 | 276 | ||
277 | static bool | ||
278 | nfs_readdir_inode_mapping_valid(struct nfs_inode *nfsi) | ||
279 | { | ||
280 | if (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) | ||
281 | return false; | ||
282 | smp_rmb(); | ||
283 | return !test_bit(NFS_INO_INVALIDATING, &nfsi->flags); | ||
284 | } | ||
285 | |||
277 | static | 286 | static |
278 | int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) | 287 | int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) |
279 | { | 288 | { |
@@ -287,8 +296,8 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des | |||
287 | struct nfs_open_dir_context *ctx = desc->file->private_data; | 296 | struct nfs_open_dir_context *ctx = desc->file->private_data; |
288 | 297 | ||
289 | new_pos = desc->current_index + i; | 298 | new_pos = desc->current_index + i; |
290 | if (ctx->attr_gencount != nfsi->attr_gencount | 299 | if (ctx->attr_gencount != nfsi->attr_gencount || |
291 | || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) { | 300 | !nfs_readdir_inode_mapping_valid(nfsi)) { |
292 | ctx->duped = 0; | 301 | ctx->duped = 0; |
293 | ctx->attr_gencount = nfsi->attr_gencount; | 302 | ctx->attr_gencount = nfsi->attr_gencount; |
294 | } else if (new_pos < desc->ctx->pos) { | 303 | } else if (new_pos < desc->ctx->pos) { |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ea00b34ff071..28a0a3cbd3b7 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -977,11 +977,11 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map | |||
977 | if (ret < 0) | 977 | if (ret < 0) |
978 | return ret; | 978 | return ret; |
979 | } | 979 | } |
980 | spin_lock(&inode->i_lock); | 980 | if (S_ISDIR(inode->i_mode)) { |
981 | nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; | 981 | spin_lock(&inode->i_lock); |
982 | if (S_ISDIR(inode->i_mode)) | ||
983 | memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); | 982 | memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); |
984 | spin_unlock(&inode->i_lock); | 983 | spin_unlock(&inode->i_lock); |
984 | } | ||
985 | nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); | 985 | nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); |
986 | nfs_fscache_wait_on_invalidate(inode); | 986 | nfs_fscache_wait_on_invalidate(inode); |
987 | 987 | ||
@@ -1008,6 +1008,7 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode) | |||
1008 | int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) | 1008 | int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) |
1009 | { | 1009 | { |
1010 | struct nfs_inode *nfsi = NFS_I(inode); | 1010 | struct nfs_inode *nfsi = NFS_I(inode); |
1011 | unsigned long *bitlock = &nfsi->flags; | ||
1011 | int ret = 0; | 1012 | int ret = 0; |
1012 | 1013 | ||
1013 | /* swapfiles are not supposed to be shared. */ | 1014 | /* swapfiles are not supposed to be shared. */ |
@@ -1019,12 +1020,46 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) | |||
1019 | if (ret < 0) | 1020 | if (ret < 0) |
1020 | goto out; | 1021 | goto out; |
1021 | } | 1022 | } |
1022 | if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { | 1023 | |
1023 | trace_nfs_invalidate_mapping_enter(inode); | 1024 | /* |
1024 | ret = nfs_invalidate_mapping(inode, mapping); | 1025 | * We must clear NFS_INO_INVALID_DATA first to ensure that |
1025 | trace_nfs_invalidate_mapping_exit(inode, ret); | 1026 | * invalidations that come in while we're shooting down the mappings |
1027 | * are respected. But, that leaves a race window where one revalidator | ||
1028 | * can clear the flag, and then another checks it before the mapping | ||
1029 | * gets invalidated. Fix that by serializing access to this part of | ||
1030 | * the function. | ||
1031 | * | ||
1032 | * At the same time, we need to allow other tasks to see whether we | ||
1033 | * might be in the middle of invalidating the pages, so we only set | ||
1034 | * the bit lock here if it looks like we're going to be doing that. | ||
1035 | */ | ||
1036 | for (;;) { | ||
1037 | ret = wait_on_bit(bitlock, NFS_INO_INVALIDATING, | ||
1038 | nfs_wait_bit_killable, TASK_KILLABLE); | ||
1039 | if (ret) | ||
1040 | goto out; | ||
1041 | spin_lock(&inode->i_lock); | ||
1042 | if (test_bit(NFS_INO_INVALIDATING, bitlock)) { | ||
1043 | spin_unlock(&inode->i_lock); | ||
1044 | continue; | ||
1045 | } | ||
1046 | if (nfsi->cache_validity & NFS_INO_INVALID_DATA) | ||
1047 | break; | ||
1048 | spin_unlock(&inode->i_lock); | ||
1049 | goto out; | ||
1026 | } | 1050 | } |
1027 | 1051 | ||
1052 | set_bit(NFS_INO_INVALIDATING, bitlock); | ||
1053 | smp_wmb(); | ||
1054 | nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; | ||
1055 | spin_unlock(&inode->i_lock); | ||
1056 | trace_nfs_invalidate_mapping_enter(inode); | ||
1057 | ret = nfs_invalidate_mapping(inode, mapping); | ||
1058 | trace_nfs_invalidate_mapping_exit(inode, ret); | ||
1059 | |||
1060 | clear_bit_unlock(NFS_INO_INVALIDATING, bitlock); | ||
1061 | smp_mb__after_clear_bit(); | ||
1062 | wake_up_bit(bitlock, NFS_INO_INVALIDATING); | ||
1028 | out: | 1063 | out: |
1029 | return ret; | 1064 | return ret; |
1030 | } | 1065 | } |
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 5609edc742a0..a5b27c2d9689 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -270,6 +270,7 @@ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *ser | |||
270 | extern int nfs41_setup_sequence(struct nfs4_session *session, | 270 | extern int nfs41_setup_sequence(struct nfs4_session *session, |
271 | struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, | 271 | struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, |
272 | struct rpc_task *task); | 272 | struct rpc_task *task); |
273 | extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); | ||
273 | extern int nfs4_proc_create_session(struct nfs_client *, struct rpc_cred *); | 274 | extern int nfs4_proc_create_session(struct nfs_client *, struct rpc_cred *); |
274 | extern int nfs4_proc_destroy_session(struct nfs4_session *, struct rpc_cred *); | 275 | extern int nfs4_proc_destroy_session(struct nfs4_session *, struct rpc_cred *); |
275 | extern int nfs4_proc_get_lease_time(struct nfs_client *clp, | 276 | extern int nfs4_proc_get_lease_time(struct nfs_client *clp, |
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 73d4ecda1e36..dbb3e1f30c68 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c | |||
@@ -372,10 +372,7 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp, | |||
372 | __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); | 372 | __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); |
373 | __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags); | 373 | __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags); |
374 | 374 | ||
375 | error = -EINVAL; | 375 | error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_GSS_KRB5I); |
376 | if (gssd_running(clp->cl_net)) | ||
377 | error = nfs_create_rpc_client(clp, timeparms, | ||
378 | RPC_AUTH_GSS_KRB5I); | ||
379 | if (error == -EINVAL) | 376 | if (error == -EINVAL) |
380 | error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX); | 377 | error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX); |
381 | if (error < 0) | 378 | if (error < 0) |
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 03fd8be8c0c5..12c8132ad408 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c | |||
@@ -335,8 +335,10 @@ static void filelayout_read_call_done(struct rpc_task *task, void *data) | |||
335 | dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status); | 335 | dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status); |
336 | 336 | ||
337 | if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) && | 337 | if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) && |
338 | task->tk_status == 0) | 338 | task->tk_status == 0) { |
339 | nfs41_sequence_done(task, &rdata->res.seq_res); | ||
339 | return; | 340 | return; |
341 | } | ||
340 | 342 | ||
341 | /* Note this may cause RPC to be resent */ | 343 | /* Note this may cause RPC to be resent */ |
342 | rdata->header->mds_ops->rpc_call_done(task, data); | 344 | rdata->header->mds_ops->rpc_call_done(task, data); |
@@ -442,8 +444,10 @@ static void filelayout_write_call_done(struct rpc_task *task, void *data) | |||
442 | struct nfs_write_data *wdata = data; | 444 | struct nfs_write_data *wdata = data; |
443 | 445 | ||
444 | if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) && | 446 | if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) && |
445 | task->tk_status == 0) | 447 | task->tk_status == 0) { |
448 | nfs41_sequence_done(task, &wdata->res.seq_res); | ||
446 | return; | 449 | return; |
450 | } | ||
447 | 451 | ||
448 | /* Note this may cause RPC to be resent */ | 452 | /* Note this may cause RPC to be resent */ |
449 | wdata->header->mds_ops->rpc_call_done(task, data); | 453 | wdata->header->mds_ops->rpc_call_done(task, data); |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a1965329a12c..42da6af77587 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -539,7 +539,7 @@ static int nfs40_sequence_done(struct rpc_task *task, | |||
539 | struct nfs4_slot *slot = res->sr_slot; | 539 | struct nfs4_slot *slot = res->sr_slot; |
540 | struct nfs4_slot_table *tbl; | 540 | struct nfs4_slot_table *tbl; |
541 | 541 | ||
542 | if (!RPC_WAS_SENT(task)) | 542 | if (slot == NULL) |
543 | goto out; | 543 | goto out; |
544 | 544 | ||
545 | tbl = slot->table; | 545 | tbl = slot->table; |
@@ -559,15 +559,10 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) | |||
559 | { | 559 | { |
560 | struct nfs4_session *session; | 560 | struct nfs4_session *session; |
561 | struct nfs4_slot_table *tbl; | 561 | struct nfs4_slot_table *tbl; |
562 | struct nfs4_slot *slot = res->sr_slot; | ||
562 | bool send_new_highest_used_slotid = false; | 563 | bool send_new_highest_used_slotid = false; |
563 | 564 | ||
564 | if (!res->sr_slot) { | 565 | tbl = slot->table; |
565 | /* just wake up the next guy waiting since | ||
566 | * we may have not consumed a slot after all */ | ||
567 | dprintk("%s: No slot\n", __func__); | ||
568 | return; | ||
569 | } | ||
570 | tbl = res->sr_slot->table; | ||
571 | session = tbl->session; | 566 | session = tbl->session; |
572 | 567 | ||
573 | spin_lock(&tbl->slot_tbl_lock); | 568 | spin_lock(&tbl->slot_tbl_lock); |
@@ -577,11 +572,11 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) | |||
577 | if (tbl->highest_used_slotid > tbl->target_highest_slotid) | 572 | if (tbl->highest_used_slotid > tbl->target_highest_slotid) |
578 | send_new_highest_used_slotid = true; | 573 | send_new_highest_used_slotid = true; |
579 | 574 | ||
580 | if (nfs41_wake_and_assign_slot(tbl, res->sr_slot)) { | 575 | if (nfs41_wake_and_assign_slot(tbl, slot)) { |
581 | send_new_highest_used_slotid = false; | 576 | send_new_highest_used_slotid = false; |
582 | goto out_unlock; | 577 | goto out_unlock; |
583 | } | 578 | } |
584 | nfs4_free_slot(tbl, res->sr_slot); | 579 | nfs4_free_slot(tbl, slot); |
585 | 580 | ||
586 | if (tbl->highest_used_slotid != NFS4_NO_SLOT) | 581 | if (tbl->highest_used_slotid != NFS4_NO_SLOT) |
587 | send_new_highest_used_slotid = false; | 582 | send_new_highest_used_slotid = false; |
@@ -592,19 +587,20 @@ out_unlock: | |||
592 | nfs41_server_notify_highest_slotid_update(session->clp); | 587 | nfs41_server_notify_highest_slotid_update(session->clp); |
593 | } | 588 | } |
594 | 589 | ||
595 | static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) | 590 | int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) |
596 | { | 591 | { |
597 | struct nfs4_session *session; | 592 | struct nfs4_session *session; |
598 | struct nfs4_slot *slot; | 593 | struct nfs4_slot *slot = res->sr_slot; |
599 | struct nfs_client *clp; | 594 | struct nfs_client *clp; |
600 | bool interrupted = false; | 595 | bool interrupted = false; |
601 | int ret = 1; | 596 | int ret = 1; |
602 | 597 | ||
598 | if (slot == NULL) | ||
599 | goto out_noaction; | ||
603 | /* don't increment the sequence number if the task wasn't sent */ | 600 | /* don't increment the sequence number if the task wasn't sent */ |
604 | if (!RPC_WAS_SENT(task)) | 601 | if (!RPC_WAS_SENT(task)) |
605 | goto out; | 602 | goto out; |
606 | 603 | ||
607 | slot = res->sr_slot; | ||
608 | session = slot->table->session; | 604 | session = slot->table->session; |
609 | 605 | ||
610 | if (slot->interrupted) { | 606 | if (slot->interrupted) { |
@@ -679,6 +675,7 @@ out: | |||
679 | /* The session may be reset by one of the error handlers. */ | 675 | /* The session may be reset by one of the error handlers. */ |
680 | dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); | 676 | dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); |
681 | nfs41_sequence_free_slot(res); | 677 | nfs41_sequence_free_slot(res); |
678 | out_noaction: | ||
682 | return ret; | 679 | return ret; |
683 | retry_nowait: | 680 | retry_nowait: |
684 | if (rpc_restart_call_prepare(task)) { | 681 | if (rpc_restart_call_prepare(task)) { |
@@ -692,6 +689,7 @@ out_retry: | |||
692 | rpc_delay(task, NFS4_POLL_RETRY_MAX); | 689 | rpc_delay(task, NFS4_POLL_RETRY_MAX); |
693 | return 0; | 690 | return 0; |
694 | } | 691 | } |
692 | EXPORT_SYMBOL_GPL(nfs41_sequence_done); | ||
695 | 693 | ||
696 | static int nfs4_sequence_done(struct rpc_task *task, | 694 | static int nfs4_sequence_done(struct rpc_task *task, |
697 | struct nfs4_sequence_res *res) | 695 | struct nfs4_sequence_res *res) |
@@ -2744,7 +2742,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f | |||
2744 | NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME| | 2742 | NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME| |
2745 | NFS_CAP_CTIME|NFS_CAP_MTIME| | 2743 | NFS_CAP_CTIME|NFS_CAP_MTIME| |
2746 | NFS_CAP_SECURITY_LABEL); | 2744 | NFS_CAP_SECURITY_LABEL); |
2747 | if (res.attr_bitmask[0] & FATTR4_WORD0_ACL) | 2745 | if (res.attr_bitmask[0] & FATTR4_WORD0_ACL && |
2746 | res.acl_bitmask & ACL4_SUPPORT_ALLOW_ACL) | ||
2748 | server->caps |= NFS_CAP_ACLS; | 2747 | server->caps |= NFS_CAP_ACLS; |
2749 | if (res.has_links != 0) | 2748 | if (res.has_links != 0) |
2750 | server->caps |= NFS_CAP_HARDLINKS; | 2749 | server->caps |= NFS_CAP_HARDLINKS; |
@@ -4321,9 +4320,7 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred) | |||
4321 | 4320 | ||
4322 | static inline int nfs4_server_supports_acls(struct nfs_server *server) | 4321 | static inline int nfs4_server_supports_acls(struct nfs_server *server) |
4323 | { | 4322 | { |
4324 | return (server->caps & NFS_CAP_ACLS) | 4323 | return server->caps & NFS_CAP_ACLS; |
4325 | && (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL) | ||
4326 | && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); | ||
4327 | } | 4324 | } |
4328 | 4325 | ||
4329 | /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that | 4326 | /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 8c21d69a9dc1..72f3bf1754ef 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -3449,7 +3449,7 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint | |||
3449 | { | 3449 | { |
3450 | __be32 *p; | 3450 | __be32 *p; |
3451 | 3451 | ||
3452 | *res = ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL; | 3452 | *res = 0; |
3453 | if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U))) | 3453 | if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U))) |
3454 | return -EIO; | 3454 | return -EIO; |
3455 | if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) { | 3455 | if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) { |
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h index 89fe741e58b1..59f838cdc009 100644 --- a/fs/nfs/nfstrace.h +++ b/fs/nfs/nfstrace.h | |||
@@ -36,6 +36,7 @@ | |||
36 | __print_flags(v, "|", \ | 36 | __print_flags(v, "|", \ |
37 | { 1 << NFS_INO_ADVISE_RDPLUS, "ADVISE_RDPLUS" }, \ | 37 | { 1 << NFS_INO_ADVISE_RDPLUS, "ADVISE_RDPLUS" }, \ |
38 | { 1 << NFS_INO_STALE, "STALE" }, \ | 38 | { 1 << NFS_INO_STALE, "STALE" }, \ |
39 | { 1 << NFS_INO_INVALIDATING, "INVALIDATING" }, \ | ||
39 | { 1 << NFS_INO_FLUSHING, "FLUSHING" }, \ | 40 | { 1 << NFS_INO_FLUSHING, "FLUSHING" }, \ |
40 | { 1 << NFS_INO_FSCACHE, "FSCACHE" }, \ | 41 | { 1 << NFS_INO_FSCACHE, "FSCACHE" }, \ |
41 | { 1 << NFS_INO_COMMIT, "COMMIT" }, \ | 42 | { 1 << NFS_INO_COMMIT, "COMMIT" }, \ |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index a44a87268a6e..9a3b6a4cd6b9 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -909,9 +909,14 @@ bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx) | |||
909 | */ | 909 | */ |
910 | static bool nfs_write_pageuptodate(struct page *page, struct inode *inode) | 910 | static bool nfs_write_pageuptodate(struct page *page, struct inode *inode) |
911 | { | 911 | { |
912 | struct nfs_inode *nfsi = NFS_I(inode); | ||
913 | |||
912 | if (nfs_have_delegated_attributes(inode)) | 914 | if (nfs_have_delegated_attributes(inode)) |
913 | goto out; | 915 | goto out; |
914 | if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_DATA|NFS_INO_REVAL_PAGECACHE)) | 916 | if (nfsi->cache_validity & (NFS_INO_INVALID_DATA|NFS_INO_REVAL_PAGECACHE)) |
917 | return false; | ||
918 | smp_rmb(); | ||
919 | if (test_bit(NFS_INO_INVALIDATING, &nfsi->flags)) | ||
915 | return false; | 920 | return false; |
916 | out: | 921 | out: |
917 | return PageUptodate(page) != 0; | 922 | return PageUptodate(page) != 0; |