bcachefs: Fix quota support for snapshots
authorKent Overstreet <kent.overstreet@gmail.com>
Wed, 27 Oct 2021 17:05:56 +0000 (13:05 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:17 +0000 (17:09 -0400)
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>
fs/bcachefs/fs.c
fs/bcachefs/fs.h
fs/bcachefs/opts.h
fs/bcachefs/quota.c
fs/bcachefs/subvolume.c
fs/bcachefs/subvolume.h

index 61027d349cd8c7f071248123a4f6ff5f67c03f9f..31adc0e0d4527601a85f3553b1c5c49e67f2b9b8 100644 (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 @@ retry:
        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 @@ err_before_quota:
                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;
index 530238780a880e249e8770099ba0d08b911a973d..a67ab1ad2a31b6dd2b32e3d8ee2aa946d1910e54 100644 (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)
 
index 10c022ec6ee0fb00ea7ebb796cc9bdbf73eb9ccf..896b8c9c118046d74d8375067a6fe1428d74a01a 100644 (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,                                                    \
index 5f1216da76d05ef03014447403128dca6ec0e641..8f8f4b0accd6054a1b42787aad1c05fb5e0f3a27 100644 (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: */
index 0ef625d21672798595db8d172d82c75768aa1e68..7e909a11818914eaec37a99d1bee18d587c186d0 100644 (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)
 {
index dde755b45392a4713e855905b7cdbac39cbf2a8c..e4c3fdcdf22f959e61c96180f9822b1d6564ddfe 100644 (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);