diff options
author | Pavel Shilovsky <piastry@etersoft.ru> | 2012-12-06 13:07:52 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-12-11 12:48:50 -0500 |
commit | c299dd0e2d3dd61d0048a9d9b021aa01f023ed0c (patch) | |
tree | 5d3f913eea056a57b2d7086742f2d55b7d78c6f4 /fs/cifs | |
parent | d387a5c50bca619d56f276a69627c2e1c6e5c548 (diff) |
CIFS: Fix write after setting a read lock for read oplock files
If we have a read oplock and set a read lock in it, we can't write to the
locked area - so, filemap_fdatawrite may fail with a no information for a
userspace application even if we request a write to non-locked area. Fix
this by populating the page cache without marking affected pages dirty
after a successful write directly to the server.
Also remove CONFIG_CIFS_SMB2 ifdefs because it's suitable for both CIFS
and SMB2 protocols.
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-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, 65 insertions, 31 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index c6e32f22fbd3..210f0af83fc4 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -229,6 +229,7 @@ cifs_alloc_inode(struct super_block *sb) | |||
229 | cifs_set_oplock_level(cifs_inode, 0); | 229 | cifs_set_oplock_level(cifs_inode, 0); |
230 | cifs_inode->delete_pending = false; | 230 | cifs_inode->delete_pending = false; |
231 | cifs_inode->invalid_mapping = false; | 231 | cifs_inode->invalid_mapping = false; |
232 | cifs_inode->leave_pages_clean = false; | ||
232 | cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ | 233 | cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ |
233 | cifs_inode->server_eof = 0; | 234 | cifs_inode->server_eof = 0; |
234 | cifs_inode->uniqueid = 0; | 235 | cifs_inode->uniqueid = 0; |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index dfab450a191e..aea1eec64911 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -1030,6 +1030,7 @@ 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 */ | ||
1033 | unsigned long time; /* jiffies of last update of inode */ | 1034 | unsigned long time; /* jiffies of last update of inode */ |
1034 | u64 server_eof; /* current file size on server -- protected by i_lock */ | 1035 | u64 server_eof; /* current file size on server -- protected by i_lock */ |
1035 | u64 uniqueid; /* server inode number */ | 1036 | u64 uniqueid; /* server inode number */ |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1b322d041f1e..0a6677ba212b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -2103,7 +2103,15 @@ 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 | set_page_dirty(page); | 2106 | /* |
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); | ||
2107 | } | 2115 | } |
2108 | 2116 | ||
2109 | if (rc > 0) { | 2117 | if (rc > 0) { |
@@ -2454,8 +2462,8 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, | |||
2454 | } | 2462 | } |
2455 | 2463 | ||
2456 | static ssize_t | 2464 | static ssize_t |
2457 | cifs_writev(struct kiocb *iocb, const struct iovec *iov, | 2465 | cifs_pagecache_writev(struct kiocb *iocb, const struct iovec *iov, |
2458 | unsigned long nr_segs, loff_t pos) | 2466 | unsigned long nr_segs, loff_t pos, bool cache_ex) |
2459 | { | 2467 | { |
2460 | struct file *file = iocb->ki_filp; | 2468 | struct file *file = iocb->ki_filp; |
2461 | struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; | 2469 | struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; |
@@ -2477,8 +2485,12 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov, | |||
2477 | server->vals->exclusive_lock_type, NULL, | 2485 | server->vals->exclusive_lock_type, NULL, |
2478 | CIFS_WRITE_OP)) { | 2486 | CIFS_WRITE_OP)) { |
2479 | mutex_lock(&inode->i_mutex); | 2487 | mutex_lock(&inode->i_mutex); |
2488 | if (!cache_ex) | ||
2489 | cinode->leave_pages_clean = true; | ||
2480 | rc = __generic_file_aio_write(iocb, iov, nr_segs, | 2490 | rc = __generic_file_aio_write(iocb, iov, nr_segs, |
2481 | &iocb->ki_pos); | 2491 | &iocb->ki_pos); |
2492 | if (!cache_ex) | ||
2493 | cinode->leave_pages_clean = false; | ||
2482 | mutex_unlock(&inode->i_mutex); | 2494 | mutex_unlock(&inode->i_mutex); |
2483 | } | 2495 | } |
2484 | 2496 | ||
@@ -2505,42 +2517,62 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, | |||
2505 | struct cifsFileInfo *cfile = (struct cifsFileInfo *) | 2517 | struct cifsFileInfo *cfile = (struct cifsFileInfo *) |
2506 | iocb->ki_filp->private_data; | 2518 | iocb->ki_filp->private_data; |
2507 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); | 2519 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); |
2508 | 2520 | ssize_t written, written2; | |
2509 | #ifdef CONFIG_CIFS_SMB2 | ||
2510 | /* | 2521 | /* |
2511 | * If we have an oplock for read and want to write a data to the file | 2522 | * We need to store clientCanCacheAll here to prevent race |
2512 | * we need to store it in the page cache and then push it to the server | 2523 | * conditions - this value can be changed during an execution |
2513 | * to be sure the next read will get a valid data. | 2524 | * of generic_file_aio_write. For CIFS it can be changed from |
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. | ||
2514 | */ | 2530 | */ |
2515 | if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead) { | 2531 | bool cache_ex = cinode->clientCanCacheAll; |
2516 | ssize_t written; | 2532 | bool cache_read = cinode->clientCanCacheRead; |
2517 | int rc; | 2533 | int rc; |
2518 | 2534 | loff_t saved_pos; | |
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; | ||
2523 | 2535 | ||
2524 | return written; | 2536 | if (cache_ex) { |
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); | ||
2525 | } | 2543 | } |
2526 | #endif | ||
2527 | 2544 | ||
2528 | /* | 2545 | /* |
2529 | * For non-oplocked files in strict cache mode we need to write the data | 2546 | * For files without exclusive oplock in strict cache mode we need to |
2530 | * to the server exactly from the pos to pos+len-1 rather than flush all | 2547 | * write the data to the server exactly from the pos to pos+len-1 rather |
2531 | * affected pages because it may cause a error with mandatory locks on | 2548 | * than flush all affected pages because it may cause a error with |
2532 | * these pages but not on the region from pos to ppos+len-1. | 2549 | * mandatory locks on these pages but not on the region from pos to |
2550 | * ppos+len-1. | ||
2533 | */ | 2551 | */ |
2552 | written = cifs_user_writev(iocb, iov, nr_segs, pos); | ||
2553 | if (!cache_read || written <= 0) | ||
2554 | return written; | ||
2534 | 2555 | ||
2535 | if (!cinode->clientCanCacheAll) | 2556 | saved_pos = iocb->ki_pos; |
2536 | return cifs_user_writev(iocb, iov, nr_segs, pos); | 2557 | iocb->ki_pos = pos; |
2537 | 2558 | /* we have a read oplock - need to store a data in the page cache */ | |
2538 | if (cap_unix(tcon->ses) && | 2559 | if (cap_unix(tcon->ses) && |
2539 | (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && | 2560 | ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) && |
2540 | ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) | 2561 | (CIFS_UNIX_FCNTL_CAP & le64_to_cpu( |
2541 | return generic_file_aio_write(iocb, iov, nr_segs, pos); | 2562 | tcon->fsUnixInfo.Capability))) |
2542 | 2563 | written2 = generic_file_aio_write(iocb, iov, nr_segs, pos); | |
2543 | return cifs_writev(iocb, iov, nr_segs, pos); | 2564 | else |
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; | ||
2544 | } | 2576 | } |
2545 | 2577 | ||
2546 | static struct cifs_readdata * | 2578 | static struct cifs_readdata * |