NTFS: Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case

where a concurrent truncate has truncated the runlist under our feet.

Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
This commit is contained in:
Anton Altaparmakov 2005-09-08 22:00:33 +01:00
parent 54b02eb01c
commit 8273d5d4c2
2 changed files with 42 additions and 11 deletions

View File

@ -84,6 +84,8 @@ ToDo/Notes:
- Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page - Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page
lock protection over the buffer submission for i/o which allows the lock protection over the buffer submission for i/o which allows the
removal of the get_bh()/put_bh() pairs for each buffer. removal of the get_bh()/put_bh() pairs for each buffer.
- Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case
where a concurrent truncate has truncated the runlist under our feet.
2.1.23 - Implement extension of resident files and make writing safe as well as 2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements... many bug fixes, cleanups, and enhancements...

View File

@ -204,6 +204,7 @@ static int ntfs_read_block(struct page *page)
nr = i = 0; nr = i = 0;
do { do {
u8 *kaddr; u8 *kaddr;
int err;
if (unlikely(buffer_uptodate(bh))) if (unlikely(buffer_uptodate(bh)))
continue; continue;
@ -211,6 +212,7 @@ static int ntfs_read_block(struct page *page)
arr[nr++] = bh; arr[nr++] = bh;
continue; continue;
} }
err = 0;
bh->b_bdev = vol->sb->s_bdev; bh->b_bdev = vol->sb->s_bdev;
/* Is the block within the allowed limits? */ /* Is the block within the allowed limits? */
if (iblock < lblock) { if (iblock < lblock) {
@ -252,7 +254,6 @@ lock_retry_remap:
goto handle_hole; goto handle_hole;
/* If first try and runlist unmapped, map and retry. */ /* If first try and runlist unmapped, map and retry. */
if (!is_retry && lcn == LCN_RL_NOT_MAPPED) { if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
int err;
is_retry = TRUE; is_retry = TRUE;
/* /*
* Attempt to map runlist, dropping lock for * Attempt to map runlist, dropping lock for
@ -263,20 +264,30 @@ lock_retry_remap:
if (likely(!err)) if (likely(!err))
goto lock_retry_remap; goto lock_retry_remap;
rl = NULL; rl = NULL;
lcn = err;
} else if (!rl) } else if (!rl)
up_read(&ni->runlist.lock); up_read(&ni->runlist.lock);
/*
* If buffer is outside the runlist, treat it as a
* hole. This can happen due to concurrent truncate
* for example.
*/
if (err == -ENOENT || lcn == LCN_ENOENT) {
err = 0;
goto handle_hole;
}
/* Hard error, zero out region. */ /* Hard error, zero out region. */
if (!err)
err = -EIO;
bh->b_blocknr = -1; bh->b_blocknr = -1;
SetPageError(page); SetPageError(page);
ntfs_error(vol->sb, "Failed to read from inode 0x%lx, " ntfs_error(vol->sb, "Failed to read from inode 0x%lx, "
"attribute type 0x%x, vcn 0x%llx, " "attribute type 0x%x, vcn 0x%llx, "
"offset 0x%x because its location on " "offset 0x%x because its location on "
"disk could not be determined%s " "disk could not be determined%s "
"(error code %lli).", ni->mft_no, "(error code %i).", ni->mft_no,
ni->type, (unsigned long long)vcn, ni->type, (unsigned long long)vcn,
vcn_ofs, is_retry ? " even after " vcn_ofs, is_retry ? " even after "
"retrying" : "", (long long)lcn); "retrying" : "", err);
} }
/* /*
* Either iblock was outside lblock limits or * Either iblock was outside lblock limits or
@ -289,9 +300,10 @@ handle_hole:
handle_zblock: handle_zblock:
kaddr = kmap_atomic(page, KM_USER0); kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr + i * blocksize, 0, blocksize); memset(kaddr + i * blocksize, 0, blocksize);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
set_buffer_uptodate(bh); flush_dcache_page(page);
if (likely(!err))
set_buffer_uptodate(bh);
} while (i++, iblock++, (bh = bh->b_this_page) != head); } while (i++, iblock++, (bh = bh->b_this_page) != head);
/* Release the lock if we took it. */ /* Release the lock if we took it. */
@ -711,20 +723,37 @@ lock_retry_remap:
if (likely(!err)) if (likely(!err))
goto lock_retry_remap; goto lock_retry_remap;
rl = NULL; rl = NULL;
lcn = err;
} else if (!rl) } else if (!rl)
up_read(&ni->runlist.lock); up_read(&ni->runlist.lock);
/*
* If buffer is outside the runlist, truncate has cut it out
* of the runlist. Just clean and clear the buffer and set it
* uptodate so it can get discarded by the VM.
*/
if (err == -ENOENT || lcn == LCN_ENOENT) {
u8 *kaddr;
bh->b_blocknr = -1;
clear_buffer_dirty(bh);
kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr + bh_offset(bh), 0, blocksize);
kunmap_atomic(kaddr, KM_USER0);
flush_dcache_page(page);
set_buffer_uptodate(bh);
err = 0;
continue;
}
/* Failed to map the buffer, even after retrying. */ /* Failed to map the buffer, even after retrying. */
if (!err)
err = -EIO;
bh->b_blocknr = -1; bh->b_blocknr = -1;
ntfs_error(vol->sb, "Failed to write to inode 0x%lx, " ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "
"attribute type 0x%x, vcn 0x%llx, offset 0x%x " "attribute type 0x%x, vcn 0x%llx, offset 0x%x "
"because its location on disk could not be " "because its location on disk could not be "
"determined%s (error code %lli).", ni->mft_no, "determined%s (error code %i).", ni->mft_no,
ni->type, (unsigned long long)vcn, ni->type, (unsigned long long)vcn,
vcn_ofs, is_retry ? " even after " vcn_ofs, is_retry ? " even after "
"retrying" : "", (long long)lcn); "retrying" : "", err);
if (!err)
err = -EIO;
break; break;
} while (block++, (bh = bh->b_this_page) != head); } while (block++, (bh = bh->b_this_page) != head);