aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2016-04-05 18:12:28 -0400
committerDave Chinner <david@fromorbit.com>2016-04-05 18:12:28 -0400
commit37992c18bba3f578860c6448b7bae18a14e535d3 (patch)
tree9598b6b0af53ccdec9d3c650df4f82efc16c3ed3
parentbb18782aa47d8cde90fed5cb0af312642e98a4fa (diff)
xfs: don't release bios on completion immediately
Completion of an ioend requires us to walk the bufferhead list to end writback on all the bufferheads. This, in turn, is needed so that we can end writeback on all the pages we just did IO on. To remove our dependency on bufferheads in writeback, we need to turn this around the other way - we need to walk the pages we've just completed IO on, and then walk the buffers attached to the pages and complete their IO. In doing this, we remove the requirement for the ioend to track bufferheads directly. To enable IO completion to walk all the pages we've submitted IO on, we need to keep the bios that we used for IO around until the ioend has been completed. We can do this simply by chaining the bios to the ioend at completion time, and then walking their pages directly just before destroying the ioend. Signed-off-by: Dave Chinner <dchinner@redhat.com> [hch: changed the xfs_finish_page_writeback calling convention] Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
-rw-r--r--fs/xfs/xfs_aops.c95
-rw-r--r--fs/xfs/xfs_aops.h5
2 files changed, 71 insertions, 29 deletions
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index d03946719992..9d9a01b50078 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -84,20 +84,64 @@ xfs_find_bdev_for_inode(
84} 84}
85 85
86/* 86/*
87 * We're now finished for good with this ioend structure. 87 * We're now finished for good with this page. Update the page state via the
88 * Update the page state via the associated buffer_heads, 88 * associated buffer_heads, paying attention to the start and end offsets that
89 * release holds on the inode and bio, and finally free 89 * we need to process on the page.
90 * up memory. Do not use the ioend after this. 90 */
91static void
92xfs_finish_page_writeback(
93 struct inode *inode,
94 struct bio_vec *bvec,
95 int error)
96{
97 unsigned int blockmask = (1 << inode->i_blkbits) - 1;
98 unsigned int end = bvec->bv_offset + bvec->bv_len - 1;
99 struct buffer_head *head, *bh;
100 unsigned int off = 0;
101
102 ASSERT(bvec->bv_offset < PAGE_SIZE);
103 ASSERT((bvec->bv_offset & blockmask) == 0);
104 ASSERT(end < PAGE_SIZE);
105 ASSERT((bvec->bv_len & blockmask) == 0);
106
107 bh = head = page_buffers(bvec->bv_page);
108
109 do {
110 if (off < bvec->bv_offset)
111 goto next_bh;
112 if (off > end)
113 break;
114 bh->b_end_io(bh, !error);
115next_bh:
116 off += bh->b_size;
117 } while ((bh = bh->b_this_page) != head);
118}
119
120/*
121 * We're now finished for good with this ioend structure. Update the page
122 * state, release holds on bios, and finally free up memory. Do not use the
123 * ioend after this.
91 */ 124 */
92STATIC void 125STATIC void
93xfs_destroy_ioend( 126xfs_destroy_ioend(
94 xfs_ioend_t *ioend) 127 struct xfs_ioend *ioend)
95{ 128{
96 struct buffer_head *bh, *next; 129 struct inode *inode = ioend->io_inode;
130 int error = ioend->io_error;
131 struct bio *bio, *next;
132
133 for (bio = ioend->io_bio_done; bio; bio = next) {
134 struct bio_vec *bvec;
135 int i;
136
137 next = bio->bi_private;
138 bio->bi_private = NULL;
97 139
98 for (bh = ioend->io_buffer_head; bh; bh = next) { 140 /* walk each page on bio, ending page IO on them */
99 next = bh->b_private; 141 bio_for_each_segment_all(bvec, bio, i)
100 bh->b_end_io(bh, !ioend->io_error); 142 xfs_finish_page_writeback(inode, bvec, error);
143
144 bio_put(bio);
101 } 145 }
102 146
103 mempool_free(ioend, xfs_ioend_pool); 147 mempool_free(ioend, xfs_ioend_pool);
@@ -286,6 +330,7 @@ xfs_alloc_ioend(
286 ioend->io_type = type; 330 ioend->io_type = type;
287 ioend->io_inode = inode; 331 ioend->io_inode = inode;
288 INIT_WORK(&ioend->io_work, xfs_end_io); 332 INIT_WORK(&ioend->io_work, xfs_end_io);
333 spin_lock_init(&ioend->io_lock);
289 return ioend; 334 return ioend;
290} 335}
291 336
@@ -365,15 +410,21 @@ STATIC void
365xfs_end_bio( 410xfs_end_bio(
366 struct bio *bio) 411 struct bio *bio)
367{ 412{
368 xfs_ioend_t *ioend = bio->bi_private; 413 struct xfs_ioend *ioend = bio->bi_private;
369 414 unsigned long flags;
370 if (!ioend->io_error)
371 ioend->io_error = bio->bi_error;
372 415
373 /* Toss bio and pass work off to an xfsdatad thread */
374 bio->bi_private = NULL; 416 bio->bi_private = NULL;
375 bio->bi_end_io = NULL; 417 bio->bi_end_io = NULL;
376 bio_put(bio); 418
419 spin_lock_irqsave(&ioend->io_lock, flags);
420 if (!ioend->io_error)
421 ioend->io_error = bio->bi_error;
422 if (!ioend->io_bio_done)
423 ioend->io_bio_done = bio;
424 else
425 ioend->io_bio_done_tail->bi_private = bio;
426 ioend->io_bio_done_tail = bio;
427 spin_unlock_irqrestore(&ioend->io_lock, flags);
377 428
378 xfs_finish_ioend(ioend); 429 xfs_finish_ioend(ioend);
379} 430}
@@ -511,21 +562,11 @@ xfs_add_to_ioend(
511 if (!wpc->ioend || wpc->io_type != wpc->ioend->io_type || 562 if (!wpc->ioend || wpc->io_type != wpc->ioend->io_type ||
512 bh->b_blocknr != wpc->last_block + 1 || 563 bh->b_blocknr != wpc->last_block + 1 ||
513 offset != wpc->ioend->io_offset + wpc->ioend->io_size) { 564 offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
514 struct xfs_ioend *new;
515
516 if (wpc->ioend) 565 if (wpc->ioend)
517 list_add(&wpc->ioend->io_list, iolist); 566 list_add(&wpc->ioend->io_list, iolist);
518 567 wpc->ioend = xfs_alloc_ioend(inode, wpc->io_type);
519 new = xfs_alloc_ioend(inode, wpc->io_type); 568 wpc->ioend->io_offset = offset;
520 new->io_offset = offset;
521 new->io_buffer_head = bh;
522 new->io_buffer_tail = bh;
523 wpc->ioend = new;
524 } else {
525 wpc->ioend->io_buffer_tail->b_private = bh;
526 wpc->ioend->io_buffer_tail = bh;
527 } 569 }
528 bh->b_private = NULL;
529 570
530retry: 571retry:
531 if (!wpc->ioend->io_bio) 572 if (!wpc->ioend->io_bio)
diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
index 8947991e0990..61a3dc3dbdf8 100644
--- a/fs/xfs/xfs_aops.h
+++ b/fs/xfs/xfs_aops.h
@@ -46,13 +46,14 @@ typedef struct xfs_ioend {
46 int io_error; /* I/O error code */ 46 int io_error; /* I/O error code */
47 atomic_t io_remaining; /* hold count */ 47 atomic_t io_remaining; /* hold count */
48 struct inode *io_inode; /* file being written to */ 48 struct inode *io_inode; /* file being written to */
49 struct buffer_head *io_buffer_head;/* buffer linked list head */
50 struct buffer_head *io_buffer_tail;/* buffer linked list tail */
51 size_t io_size; /* size of the extent */ 49 size_t io_size; /* size of the extent */
52 xfs_off_t io_offset; /* offset in the file */ 50 xfs_off_t io_offset; /* offset in the file */
53 struct work_struct io_work; /* xfsdatad work queue */ 51 struct work_struct io_work; /* xfsdatad work queue */
54 struct xfs_trans *io_append_trans;/* xact. for size update */ 52 struct xfs_trans *io_append_trans;/* xact. for size update */
55 struct bio *io_bio; /* bio being built */ 53 struct bio *io_bio; /* bio being built */
54 struct bio *io_bio_done; /* bios completed */
55 struct bio *io_bio_done_tail; /* bios completed */
56 spinlock_t io_lock; /* for bio completion list */
56} xfs_ioend_t; 57} xfs_ioend_t;
57 58
58extern const struct address_space_operations xfs_address_space_operations; 59extern const struct address_space_operations xfs_address_space_operations;