diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index 61027d349cd8c7f071248123a4f6ff5f67c03f9f..31adc0e0d4527601a85f3553b1c5c49e67f2b9b8 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -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;
diff --git a/fs/bcachefs/fs.h b/fs/bcachefs/fs.h
index 530238780a880e249e8770099ba0d08b911a973d..a67ab1ad2a31b6dd2b32e3d8ee2aa946d1910e54 100644
--- a/fs/bcachefs/fs.h
+++ b/fs/bcachefs/fs.h
@@ -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)
 
diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h
index 10c022ec6ee0fb00ea7ebb796cc9bdbf73eb9ccf..896b8c9c118046d74d8375067a6fe1428d74a01a 100644
--- a/fs/bcachefs/opts.h
+++ b/fs/bcachefs/opts.h
@@ -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,							\
diff --git a/fs/bcachefs/quota.c b/fs/bcachefs/quota.c
index 5f1216da76d05ef03014447403128dca6ec0e641..8f8f4b0accd6054a1b42787aad1c05fb5e0f3a27 100644
--- a/fs/bcachefs/quota.c
+++ b/fs/bcachefs/quota.c
@@ -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: */
diff --git a/fs/bcachefs/subvolume.c b/fs/bcachefs/subvolume.c
index 0ef625d21672798595db8d172d82c75768aa1e68..7e909a11818914eaec37a99d1bee18d587c186d0 100644
--- a/fs/bcachefs/subvolume.c
+++ b/fs/bcachefs/subvolume.c
@@ -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)
 {
diff --git a/fs/bcachefs/subvolume.h b/fs/bcachefs/subvolume.h
index dde755b45392a4713e855905b7cdbac39cbf2a8c..e4c3fdcdf22f959e61c96180f9822b1d6564ddfe 100644
--- a/fs/bcachefs/subvolume.h
+++ b/fs/bcachefs/subvolume.h
@@ -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);