diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2019-11-01 21:35:25 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2019-11-04 15:56:31 -0500 |
commit | b4482e462d34d548412ded96050f0db441f243f3 (patch) | |
tree | e5e215e6e3bf55e6ab9db9a1b64079289582d1a8 | |
parent | 9005347796b08dbb066ea9a49fd6d741643378aa (diff) |
bcachefs: Don't hold inode lock longer than necessary in dio write path
In theory we should be able to do (non appending/extending) dio writes
without taking the inode lock at all - but this gets us most of the way
there.
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | fs/bcachefs/fs-io.c | 112 |
1 files changed, 64 insertions, 48 deletions
diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c index b4a9f81fcc75..90a9bfa41787 100644 --- a/fs/bcachefs/fs-io.c +++ b/fs/bcachefs/fs-io.c @@ -1751,9 +1751,6 @@ static long bch2_dio_write_loop(struct dio_write *dio) if (dio->loop) goto loop; - inode_dio_begin(&inode->v); - __pagecache_block_get(&mapping->add_lock); - /* Write and invalidate pagecache range that we're writing to: */ offset = req->ki_pos + (dio->op.written << 9); ret = write_invalidate_inode_pages_range(mapping, @@ -1883,25 +1880,49 @@ static void bch2_dio_write_loop_async(struct bch_write_op *op) bch2_dio_write_loop(dio); } -static int bch2_direct_IO_write(struct kiocb *req, - struct iov_iter *iter, - bool swap) +static noinline +ssize_t bch2_direct_write(struct kiocb *req, struct iov_iter *iter) { struct file *file = req->ki_filp; + struct address_space *mapping = file->f_mapping; struct bch_inode_info *inode = file_bch_inode(file); struct bch_fs *c = inode->v.i_sb->s_fs_info; struct bch_io_opts opts = io_opts(c, &inode->ei_inode); struct dio_write *dio; struct bio *bio; + bool locked = true, extending; ssize_t ret; - lockdep_assert_held(&inode->v.i_rwsem); + prefetch(&c->opts); + prefetch((void *) &c->opts + 64); + prefetch(&inode->ei_inode); + prefetch((void *) &inode->ei_inode + 64); - if (unlikely(!iter->count)) - return 0; + inode_lock(&inode->v); + + ret = generic_write_checks(req, iter); + if (unlikely(ret <= 0)) + goto err; + + ret = file_remove_privs(file); + if (unlikely(ret)) + goto err; + + ret = file_update_time(file); + if (unlikely(ret)) + goto err; if (unlikely((req->ki_pos|iter->count) & (block_bytes(c) - 1))) - return -EINVAL; + goto err; + + inode_dio_begin(&inode->v); + __pagecache_block_get(&mapping->add_lock); + + extending = req->ki_pos + iter->count > inode->v.i_size; + if (!extending) { + inode_unlock(&inode->v); + locked = false; + } bio = bio_alloc_bioset(GFP_KERNEL, iov_iter_npages(iter, BIO_MAX_PAGES), @@ -1911,8 +1932,7 @@ static int bch2_direct_IO_write(struct kiocb *req, dio->req = req; dio->mm = current->mm; dio->loop = false; - dio->sync = is_sync_kiocb(req) || - req->ki_pos + iter->count > inode->v.i_size; + dio->sync = is_sync_kiocb(req) || extending; dio->free_iov = false; dio->quota_res.sectors = 0; dio->iter = *iter; @@ -1931,7 +1951,7 @@ static int bch2_direct_IO_write(struct kiocb *req, ret = bch2_quota_reservation_add(c, inode, &dio->quota_res, iter->count >> 9, true); if (unlikely(ret)) - goto err; + goto err_put_bio; dio->op.nr_replicas = dio->op.opts.data_replicas; @@ -1942,14 +1962,22 @@ static int bch2_direct_IO_write(struct kiocb *req, req->ki_pos >> 9), iter->count >> 9, dio->op.opts.data_replicas)) - goto err; + goto err_put_bio; - return bch2_dio_write_loop(dio); + ret = bch2_dio_write_loop(dio); err: + if (locked) + inode_unlock(&inode->v); + if (ret > 0) + req->ki_pos += ret; + return ret; +err_put_bio: + __pagecache_block_put(&mapping->add_lock); bch2_disk_reservation_put(c, &dio->op.res); bch2_quota_reservation_put(c, inode, &dio->quota_res); bio_put(bio); - return ret; + inode_dio_end(&inode->v); + goto err; } ssize_t bch2_direct_IO(struct kiocb *req, struct iov_iter *iter) @@ -1957,61 +1985,49 @@ ssize_t bch2_direct_IO(struct kiocb *req, struct iov_iter *iter) struct blk_plug plug; ssize_t ret; + if (iov_iter_rw(iter) == WRITE) + return -EINVAL; + blk_start_plug(&plug); - ret = iov_iter_rw(iter) == WRITE - ? bch2_direct_IO_write(req, iter, false) - : bch2_direct_IO_read(req, iter); + ret = bch2_direct_IO_read(req, iter); blk_finish_plug(&plug); return ret; } -static ssize_t -bch2_direct_write(struct kiocb *iocb, struct iov_iter *iter) -{ - return bch2_direct_IO_write(iocb, iter, true); -} - -static ssize_t __bch2_write_iter(struct kiocb *iocb, struct iov_iter *from) +ssize_t bch2_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct bch_inode_info *inode = file_bch_inode(file); - ssize_t ret; + ssize_t ret; + + if (iocb->ki_flags & IOCB_DIRECT) + return bch2_direct_write(iocb, from); /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(&inode->v); + inode_lock(&inode->v); + + ret = generic_write_checks(iocb, from); + if (ret <= 0) + goto unlock; + ret = file_remove_privs(file); if (ret) - goto out; + goto unlock; ret = file_update_time(file); if (ret) - goto out; - - ret = iocb->ki_flags & IOCB_DIRECT - ? bch2_direct_write(iocb, from) - : bch2_buffered_write(iocb, from); + goto unlock; + ret = bch2_buffered_write(iocb, from); if (likely(ret > 0)) iocb->ki_pos += ret; -out: +unlock: + inode_unlock(&inode->v); current->backing_dev_info = NULL; - return ret; -} -ssize_t bch2_write_iter(struct kiocb *iocb, struct iov_iter *from) -{ - struct bch_inode_info *inode = file_bch_inode(iocb->ki_filp); - bool direct = iocb->ki_flags & IOCB_DIRECT; - ssize_t ret; - - inode_lock(&inode->v); - ret = generic_write_checks(iocb, from); if (ret > 0) - ret = __bch2_write_iter(iocb, from); - inode_unlock(&inode->v); - - if (ret > 0 && !direct) ret = generic_write_sync(iocb, ret); return ret; |