summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2016-03-11 20:27:50 -0900
committerKent Overstreet <kent.overstreet@gmail.com>2017-01-18 21:37:40 -0900
commitb5d33d14bb438131e6aff533564e098c7c84bbab (patch)
treedbf9a3328a60efde3064be8ec18eaa77938968b2
parenta5ce0a286f16e6317e96dcd5514bffb2021ebdf7 (diff)
bcachefs: i_sectors accounting
-rw-r--r--drivers/md/bcache/debug.c16
-rw-r--r--drivers/md/bcache/extents.h12
-rw-r--r--drivers/md/bcache/fs-gc.c48
-rw-r--r--drivers/md/bcache/fs-gc.h1
-rw-r--r--drivers/md/bcache/fs-io.c250
-rw-r--r--drivers/md/bcache/fs-io.h9
-rw-r--r--drivers/md/bcache/fs.c12
-rw-r--r--drivers/md/bcache/fs.h5
-rw-r--r--include/uapi/linux/bcache.h5
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(&copy.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;