|
@@ -1187,11 +1187,18 @@ __xfs_get_blocks(
|
|
|
&imap, nimaps);
|
|
|
if (error)
|
|
|
return -error;
|
|
|
+ new = 1;
|
|
|
} else {
|
|
|
/*
|
|
|
* Delalloc reservations do not require a transaction,
|
|
|
- * we can go on without dropping the lock here.
|
|
|
+ * we can go on without dropping the lock here. If we
|
|
|
+ * are allocating a new delalloc block, make sure that
|
|
|
+ * we set the new flag so that we mark the buffer new so
|
|
|
+ * that we know that it is newly allocated if the write
|
|
|
+ * fails.
|
|
|
*/
|
|
|
+ if (nimaps && imap.br_startblock == HOLESTARTBLOCK)
|
|
|
+ new = 1;
|
|
|
error = xfs_iomap_write_delay(ip, offset, size, &imap);
|
|
|
if (error)
|
|
|
goto out_unlock;
|
|
@@ -1408,52 +1415,91 @@ out_destroy_ioend:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Punch out the delalloc blocks we have already allocated.
|
|
|
+ *
|
|
|
+ * Don't bother with xfs_setattr given that nothing can have made it to disk yet
|
|
|
+ * as the page is still locked at this point.
|
|
|
+ */
|
|
|
+STATIC void
|
|
|
+xfs_vm_kill_delalloc_range(
|
|
|
+ struct inode *inode,
|
|
|
+ loff_t start,
|
|
|
+ loff_t end)
|
|
|
+{
|
|
|
+ struct xfs_inode *ip = XFS_I(inode);
|
|
|
+ xfs_fileoff_t start_fsb;
|
|
|
+ xfs_fileoff_t end_fsb;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ start_fsb = XFS_B_TO_FSB(ip->i_mount, start);
|
|
|
+ end_fsb = XFS_B_TO_FSB(ip->i_mount, end);
|
|
|
+ if (end_fsb <= start_fsb)
|
|
|
+ return;
|
|
|
+
|
|
|
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
|
|
|
+ error = xfs_bmap_punch_delalloc_range(ip, start_fsb,
|
|
|
+ end_fsb - start_fsb);
|
|
|
+ if (error) {
|
|
|
+ /* something screwed, just bail */
|
|
|
+ if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) {
|
|
|
+ xfs_alert(ip->i_mount,
|
|
|
+ "xfs_vm_write_failed: unable to clean up ino %lld",
|
|
|
+ ip->i_ino);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
|
|
+}
|
|
|
+
|
|
|
STATIC void
|
|
|
xfs_vm_write_failed(
|
|
|
- struct address_space *mapping,
|
|
|
- loff_t to)
|
|
|
+ struct inode *inode,
|
|
|
+ struct page *page,
|
|
|
+ loff_t pos,
|
|
|
+ unsigned len)
|
|
|
{
|
|
|
- struct inode *inode = mapping->host;
|
|
|
+ loff_t block_offset = pos & PAGE_MASK;
|
|
|
+ loff_t block_start;
|
|
|
+ loff_t block_end;
|
|
|
+ loff_t from = pos & (PAGE_CACHE_SIZE - 1);
|
|
|
+ loff_t to = from + len;
|
|
|
+ struct buffer_head *bh, *head;
|
|
|
|
|
|
- if (to > inode->i_size) {
|
|
|
- /*
|
|
|
- * Punch out the delalloc blocks we have already allocated.
|
|
|
- *
|
|
|
- * Don't bother with xfs_setattr given that nothing can have
|
|
|
- * made it to disk yet as the page is still locked at this
|
|
|
- * point.
|
|
|
- */
|
|
|
- struct xfs_inode *ip = XFS_I(inode);
|
|
|
- xfs_fileoff_t start_fsb;
|
|
|
- xfs_fileoff_t end_fsb;
|
|
|
- int error;
|
|
|
+ ASSERT(block_offset + from == pos);
|
|
|
|
|
|
- truncate_pagecache(inode, to, inode->i_size);
|
|
|
+ head = page_buffers(page);
|
|
|
+ block_start = 0;
|
|
|
+ for (bh = head; bh != head || !block_start;
|
|
|
+ bh = bh->b_this_page, block_start = block_end,
|
|
|
+ block_offset += bh->b_size) {
|
|
|
+ block_end = block_start + bh->b_size;
|
|
|
|
|
|
- /*
|
|
|
- * Check if there are any blocks that are outside of i_size
|
|
|
- * that need to be trimmed back.
|
|
|
- */
|
|
|
- start_fsb = XFS_B_TO_FSB(ip->i_mount, inode->i_size);
|
|
|
- end_fsb = XFS_B_TO_FSB(ip->i_mount, to);
|
|
|
- if (end_fsb <= start_fsb)
|
|
|
- return;
|
|
|
-
|
|
|
- xfs_ilock(ip, XFS_ILOCK_EXCL);
|
|
|
- error = xfs_bmap_punch_delalloc_range(ip, start_fsb,
|
|
|
- end_fsb - start_fsb);
|
|
|
- if (error) {
|
|
|
- /* something screwed, just bail */
|
|
|
- if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) {
|
|
|
- xfs_alert(ip->i_mount,
|
|
|
- "xfs_vm_write_failed: unable to clean up ino %lld",
|
|
|
- ip->i_ino);
|
|
|
- }
|
|
|
- }
|
|
|
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
|
|
+ /* skip buffers before the write */
|
|
|
+ if (block_end <= from)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* if the buffer is after the write, we're done */
|
|
|
+ if (block_start >= to)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (!buffer_delay(bh))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!buffer_new(bh) && block_offset < i_size_read(inode))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ xfs_vm_kill_delalloc_range(inode, block_offset,
|
|
|
+ block_offset + bh->b_size);
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This used to call block_write_begin(), but it unlocks and releases the page
|
|
|
+ * on error, and we need that page to be able to punch stale delalloc blocks out
|
|
|
+ * on failure. hence we copy-n-waste it here and call xfs_vm_write_failed() at
|
|
|
+ * the appropriate point.
|
|
|
+ */
|
|
|
STATIC int
|
|
|
xfs_vm_write_begin(
|
|
|
struct file *file,
|
|
@@ -1464,15 +1510,40 @@ xfs_vm_write_begin(
|
|
|
struct page **pagep,
|
|
|
void **fsdata)
|
|
|
{
|
|
|
- int ret;
|
|
|
+ pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
|
|
+ struct page *page;
|
|
|
+ int status;
|
|
|
|
|
|
- ret = block_write_begin(mapping, pos, len, flags | AOP_FLAG_NOFS,
|
|
|
- pagep, xfs_get_blocks);
|
|
|
- if (unlikely(ret))
|
|
|
- xfs_vm_write_failed(mapping, pos + len);
|
|
|
- return ret;
|
|
|
+ ASSERT(len <= PAGE_CACHE_SIZE);
|
|
|
+
|
|
|
+ page = grab_cache_page_write_begin(mapping, index,
|
|
|
+ flags | AOP_FLAG_NOFS);
|
|
|
+ if (!page)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ status = __block_write_begin(page, pos, len, xfs_get_blocks);
|
|
|
+ if (unlikely(status)) {
|
|
|
+ struct inode *inode = mapping->host;
|
|
|
+
|
|
|
+ xfs_vm_write_failed(inode, page, pos, len);
|
|
|
+ unlock_page(page);
|
|
|
+
|
|
|
+ if (pos + len > i_size_read(inode))
|
|
|
+ truncate_pagecache(inode, pos + len, i_size_read(inode));
|
|
|
+
|
|
|
+ page_cache_release(page);
|
|
|
+ page = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ *pagep = page;
|
|
|
+ return status;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * On failure, we only need to kill delalloc blocks beyond EOF because they
|
|
|
+ * will never be written. For blocks within EOF, generic_write_end() zeros them
|
|
|
+ * so they are safe to leave alone and be written with all the other valid data.
|
|
|
+ */
|
|
|
STATIC int
|
|
|
xfs_vm_write_end(
|
|
|
struct file *file,
|
|
@@ -1485,9 +1556,19 @@ xfs_vm_write_end(
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
+ ASSERT(len <= PAGE_CACHE_SIZE);
|
|
|
+
|
|
|
ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
|
|
|
- if (unlikely(ret < len))
|
|
|
- xfs_vm_write_failed(mapping, pos + len);
|
|
|
+ if (unlikely(ret < len)) {
|
|
|
+ struct inode *inode = mapping->host;
|
|
|
+ size_t isize = i_size_read(inode);
|
|
|
+ loff_t to = pos + len;
|
|
|
+
|
|
|
+ if (to > isize) {
|
|
|
+ truncate_pagecache(inode, to, isize);
|
|
|
+ xfs_vm_kill_delalloc_range(inode, isize, to);
|
|
|
+ }
|
|
|
+ }
|
|
|
return ret;
|
|
|
}
|
|
|
|