diff options
author | Pavel Shilovsky <piastry@etersoft.ru> | 2012-12-21 06:05:47 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2013-01-01 23:59:55 -0500 |
commit | ca8aa29c60238720af2ca2a5caab25fa0c70067e (patch) | |
tree | cc5940d0c0acaa31b19793b456423764ac97afe0 /fs | |
parent | 31efee60f489c759c341454d755a9fd13de8c03d (diff) |
Revert "CIFS: Fix write after setting a read lock for read oplock files"
that solution has data races and can end up two identical writes to the
server: when clientCanCacheAll value can be changed during the execution
of __generic_file_aio_write.
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifsfs.c | 1 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
-rw-r--r-- | fs/cifs/file.c | 94 |
3 files changed, 31 insertions, 65 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f653835d067b..de7f9168a118 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -228,7 +228,6 @@ cifs_alloc_inode(struct super_block *sb) | |||
228 | cifs_set_oplock_level(cifs_inode, 0); | 228 | cifs_set_oplock_level(cifs_inode, 0); |
229 | cifs_inode->delete_pending = false; | 229 | cifs_inode->delete_pending = false; |
230 | cifs_inode->invalid_mapping = false; | 230 | cifs_inode->invalid_mapping = false; |
231 | cifs_inode->leave_pages_clean = false; | ||
232 | cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ | 231 | cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ |
233 | cifs_inode->server_eof = 0; | 232 | cifs_inode->server_eof = 0; |
234 | cifs_inode->uniqueid = 0; | 233 | cifs_inode->uniqueid = 0; |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index aea1eec64911..dfab450a191e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -1030,7 +1030,6 @@ struct cifsInodeInfo { | |||
1030 | bool clientCanCacheAll; /* read and writebehind oplock */ | 1030 | bool clientCanCacheAll; /* read and writebehind oplock */ |
1031 | bool delete_pending; /* DELETE_ON_CLOSE is set */ | 1031 | bool delete_pending; /* DELETE_ON_CLOSE is set */ |
1032 | bool invalid_mapping; /* pagecache is invalid */ | 1032 | bool invalid_mapping; /* pagecache is invalid */ |
1033 | bool leave_pages_clean; /* protected by i_mutex, not set pages dirty */ | ||
1034 | unsigned long time; /* jiffies of last update of inode */ | 1033 | unsigned long time; /* jiffies of last update of inode */ |
1035 | u64 server_eof; /* current file size on server -- protected by i_lock */ | 1034 | u64 server_eof; /* current file size on server -- protected by i_lock */ |
1036 | u64 uniqueid; /* server inode number */ | 1035 | u64 uniqueid; /* server inode number */ |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0a6677ba212b..1b322d041f1e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -2103,15 +2103,7 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, | |||
2103 | } else { | 2103 | } else { |
2104 | rc = copied; | 2104 | rc = copied; |
2105 | pos += copied; | 2105 | pos += copied; |
2106 | /* | 2106 | set_page_dirty(page); |
2107 | * When we use strict cache mode and cifs_strict_writev was run | ||
2108 | * with level II oplock (indicated by leave_pages_clean field of | ||
2109 | * CIFS_I(inode)), we can leave pages clean - cifs_strict_writev | ||
2110 | * sent the data to the server itself. | ||
2111 | */ | ||
2112 | if (!CIFS_I(inode)->leave_pages_clean || | ||
2113 | !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)) | ||
2114 | set_page_dirty(page); | ||
2115 | } | 2107 | } |
2116 | 2108 | ||
2117 | if (rc > 0) { | 2109 | if (rc > 0) { |
@@ -2462,8 +2454,8 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, | |||
2462 | } | 2454 | } |
2463 | 2455 | ||
2464 | static ssize_t | 2456 | static ssize_t |
2465 | cifs_pagecache_writev(struct kiocb *iocb, const struct iovec *iov, | 2457 | cifs_writev(struct kiocb *iocb, const struct iovec *iov, |
2466 | unsigned long nr_segs, loff_t pos, bool cache_ex) | 2458 | unsigned long nr_segs, loff_t pos) |
2467 | { | 2459 | { |
2468 | struct file *file = iocb->ki_filp; | 2460 | struct file *file = iocb->ki_filp; |
2469 | struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; | 2461 | struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; |
@@ -2485,12 +2477,8 @@ cifs_pagecache_writev(struct kiocb *iocb, const struct iovec *iov, | |||
2485 | server->vals->exclusive_lock_type, NULL, | 2477 | server->vals->exclusive_lock_type, NULL, |
2486 | CIFS_WRITE_OP)) { | 2478 | CIFS_WRITE_OP)) { |
2487 | mutex_lock(&inode->i_mutex); | 2479 | mutex_lock(&inode->i_mutex); |
2488 | if (!cache_ex) | ||
2489 | cinode->leave_pages_clean = true; | ||
2490 | rc = __generic_file_aio_write(iocb, iov, nr_segs, | 2480 | rc = __generic_file_aio_write(iocb, iov, nr_segs, |
2491 | &iocb->ki_pos); | 2481 | &iocb->ki_pos); |
2492 | if (!cache_ex) | ||
2493 | cinode->leave_pages_clean = false; | ||
2494 | mutex_unlock(&inode->i_mutex); | 2482 | mutex_unlock(&inode->i_mutex); |
2495 | } | 2483 | } |
2496 | 2484 | ||
@@ -2517,62 +2505,42 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, | |||
2517 | struct cifsFileInfo *cfile = (struct cifsFileInfo *) | 2505 | struct cifsFileInfo *cfile = (struct cifsFileInfo *) |
2518 | iocb->ki_filp->private_data; | 2506 | iocb->ki_filp->private_data; |
2519 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); | 2507 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); |
2520 | ssize_t written, written2; | 2508 | |
2509 | #ifdef CONFIG_CIFS_SMB2 | ||
2521 | /* | 2510 | /* |
2522 | * We need to store clientCanCacheAll here to prevent race | 2511 | * If we have an oplock for read and want to write a data to the file |
2523 | * conditions - this value can be changed during an execution | 2512 | * we need to store it in the page cache and then push it to the server |
2524 | * of generic_file_aio_write. For CIFS it can be changed from | 2513 | * to be sure the next read will get a valid data. |
2525 | * true to false only, but for SMB2 it can be changed both from | ||
2526 | * true to false and vice versa. So, we can end up with a data | ||
2527 | * stored in the cache, not marked dirty and not sent to the | ||
2528 | * server if this value changes its state from false to true | ||
2529 | * after cifs_write_end. | ||
2530 | */ | 2514 | */ |
2531 | bool cache_ex = cinode->clientCanCacheAll; | 2515 | if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead) { |
2532 | bool cache_read = cinode->clientCanCacheRead; | 2516 | ssize_t written; |
2533 | int rc; | 2517 | int rc; |
2534 | loff_t saved_pos; | 2518 | |
2519 | written = generic_file_aio_write(iocb, iov, nr_segs, pos); | ||
2520 | rc = filemap_fdatawrite(inode->i_mapping); | ||
2521 | if (rc) | ||
2522 | return (ssize_t)rc; | ||
2535 | 2523 | ||
2536 | if (cache_ex) { | 2524 | return written; |
2537 | if (cap_unix(tcon->ses) && | ||
2538 | ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) && | ||
2539 | (CIFS_UNIX_FCNTL_CAP & le64_to_cpu( | ||
2540 | tcon->fsUnixInfo.Capability))) | ||
2541 | return generic_file_aio_write(iocb, iov, nr_segs, pos); | ||
2542 | return cifs_pagecache_writev(iocb, iov, nr_segs, pos, cache_ex); | ||
2543 | } | 2525 | } |
2526 | #endif | ||
2544 | 2527 | ||
2545 | /* | 2528 | /* |
2546 | * For files without exclusive oplock in strict cache mode we need to | 2529 | * For non-oplocked files in strict cache mode we need to write the data |
2547 | * write the data to the server exactly from the pos to pos+len-1 rather | 2530 | * to the server exactly from the pos to pos+len-1 rather than flush all |
2548 | * than flush all affected pages because it may cause a error with | 2531 | * affected pages because it may cause a error with mandatory locks on |
2549 | * mandatory locks on these pages but not on the region from pos to | 2532 | * these pages but not on the region from pos to ppos+len-1. |
2550 | * ppos+len-1. | ||
2551 | */ | 2533 | */ |
2552 | written = cifs_user_writev(iocb, iov, nr_segs, pos); | ||
2553 | if (!cache_read || written <= 0) | ||
2554 | return written; | ||
2555 | 2534 | ||
2556 | saved_pos = iocb->ki_pos; | 2535 | if (!cinode->clientCanCacheAll) |
2557 | iocb->ki_pos = pos; | 2536 | return cifs_user_writev(iocb, iov, nr_segs, pos); |
2558 | /* we have a read oplock - need to store a data in the page cache */ | 2537 | |
2559 | if (cap_unix(tcon->ses) && | 2538 | if (cap_unix(tcon->ses) && |
2560 | ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) && | 2539 | (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && |
2561 | (CIFS_UNIX_FCNTL_CAP & le64_to_cpu( | 2540 | ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) |
2562 | tcon->fsUnixInfo.Capability))) | 2541 | return generic_file_aio_write(iocb, iov, nr_segs, pos); |
2563 | written2 = generic_file_aio_write(iocb, iov, nr_segs, pos); | 2542 | |
2564 | else | 2543 | return cifs_writev(iocb, iov, nr_segs, pos); |
2565 | written2 = cifs_pagecache_writev(iocb, iov, nr_segs, pos, | ||
2566 | cache_ex); | ||
2567 | /* errors occured during writing - invalidate the page cache */ | ||
2568 | if (written2 < 0) { | ||
2569 | rc = cifs_invalidate_mapping(inode); | ||
2570 | if (rc) | ||
2571 | written = (ssize_t)rc; | ||
2572 | else | ||
2573 | iocb->ki_pos = saved_pos; | ||
2574 | } | ||
2575 | return written; | ||
2576 | } | 2544 | } |
2577 | 2545 | ||
2578 | static struct cifs_readdata * | 2546 | static struct cifs_readdata * |