aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/inode.c
diff options
context:
space:
mode:
authorSteve French <sfrench@us.ibm.com>2007-02-26 11:46:11 -0500
committerSteve French <sfrench@us.ibm.com>2007-02-26 11:46:11 -0500
commit3677db10a635a39f63ea509f8f0056d95589ff90 (patch)
tree5256a408110c91947d9b9543199003fb976948a8 /fs/cifs/inode.c
parent9654640d0af8f2de40ff3807d3695109d3463f54 (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/inode.c')
-rw-r--r--fs/cifs/inode.c50
1 files changed, 48 insertions, 2 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 37c6ce87416b..24df13a256e5 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -143,10 +143,10 @@ int cifs_get_inode_info_unix(struct inode **pinode,
143 inode->i_gid = le64_to_cpu(findData.Gid); 143 inode->i_gid = le64_to_cpu(findData.Gid);
144 inode->i_nlink = le64_to_cpu(findData.Nlinks); 144 inode->i_nlink = le64_to_cpu(findData.Nlinks);
145 145
146 spin_lock(&inode->i_lock);
146 if (is_size_safe_to_change(cifsInfo, end_of_file)) { 147 if (is_size_safe_to_change(cifsInfo, end_of_file)) {
147 /* can not safely change the file size here if the 148 /* can not safely change the file size here if the
148 client is writing to it due to potential races */ 149 client is writing to it due to potential races */
149
150 i_size_write(inode, end_of_file); 150 i_size_write(inode, end_of_file);
151 151
152 /* blksize needs to be multiple of two. So safer to default to 152 /* blksize needs to be multiple of two. So safer to default to
@@ -162,6 +162,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
162 /* for this calculation */ 162 /* for this calculation */
163 inode->i_blocks = (512 - 1 + num_of_bytes) >> 9; 163 inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
164 } 164 }
165 spin_unlock(&inode->i_lock);
165 166
166 if (num_of_bytes < end_of_file) 167 if (num_of_bytes < end_of_file)
167 cFYI(1, ("allocation size less than end of file")); 168 cFYI(1, ("allocation size less than end of file"));
@@ -496,6 +497,8 @@ int cifs_get_inode_info(struct inode **pinode,
496 /* BB add code here - 497 /* BB add code here -
497 validate if device or weird share or device type? */ 498 validate if device or weird share or device type? */
498 } 499 }
500
501 spin_lock(&inode->i_lock);
499 if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) { 502 if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) {
500 /* can not safely shrink the file size here if the 503 /* can not safely shrink the file size here if the
501 client is writing to it due to potential races */ 504 client is writing to it due to potential races */
@@ -506,6 +509,7 @@ int cifs_get_inode_info(struct inode **pinode,
506 inode->i_blocks = (512 - 1 + le64_to_cpu( 509 inode->i_blocks = (512 - 1 + le64_to_cpu(
507 pfindData->AllocationSize)) >> 9; 510 pfindData->AllocationSize)) >> 9;
508 } 511 }
512 spin_unlock(&inode->i_lock);
509 513
510 inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); 514 inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
511 515
@@ -834,8 +838,10 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
834 838
835 if (!rc) { 839 if (!rc) {
836 drop_nlink(inode); 840 drop_nlink(inode);
841 spin_lock(&direntry->d_inode->i_lock);
837 i_size_write(direntry->d_inode,0); 842 i_size_write(direntry->d_inode,0);
838 clear_nlink(direntry->d_inode); 843 clear_nlink(direntry->d_inode);
844 spin_unlock(&direntry->d_inode->i_lock);
839 } 845 }
840 846
841 cifsInode = CIFS_I(direntry->d_inode); 847 cifsInode = CIFS_I(direntry->d_inode);
@@ -1128,6 +1134,46 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from)
1128 return rc; 1134 return rc;
1129} 1135}
1130 1136
1137int cifs_vmtruncate(struct inode * inode, loff_t offset)
1138{
1139 struct address_space *mapping = inode->i_mapping;
1140 unsigned long limit;
1141
1142 if (inode->i_size < offset)
1143 goto do_expand;
1144 /*
1145 * truncation of in-use swapfiles is disallowed - it would cause
1146 * subsequent swapout to scribble on the now-freed blocks.
1147 */
1148 if (IS_SWAPFILE(inode))
1149 goto out_busy;
1150 spin_lock(&inode->i_lock);
1151 i_size_write(inode, offset);
1152 spin_unlock(&inode->i_lock);
1153 unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
1154 truncate_inode_pages(mapping, offset);
1155 goto out_truncate;
1156
1157do_expand:
1158 limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
1159 if (limit != RLIM_INFINITY && offset > limit)
1160 goto out_sig;
1161 if (offset > inode->i_sb->s_maxbytes)
1162 goto out_big;
1163 i_size_write(inode, offset);
1164
1165out_truncate:
1166 if (inode->i_op && inode->i_op->truncate)
1167 inode->i_op->truncate(inode);
1168 return 0;
1169out_sig:
1170 send_sig(SIGXFSZ, current, 0);
1171out_big:
1172 return -EFBIG;
1173out_busy:
1174 return -ETXTBSY;
1175}
1176
1131int cifs_setattr(struct dentry *direntry, struct iattr *attrs) 1177int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
1132{ 1178{
1133 int xid; 1179 int xid;
@@ -1244,7 +1290,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
1244 */ 1290 */
1245 1291
1246 if (rc == 0) { 1292 if (rc == 0) {
1247 rc = vmtruncate(direntry->d_inode, attrs->ia_size); 1293 rc = cifs_vmtruncate(direntry->d_inode, attrs->ia_size);
1248 cifs_truncate_page(direntry->d_inode->i_mapping, 1294 cifs_truncate_page(direntry->d_inode->i_mapping,
1249 direntry->d_inode->i_size); 1295 direntry->d_inode->i_size);
1250 } else 1296 } else