diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2016-03-11 20:27:50 -0900 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2017-01-18 21:37:40 -0900 |
commit | b5d33d14bb438131e6aff533564e098c7c84bbab (patch) | |
tree | dbf9a3328a60efde3064be8ec18eaa77938968b2 | |
parent | a5ce0a286f16e6317e96dcd5514bffb2021ebdf7 (diff) |
bcachefs: i_sectors accounting
-rw-r--r-- | drivers/md/bcache/debug.c | 16 | ||||
-rw-r--r-- | drivers/md/bcache/extents.h | 12 | ||||
-rw-r--r-- | drivers/md/bcache/fs-gc.c | 48 | ||||
-rw-r--r-- | drivers/md/bcache/fs-gc.h | 1 | ||||
-rw-r--r-- | drivers/md/bcache/fs-io.c | 250 | ||||
-rw-r--r-- | drivers/md/bcache/fs-io.h | 9 | ||||
-rw-r--r-- | drivers/md/bcache/fs.c | 12 | ||||
-rw-r--r-- | drivers/md/bcache/fs.h | 5 | ||||
-rw-r--r-- | include/uapi/linux/bcache.h | 5 |
9 files changed, 324 insertions, 34 deletions
diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c index 012844b6ad36..bbf29178ae9d 100644 --- a/drivers/md/bcache/debug.c +++ b/drivers/md/bcache/debug.c @@ -14,6 +14,7 @@ #include "debug.h" #include "error.h" #include "extents.h" +#include "fs-gc.h" #include "inode.h" #include "io.h" #include "super.h" @@ -188,7 +189,7 @@ void bch_verify_inode_refs(struct cache_set *c) struct bkey_s_c k; struct bkey_i_inode inode; u64 cur_inum = 0; - u64 i_size; + u64 i_size, i_sectors; u16 i_mode; char buf[100]; @@ -209,6 +210,17 @@ void bch_verify_inode_refs(struct cache_set *c) return; } + if (k.k->p.inode != cur_inum) { + BUG_ON(le32_to_cpu(inode.v.i_flags) & BCH_INODE_I_SIZE_DIRTY); + BUG_ON(le32_to_cpu(inode.v.i_flags) & BCH_INODE_I_SECTORS_DIRTY); + + i_sectors = bch_count_inode_sectors(c, inode.k.p.inode); + cache_set_inconsistent_on(i_sectors != + le64_to_cpu(inode.v.i_sectors), c, + "i_sectors wrong: got %llu, should be %llu", + le64_to_cpu(inode.v.i_sectors), i_sectors); + } + cur_inum = k.k->p.inode; i_mode = le16_to_cpu(inode.v.i_mode); i_size = le64_to_cpu(inode.v.i_size); @@ -219,8 +231,6 @@ void bch_verify_inode_refs(struct cache_set *c) "extent for non regular file, inode %llu mode %u", k.k->p.inode, i_mode); - BUG_ON(le32_to_cpu(inode.v.i_flags) & BCH_INODE_I_SIZE_DIRTY); - if (k.k->p.offset > round_up(i_size, PAGE_SIZE) >> 9) { bch_bkey_val_to_text(c, BTREE_ID_EXTENTS, buf, sizeof(buf), k); diff --git a/drivers/md/bcache/extents.h b/drivers/md/bcache/extents.h index 082745c6854c..1dc89a7e8f14 100644 --- a/drivers/md/bcache/extents.h +++ b/drivers/md/bcache/extents.h @@ -71,6 +71,18 @@ static inline bool bkey_extent_is_data(const struct bkey *k) } } +static inline bool bkey_extent_is_allocation(const struct bkey *k) +{ + switch (k->type) { + case BCH_EXTENT: + case BCH_EXTENT_CACHED: + case BCH_RESERVATION: + return true; + default: + return false; + } +} + static inline bool bkey_extent_is_cached(const struct bkey *k) { return k->type == BCH_EXTENT_CACHED; diff --git a/drivers/md/bcache/fs-gc.c b/drivers/md/bcache/fs-gc.c index d1637c9b520a..772ef7bb59f4 100644 --- a/drivers/md/bcache/fs-gc.c +++ b/drivers/md/bcache/fs-gc.c @@ -79,6 +79,23 @@ static int bch_gc_walk_dirents(struct cache_set *c, struct nlinks *links, return bch_btree_iter_unlock(&iter); } +s64 bch_count_inode_sectors(struct cache_set *c, u64 inum) +{ + struct btree_iter iter; + struct bkey_s_c k; + u64 sectors = 0; + + for_each_btree_key(&iter, c, BTREE_ID_EXTENTS, POS(inum, 0), k) { + if (k.k->p.inode != inum) + break; + + if (bkey_extent_is_allocation(k.k)) + sectors += k.k->size; + } + + return bch_btree_iter_unlock(&iter) ?: sectors; +} + static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter, struct bkey_s_c_inode inode, struct nlink link) { @@ -88,6 +105,7 @@ static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter, u32 i_flags = le32_to_cpu(inode.v->i_flags); u32 i_nlink = le32_to_cpu(inode.v->i_nlink); u64 i_size = le64_to_cpu(inode.v->i_size); + s64 i_sectors = 0; cache_set_inconsistent_on(i_nlink < link.count, c, "i_link too small (%u < %u, type %i)", @@ -103,7 +121,6 @@ static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter, if (c->opts.verbose_recovery) pr_info("deleting inum %llu", inode.k->p.inode); - bch_btree_iter_unlock(iter); return bch_inode_rm(c, inode.k->p.inode); } @@ -121,9 +138,25 @@ static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter, NULL, NULL); if (ret) return ret; + + /* + * We truncated without our normal sector accounting hook, just + * make sure we recalculate it: + */ + i_flags |= BCH_INODE_I_SECTORS_DIRTY; + } + + if (i_flags & BCH_INODE_I_SECTORS_DIRTY) { + if (c->opts.verbose_recovery) + pr_info("recounting sectors for inode %llu", inode.k->p.inode); + + i_sectors = bch_count_inode_sectors(c, inode.k->p.inode); + if (i_sectors < 0) + return i_sectors; } if (i_nlink != link.count + link.dir_count || + i_flags & BCH_INODE_I_SECTORS_DIRTY || i_flags & BCH_INODE_I_SIZE_DIRTY) { if (c->opts.verbose_recovery && i_nlink != link.count + link.dir_count) @@ -133,12 +166,15 @@ static int bch_gc_do_inode(struct cache_set *c, struct btree_iter *iter, bkey_reassemble(&update.k_i, inode.s_c); update.v.i_nlink = cpu_to_le32(link.count + link.dir_count); - update.v.i_flags = cpu_to_le32(i_flags & ~BCH_INODE_I_SIZE_DIRTY); + update.v.i_flags = cpu_to_le32(i_flags & + ~(BCH_INODE_I_SIZE_DIRTY|BCH_INODE_I_SECTORS_DIRTY)); + + if (i_flags & BCH_INODE_I_SECTORS_DIRTY) + update.v.i_sectors = cpu_to_le64(i_sectors); return bch_btree_insert_at(iter, &keylist_single(&update.k_i), NULL, NULL, - BTREE_INSERT_ATOMIC| BTREE_INSERT_NOFAIL); } @@ -173,6 +209,12 @@ static int bch_gc_walk_inodes(struct cache_set *c, struct nlinks *links, switch (k.k->type) { case BCH_INODE_FS: + /* + * Avoid potential deadlocks with iter for + * truncate/rm/etc.: + */ + bch_btree_iter_unlock(&iter); + ret = bch_gc_do_inode(c, &iter, bkey_s_c_to_inode(k), *link); diff --git a/drivers/md/bcache/fs-gc.h b/drivers/md/bcache/fs-gc.h index 4fb5728820ea..9e0a34cc2e5f 100644 --- a/drivers/md/bcache/fs-gc.h +++ b/drivers/md/bcache/fs-gc.h @@ -1,6 +1,7 @@ #ifndef _BCACHE_FS_GC_H #define _BCACHE_FS_GC_H +s64 bch_count_inode_sectors(struct cache_set *, u64); int bch_gc_inode_nlinks(struct cache_set *); #endif /* _BCACHE_FS_GC_H */ diff --git a/drivers/md/bcache/fs-io.c b/drivers/md/bcache/fs-io.c index 5e82223ca466..f0c8d9a2eb34 100644 --- a/drivers/md/bcache/fs-io.c +++ b/drivers/md/bcache/fs-io.c @@ -5,6 +5,7 @@ #include "clock.h" #include "error.h" #include "fs.h" +#include "fs-gc.h" #include "fs-io.h" #include "inode.h" #include "journal.h" @@ -52,7 +53,7 @@ static int inode_set_size(struct bch_inode_info *ei, struct bch_inode *bi, else i_flags &= ~BCH_INODE_I_SIZE_DIRTY; - bi->i_flags = cpu_to_le32(i_flags);; + bi->i_flags = cpu_to_le32(i_flags); return 0; } @@ -78,9 +79,11 @@ static int check_make_i_size_dirty(struct bch_inode_info *ei, loff_t offset) unsigned seq; int ret = 0; + BUG_ON(offset > round_up(ei->i_size, PAGE_SIZE) && + !atomic_long_read(&ei->i_size_dirty_count)); do { seq = read_seqcount_begin(&ei->shadow_i_size_lock); - need_set_dirty = offset > ei->i_size && + need_set_dirty = offset > round_up(ei->i_size, PAGE_SIZE) && !(ei->i_flags & BCH_INODE_I_SIZE_DIRTY); } while (read_seqcount_retry(&ei->shadow_i_size_lock, seq)); @@ -91,7 +94,7 @@ static int check_make_i_size_dirty(struct bch_inode_info *ei, loff_t offset) /* recheck under lock.. */ - if (offset > ei->i_size && + if (offset > round_up(ei->i_size, PAGE_SIZE) && !(ei->i_flags & BCH_INODE_I_SIZE_DIRTY)) { struct cache_set *c = ei->vfs_inode.i_sb->s_fs_info; @@ -190,6 +193,123 @@ static struct i_size_update *i_size_update_new(struct bch_inode_info *ei, return u; } +/* i_sectors accounting: */ + +static void i_sectors_hook_fn(struct btree_insert_hook *hook, + struct btree_iter *iter, + struct bkey_s_c k, + struct bkey_i *insert, + struct journal_res *res) +{ + struct i_sectors_hook *h = container_of(hook, + struct i_sectors_hook, hook); + + if (k.k) { + if (!bkey_extent_is_allocation(k.k)) + return; + + switch (bch_extent_overlap(&insert->k, k.k)) { + case BCH_EXTENT_OVERLAP_FRONT: + h->sectors -= insert->k.p.offset - bkey_start_offset(k.k); + break; + + case BCH_EXTENT_OVERLAP_BACK: + h->sectors -= k.k->p.offset - bkey_start_offset(&insert->k); + break; + + case BCH_EXTENT_OVERLAP_ALL: + h->sectors -= k.k->size; + break; + + case BCH_EXTENT_OVERLAP_MIDDLE: + h->sectors -= insert->k.size; + break; + } + } else { + if (!bkey_extent_is_allocation(&insert->k)) + return; + + h->sectors += insert->k.size; + } +} + +static int inode_set_i_sectors_dirty(struct bch_inode_info *ei, + struct bch_inode *bi, void *p) +{ + BUG_ON(le32_to_cpu(bi->i_flags) & BCH_INODE_I_SECTORS_DIRTY); + + bi->i_flags = cpu_to_le32(le32_to_cpu(bi->i_flags)| + BCH_INODE_I_SECTORS_DIRTY); + return 0; +} + +static int inode_clear_i_sectors_dirty(struct bch_inode_info *ei, + struct bch_inode *bi, void *p) +{ + BUG_ON(!(le32_to_cpu(bi->i_flags) & BCH_INODE_I_SECTORS_DIRTY)); + + bi->i_sectors = cpu_to_le64(atomic64_read(&ei->i_sectors)); + bi->i_flags = cpu_to_le32(le32_to_cpu(bi->i_flags) & + ~BCH_INODE_I_SECTORS_DIRTY); + return 0; +} + +static void i_sectors_dirty_put(struct bch_inode_info *ei, + struct i_sectors_hook *h) +{ + struct inode *inode = &ei->vfs_inode; + + if (h->sectors) { + atomic64_add(h->sectors, &ei->i_sectors); + + EBUG_ON(atomic64_read(&ei->i_sectors) < 0); + + spin_lock(&inode->i_lock); + inode->i_blocks = atomic64_read(&ei->i_sectors); + spin_unlock(&inode->i_lock); + } + + EBUG_ON(atomic_long_read(&ei->i_sectors_dirty_count) <= 0); + + mutex_lock(&ei->update_lock); + + if (atomic_long_dec_and_test(&ei->i_sectors_dirty_count)) { + struct cache_set *c = ei->vfs_inode.i_sb->s_fs_info; + int ret = __bch_write_inode(c, ei, inode_clear_i_sectors_dirty, NULL); + + ret = ret; + } + + mutex_unlock(&ei->update_lock); +} + +static int __must_check i_sectors_dirty_get(struct bch_inode_info *ei, + struct i_sectors_hook *h) +{ + int ret = 0; + + h->hook.fn = i_sectors_hook_fn; + h->sectors = 0; + + if (atomic_long_inc_not_zero(&ei->i_sectors_dirty_count)) + return 0; + + mutex_lock(&ei->update_lock); + + if (!(ei->i_flags & BCH_INODE_I_SECTORS_DIRTY)) { + struct cache_set *c = ei->vfs_inode.i_sb->s_fs_info; + + ret = __bch_write_inode(c, ei, inode_set_i_sectors_dirty, NULL); + } + + if (!ret) + atomic_long_inc(&ei->i_sectors_dirty_count); + + mutex_unlock(&ei->update_lock); + + return ret; +} + /* page state: */ /* stored in page->private: */ @@ -530,6 +650,8 @@ static void bch_writepage_io_done(struct closure *cl) for (i = 0; i < ARRAY_SIZE(io->i_size_update_count); i++) i_size_update_put(c, ei, i, io->i_size_update_count[i]); + i_sectors_dirty_put(ei, &io->i_sectors_hook); + bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; @@ -570,6 +692,8 @@ alloc_io: if (!w->io) { struct bio *bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, bch_writepage_bioset); + int ret; + w->io = container_of(bio, struct bch_writepage_io, bio.bio.bio); closure_init(&w->io->cl, NULL); @@ -578,9 +702,18 @@ alloc_io: sizeof(w->io->i_size_update_count)); w->io->sectors_reserved = 0; + ret = i_sectors_dirty_get(ei, &w->io->i_sectors_hook); + /* + * i_sectors_dirty_get() will only return an error if it failed + * to set the I_SECTORS_DIRTY flag - however, we're already + * holding a ref (in bch_writepage() or bch_writepages()) so + * the flag must already be set: + */ + BUG_ON(ret); + bch_write_op_init(&w->io->op, w->c, &w->io->bio, NULL, bkey_to_s_c(&KEY(w->inum, 0, 0)), - NULL, + &w->io->i_sectors_hook.hook, &ei->journal_seq, 0); } @@ -676,39 +809,53 @@ do_io: int bch_writepages(struct address_space *mapping, struct writeback_control *wbc) { - int ret; + struct bch_inode_info *ei = to_bch_ei(mapping->host); + struct i_sectors_hook i_sectors_hook; struct bch_writepage w = { .c = mapping->host->i_sb->s_fs_info, .inum = mapping->host->i_ino, .io = NULL, }; + int ret; + + ret = i_sectors_dirty_get(ei, &i_sectors_hook); + if (ret) + return ret; ret = write_cache_pages(mapping, wbc, __bch_writepage, &w); if (w.io) bch_writepage_do_io(w.io); + i_sectors_dirty_put(ei, &i_sectors_hook); + return ret; } int bch_writepage(struct page *page, struct writeback_control *wbc) { struct inode *inode = page->mapping->host; - int ret; + struct bch_inode_info *ei = to_bch_ei(inode); + struct i_sectors_hook i_sectors_hook; struct bch_writepage w = { .c = inode->i_sb->s_fs_info, .inum = inode->i_ino, .io = NULL, }; + int ret; - ret = __bch_writepage(page, wbc, &w); + ret = i_sectors_dirty_get(ei, &i_sectors_hook); if (ret) return ret; + ret = __bch_writepage(page, wbc, &w); + if (w.io) bch_writepage_do_io(w.io); - return 0; + i_sectors_dirty_put(ei, &i_sectors_hook); + + return ret; } static void bch_read_single_page_end_io(struct bio *bio) @@ -990,6 +1137,10 @@ out: static void __bch_dio_write_complete(struct dio_write *dio) { + struct bch_inode_info *ei = to_bch_ei(dio->req->ki_filp->f_inode); + + i_sectors_dirty_put(ei, &dio->i_sectors_hook); + inode_dio_end(dio->req->ki_filp->f_inode); if (dio->iovec && dio->iovec != dio->inline_vecs) @@ -1051,7 +1202,7 @@ static void bch_do_direct_IO_write(struct dio_write *dio, bool sync) bkey_to_s_c(&KEY(inode->i_ino, bio_end_sector(bio), bio_sectors(bio))), - NULL, + &dio->i_sectors_hook.hook, &ei->journal_seq, flags); task_io_account_write(bio->bi_iter.bi_size); @@ -1127,12 +1278,12 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, } ret = check_make_i_size_dirty(ei, offset + iter->count); - if (ret) { - if (dio->append) - i_size_dirty_put(ei); - bio_put(bio); - return ret; - } + if (ret) + goto err; + + ret = i_sectors_dirty_get(ei, &dio->i_sectors_hook); + if (ret) + goto err; closure_init(&dio->cl, NULL); @@ -1204,6 +1355,11 @@ static int bch_direct_IO_write(struct cache_set *c, struct kiocb *req, dio->iter.count ? system_wq : NULL); return -EIOCBQUEUED; } +err: + if (dio->append) + i_size_dirty_put(ei); + bio_put(bio); + return ret; } ssize_t bch_direct_IO(struct kiocb *req, struct iov_iter *iter) @@ -1655,14 +1811,26 @@ int bch_truncate(struct inode *inode, struct iattr *iattr) * here (new i_size < current i_size): */ if (shrink) { - ret = bch_truncate_page(inode->i_mapping, iattr->ia_size); + struct i_sectors_hook i_sectors_hook; + int ret; + + ret = i_sectors_dirty_get(ei, &i_sectors_hook); if (unlikely(ret)) return ret; + ret = bch_truncate_page(inode->i_mapping, iattr->ia_size); + if (unlikely(ret)) { + i_sectors_dirty_put(ei, &i_sectors_hook); + return ret; + } + ret = bch_inode_truncate(c, inode->i_ino, round_up(iattr->ia_size, PAGE_SIZE) >> 9, - NULL, + &i_sectors_hook.hook, &ei->journal_seq); + + i_sectors_dirty_put(ei, &i_sectors_hook); + if (unlikely(ret)) return ret; } @@ -1701,11 +1869,23 @@ static long bch_fpunch(struct inode *inode, loff_t offset, loff_t len) truncate_pagecache_range(inode, offset, offset + len - 1); - if (discard_start < discard_end) + if (discard_start < discard_end) { + struct i_sectors_hook i_sectors_hook; + int ret; + + ret = i_sectors_dirty_get(ei, &i_sectors_hook); + if (unlikely(ret)) + goto out; + ret = bch_discard(c, POS(ino, discard_start), POS(ino, discard_end), - 0, NULL, &ei->journal_seq); + 0, + &i_sectors_hook.hook, + &ei->journal_seq); + + i_sectors_dirty_put(ei, &i_sectors_hook); + } out: inode_unlock(inode); @@ -1721,6 +1901,7 @@ static long bch_fcollapse(struct inode *inode, loff_t offset, loff_t len) BKEY_PADDED(k) copy; struct bkey_s_c k; struct i_size_update *u; + struct i_sectors_hook i_sectors_hook; loff_t new_size; unsigned idx; int ret; @@ -1770,6 +1951,10 @@ static long bch_fcollapse(struct inode *inode, loff_t offset, loff_t len) if (ret) goto err; + ret = i_sectors_dirty_get(ei, &i_sectors_hook); + if (ret) + goto err; + while (bkey_cmp(dst.pos, POS(inode->i_ino, round_up(new_size, PAGE_SIZE) >> 9)) < 0) { @@ -1799,7 +1984,8 @@ static long bch_fcollapse(struct inode *inode, loff_t offset, loff_t len) ret = bch_btree_insert_at(&dst, &keylist_single(©.k), - NULL, &ei->journal_seq, + &i_sectors_hook.hook, + &ei->journal_seq, BTREE_INSERT_ATOMIC| BTREE_INSERT_NOFAIL); if (ret < 0 && ret != -EINTR) @@ -1813,10 +1999,13 @@ static long bch_fcollapse(struct inode *inode, loff_t offset, loff_t len) ret = bch_inode_truncate(c, inode->i_ino, round_up(new_size, PAGE_SIZE) >> 9, - NULL, &ei->journal_seq); + &i_sectors_hook.hook, + &ei->journal_seq); if (ret) goto err_unwind; + i_sectors_dirty_put(ei, &i_sectors_hook); + mutex_lock(&ei->update_lock); /* @@ -1841,6 +2030,7 @@ static long bch_fcollapse(struct inode *inode, loff_t offset, loff_t len) return ret; err_unwind: + i_sectors_dirty_put(ei, &i_sectors_hook); BUG(); err: bch_btree_iter_unlock(&src); @@ -1854,6 +2044,7 @@ static long bch_fallocate(struct inode *inode, int mode, { struct bch_inode_info *ei = to_bch_ei(inode); struct cache_set *c = inode->i_sb->s_fs_info; + struct i_sectors_hook i_sectors_hook; struct btree_iter iter; struct bkey_i reservation; struct bkey_s_c k; @@ -1904,13 +2095,17 @@ static long bch_fallocate(struct inode *inode, int mode, bch_btree_iter_set_pos(&iter, POS(inode->i_ino, block_start >> 9)); end = POS(inode->i_ino, block_end >> 9); + ret = i_sectors_dirty_get(ei, &i_sectors_hook); + if (unlikely(ret)) + goto err; + while (bkey_cmp(iter.pos, end) < 0) { unsigned flags = 0; k = bch_btree_iter_peek_with_holes(&iter); if (!k.k) { ret = bch_btree_iter_unlock(&iter) ?: -EIO; - goto err; + goto err_put_sectors_dirty; } /* already reserved */ @@ -1941,21 +2136,24 @@ static long bch_fallocate(struct inode *inode, int mode, ret = reserve_sectors(c, sectors); if (ret) - goto err; + goto err_put_sectors_dirty; ret = bch_btree_insert_at(&iter, &keylist_single(&reservation), - NULL, &ei->journal_seq, + &i_sectors_hook.hook, + &ei->journal_seq, BTREE_INSERT_ATOMIC|flags); atomic64_sub_bug(sectors, &c->sectors_reserved); if (ret < 0 && ret != -EINTR) - goto err; + goto err_put_sectors_dirty; } bch_btree_iter_unlock(&iter); + i_sectors_dirty_put(ei, &i_sectors_hook); + if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size > inode->i_size) { struct i_size_update *u; @@ -1975,6 +2173,8 @@ static long bch_fallocate(struct inode *inode, int mode, inode_unlock(inode); return 0; +err_put_sectors_dirty: + i_sectors_dirty_put(ei, &i_sectors_hook); err: bch_btree_iter_unlock(&iter); inode_unlock(inode); diff --git a/drivers/md/bcache/fs-io.h b/drivers/md/bcache/fs-io.h index cb4574785ca1..4cfc9dcf654d 100644 --- a/drivers/md/bcache/fs-io.h +++ b/drivers/md/bcache/fs-io.h @@ -30,6 +30,11 @@ int bch_releasepage(struct page *, gfp_t); int bch_migrate_page(struct address_space *, struct page *, struct page *, enum migrate_mode); +struct i_sectors_hook { + struct btree_insert_hook hook; + s64 sectors; +}; + struct bch_writepage_io { struct closure cl; @@ -38,6 +43,8 @@ struct bch_writepage_io { unsigned long sectors_reserved; struct bch_write_op op; + struct i_sectors_hook i_sectors_hook; + /* must come last: */ struct bch_write_bio bio; }; @@ -59,6 +66,8 @@ struct dio_write { struct mm_struct *mm; struct bch_write_op iop; + struct i_sectors_hook i_sectors_hook; + /* must be last: */ struct bch_write_bio bio; }; diff --git a/drivers/md/bcache/fs.c b/drivers/md/bcache/fs.c index 19544b5db60f..8d4ef341cdd0 100644 --- a/drivers/md/bcache/fs.c +++ b/drivers/md/bcache/fs.c @@ -6,6 +6,7 @@ #include "dirent.h" #include "extents.h" #include "fs.h" +#include "fs-gc.h" #include "fs-io.h" #include "inode.h" #include "journal.h" @@ -151,8 +152,8 @@ out: return ret < 0 ? ret : 0; } -static int __must_check bch_write_inode(struct cache_set *c, - struct bch_inode_info *ei) +int __must_check bch_write_inode(struct cache_set *c, + struct bch_inode_info *ei) { return __bch_write_inode(c, ei, NULL, NULL); } @@ -1010,6 +1011,9 @@ static void bch_inode_init(struct bch_inode_info *ei, i_uid_write(inode, le32_to_cpu(bi->i_uid)); i_gid_write(inode, le32_to_cpu(bi->i_gid)); + atomic64_set(&ei->i_sectors, le64_to_cpu(bi->i_sectors)); + inode->i_blocks = atomic64_read(&ei->i_sectors); + inode->i_ino = bkey_inode.k->p.inode; set_nlink(inode, le32_to_cpu(bi->i_nlink)); inode->i_rdev = le32_to_cpu(bi->i_dev); @@ -1063,6 +1067,7 @@ static struct inode *bch_alloc_inode(struct super_block *sb) ei->flags = 0; seqcount_init(&ei->shadow_i_size_lock); + atomic_long_set(&ei->i_sectors_dirty_count, 0); return &ei->vfs_inode; } @@ -1108,16 +1113,19 @@ static void bch_evict_inode(struct inode *inode) /* bch_inode_create() failed: */ BUG_ON(!fifo_empty(&ei->i_size_updates)); + BUG_ON(atomic_long_read(&ei->i_sectors_dirty_count)); clear_inode(inode); } else if (inode->i_nlink) { truncate_inode_pages_final(&inode->i_data); BUG_ON(!fifo_empty(&ei->i_size_updates)); + BUG_ON(atomic_long_read(&ei->i_sectors_dirty_count)); clear_inode(inode); } else { truncate_inode_pages_final(&inode->i_data); BUG_ON(!fifo_empty(&ei->i_size_updates)); + BUG_ON(atomic_long_read(&ei->i_sectors_dirty_count)); clear_inode(inode); bch_inode_rm(c, inode->i_ino); diff --git a/drivers/md/bcache/fs.h b/drivers/md/bcache/fs.h index fd8155276777..a2922163479e 100644 --- a/drivers/md/bcache/fs.h +++ b/drivers/md/bcache/fs.h @@ -37,6 +37,9 @@ struct bch_inode_info { u64 i_size; u32 i_flags; seqcount_t shadow_i_size_lock; + + atomic_long_t i_sectors_dirty_count; + atomic64_t i_sectors; }; enum { @@ -57,5 +60,7 @@ typedef int (*inode_set_fn)(struct bch_inode_info *, int __must_check __bch_write_inode(struct cache_set *, struct bch_inode_info *, inode_set_fn, void *); +int __must_check bch_write_inode(struct cache_set *, + struct bch_inode_info *); #endif /* _BCACHE_FS_H */ diff --git a/include/uapi/linux/bcache.h b/include/uapi/linux/bcache.h index 2b404c44debc..4c8a456cbe19 100644 --- a/include/uapi/linux/bcache.h +++ b/include/uapi/linux/bcache.h @@ -495,9 +495,10 @@ enum { __BCH_INODE_NOATIME = 4, __BCH_INODE_I_SIZE_DIRTY= 5, + __BCH_INODE_I_SECTORS_DIRTY= 6, /* not implemented yet: */ - __BCH_INODE_HAS_XATTRS = 6, /* has xattrs in xattr btree */ + __BCH_INODE_HAS_XATTRS = 7, /* has xattrs in xattr btree */ }; #define BCH_INODE_SYNC (1 << __BCH_INODE_SYNC) @@ -506,6 +507,7 @@ enum { #define BCH_INODE_NODUMP (1 << __BCH_INODE_NODUMP) #define BCH_INODE_NOATIME (1 << __BCH_INODE_NOATIME) #define BCH_INODE_I_SIZE_DIRTY (1 << __BCH_INODE_I_SIZE_DIRTY) +#define BCH_INODE_I_SECTORS_DIRTY (1 << __BCH_INODE_I_SECTORS_DIRTY) #define BCH_INODE_HAS_XATTRS (1 << __BCH_INODE_HAS_XATTRS) struct bch_inode { @@ -521,6 +523,7 @@ struct bch_inode { __le64 i_mtime; __le64 i_size; + __le64 i_sectors; __le32 i_uid; __le32 i_gid; |