diff options
-rw-r--r-- | fs/ext4/ext4.h | 5 | ||||
-rw-r--r-- | fs/ext4/fsync.c | 4 | ||||
-rw-r--r-- | fs/ext4/page-io.c | 138 |
3 files changed, 82 insertions, 65 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b69a733b5b42..74db579bb482 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -180,8 +180,7 @@ struct ext4_map_blocks { | |||
180 | * Flags for ext4_io_end->flags | 180 | * Flags for ext4_io_end->flags |
181 | */ | 181 | */ |
182 | #define EXT4_IO_END_UNWRITTEN 0x0001 | 182 | #define EXT4_IO_END_UNWRITTEN 0x0001 |
183 | #define EXT4_IO_END_ERROR 0x0002 | 183 | #define EXT4_IO_END_DIRECT 0x0002 |
184 | #define EXT4_IO_END_DIRECT 0x0004 | ||
185 | 184 | ||
186 | /* | 185 | /* |
187 | * For converting uninitialized extents on a work queue. 'handle' is used for | 186 | * For converting uninitialized extents on a work queue. 'handle' is used for |
@@ -192,6 +191,8 @@ typedef struct ext4_io_end { | |||
192 | handle_t *handle; /* handle reserved for extent | 191 | handle_t *handle; /* handle reserved for extent |
193 | * conversion */ | 192 | * conversion */ |
194 | struct inode *inode; /* file being written to */ | 193 | struct inode *inode; /* file being written to */ |
194 | struct bio *bio; /* Linked list of completed | ||
195 | * bios covering the extent */ | ||
195 | unsigned int flag; /* unwritten or not */ | 196 | unsigned int flag; /* unwritten or not */ |
196 | loff_t offset; /* offset in the file */ | 197 | loff_t offset; /* offset in the file */ |
197 | ssize_t size; /* size of the extent */ | 198 | ssize_t size; /* size of the extent */ |
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c index e0ba8a408def..dcc881b30849 100644 --- a/fs/ext4/fsync.c +++ b/fs/ext4/fsync.c | |||
@@ -132,10 +132,6 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync) | |||
132 | if (inode->i_sb->s_flags & MS_RDONLY) | 132 | if (inode->i_sb->s_flags & MS_RDONLY) |
133 | goto out; | 133 | goto out; |
134 | 134 | ||
135 | ret = ext4_flush_unwritten_io(inode); | ||
136 | if (ret < 0) | ||
137 | goto out; | ||
138 | |||
139 | if (!journal) { | 135 | if (!journal) { |
140 | ret = __sync_inode(inode, datasync); | 136 | ret = __sync_inode(inode, datasync); |
141 | if (!ret && !hlist_empty(&inode->i_dentry)) | 137 | if (!ret && !hlist_empty(&inode->i_dentry)) |
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index bcdfd6bdde06..755741c211a4 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c | |||
@@ -64,14 +64,83 @@ void ext4_ioend_shutdown(struct inode *inode) | |||
64 | cancel_work_sync(&EXT4_I(inode)->i_unrsv_conversion_work); | 64 | cancel_work_sync(&EXT4_I(inode)->i_unrsv_conversion_work); |
65 | } | 65 | } |
66 | 66 | ||
67 | /* | ||
68 | * Print an buffer I/O error compatible with the fs/buffer.c. This | ||
69 | * provides compatibility with dmesg scrapers that look for a specific | ||
70 | * buffer I/O error message. We really need a unified error reporting | ||
71 | * structure to userspace ala Digital Unix's uerf system, but it's | ||
72 | * probably not going to happen in my lifetime, due to LKML politics... | ||
73 | */ | ||
74 | static void buffer_io_error(struct buffer_head *bh) | ||
75 | { | ||
76 | char b[BDEVNAME_SIZE]; | ||
77 | printk(KERN_ERR "Buffer I/O error on device %s, logical block %llu\n", | ||
78 | bdevname(bh->b_bdev, b), | ||
79 | (unsigned long long)bh->b_blocknr); | ||
80 | } | ||
81 | |||
82 | static void ext4_finish_bio(struct bio *bio) | ||
83 | { | ||
84 | int i; | ||
85 | int error = !test_bit(BIO_UPTODATE, &bio->bi_flags); | ||
86 | |||
87 | for (i = 0; i < bio->bi_vcnt; i++) { | ||
88 | struct bio_vec *bvec = &bio->bi_io_vec[i]; | ||
89 | struct page *page = bvec->bv_page; | ||
90 | struct buffer_head *bh, *head; | ||
91 | unsigned bio_start = bvec->bv_offset; | ||
92 | unsigned bio_end = bio_start + bvec->bv_len; | ||
93 | unsigned under_io = 0; | ||
94 | unsigned long flags; | ||
95 | |||
96 | if (!page) | ||
97 | continue; | ||
98 | |||
99 | if (error) { | ||
100 | SetPageError(page); | ||
101 | set_bit(AS_EIO, &page->mapping->flags); | ||
102 | } | ||
103 | bh = head = page_buffers(page); | ||
104 | /* | ||
105 | * We check all buffers in the page under BH_Uptodate_Lock | ||
106 | * to avoid races with other end io clearing async_write flags | ||
107 | */ | ||
108 | local_irq_save(flags); | ||
109 | bit_spin_lock(BH_Uptodate_Lock, &head->b_state); | ||
110 | do { | ||
111 | if (bh_offset(bh) < bio_start || | ||
112 | bh_offset(bh) + bh->b_size > bio_end) { | ||
113 | if (buffer_async_write(bh)) | ||
114 | under_io++; | ||
115 | continue; | ||
116 | } | ||
117 | clear_buffer_async_write(bh); | ||
118 | if (error) | ||
119 | buffer_io_error(bh); | ||
120 | } while ((bh = bh->b_this_page) != head); | ||
121 | bit_spin_unlock(BH_Uptodate_Lock, &head->b_state); | ||
122 | local_irq_restore(flags); | ||
123 | if (!under_io) | ||
124 | end_page_writeback(page); | ||
125 | } | ||
126 | } | ||
127 | |||
67 | static void ext4_release_io_end(ext4_io_end_t *io_end) | 128 | static void ext4_release_io_end(ext4_io_end_t *io_end) |
68 | { | 129 | { |
130 | struct bio *bio, *next_bio; | ||
131 | |||
69 | BUG_ON(!list_empty(&io_end->list)); | 132 | BUG_ON(!list_empty(&io_end->list)); |
70 | BUG_ON(io_end->flag & EXT4_IO_END_UNWRITTEN); | 133 | BUG_ON(io_end->flag & EXT4_IO_END_UNWRITTEN); |
71 | WARN_ON(io_end->handle); | 134 | WARN_ON(io_end->handle); |
72 | 135 | ||
73 | if (atomic_dec_and_test(&EXT4_I(io_end->inode)->i_ioend_count)) | 136 | if (atomic_dec_and_test(&EXT4_I(io_end->inode)->i_ioend_count)) |
74 | wake_up_all(ext4_ioend_wq(io_end->inode)); | 137 | wake_up_all(ext4_ioend_wq(io_end->inode)); |
138 | |||
139 | for (bio = io_end->bio; bio; bio = next_bio) { | ||
140 | next_bio = bio->bi_private; | ||
141 | ext4_finish_bio(bio); | ||
142 | bio_put(bio); | ||
143 | } | ||
75 | if (io_end->flag & EXT4_IO_END_DIRECT) | 144 | if (io_end->flag & EXT4_IO_END_DIRECT) |
76 | inode_dio_done(io_end->inode); | 145 | inode_dio_done(io_end->inode); |
77 | if (io_end->iocb) | 146 | if (io_end->iocb) |
@@ -267,79 +336,31 @@ ext4_io_end_t *ext4_get_io_end(ext4_io_end_t *io_end) | |||
267 | return io_end; | 336 | return io_end; |
268 | } | 337 | } |
269 | 338 | ||
270 | /* | ||
271 | * Print an buffer I/O error compatible with the fs/buffer.c. This | ||
272 | * provides compatibility with dmesg scrapers that look for a specific | ||
273 | * buffer I/O error message. We really need a unified error reporting | ||
274 | * structure to userspace ala Digital Unix's uerf system, but it's | ||
275 | * probably not going to happen in my lifetime, due to LKML politics... | ||
276 | */ | ||
277 | static void buffer_io_error(struct buffer_head *bh) | ||
278 | { | ||
279 | char b[BDEVNAME_SIZE]; | ||
280 | printk(KERN_ERR "Buffer I/O error on device %s, logical block %llu\n", | ||
281 | bdevname(bh->b_bdev, b), | ||
282 | (unsigned long long)bh->b_blocknr); | ||
283 | } | ||
284 | |||
285 | static void ext4_end_bio(struct bio *bio, int error) | 339 | static void ext4_end_bio(struct bio *bio, int error) |
286 | { | 340 | { |
287 | ext4_io_end_t *io_end = bio->bi_private; | 341 | ext4_io_end_t *io_end = bio->bi_private; |
288 | struct inode *inode; | ||
289 | int i; | ||
290 | int blocksize; | ||
291 | sector_t bi_sector = bio->bi_sector; | 342 | sector_t bi_sector = bio->bi_sector; |
292 | 343 | ||
293 | BUG_ON(!io_end); | 344 | BUG_ON(!io_end); |
294 | inode = io_end->inode; | ||
295 | blocksize = 1 << inode->i_blkbits; | ||
296 | bio->bi_private = NULL; | ||
297 | bio->bi_end_io = NULL; | 345 | bio->bi_end_io = NULL; |
298 | if (test_bit(BIO_UPTODATE, &bio->bi_flags)) | 346 | if (test_bit(BIO_UPTODATE, &bio->bi_flags)) |
299 | error = 0; | 347 | error = 0; |
300 | for (i = 0; i < bio->bi_vcnt; i++) { | ||
301 | struct bio_vec *bvec = &bio->bi_io_vec[i]; | ||
302 | struct page *page = bvec->bv_page; | ||
303 | struct buffer_head *bh, *head; | ||
304 | unsigned bio_start = bvec->bv_offset; | ||
305 | unsigned bio_end = bio_start + bvec->bv_len; | ||
306 | unsigned under_io = 0; | ||
307 | unsigned long flags; | ||
308 | 348 | ||
309 | if (!page) | 349 | if (io_end->flag & EXT4_IO_END_UNWRITTEN) { |
310 | continue; | ||
311 | |||
312 | if (error) { | ||
313 | SetPageError(page); | ||
314 | set_bit(AS_EIO, &page->mapping->flags); | ||
315 | } | ||
316 | bh = head = page_buffers(page); | ||
317 | /* | 350 | /* |
318 | * We check all buffers in the page under BH_Uptodate_Lock | 351 | * Link bio into list hanging from io_end. We have to do it |
319 | * to avoid races with other end io clearing async_write flags | 352 | * atomically as bio completions can be racing against each |
353 | * other. | ||
320 | */ | 354 | */ |
321 | local_irq_save(flags); | 355 | bio->bi_private = xchg(&io_end->bio, bio); |
322 | bit_spin_lock(BH_Uptodate_Lock, &head->b_state); | 356 | } else { |
323 | do { | 357 | ext4_finish_bio(bio); |
324 | if (bh_offset(bh) < bio_start || | 358 | bio_put(bio); |
325 | bh_offset(bh) + blocksize > bio_end) { | ||
326 | if (buffer_async_write(bh)) | ||
327 | under_io++; | ||
328 | continue; | ||
329 | } | ||
330 | clear_buffer_async_write(bh); | ||
331 | if (error) | ||
332 | buffer_io_error(bh); | ||
333 | } while ((bh = bh->b_this_page) != head); | ||
334 | bit_spin_unlock(BH_Uptodate_Lock, &head->b_state); | ||
335 | local_irq_restore(flags); | ||
336 | if (!under_io) | ||
337 | end_page_writeback(page); | ||
338 | } | 359 | } |
339 | bio_put(bio); | ||
340 | 360 | ||
341 | if (error) { | 361 | if (error) { |
342 | io_end->flag |= EXT4_IO_END_ERROR; | 362 | struct inode *inode = io_end->inode; |
363 | |||
343 | ext4_warning(inode->i_sb, "I/O error writing to inode %lu " | 364 | ext4_warning(inode->i_sb, "I/O error writing to inode %lu " |
344 | "(offset %llu size %ld starting block %llu)", | 365 | "(offset %llu size %ld starting block %llu)", |
345 | inode->i_ino, | 366 | inode->i_ino, |
@@ -348,7 +369,6 @@ static void ext4_end_bio(struct bio *bio, int error) | |||
348 | (unsigned long long) | 369 | (unsigned long long) |
349 | bi_sector >> (inode->i_blkbits - 9)); | 370 | bi_sector >> (inode->i_blkbits - 9)); |
350 | } | 371 | } |
351 | |||
352 | ext4_put_io_end_defer(io_end); | 372 | ext4_put_io_end_defer(io_end); |
353 | } | 373 | } |
354 | 374 | ||