diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2015-10-16 00:26:39 -0800 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2017-01-18 21:35:42 -0900 |
commit | 2e604b7247216fe22869d892e707a28dd5112b6c (patch) | |
tree | 62a879614677cc0fb9f6de4ee061b880b17ac1c4 | |
parent | da56fe102f827125574396011761dc59f9ef42e7 (diff) |
bcachefs: fix partial O_DIRECT writes
if a write fails partway through, we have to return the part that completed - if
we don't, appends in particular break badly
-rw-r--r-- | drivers/md/bcache/fs.c | 51 |
1 files changed, 30 insertions, 21 deletions
diff --git a/drivers/md/bcache/fs.c b/drivers/md/bcache/fs.c index 2860d2eb351f..d0c1ec589ec9 100644 --- a/drivers/md/bcache/fs.c +++ b/drivers/md/bcache/fs.c @@ -2238,7 +2238,8 @@ out: struct dio_write { struct closure cl; struct kiocb *req; - long ret; + long written; + long error; bool append; }; @@ -2259,7 +2260,7 @@ static void bch_dio_write_complete(struct closure *cl) { struct dio_write *dio = container_of(cl, struct dio_write, cl); struct kiocb *req = dio->req; - long ret = dio->ret; + long ret = dio->written ?: dio->error; __bch_dio_write_complete(dio); req->ki_complete(req, ret, 0); @@ -2272,8 +2273,11 @@ static void bch_direct_IO_write_done(struct closure *cl) struct bio_vec *bv; int i; + op->dio->written += op->iop.written << 9; + if (op->iop.error) - op->dio->ret = op->iop.error; + op->dio->error = op->iop.error; + closure_put(&op->dio->cl); bio_for_each_segment_all(bv, &op->bio.bio.bio, i) @@ -2289,8 +2293,8 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, struct dio_write *dio; struct dio_write_bio *op; struct bio *bio; - unsigned long inum = inode->i_ino; unsigned flags = BCH_WRITE_CHECK_ENOSPC; + loff_t orig_offset = offset; ssize_t ret; lockdep_assert_held(&inode->i_rwsem); @@ -2303,7 +2307,8 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, return -ENOMEM; dio->req = req; - dio->ret = 0; + dio->written = 0; + dio->error = 0; dio->append = false; if (offset + iter->count > inode->i_size) { @@ -2326,8 +2331,7 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, closure_init(&dio->cl, NULL); - /* Decremented by inode_dio_done(): */ - atomic_inc(&inode->i_dio_count); + inode_dio_begin(inode); while (iter->count) { size_t pages = iov_iter_npages(iter, BIO_MAX_PAGES); @@ -2335,7 +2339,7 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, op = kmalloc(sizeof(*op) + sizeof(struct bio_vec) * pages, GFP_NOIO); if (!op) { - dio->ret = -ENOMEM; + dio->error = -ENOMEM; break; } @@ -2347,21 +2351,19 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, ret = bio_get_user_pages(bio, iter, 0); if (ret < 0) { - if (!dio->ret) - dio->ret = ret; + dio->error = ret; kfree(op); break; } - offset += bio->bi_iter.bi_size; - dio->ret += bio->bi_iter.bi_size; + offset += bio->bi_iter.bi_size; closure_get(&dio->cl); op->dio = dio; closure_init(&op->cl, NULL); bch_write_op_init(&op->iop, c, &op->bio, NULL, - bkey_to_s_c(&KEY(inum, + bkey_to_s_c(&KEY(inode->i_ino, bio_end_sector(bio), bio_sectors(bio))), bkey_s_c_null, @@ -2372,6 +2374,9 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, closure_call(&op->iop.cl, bch_write, NULL, &op->cl); closure_return_with_destructor_noreturn(&op->cl, bch_direct_IO_write_done); + + if (iter->count) + closure_sync(&dio->cl); } if (is_sync_kiocb(req) || dio->append) { @@ -2381,7 +2386,7 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, */ closure_sync(&dio->cl); closure_debug_destroy(&dio->cl); - ret = dio->ret; + ret = dio->written ?: dio->error; /* * XXX: if the bch_write call errors, we don't handle partial @@ -2389,23 +2394,27 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, */ if (dio->append) { + loff_t new_i_size = orig_offset + dio->written; int ret2 = 0; - if (ret > 0 && - offset > inode->i_size) { + if (dio->written && + new_i_size > inode->i_size) { struct i_size_update *u; unsigned idx; mutex_lock(&ei->update_lock); - bch_i_size_write(inode, offset); - i_size_dirty_put(ei); + bch_i_size_write(inode, new_i_size); - fifo_for_each_entry_ptr(u, &ei->i_size_updates, idx) - if (u->new_i_size < offset) + fifo_for_each_entry_ptr(u, &ei->i_size_updates, idx) { + if (u->new_i_size < new_i_size) u->new_i_size = -1; + else + BUG(); + } - ret2 = bch_write_inode_size(c, ei, offset); + i_size_dirty_put(ei); + ret2 = bch_write_inode_size(c, ei, new_i_size); mutex_unlock(&ei->update_lock); } else { |