diff options
author | Steve French <sfrench@us.ibm.com> | 2007-02-26 11:46:11 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2007-02-26 11:46:11 -0500 |
commit | 3677db10a635a39f63ea509f8f0056d95589ff90 (patch) | |
tree | 5256a408110c91947d9b9543199003fb976948a8 /fs/cifs/file.c | |
parent | 9654640d0af8f2de40ff3807d3695109d3463f54 (diff) |
[CIFS] Fix locking problem around some cifs uses of i_size write
Could cause hangs on smp systems in i_size_read on a cifs inode
whose size has been previously simultaneously updated from
different processes.
Thanks to Brian Wang for some great testing/debugging on this
hard problem.
Fixes kernel bugzilla #7903
CC: Shirish Pargoankar <shirishp@us.ibm.com>
CC: Shaggy <shaggy@us.ibm.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r-- | fs/cifs/file.c | 43 |
1 files changed, 23 insertions, 20 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index a1265c9bfec0..c07ff8317a8b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -879,18 +879,19 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, | |||
879 | cifs_stats_bytes_written(pTcon, total_written); | 879 | cifs_stats_bytes_written(pTcon, total_written); |
880 | 880 | ||
881 | /* since the write may have blocked check these pointers again */ | 881 | /* since the write may have blocked check these pointers again */ |
882 | if (file->f_path.dentry) { | 882 | if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { |
883 | if (file->f_path.dentry->d_inode) { | 883 | struct inode *inode = file->f_path.dentry->d_inode; |
884 | struct inode *inode = file->f_path.dentry->d_inode; | 884 | /* Do not update local mtime - server will set its actual value on write |
885 | inode->i_ctime = inode->i_mtime = | 885 | * inode->i_ctime = inode->i_mtime = |
886 | current_fs_time(inode->i_sb); | 886 | * current_fs_time(inode->i_sb);*/ |
887 | if (total_written > 0) { | 887 | if (total_written > 0) { |
888 | if (*poffset > file->f_path.dentry->d_inode->i_size) | 888 | spin_lock(&inode->i_lock); |
889 | i_size_write(file->f_path.dentry->d_inode, | 889 | if (*poffset > file->f_path.dentry->d_inode->i_size) |
890 | i_size_write(file->f_path.dentry->d_inode, | ||
890 | *poffset); | 891 | *poffset); |
891 | } | 892 | spin_unlock(&inode->i_lock); |
892 | mark_inode_dirty_sync(file->f_path.dentry->d_inode); | ||
893 | } | 893 | } |
894 | mark_inode_dirty_sync(file->f_path.dentry->d_inode); | ||
894 | } | 895 | } |
895 | FreeXid(xid); | 896 | FreeXid(xid); |
896 | return total_written; | 897 | return total_written; |
@@ -1012,18 +1013,18 @@ static ssize_t cifs_write(struct file *file, const char *write_data, | |||
1012 | cifs_stats_bytes_written(pTcon, total_written); | 1013 | cifs_stats_bytes_written(pTcon, total_written); |
1013 | 1014 | ||
1014 | /* since the write may have blocked check these pointers again */ | 1015 | /* since the write may have blocked check these pointers again */ |
1015 | if (file->f_path.dentry) { | 1016 | if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { |
1016 | if (file->f_path.dentry->d_inode) { | ||
1017 | /*BB We could make this contingent on superblock ATIME flag too */ | 1017 | /*BB We could make this contingent on superblock ATIME flag too */ |
1018 | /* file->f_path.dentry->d_inode->i_ctime = | 1018 | /* file->f_path.dentry->d_inode->i_ctime = |
1019 | file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/ | 1019 | file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/ |
1020 | if (total_written > 0) { | 1020 | if (total_written > 0) { |
1021 | if (*poffset > file->f_path.dentry->d_inode->i_size) | 1021 | spin_lock(&file->f_path.dentry->d_inode->i_lock); |
1022 | i_size_write(file->f_path.dentry->d_inode, | 1022 | if (*poffset > file->f_path.dentry->d_inode->i_size) |
1023 | *poffset); | 1023 | i_size_write(file->f_path.dentry->d_inode, |
1024 | } | 1024 | *poffset); |
1025 | mark_inode_dirty_sync(file->f_path.dentry->d_inode); | 1025 | spin_unlock(&file->f_path.dentry->d_inode->i_lock); |
1026 | } | 1026 | } |
1027 | mark_inode_dirty_sync(file->f_path.dentry->d_inode); | ||
1027 | } | 1028 | } |
1028 | FreeXid(xid); | 1029 | FreeXid(xid); |
1029 | return total_written; | 1030 | return total_written; |
@@ -1400,6 +1401,7 @@ static int cifs_commit_write(struct file *file, struct page *page, | |||
1400 | xid = GetXid(); | 1401 | xid = GetXid(); |
1401 | cFYI(1, ("commit write for page %p up to position %lld for %d", | 1402 | cFYI(1, ("commit write for page %p up to position %lld for %d", |
1402 | page, position, to)); | 1403 | page, position, to)); |
1404 | spin_lock(&inode->i_lock); | ||
1403 | if (position > inode->i_size) { | 1405 | if (position > inode->i_size) { |
1404 | i_size_write(inode, position); | 1406 | i_size_write(inode, position); |
1405 | /* if (file->private_data == NULL) { | 1407 | /* if (file->private_data == NULL) { |
@@ -1429,6 +1431,7 @@ static int cifs_commit_write(struct file *file, struct page *page, | |||
1429 | cFYI(1, (" SetEOF (commit write) rc = %d", rc)); | 1431 | cFYI(1, (" SetEOF (commit write) rc = %d", rc)); |
1430 | } */ | 1432 | } */ |
1431 | } | 1433 | } |
1434 | spin_unlock(&inode->i_lock); | ||
1432 | if (!PageUptodate(page)) { | 1435 | if (!PageUptodate(page)) { |
1433 | position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + offset; | 1436 | position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + offset; |
1434 | /* can not rely on (or let) writepage write this data */ | 1437 | /* can not rely on (or let) writepage write this data */ |