summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@sgi.com>2005-09-05 08:23:35 +1000
committerNathan Scott <nathans@sgi.com>2005-09-05 08:23:35 +1000
commitc1a073bdff997216eac25254a2716faf640e4e8d (patch)
tree0121a8f6fe10e62bbd943defafe6a92c03e633b6 /fs
parentf09738638d3bae6501e8e160c66233832d8c280f (diff)
downloadlinux-c1a073bdff997216eac25254a2716faf640e4e8d.tar.gz
linux-c1a073bdff997216eac25254a2716faf640e4e8d.tar.bz2
linux-c1a073bdff997216eac25254a2716faf640e4e8d.zip
[XFS] Delay I/O completion for unwritten extents after conversion
SGI-PV: 936584 SGI-Modid: xfs-linux:xfs-kern:196886a Signed-off-by: Christoph Hellwig <hch@sgi.com> Signed-off-by: Nathan Scott <nathans@sgi.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c27
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.h1
2 files changed, 26 insertions, 2 deletions
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c
index 2add9a8a8df7..ea615e2f476d 100644
--- a/fs/xfs/linux-2.6/xfs_aops.c
+++ b/fs/xfs/linux-2.6/xfs_aops.c
@@ -136,10 +136,21 @@ xfs_end_bio_unwritten(
vnode_t *vp = ioend->io_vnode;
xfs_off_t offset = ioend->io_offset;
size_t size = ioend->io_size;
+ struct buffer_head *bh, *next;
int error;
if (ioend->io_uptodate)
VOP_BMAP(vp, offset, size, BMAPI_UNWRITTEN, NULL, NULL, error);
+
+ /* ioend->io_buffer_head is only non-NULL for buffered I/O */
+ for (bh = ioend->io_buffer_head; bh; bh = next) {
+ next = bh->b_private;
+
+ bh->b_end_io = NULL;
+ clear_buffer_unwritten(bh);
+ end_buffer_async_write(bh, ioend->io_uptodate);
+ }
+
xfs_destroy_ioend(ioend);
}
@@ -165,6 +176,7 @@ xfs_alloc_ioend(
atomic_set(&ioend->io_remaining, 1);
ioend->io_uptodate = 1; /* cleared if any I/O fails */
ioend->io_vnode = LINVFS_GET_VP(inode);
+ ioend->io_buffer_head = NULL;
atomic_inc(&ioend->io_vnode->v_iocount);
ioend->io_offset = 0;
ioend->io_size = 0;
@@ -180,15 +192,26 @@ linvfs_unwritten_done(
int uptodate)
{
xfs_ioend_t *ioend = bh->b_private;
+ static spinlock_t unwritten_done_lock = SPIN_LOCK_UNLOCKED;
+ unsigned long flags;
ASSERT(buffer_unwritten(bh));
bh->b_end_io = NULL;
- clear_buffer_unwritten(bh);
+
if (!uptodate)
ioend->io_uptodate = 0;
+ /*
+ * Deep magic here. We reuse b_private in the buffer_heads to build
+ * a chain for completing the I/O from user context after we've issued
+ * a transaction to convert the unwritten extent.
+ */
+ spin_lock_irqsave(&unwritten_done_lock, flags);
+ bh->b_private = ioend->io_buffer_head;
+ ioend->io_buffer_head = bh;
+ spin_unlock_irqrestore(&unwritten_done_lock, flags);
+
xfs_finish_ioend(ioend);
- end_buffer_async_write(bh, uptodate);
}
STATIC int
diff --git a/fs/xfs/linux-2.6/xfs_aops.h b/fs/xfs/linux-2.6/xfs_aops.h
index ee46307a7321..2fa62974a04d 100644
--- a/fs/xfs/linux-2.6/xfs_aops.h
+++ b/fs/xfs/linux-2.6/xfs_aops.h
@@ -41,6 +41,7 @@ typedef struct xfs_ioend {
unsigned int io_uptodate; /* I/O status register */
atomic_t io_remaining; /* hold count */
struct vnode *io_vnode; /* file being written to */
+ struct buffer_head *io_buffer_head;/* buffer linked list head */
size_t io_size; /* size of the extent */
xfs_off_t io_offset; /* offset in the file */
struct work_struct io_work; /* xfsdatad work queue */