bcachefs: Fix quota support for snapshots

Quota support was disabled when snapshots were released, because of some
tricky interactions with snpashots. We're sidestepping that for now -
we're simply disabling quota accounting on snapshot subvolumes.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
This commit is contained in:
Kent Overstreet 2021-10-27 13:05:56 -04:00 committed by Kent Overstreet
parent b44a66a641
commit 9ca4853b98
6 changed files with 96 additions and 30 deletions

View file

@ -39,7 +39,8 @@ static struct kmem_cache *bch2_inode_cache;
static void bch2_vfs_inode_init(struct btree_trans *, subvol_inum,
struct bch_inode_info *,
struct bch_inode_unpacked *);
struct bch_inode_unpacked *,
struct bch_subvolume *);
static void __pagecache_lock_put(struct pagecache_lock *lock, long i)
{
@ -225,6 +226,7 @@ struct inode *bch2_vfs_inode_get(struct bch_fs *c, subvol_inum inum)
struct bch_inode_unpacked inode_u;
struct bch_inode_info *inode;
struct btree_trans trans;
struct bch_subvolume subvol;
int ret;
inode = to_bch_ei(iget5_locked(c->vfs_sb,
@ -239,10 +241,11 @@ struct inode *bch2_vfs_inode_get(struct bch_fs *c, subvol_inum inum)
bch2_trans_init(&trans, c, 8, 0);
ret = lockrestart_do(&trans,
bch2_subvolume_get(&trans, inum.subvol, true, 0, &subvol) ?:
bch2_inode_find_by_inum_trans(&trans, inum, &inode_u));
if (!ret)
bch2_vfs_inode_init(&trans, inum, inode, &inode_u);
bch2_vfs_inode_init(&trans, inum, inode, &inode_u, &subvol);
bch2_trans_exit(&trans);
if (ret) {
@ -268,6 +271,7 @@ __bch2_create(struct mnt_idmap *idmap,
struct bch_inode_unpacked inode_u;
struct posix_acl *default_acl = NULL, *acl = NULL;
subvol_inum inum;
struct bch_subvolume subvol;
u64 journal_seq = 0;
int ret;
@ -310,7 +314,12 @@ __bch2_create(struct mnt_idmap *idmap,
if (unlikely(ret))
goto err_before_quota;
ret = bch2_trans_commit(&trans, NULL, &journal_seq, 0);
inum.subvol = inode_u.bi_subvol ?: dir->ei_subvol;
inum.inum = inode_u.bi_inum;
ret = bch2_subvolume_get(&trans, inum.subvol, true,
BTREE_ITER_WITH_UPDATES, &subvol) ?:
bch2_trans_commit(&trans, NULL, &journal_seq, 0);
if (unlikely(ret)) {
bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, -1,
KEY_TYPE_QUOTA_WARN);
@ -326,11 +335,8 @@ __bch2_create(struct mnt_idmap *idmap,
mutex_unlock(&dir->ei_update_lock);
}
inum.subvol = inode_u.bi_subvol ?: dir->ei_subvol;
inum.inum = inode_u.bi_inum;
bch2_iget5_set(&inode->v, &inum);
bch2_vfs_inode_init(&trans, inum, inode, &inode_u);
bch2_vfs_inode_init(&trans, inum, inode, &inode_u, &subvol);
set_cached_acl(&inode->v, ACL_TYPE_ACCESS, acl);
set_cached_acl(&inode->v, ACL_TYPE_DEFAULT, default_acl);
@ -1352,10 +1358,16 @@ static const struct export_operations bch_export_ops = {
static void bch2_vfs_inode_init(struct btree_trans *trans, subvol_inum inum,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi)
struct bch_inode_unpacked *bi,
struct bch_subvolume *subvol)
{
bch2_inode_update_after_write(trans, inode, bi, ~0);
if (BCH_SUBVOLUME_SNAP(subvol))
set_bit(EI_INODE_SNAPSHOT, &inode->ei_flags);
else
clear_bit(EI_INODE_SNAPSHOT, &inode->ei_flags);
inode->v.i_blocks = bi->bi_sectors;
inode->v.i_ino = bi->bi_inum;
inode->v.i_rdev = bi->bi_dev;

View file

@ -63,6 +63,12 @@ static inline subvol_inum inode_inum(struct bch_inode_info *inode)
*/
#define EI_INODE_ERROR 0
/*
* Set in the inode is in a snapshot subvolume - we don't do quota accounting in
* those:
*/
#define EI_INODE_SNAPSHOT 1
#define to_bch_ei(_inode) \
container_of_or_null(_inode, struct bch_inode_info, v)

View file

@ -223,19 +223,19 @@ enum opt_type {
BCH_SB_POSIX_ACL, true, \
NULL, "Enable POSIX acls") \
x(usrquota, u8, \
0, \
OPT_FORMAT|OPT_MOUNT, \
OPT_BOOL(), \
NO_SB_OPT, false, \
BCH_SB_USRQUOTA, false, \
NULL, "Enable user quotas") \
x(grpquota, u8, \
0, \
OPT_FORMAT|OPT_MOUNT, \
OPT_BOOL(), \
NO_SB_OPT, false, \
BCH_SB_GRPQUOTA, false, \
NULL, "Enable group quotas") \
x(prjquota, u8, \
0, \
OPT_FORMAT|OPT_MOUNT, \
OPT_BOOL(), \
NO_SB_OPT, false, \
BCH_SB_PRJQUOTA, false, \
NULL, "Enable project quotas") \
x(degraded, u8, \
OPT_MOUNT, \

View file

@ -3,6 +3,7 @@
#include "btree_update.h"
#include "inode.h"
#include "quota.h"
#include "subvolume.h"
#include "super-io.h"
static const char *bch2_sb_validate_quota(struct bch_sb *sb,
@ -415,14 +416,55 @@ static void bch2_sb_quota_read(struct bch_fs *c)
}
}
static int bch2_fs_quota_read_inode(struct btree_trans *trans,
struct btree_iter *iter)
{
struct bch_fs *c = trans->c;
struct bch_inode_unpacked u;
struct bch_subvolume subvolume;
struct bkey_s_c k;
int ret;
k = bch2_btree_iter_peek(iter);
ret = bkey_err(k);
if (ret)
return ret;
if (!k.k)
return 1;
ret = bch2_snapshot_get_subvol(trans, k.k->p.snapshot, &subvolume);
if (ret)
return ret;
/*
* We don't do quota accounting in snapshots:
*/
if (BCH_SUBVOLUME_SNAP(&subvolume))
goto advance;
if (!bkey_is_inode(k.k))
goto advance;
ret = bch2_inode_unpack(k, &u);
if (ret)
return ret;
bch2_quota_acct(c, bch_qid(&u), Q_SPC, u.bi_sectors,
KEY_TYPE_QUOTA_NOCHECK);
bch2_quota_acct(c, bch_qid(&u), Q_INO, 1,
KEY_TYPE_QUOTA_NOCHECK);
advance:
bch2_btree_iter_set_pos(iter, POS(iter->pos.inode, iter->pos.offset + 1));
return 0;
}
int bch2_fs_quota_read(struct bch_fs *c)
{
unsigned i, qtypes = enabled_qtypes(c);
struct bch_memquota_type *q;
struct btree_trans trans;
struct btree_iter iter;
struct bch_inode_unpacked u;
struct bkey_s_c k;
int ret;
mutex_lock(&c->sb_lock);
@ -437,23 +479,18 @@ int bch2_fs_quota_read(struct bch_fs *c)
bch2_trans_init(&trans, c, 0, 0);
for_each_btree_key(&trans, iter, BTREE_ID_inodes, POS_MIN,
BTREE_ITER_PREFETCH, k, ret) {
if (bkey_is_inode(k.k)) {
ret = bch2_inode_unpack(k, &u);
if (ret)
return ret;
bch2_quota_acct(c, bch_qid(&u), Q_SPC, u.bi_sectors,
KEY_TYPE_QUOTA_NOCHECK);
bch2_quota_acct(c, bch_qid(&u), Q_INO, 1,
KEY_TYPE_QUOTA_NOCHECK);
}
}
bch2_trans_iter_init(&trans, &iter, BTREE_ID_inodes, POS_MIN,
BTREE_ITER_INTENT|
BTREE_ITER_PREFETCH|
BTREE_ITER_ALL_SNAPSHOTS);
do {
ret = lockrestart_do(&trans,
bch2_fs_quota_read_inode(&trans, &iter));
} while (!ret);
bch2_trans_iter_exit(&trans, &iter);
bch2_trans_exit(&trans);
return ret;
return ret < 0 ? ret : 0;
}
/* Enable/disable/delete quotas for an entire filesystem: */

View file

@ -789,6 +789,15 @@ int bch2_subvolume_get(struct btree_trans *trans, unsigned subvol,
return ret;
}
int bch2_snapshot_get_subvol(struct btree_trans *trans, u32 snapshot,
struct bch_subvolume *subvol)
{
struct bch_snapshot snap;
return snapshot_lookup(trans, snapshot, &snap) ?:
bch2_subvolume_get(trans, le32_to_cpu(snap.subvol), true, 0, subvol);
}
int bch2_subvolume_get_snapshot(struct btree_trans *trans, u32 subvol,
u32 *snapid)
{

View file

@ -118,6 +118,8 @@ void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c)
int bch2_subvolume_get(struct btree_trans *, unsigned,
bool, int, struct bch_subvolume *);
int bch2_snapshot_get_subvol(struct btree_trans *, u32,
struct bch_subvolume *);
int bch2_subvolume_get_snapshot(struct btree_trans *, u32, u32 *);
int bch2_subvolume_delete(struct btree_trans *, u32);