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/inode.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/inode.c')
-rw-r--r-- | fs/cifs/inode.c | 50 |
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 | ||
1137 | int 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 | |||
1157 | do_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 | |||
1165 | out_truncate: | ||
1166 | if (inode->i_op && inode->i_op->truncate) | ||
1167 | inode->i_op->truncate(inode); | ||
1168 | return 0; | ||
1169 | out_sig: | ||
1170 | send_sig(SIGXFSZ, current, 0); | ||
1171 | out_big: | ||
1172 | return -EFBIG; | ||
1173 | out_busy: | ||
1174 | return -ETXTBSY; | ||
1175 | } | ||
1176 | |||
1131 | int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | 1177 | int 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 |