summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-04-11 18:39:16 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2018-05-22 00:44:18 -0400
commit51768abe7638c3e8fe8aed86db4d582f74911b2e (patch)
treeb7c37649c6a7fb32bc8ecceeb54a5a23f5f0d143
parentb5189c85527cd13ca46640bf3206678346154118 (diff)
bcachefs: Fix a locking bug
bch2_releasepage() can't take locks that are held while allocating memory, unless GFP_NOFS is used - ei_update_lock is held while calling into btree code that uses GFP_NOIO
-rw-r--r--fs/bcachefs/fs-io.c25
-rw-r--r--fs/bcachefs/fs.c1
-rw-r--r--fs/bcachefs/fs.h2
3 files changed, 13 insertions, 15 deletions
diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c
index e2855743540a..a5e70e3d7ff9 100644
--- a/fs/bcachefs/fs-io.c
+++ b/fs/bcachefs/fs-io.c
@@ -124,13 +124,13 @@ static void bch2_quota_reservation_put(struct bch_fs *c,
if (!res->sectors)
return;
- mutex_lock(&inode->ei_update_lock);
+ mutex_lock(&inode->ei_quota_lock);
BUG_ON(res->sectors > inode->ei_quota_reserved);
bch2_quota_acct(c, inode->ei_qid, Q_SPC,
-((s64) res->sectors), BCH_QUOTA_PREALLOC);
inode->ei_quota_reserved -= res->sectors;
- mutex_unlock(&inode->ei_update_lock);
+ mutex_unlock(&inode->ei_quota_lock);
res->sectors = 0;
}
@@ -143,14 +143,14 @@ static int bch2_quota_reservation_add(struct bch_fs *c,
{
int ret;
- mutex_lock(&inode->ei_update_lock);
+ mutex_lock(&inode->ei_quota_lock);
ret = bch2_quota_acct(c, inode->ei_qid, Q_SPC, sectors,
check_enospc ? BCH_QUOTA_PREALLOC : BCH_QUOTA_NOCHECK);
if (likely(!ret)) {
inode->ei_quota_reserved += sectors;
res->sectors += sectors;
}
- mutex_unlock(&inode->ei_update_lock);
+ mutex_unlock(&inode->ei_quota_lock);
return ret;
}
@@ -195,9 +195,10 @@ static int __must_check bch2_write_inode_size(struct bch_fs *c,
return __bch2_write_inode(c, inode, inode_set_size, &new_size);
}
-static void __i_sectors_acct(struct bch_fs *c, struct bch_inode_info *inode,
- struct quota_res *quota_res, int sectors)
+static void i_sectors_acct(struct bch_fs *c, struct bch_inode_info *inode,
+ struct quota_res *quota_res, int sectors)
{
+ mutex_lock(&inode->ei_quota_lock);
#ifdef CONFIG_BCACHEFS_QUOTA
if (quota_res && sectors > 0) {
BUG_ON(sectors > quota_res->sectors);
@@ -210,14 +211,7 @@ static void __i_sectors_acct(struct bch_fs *c, struct bch_inode_info *inode,
}
#endif
inode->v.i_blocks += sectors;
-}
-
-static void i_sectors_acct(struct bch_fs *c, struct bch_inode_info *inode,
- struct quota_res *quota_res, int sectors)
-{
- mutex_lock(&inode->ei_update_lock);
- __i_sectors_acct(c, inode, quota_res, sectors);
- mutex_unlock(&inode->ei_update_lock);
+ mutex_unlock(&inode->ei_quota_lock);
}
/* i_sectors accounting: */
@@ -265,7 +259,7 @@ static int i_sectors_dirty_finish(struct bch_fs *c, struct i_sectors_hook *h)
if (h->new_i_size != U64_MAX)
i_size_write(&h->inode->v, h->new_i_size);
- __i_sectors_acct(c, h->inode, &h->quota_res, h->sectors);
+ i_sectors_acct(c, h->inode, &h->quota_res, h->sectors);
ret = __bch2_write_inode(c, h->inode, i_sectors_dirty_finish_fn, h);
mutex_unlock(&h->inode->ei_update_lock);
@@ -773,6 +767,7 @@ void bch2_invalidatepage(struct page *page, unsigned int offset,
int bch2_releasepage(struct page *page, gfp_t gfp_mask)
{
+ /* XXX: this can't take locks that are held while we allocate memory */
EBUG_ON(!PageLocked(page));
EBUG_ON(PageWriteback(page));
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index c7e842ee8437..fb30f0d95377 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -1028,6 +1028,7 @@ static struct inode *bch2_alloc_inode(struct super_block *sb)
inode_init_once(&inode->v);
mutex_init(&inode->ei_update_lock);
+ mutex_init(&inode->ei_quota_lock);
inode->ei_journal_seq = 0;
return &inode->v;
diff --git a/fs/bcachefs/fs.h b/fs/bcachefs/fs.h
index fddfb2d2fcaf..fbbc7a3a3cb7 100644
--- a/fs/bcachefs/fs.h
+++ b/fs/bcachefs/fs.h
@@ -15,6 +15,8 @@ struct bch_inode_info {
u64 ei_journal_seq;
u64 ei_quota_reserved;
unsigned long ei_last_dirtied;
+
+ struct mutex ei_quota_lock;
struct bch_qid ei_qid;
struct bch_hash_info ei_str_hash;