diff options
-rw-r--r-- | fs/ntfs/ChangeLog | 2 | ||||
-rw-r--r-- | fs/ntfs/aops.c | 51 |
2 files changed, 42 insertions, 11 deletions
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index 10fc2385efa7..7f24cb19c69e 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog | |||
@@ -84,6 +84,8 @@ ToDo/Notes: | |||
84 | - Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page | 84 | - Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page |
85 | lock protection over the buffer submission for i/o which allows the | 85 | lock protection over the buffer submission for i/o which allows the |
86 | removal of the get_bh()/put_bh() pairs for each buffer. | 86 | removal of the get_bh()/put_bh() pairs for each buffer. |
87 | - Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case | ||
88 | where a concurrent truncate has truncated the runlist under our feet. | ||
87 | 89 | ||
88 | 2.1.23 - Implement extension of resident files and make writing safe as well as | 90 | 2.1.23 - Implement extension of resident files and make writing safe as well as |
89 | many bug fixes, cleanups, and enhancements... | 91 | many bug fixes, cleanups, and enhancements... |
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 821dad7d14c8..59389a8801bc 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c | |||
@@ -204,6 +204,7 @@ static int ntfs_read_block(struct page *page) | |||
204 | nr = i = 0; | 204 | nr = i = 0; |
205 | do { | 205 | do { |
206 | u8 *kaddr; | 206 | u8 *kaddr; |
207 | int err; | ||
207 | 208 | ||
208 | if (unlikely(buffer_uptodate(bh))) | 209 | if (unlikely(buffer_uptodate(bh))) |
209 | continue; | 210 | continue; |
@@ -211,6 +212,7 @@ static int ntfs_read_block(struct page *page) | |||
211 | arr[nr++] = bh; | 212 | arr[nr++] = bh; |
212 | continue; | 213 | continue; |
213 | } | 214 | } |
215 | err = 0; | ||
214 | bh->b_bdev = vol->sb->s_bdev; | 216 | bh->b_bdev = vol->sb->s_bdev; |
215 | /* Is the block within the allowed limits? */ | 217 | /* Is the block within the allowed limits? */ |
216 | if (iblock < lblock) { | 218 | if (iblock < lblock) { |
@@ -252,7 +254,6 @@ lock_retry_remap: | |||
252 | goto handle_hole; | 254 | goto handle_hole; |
253 | /* If first try and runlist unmapped, map and retry. */ | 255 | /* If first try and runlist unmapped, map and retry. */ |
254 | if (!is_retry && lcn == LCN_RL_NOT_MAPPED) { | 256 | if (!is_retry && lcn == LCN_RL_NOT_MAPPED) { |
255 | int err; | ||
256 | is_retry = TRUE; | 257 | is_retry = TRUE; |
257 | /* | 258 | /* |
258 | * Attempt to map runlist, dropping lock for | 259 | * Attempt to map runlist, dropping lock for |
@@ -263,20 +264,30 @@ lock_retry_remap: | |||
263 | if (likely(!err)) | 264 | if (likely(!err)) |
264 | goto lock_retry_remap; | 265 | goto lock_retry_remap; |
265 | rl = NULL; | 266 | rl = NULL; |
266 | lcn = err; | ||
267 | } else if (!rl) | 267 | } else if (!rl) |
268 | up_read(&ni->runlist.lock); | 268 | up_read(&ni->runlist.lock); |
269 | /* | ||
270 | * If buffer is outside the runlist, treat it as a | ||
271 | * hole. This can happen due to concurrent truncate | ||
272 | * for example. | ||
273 | */ | ||
274 | if (err == -ENOENT || lcn == LCN_ENOENT) { | ||
275 | err = 0; | ||
276 | goto handle_hole; | ||
277 | } | ||
269 | /* Hard error, zero out region. */ | 278 | /* Hard error, zero out region. */ |
279 | if (!err) | ||
280 | err = -EIO; | ||
270 | bh->b_blocknr = -1; | 281 | bh->b_blocknr = -1; |
271 | SetPageError(page); | 282 | SetPageError(page); |
272 | ntfs_error(vol->sb, "Failed to read from inode 0x%lx, " | 283 | ntfs_error(vol->sb, "Failed to read from inode 0x%lx, " |
273 | "attribute type 0x%x, vcn 0x%llx, " | 284 | "attribute type 0x%x, vcn 0x%llx, " |
274 | "offset 0x%x because its location on " | 285 | "offset 0x%x because its location on " |
275 | "disk could not be determined%s " | 286 | "disk could not be determined%s " |
276 | "(error code %lli).", ni->mft_no, | 287 | "(error code %i).", ni->mft_no, |
277 | ni->type, (unsigned long long)vcn, | 288 | ni->type, (unsigned long long)vcn, |
278 | vcn_ofs, is_retry ? " even after " | 289 | vcn_ofs, is_retry ? " even after " |
279 | "retrying" : "", (long long)lcn); | 290 | "retrying" : "", err); |
280 | } | 291 | } |
281 | /* | 292 | /* |
282 | * Either iblock was outside lblock limits or | 293 | * Either iblock was outside lblock limits or |
@@ -289,9 +300,10 @@ handle_hole: | |||
289 | handle_zblock: | 300 | handle_zblock: |
290 | kaddr = kmap_atomic(page, KM_USER0); | 301 | kaddr = kmap_atomic(page, KM_USER0); |
291 | memset(kaddr + i * blocksize, 0, blocksize); | 302 | memset(kaddr + i * blocksize, 0, blocksize); |
292 | flush_dcache_page(page); | ||
293 | kunmap_atomic(kaddr, KM_USER0); | 303 | kunmap_atomic(kaddr, KM_USER0); |
294 | set_buffer_uptodate(bh); | 304 | flush_dcache_page(page); |
305 | if (likely(!err)) | ||
306 | set_buffer_uptodate(bh); | ||
295 | } while (i++, iblock++, (bh = bh->b_this_page) != head); | 307 | } while (i++, iblock++, (bh = bh->b_this_page) != head); |
296 | 308 | ||
297 | /* Release the lock if we took it. */ | 309 | /* Release the lock if we took it. */ |
@@ -711,20 +723,37 @@ lock_retry_remap: | |||
711 | if (likely(!err)) | 723 | if (likely(!err)) |
712 | goto lock_retry_remap; | 724 | goto lock_retry_remap; |
713 | rl = NULL; | 725 | rl = NULL; |
714 | lcn = err; | ||
715 | } else if (!rl) | 726 | } else if (!rl) |
716 | up_read(&ni->runlist.lock); | 727 | up_read(&ni->runlist.lock); |
728 | /* | ||
729 | * If buffer is outside the runlist, truncate has cut it out | ||
730 | * of the runlist. Just clean and clear the buffer and set it | ||
731 | * uptodate so it can get discarded by the VM. | ||
732 | */ | ||
733 | if (err == -ENOENT || lcn == LCN_ENOENT) { | ||
734 | u8 *kaddr; | ||
735 | |||
736 | bh->b_blocknr = -1; | ||
737 | clear_buffer_dirty(bh); | ||
738 | kaddr = kmap_atomic(page, KM_USER0); | ||
739 | memset(kaddr + bh_offset(bh), 0, blocksize); | ||
740 | kunmap_atomic(kaddr, KM_USER0); | ||
741 | flush_dcache_page(page); | ||
742 | set_buffer_uptodate(bh); | ||
743 | err = 0; | ||
744 | continue; | ||
745 | } | ||
717 | /* Failed to map the buffer, even after retrying. */ | 746 | /* Failed to map the buffer, even after retrying. */ |
747 | if (!err) | ||
748 | err = -EIO; | ||
718 | bh->b_blocknr = -1; | 749 | bh->b_blocknr = -1; |
719 | ntfs_error(vol->sb, "Failed to write to inode 0x%lx, " | 750 | ntfs_error(vol->sb, "Failed to write to inode 0x%lx, " |
720 | "attribute type 0x%x, vcn 0x%llx, offset 0x%x " | 751 | "attribute type 0x%x, vcn 0x%llx, offset 0x%x " |
721 | "because its location on disk could not be " | 752 | "because its location on disk could not be " |
722 | "determined%s (error code %lli).", ni->mft_no, | 753 | "determined%s (error code %i).", ni->mft_no, |
723 | ni->type, (unsigned long long)vcn, | 754 | ni->type, (unsigned long long)vcn, |
724 | vcn_ofs, is_retry ? " even after " | 755 | vcn_ofs, is_retry ? " even after " |
725 | "retrying" : "", (long long)lcn); | 756 | "retrying" : "", err); |
726 | if (!err) | ||
727 | err = -EIO; | ||
728 | break; | 757 | break; |
729 | } while (block++, (bh = bh->b_this_page) != head); | 758 | } while (block++, (bh = bh->b_this_page) != head); |
730 | 759 | ||