Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Kent Overstreet | 3421 | 99.42% | 37 | 94.87% |
Brian Foster | 12 | 0.35% | 1 | 2.56% |
zhuxiaohui | 8 | 0.23% | 1 | 2.56% |
Total | 3441 | 39 |
// SPDX-License-Identifier: GPL-2.0 #ifndef NO_BCACHEFS_FS #include "bcachefs.h" #include "alloc_foreground.h" #include "fs.h" #include "fs-io.h" #include "fs-io-direct.h" #include "fs-io-pagecache.h" #include "io_read.h" #include "io_write.h" #include <linux/kthread.h> #include <linux/pagemap.h> #include <linux/prefetch.h> #include <linux/task_io_accounting_ops.h> /* O_DIRECT reads */ struct dio_read { struct closure cl; struct kiocb *req; long ret; bool should_dirty; struct bch_read_bio rbio; }; static void bio_check_or_release(struct bio *bio, bool check_dirty) { if (check_dirty) { bio_check_pages_dirty(bio); } else { bio_release_pages(bio, false); bio_put(bio); } } static CLOSURE_CALLBACK(bch2_dio_read_complete) { closure_type(dio, struct dio_read, cl); dio->req->ki_complete(dio->req, dio->ret); bio_check_or_release(&dio->rbio.bio, dio->should_dirty); } static void bch2_direct_IO_read_endio(struct bio *bio) { struct dio_read *dio = bio->bi_private; if (bio->bi_status) dio->ret = blk_status_to_errno(bio->bi_status); closure_put(&dio->cl); } static void bch2_direct_IO_read_split_endio(struct bio *bio) { struct dio_read *dio = bio->bi_private; bool should_dirty = dio->should_dirty; bch2_direct_IO_read_endio(bio); bio_check_or_release(bio, should_dirty); } static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter) { struct file *file = req->ki_filp; 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; struct dio_read *dio; struct bio *bio; loff_t offset = req->ki_pos; bool sync = is_sync_kiocb(req); size_t shorten; ssize_t ret; bch2_inode_opts_get(&opts, c, &inode->ei_inode); /* bios must be 512 byte aligned: */ if ((offset|iter->count) & (SECTOR_SIZE - 1)) return -EINVAL; ret = min_t(loff_t, iter->count, max_t(loff_t, 0, i_size_read(&inode->v) - offset)); if (!ret) return ret; shorten = iov_iter_count(iter) - round_up(ret, block_bytes(c)); if (shorten >= iter->count) shorten = 0; iter->count -= shorten; bio = bio_alloc_bioset(NULL, bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS), REQ_OP_READ, GFP_KERNEL, &c->dio_read_bioset); bio->bi_end_io = bch2_direct_IO_read_endio; dio = container_of(bio, struct dio_read, rbio.bio); closure_init(&dio->cl, NULL); /* * this is a _really_ horrible hack just to avoid an atomic sub at the * end: */ if (!sync) { set_closure_fn(&dio->cl, bch2_dio_read_complete, NULL); atomic_set(&dio->cl.remaining, CLOSURE_REMAINING_INITIALIZER - CLOSURE_RUNNING + CLOSURE_DESTRUCTOR); } else { atomic_set(&dio->cl.remaining, CLOSURE_REMAINING_INITIALIZER + 1); dio->cl.closure_get_happened = true; } dio->req = req; dio->ret = ret; /* * This is one of the sketchier things I've encountered: we have to skip * the dirtying of requests that are internal from the kernel (i.e. from * loopback), because we'll deadlock on page_lock. */ dio->should_dirty = iter_is_iovec(iter); goto start; while (iter->count) { bio = bio_alloc_bioset(NULL, bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS), REQ_OP_READ, GFP_KERNEL, &c->bio_read); bio->bi_end_io = bch2_direct_IO_read_split_endio; start: bio->bi_opf = REQ_OP_READ|REQ_SYNC; bio->bi_iter.bi_sector = offset >> 9; bio->bi_private = dio; ret = bio_iov_iter_get_pages(bio, iter); if (ret < 0) { /* XXX: fault inject this path */ bio->bi_status = BLK_STS_RESOURCE; bio_endio(bio); break; } offset += bio->bi_iter.bi_size; if (dio->should_dirty) bio_set_pages_dirty(bio); if (iter->count) closure_get(&dio->cl); bch2_read(c, rbio_init(bio, opts), inode_inum(inode)); } iter->count += shorten; if (sync) { closure_sync(&dio->cl); closure_debug_destroy(&dio->cl); ret = dio->ret; bio_check_or_release(&dio->rbio.bio, dio->should_dirty); return ret; } else { return -EIOCBQUEUED; } } ssize_t bch2_read_iter(struct kiocb *iocb, struct iov_iter *iter) { struct file *file = iocb->ki_filp; struct bch_inode_info *inode = file_bch_inode(file); struct address_space *mapping = file->f_mapping; size_t count = iov_iter_count(iter); ssize_t ret; if (!count) return 0; /* skip atime */ if (iocb->ki_flags & IOCB_DIRECT) { struct blk_plug plug; if (unlikely(mapping->nrpages)) { ret = filemap_write_and_wait_range(mapping, iocb->ki_pos, iocb->ki_pos + count - 1); if (ret < 0) goto out; } file_accessed(file); blk_start_plug(&plug); ret = bch2_direct_IO_read(iocb, iter); blk_finish_plug(&plug); if (ret >= 0) iocb->ki_pos += ret; } else { bch2_pagecache_add_get(inode); ret = generic_file_read_iter(iocb, iter); bch2_pagecache_add_put(inode); } out: return bch2_err_class(ret); } /* O_DIRECT writes */ struct dio_write { struct kiocb *req; struct address_space *mapping; struct bch_inode_info *inode; struct mm_struct *mm; const struct iovec *iov; unsigned loop:1, extending:1, sync:1, flush:1; struct quota_res quota_res; u64 written; struct iov_iter iter; struct iovec inline_vecs[2]; /* must be last: */ struct bch_write_op op; }; static bool bch2_check_range_allocated(struct bch_fs *c, subvol_inum inum, u64 offset, u64 size, unsigned nr_replicas, bool compressed) { struct btree_trans *trans = bch2_trans_get(c); struct btree_iter iter; struct bkey_s_c k; u64 end = offset + size; u32 snapshot; bool ret = true; int err; retry: bch2_trans_begin(trans); err = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot); if (err) goto err; for_each_btree_key_norestart(trans, iter, BTREE_ID_extents, SPOS(inum.inum, offset, snapshot), BTREE_ITER_SLOTS, k, err) { if (bkey_ge(bkey_start_pos(k.k), POS(inum.inum, end))) break; if (k.k->p.snapshot != snapshot || nr_replicas > bch2_bkey_replicas(c, k) || (!compressed && bch2_bkey_sectors_compressed(k))) { ret = false; break; } } offset = iter.pos.offset; bch2_trans_iter_exit(trans, &iter); err: if (bch2_err_matches(err, BCH_ERR_transaction_restart)) goto retry; bch2_trans_put(trans); return err ? false : ret; } static noinline bool bch2_dio_write_check_allocated(struct dio_write *dio) { struct bch_fs *c = dio->op.c; struct bch_inode_info *inode = dio->inode; struct bio *bio = &dio->op.wbio.bio; return bch2_check_range_allocated(c, inode_inum(inode), dio->op.pos.offset, bio_sectors(bio), dio->op.opts.data_replicas, dio->op.opts.compression != 0); } static void bch2_dio_write_loop_async(struct bch_write_op *); static __always_inline long bch2_dio_write_done(struct dio_write *dio); /* * We're going to return -EIOCBQUEUED, but we haven't finished consuming the * iov_iter yet, so we need to stash a copy of the iovec: it might be on the * caller's stack, we're not guaranteed that it will live for the duration of * the IO: */ static noinline int bch2_dio_write_copy_iov(struct dio_write *dio) { struct iovec *iov = dio->inline_vecs; /* * iov_iter has a single embedded iovec - nothing to do: */ if (iter_is_ubuf(&dio->iter)) return 0; /* * We don't currently handle non-iovec iov_iters here - return an error, * and we'll fall back to doing the IO synchronously: */ if (!iter_is_iovec(&dio->iter)) return -1; if (dio->iter.nr_segs > ARRAY_SIZE(dio->inline_vecs)) { dio->iov = iov = kmalloc_array(dio->iter.nr_segs, sizeof(*iov), GFP_KERNEL); if (unlikely(!iov)) return -ENOMEM; } memcpy(iov, dio->iter.__iov, dio->iter.nr_segs * sizeof(*iov)); dio->iter.__iov = iov; return 0; } static CLOSURE_CALLBACK(bch2_dio_write_flush_done) { closure_type(dio, struct dio_write, op.cl); struct bch_fs *c = dio->op.c; closure_debug_destroy(cl); dio->op.error = bch2_journal_error(&c->journal); bch2_dio_write_done(dio); } static noinline void bch2_dio_write_flush(struct dio_write *dio) { struct bch_fs *c = dio->op.c; struct bch_inode_unpacked inode; int ret; dio->flush = 0; closure_init(&dio->op.cl, NULL); if (!dio->op.error) { ret = bch2_inode_find_by_inum(c, inode_inum(dio->inode), &inode); if (ret) { dio->op.error = ret; } else { bch2_journal_flush_seq_async(&c->journal, inode.bi_journal_seq, &dio->op.cl); bch2_inode_flush_nocow_writes_async(c, dio->inode, &dio->op.cl); } } if (dio->sync) { closure_sync(&dio->op.cl); closure_debug_destroy(&dio->op.cl); } else { continue_at(&dio->op.cl, bch2_dio_write_flush_done, NULL); } } static __always_inline long bch2_dio_write_done(struct dio_write *dio) { struct kiocb *req = dio->req; struct bch_inode_info *inode = dio->inode; bool sync = dio->sync; long ret; if (unlikely(dio->flush)) { bch2_dio_write_flush(dio); if (!sync) return -EIOCBQUEUED; } bch2_pagecache_block_put(inode); kfree(dio->iov); ret = dio->op.error ?: ((long) dio->written << 9); bio_put(&dio->op.wbio.bio); bch2_write_ref_put(dio->op.c, BCH_WRITE_REF_dio_write); /* inode->i_dio_count is our ref on inode and thus bch_fs */ inode_dio_end(&inode->v); if (ret < 0) ret = bch2_err_class(ret); if (!sync) { req->ki_complete(req, ret); ret = -EIOCBQUEUED; } return ret; } static __always_inline void bch2_dio_write_end(struct dio_write *dio) { struct bch_fs *c = dio->op.c; struct kiocb *req = dio->req; struct bch_inode_info *inode = dio->inode; struct bio *bio = &dio->op.wbio.bio; req->ki_pos += (u64) dio->op.written << 9; dio->written += dio->op.written; if (dio->extending) { spin_lock(&inode->v.i_lock); if (req->ki_pos > inode->v.i_size) i_size_write(&inode->v, req->ki_pos); spin_unlock(&inode->v.i_lock); } if (dio->op.i_sectors_delta || dio->quota_res.sectors) { mutex_lock(&inode->ei_quota_lock); __bch2_i_sectors_acct(c, inode, &dio->quota_res, dio->op.i_sectors_delta); __bch2_quota_reservation_put(c, inode, &dio->quota_res); mutex_unlock(&inode->ei_quota_lock); } bio_release_pages(bio, false); if (unlikely(dio->op.error)) set_bit(EI_INODE_ERROR, &inode->ei_flags); } static __always_inline long bch2_dio_write_loop(struct dio_write *dio) { struct bch_fs *c = dio->op.c; struct kiocb *req = dio->req; struct address_space *mapping = dio->mapping; struct bch_inode_info *inode = dio->inode; struct bch_io_opts opts; struct bio *bio = &dio->op.wbio.bio; unsigned unaligned, iter_count; bool sync = dio->sync, dropped_locks; long ret; bch2_inode_opts_get(&opts, c, &inode->ei_inode); while (1) { iter_count = dio->iter.count; EBUG_ON(current->faults_disabled_mapping); current->faults_disabled_mapping = mapping; ret = bio_iov_iter_get_pages(bio, &dio->iter); dropped_locks = fdm_dropped_locks(); current->faults_disabled_mapping = NULL; /* * If the fault handler returned an error but also signalled * that it dropped & retook ei_pagecache_lock, we just need to * re-shoot down the page cache and retry: */ if (dropped_locks && ret) ret = 0; if (unlikely(ret < 0)) goto err; if (unlikely(dropped_locks)) { ret = bch2_write_invalidate_inode_pages_range(mapping, req->ki_pos, req->ki_pos + iter_count - 1); if (unlikely(ret)) goto err; if (!bio->bi_iter.bi_size) continue; } unaligned = bio->bi_iter.bi_size & (block_bytes(c) - 1); bio->bi_iter.bi_size -= unaligned; iov_iter_revert(&dio->iter, unaligned); if (!bio->bi_iter.bi_size) { /* * bio_iov_iter_get_pages was only able to get < * blocksize worth of pages: */ ret = -EFAULT; goto err; } bch2_write_op_init(&dio->op, c, opts); dio->op.end_io = sync ? NULL : bch2_dio_write_loop_async; dio->op.target = dio->op.opts.foreground_target; dio->op.write_point = writepoint_hashed((unsigned long) current); dio->op.nr_replicas = dio->op.opts.data_replicas; dio->op.subvol = inode->ei_subvol; dio->op.pos = POS(inode->v.i_ino, (u64) req->ki_pos >> 9); dio->op.devs_need_flush = &inode->ei_devs_need_flush; if (sync) dio->op.flags |= BCH_WRITE_SYNC; dio->op.flags |= BCH_WRITE_CHECK_ENOSPC; ret = bch2_quota_reservation_add(c, inode, &dio->quota_res, bio_sectors(bio), true); if (unlikely(ret)) goto err; ret = bch2_disk_reservation_get(c, &dio->op.res, bio_sectors(bio), dio->op.opts.data_replicas, 0); if (unlikely(ret) && !bch2_dio_write_check_allocated(dio)) goto err; task_io_account_write(bio->bi_iter.bi_size); if (unlikely(dio->iter.count) && !dio->sync && !dio->loop && bch2_dio_write_copy_iov(dio)) dio->sync = sync = true; dio->loop = true; closure_call(&dio->op.cl, bch2_write, NULL, NULL); if (!sync) return -EIOCBQUEUED; bch2_dio_write_end(dio); if (likely(!dio->iter.count) || dio->op.error) break; bio_reset(bio, NULL, REQ_OP_WRITE | REQ_SYNC | REQ_IDLE); } out: return bch2_dio_write_done(dio); err: dio->op.error = ret; bio_release_pages(bio, false); bch2_quota_reservation_put(c, inode, &dio->quota_res); goto out; } static noinline __cold void bch2_dio_write_continue(struct dio_write *dio) { struct mm_struct *mm = dio->mm; bio_reset(&dio->op.wbio.bio, NULL, REQ_OP_WRITE); if (mm) kthread_use_mm(mm); bch2_dio_write_loop(dio); if (mm) kthread_unuse_mm(mm); } static void bch2_dio_write_loop_async(struct bch_write_op *op) { struct dio_write *dio = container_of(op, struct dio_write, op); bch2_dio_write_end(dio); if (likely(!dio->iter.count) || dio->op.error) bch2_dio_write_done(dio); else bch2_dio_write_continue(dio); } 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 dio_write *dio; struct bio *bio; bool locked = true, extending; ssize_t ret; prefetch(&c->opts); prefetch((void *) &c->opts + 64); prefetch(&inode->ei_inode); prefetch((void *) &inode->ei_inode + 64); if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_dio_write)) return -EROFS; inode_lock(&inode->v); ret = generic_write_checks(req, iter); if (unlikely(ret <= 0)) goto err_put_write_ref; ret = file_remove_privs(file); if (unlikely(ret)) goto err_put_write_ref; ret = file_update_time(file); if (unlikely(ret)) goto err_put_write_ref; if (unlikely((req->ki_pos|iter->count) & (block_bytes(c) - 1))) goto err_put_write_ref; inode_dio_begin(&inode->v); bch2_pagecache_block_get(inode); extending = req->ki_pos + iter->count > inode->v.i_size; if (!extending) { inode_unlock(&inode->v); locked = false; } bio = bio_alloc_bioset(NULL, bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS), REQ_OP_WRITE | REQ_SYNC | REQ_IDLE, GFP_KERNEL, &c->dio_write_bioset); dio = container_of(bio, struct dio_write, op.wbio.bio); dio->req = req; dio->mapping = mapping; dio->inode = inode; dio->mm = current->mm; dio->iov = NULL; dio->loop = false; dio->extending = extending; dio->sync = is_sync_kiocb(req) || extending; dio->flush = iocb_is_dsync(req) && !c->opts.journal_flush_disabled; dio->quota_res.sectors = 0; dio->written = 0; dio->iter = *iter; dio->op.c = c; if (unlikely(mapping->nrpages)) { ret = bch2_write_invalidate_inode_pages_range(mapping, req->ki_pos, req->ki_pos + iter->count - 1); if (unlikely(ret)) goto err_put_bio; } ret = bch2_dio_write_loop(dio); out: if (locked) inode_unlock(&inode->v); return ret; err_put_bio: bch2_pagecache_block_put(inode); bio_put(bio); inode_dio_end(&inode->v); err_put_write_ref: bch2_write_ref_put(c, BCH_WRITE_REF_dio_write); goto out; } void bch2_fs_fs_io_direct_exit(struct bch_fs *c) { bioset_exit(&c->dio_write_bioset); bioset_exit(&c->dio_read_bioset); } int bch2_fs_fs_io_direct_init(struct bch_fs *c) { if (bioset_init(&c->dio_read_bioset, 4, offsetof(struct dio_read, rbio.bio), BIOSET_NEED_BVECS)) return -BCH_ERR_ENOMEM_dio_read_bioset_init; if (bioset_init(&c->dio_write_bioset, 4, offsetof(struct dio_write, op.wbio.bio), BIOSET_NEED_BVECS)) return -BCH_ERR_ENOMEM_dio_write_bioset_init; return 0; } #endif /* NO_BCACHEFS_FS */
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1