Commit 9ca4853b authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

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: default avatarKent Overstreet <kent.overstreet@gmail.com>
parent b44a66a6
...@@ -39,7 +39,8 @@ static struct kmem_cache *bch2_inode_cache; ...@@ -39,7 +39,8 @@ static struct kmem_cache *bch2_inode_cache;
static void bch2_vfs_inode_init(struct btree_trans *, subvol_inum, static void bch2_vfs_inode_init(struct btree_trans *, subvol_inum,
struct bch_inode_info *, 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) 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) ...@@ -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_unpacked inode_u;
struct bch_inode_info *inode; struct bch_inode_info *inode;
struct btree_trans trans; struct btree_trans trans;
struct bch_subvolume subvol;
int ret; int ret;
inode = to_bch_ei(iget5_locked(c->vfs_sb, 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) ...@@ -239,10 +241,11 @@ struct inode *bch2_vfs_inode_get(struct bch_fs *c, subvol_inum inum)
bch2_trans_init(&trans, c, 8, 0); bch2_trans_init(&trans, c, 8, 0);
ret = lockrestart_do(&trans, ret = lockrestart_do(&trans,
bch2_subvolume_get(&trans, inum.subvol, true, 0, &subvol) ?:
bch2_inode_find_by_inum_trans(&trans, inum, &inode_u)); bch2_inode_find_by_inum_trans(&trans, inum, &inode_u));
if (!ret) 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); bch2_trans_exit(&trans);
if (ret) { if (ret) {
...@@ -268,6 +271,7 @@ __bch2_create(struct mnt_idmap *idmap, ...@@ -268,6 +271,7 @@ __bch2_create(struct mnt_idmap *idmap,
struct bch_inode_unpacked inode_u; struct bch_inode_unpacked inode_u;
struct posix_acl *default_acl = NULL, *acl = NULL; struct posix_acl *default_acl = NULL, *acl = NULL;
subvol_inum inum; subvol_inum inum;
struct bch_subvolume subvol;
u64 journal_seq = 0; u64 journal_seq = 0;
int ret; int ret;
...@@ -310,7 +314,12 @@ __bch2_create(struct mnt_idmap *idmap, ...@@ -310,7 +314,12 @@ __bch2_create(struct mnt_idmap *idmap,
if (unlikely(ret)) if (unlikely(ret))
goto err_before_quota; 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)) { if (unlikely(ret)) {
bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, -1, bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, -1,
KEY_TYPE_QUOTA_WARN); KEY_TYPE_QUOTA_WARN);
...@@ -326,11 +335,8 @@ __bch2_create(struct mnt_idmap *idmap, ...@@ -326,11 +335,8 @@ __bch2_create(struct mnt_idmap *idmap,
mutex_unlock(&dir->ei_update_lock); 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_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_ACCESS, acl);
set_cached_acl(&inode->v, ACL_TYPE_DEFAULT, default_acl); set_cached_acl(&inode->v, ACL_TYPE_DEFAULT, default_acl);
...@@ -1352,10 +1358,16 @@ static const struct export_operations bch_export_ops = { ...@@ -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, static void bch2_vfs_inode_init(struct btree_trans *trans, subvol_inum inum,
struct bch_inode_info *inode, 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); 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_blocks = bi->bi_sectors;
inode->v.i_ino = bi->bi_inum; inode->v.i_ino = bi->bi_inum;
inode->v.i_rdev = bi->bi_dev; inode->v.i_rdev = bi->bi_dev;
......
...@@ -63,6 +63,12 @@ static inline subvol_inum inode_inum(struct bch_inode_info *inode) ...@@ -63,6 +63,12 @@ static inline subvol_inum inode_inum(struct bch_inode_info *inode)
*/ */
#define EI_INODE_ERROR 0 #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) \ #define to_bch_ei(_inode) \
container_of_or_null(_inode, struct bch_inode_info, v) container_of_or_null(_inode, struct bch_inode_info, v)
......
...@@ -223,19 +223,19 @@ enum opt_type { ...@@ -223,19 +223,19 @@ enum opt_type {
BCH_SB_POSIX_ACL, true, \ BCH_SB_POSIX_ACL, true, \
NULL, "Enable POSIX acls") \ NULL, "Enable POSIX acls") \
x(usrquota, u8, \ x(usrquota, u8, \
0, \ OPT_FORMAT|OPT_MOUNT, \
OPT_BOOL(), \ OPT_BOOL(), \
NO_SB_OPT, false, \ BCH_SB_USRQUOTA, false, \
NULL, "Enable user quotas") \ NULL, "Enable user quotas") \
x(grpquota, u8, \ x(grpquota, u8, \
0, \ OPT_FORMAT|OPT_MOUNT, \
OPT_BOOL(), \ OPT_BOOL(), \
NO_SB_OPT, false, \ BCH_SB_GRPQUOTA, false, \
NULL, "Enable group quotas") \ NULL, "Enable group quotas") \
x(prjquota, u8, \ x(prjquota, u8, \
0, \ OPT_FORMAT|OPT_MOUNT, \
OPT_BOOL(), \ OPT_BOOL(), \
NO_SB_OPT, false, \ BCH_SB_PRJQUOTA, false, \
NULL, "Enable project quotas") \ NULL, "Enable project quotas") \
x(degraded, u8, \ x(degraded, u8, \
OPT_MOUNT, \ OPT_MOUNT, \
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "btree_update.h" #include "btree_update.h"
#include "inode.h" #include "inode.h"
#include "quota.h" #include "quota.h"
#include "subvolume.h"
#include "super-io.h" #include "super-io.h"
static const char *bch2_sb_validate_quota(struct bch_sb *sb, 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) ...@@ -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) int bch2_fs_quota_read(struct bch_fs *c)
{ {
unsigned i, qtypes = enabled_qtypes(c); unsigned i, qtypes = enabled_qtypes(c);
struct bch_memquota_type *q; struct bch_memquota_type *q;
struct btree_trans trans; struct btree_trans trans;
struct btree_iter iter; struct btree_iter iter;
struct bch_inode_unpacked u;
struct bkey_s_c k;
int ret; int ret;
mutex_lock(&c->sb_lock); mutex_lock(&c->sb_lock);
...@@ -437,23 +479,18 @@ int bch2_fs_quota_read(struct bch_fs *c) ...@@ -437,23 +479,18 @@ int bch2_fs_quota_read(struct bch_fs *c)
bch2_trans_init(&trans, c, 0, 0); bch2_trans_init(&trans, c, 0, 0);
for_each_btree_key(&trans, iter, BTREE_ID_inodes, POS_MIN, bch2_trans_iter_init(&trans, &iter, BTREE_ID_inodes, POS_MIN,
BTREE_ITER_PREFETCH, k, ret) { BTREE_ITER_INTENT|
if (bkey_is_inode(k.k)) { BTREE_ITER_PREFETCH|
ret = bch2_inode_unpack(k, &u); BTREE_ITER_ALL_SNAPSHOTS);
if (ret) do {
return ret; ret = lockrestart_do(&trans,
bch2_fs_quota_read_inode(&trans, &iter));
bch2_quota_acct(c, bch_qid(&u), Q_SPC, u.bi_sectors, } while (!ret);
KEY_TYPE_QUOTA_NOCHECK);
bch2_quota_acct(c, bch_qid(&u), Q_INO, 1,
KEY_TYPE_QUOTA_NOCHECK);
}
}
bch2_trans_iter_exit(&trans, &iter); bch2_trans_iter_exit(&trans, &iter);
bch2_trans_exit(&trans); bch2_trans_exit(&trans);
return ret; return ret < 0 ? ret : 0;
} }
/* Enable/disable/delete quotas for an entire filesystem: */ /* Enable/disable/delete quotas for an entire filesystem: */
......
...@@ -789,6 +789,15 @@ int bch2_subvolume_get(struct btree_trans *trans, unsigned subvol, ...@@ -789,6 +789,15 @@ int bch2_subvolume_get(struct btree_trans *trans, unsigned subvol,
return ret; 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, int bch2_subvolume_get_snapshot(struct btree_trans *trans, u32 subvol,
u32 *snapid) u32 *snapid)
{ {
......
...@@ -118,6 +118,8 @@ void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c) ...@@ -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, int bch2_subvolume_get(struct btree_trans *, unsigned,
bool, int, struct bch_subvolume *); 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_get_snapshot(struct btree_trans *, u32, u32 *);
int bch2_subvolume_delete(struct btree_trans *, u32); int bch2_subvolume_delete(struct btree_trans *, u32);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment