X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs%2Fquota.c;h=e68b34eab90a912a55727b1da0428b3655cb3834;hb=378ae738d5ea505c1751f23b4c56e03bb2422f33;hp=ca029a00e7b8044c5815b783a3437628942ecdda;hpb=63805882d4a60de63ed82d81ef4cfbb6e499753a;p=bcachefs-tools-debian diff --git a/libbcachefs/quota.c b/libbcachefs/quota.c index ca029a0..e68b34e 100644 --- a/libbcachefs/quota.c +++ b/libbcachefs/quota.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 #include "bcachefs.h" #include "btree_update.h" +#include "errcode.h" +#include "error.h" #include "inode.h" #include "quota.h" -#include "subvolume.h" +#include "snapshot.h" #include "super-io.h" static const char * const bch2_quota_types[] = { @@ -23,9 +25,9 @@ static int bch2_sb_quota_validate(struct bch_sb *sb, struct bch_sb_field *f, struct bch_sb_field_quota *q = field_to_type(f, quota); if (vstruct_bytes(&q->field) < sizeof(*q)) { - pr_buf(err, "wrong size (got %zu should be %zu)", + prt_printf(err, "wrong size (got %zu should be %zu)", vstruct_bytes(&q->field), sizeof(*q)); - return -EINVAL; + return -BCH_ERR_invalid_sb_quota; } return 0; @@ -38,17 +40,17 @@ static void bch2_sb_quota_to_text(struct printbuf *out, struct bch_sb *sb, unsigned qtyp, counter; for (qtyp = 0; qtyp < ARRAY_SIZE(q->q); qtyp++) { - pr_buf(out, "%s: flags %llx", + prt_printf(out, "%s: flags %llx", bch2_quota_types[qtyp], le64_to_cpu(q->q[qtyp].flags)); for (counter = 0; counter < Q_COUNTERS; counter++) - pr_buf(out, " %s timelimit %u warnlimit %u", + prt_printf(out, " %s timelimit %u warnlimit %u", bch2_quota_counters[counter], le32_to_cpu(q->q[qtyp].c[counter].timelimit), le32_to_cpu(q->q[qtyp].c[counter].warnlimit)); - pr_newline(out); + prt_newline(out); } } @@ -57,15 +59,18 @@ const struct bch_sb_field_ops bch_sb_field_ops_quota = { .to_text = bch2_sb_quota_to_text, }; -const char *bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k) +int bch2_quota_invalid(struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) { - if (k.k->p.inode >= QTYP_NR) - return "invalid quota type"; - - if (bkey_val_bytes(k.k) != sizeof(struct bch_quota)) - return "incorrect value size"; + int ret = 0; - return NULL; + bkey_fsck_err_on(k.k->p.inode >= QTYP_NR, c, err, + quota_type_invalid, + "invalid quota type (%llu >= %u)", + k.k->p.inode, QTYP_NR); +fsck_err: + return ret; } void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c, @@ -75,7 +80,7 @@ void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c, unsigned i; for (i = 0; i < Q_COUNTERS; i++) - pr_buf(out, "%s hardlimit %llu softlimit %llu", + prt_printf(out, "%s hardlimit %llu softlimit %llu", bch2_quota_counters[i], le64_to_cpu(dq.v->c[i].hardlimit), le64_to_cpu(dq.v->c[i].softlimit)); @@ -87,6 +92,113 @@ void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c, #include #include +static void qc_info_to_text(struct printbuf *out, struct qc_info *i) +{ + printbuf_tabstops_reset(out); + printbuf_tabstop_push(out, 20); + + prt_str(out, "i_fieldmask"); + prt_tab(out); + prt_printf(out, "%x", i->i_fieldmask); + prt_newline(out); + + prt_str(out, "i_flags"); + prt_tab(out); + prt_printf(out, "%u", i->i_flags); + prt_newline(out); + + prt_str(out, "i_spc_timelimit"); + prt_tab(out); + prt_printf(out, "%u", i->i_spc_timelimit); + prt_newline(out); + + prt_str(out, "i_ino_timelimit"); + prt_tab(out); + prt_printf(out, "%u", i->i_ino_timelimit); + prt_newline(out); + + prt_str(out, "i_rt_spc_timelimit"); + prt_tab(out); + prt_printf(out, "%u", i->i_rt_spc_timelimit); + prt_newline(out); + + prt_str(out, "i_spc_warnlimit"); + prt_tab(out); + prt_printf(out, "%u", i->i_spc_warnlimit); + prt_newline(out); + + prt_str(out, "i_ino_warnlimit"); + prt_tab(out); + prt_printf(out, "%u", i->i_ino_warnlimit); + prt_newline(out); + + prt_str(out, "i_rt_spc_warnlimit"); + prt_tab(out); + prt_printf(out, "%u", i->i_rt_spc_warnlimit); + prt_newline(out); +} + +static void qc_dqblk_to_text(struct printbuf *out, struct qc_dqblk *q) +{ + printbuf_tabstops_reset(out); + printbuf_tabstop_push(out, 20); + + prt_str(out, "d_fieldmask"); + prt_tab(out); + prt_printf(out, "%x", q->d_fieldmask); + prt_newline(out); + + prt_str(out, "d_spc_hardlimit"); + prt_tab(out); + prt_printf(out, "%llu", q->d_spc_hardlimit); + prt_newline(out); + + prt_str(out, "d_spc_softlimit"); + prt_tab(out); + prt_printf(out, "%llu", q->d_spc_softlimit); + prt_newline(out); + + prt_str(out, "d_ino_hardlimit"); + prt_tab(out); + prt_printf(out, "%llu", q->d_ino_hardlimit); + prt_newline(out); + + prt_str(out, "d_ino_softlimit"); + prt_tab(out); + prt_printf(out, "%llu", q->d_ino_softlimit); + prt_newline(out); + + prt_str(out, "d_space"); + prt_tab(out); + prt_printf(out, "%llu", q->d_space); + prt_newline(out); + + prt_str(out, "d_ino_count"); + prt_tab(out); + prt_printf(out, "%llu", q->d_ino_count); + prt_newline(out); + + prt_str(out, "d_ino_timer"); + prt_tab(out); + prt_printf(out, "%llu", q->d_ino_timer); + prt_newline(out); + + prt_str(out, "d_spc_timer"); + prt_tab(out); + prt_printf(out, "%llu", q->d_spc_timer); + prt_newline(out); + + prt_str(out, "d_ino_warns"); + prt_tab(out); + prt_printf(out, "%i", q->d_ino_warns); + prt_newline(out); + + prt_str(out, "d_spc_warns"); + prt_tab(out); + prt_printf(out, "%i", q->d_spc_warns); + prt_newline(out); +} + static inline unsigned __next_qtype(unsigned i, unsigned qtypes) { qtypes >>= i; @@ -217,34 +329,20 @@ static int bch2_quota_check_limit(struct bch_fs *c, if (qc->hardlimit && qc->hardlimit < n && !ignore_hardlimit(q)) { - if (mode == KEY_TYPE_QUOTA_PREALLOC) - return -EDQUOT; - prepare_warning(qc, qtype, counter, msgs, HARDWARN); + return -EDQUOT; } if (qc->softlimit && - qc->softlimit < n && - qc->timer && - ktime_get_real_seconds() >= qc->timer && - !ignore_hardlimit(q)) { - if (mode == KEY_TYPE_QUOTA_PREALLOC) - return -EDQUOT; - - prepare_warning(qc, qtype, counter, msgs, SOFTLONGWARN); - } - - if (qc->softlimit && - qc->softlimit < n && - qc->timer == 0) { - if (mode == KEY_TYPE_QUOTA_PREALLOC) + qc->softlimit < n) { + if (qc->timer == 0) { + qc->timer = ktime_get_real_seconds() + q->limits[counter].timelimit; + prepare_warning(qc, qtype, counter, msgs, SOFTWARN); + } else if (ktime_get_real_seconds() >= qc->timer && + !ignore_hardlimit(q)) { + prepare_warning(qc, qtype, counter, msgs, SOFTLONGWARN); return -EDQUOT; - - prepare_warning(qc, qtype, counter, msgs, SOFTWARN); - - /* XXX is this the right one? */ - qc->timer = ktime_get_real_seconds() + - q->limits[counter].warnlimit; + } } return 0; @@ -263,16 +361,16 @@ int bch2_quota_acct(struct bch_fs *c, struct bch_qid qid, memset(&msgs, 0, sizeof(msgs)); + for_each_set_qtype(c, i, q, qtypes) { + mq[i] = genradix_ptr_alloc(&q->table, qid.q[i], GFP_KERNEL); + if (!mq[i]) + return -ENOMEM; + } + for_each_set_qtype(c, i, q, qtypes) mutex_lock_nested(&q->lock, i); for_each_set_qtype(c, i, q, qtypes) { - mq[i] = genradix_ptr_alloc(&q->table, qid.q[i], GFP_NOFS); - if (!mq[i]) { - ret = -ENOMEM; - goto err; - } - ret = bch2_quota_check_limit(c, i, mq[i], &msgs, counter, v, mode); if (ret) goto err; @@ -315,18 +413,17 @@ int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes, memset(&msgs, 0, sizeof(msgs)); + for_each_set_qtype(c, i, q, qtypes) { + src_q[i] = genradix_ptr_alloc(&q->table, src.q[i], GFP_KERNEL); + dst_q[i] = genradix_ptr_alloc(&q->table, dst.q[i], GFP_KERNEL); + if (!src_q[i] || !dst_q[i]) + return -ENOMEM; + } + for_each_set_qtype(c, i, q, qtypes) mutex_lock_nested(&q->lock, i); for_each_set_qtype(c, i, q, qtypes) { - src_q[i] = genradix_ptr_alloc(&q->table, src.q[i], GFP_NOFS); - dst_q[i] = genradix_ptr_alloc(&q->table, dst.q[i], GFP_NOFS); - - if (!src_q[i] || !dst_q[i]) { - ret = -ENOMEM; - goto err; - } - ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_SPC, dst_q[i]->c[Q_SPC].v + space, mode); @@ -354,7 +451,8 @@ err: return ret; } -static int __bch2_quota_set(struct bch_fs *c, struct bkey_s_c k) +static int __bch2_quota_set(struct bch_fs *c, struct bkey_s_c k, + struct qc_dqblk *qdq) { struct bkey_s_c_quota dq; struct bch_memquota_type *q; @@ -363,6 +461,9 @@ static int __bch2_quota_set(struct bch_fs *c, struct bkey_s_c k) BUG_ON(k.k->p.inode >= QTYP_NR); + if (!((1U << k.k->p.inode) & enabled_qtypes(c))) + return 0; + switch (k.k->type) { case KEY_TYPE_quota: dq = bkey_s_c_to_quota(k); @@ -380,36 +481,21 @@ static int __bch2_quota_set(struct bch_fs *c, struct bkey_s_c k) mq->c[i].softlimit = le64_to_cpu(dq.v->c[i].softlimit); } + if (qdq && qdq->d_fieldmask & QC_SPC_TIMER) + mq->c[Q_SPC].timer = qdq->d_spc_timer; + if (qdq && qdq->d_fieldmask & QC_SPC_WARNS) + mq->c[Q_SPC].warns = qdq->d_spc_warns; + if (qdq && qdq->d_fieldmask & QC_INO_TIMER) + mq->c[Q_INO].timer = qdq->d_ino_timer; + if (qdq && qdq->d_fieldmask & QC_INO_WARNS) + mq->c[Q_INO].warns = qdq->d_ino_warns; + mutex_unlock(&q->lock); } return 0; } -static int bch2_quota_init_type(struct bch_fs *c, enum quota_types type) -{ - struct btree_trans trans; - struct btree_iter iter; - struct bkey_s_c k; - int ret = 0; - - bch2_trans_init(&trans, c, 0, 0); - - for_each_btree_key(&trans, iter, BTREE_ID_quotas, POS(type, 0), - BTREE_ITER_PREFETCH, k, ret) { - if (k.k->p.inode != type) - break; - - ret = __bch2_quota_set(c, k); - if (ret) - break; - } - bch2_trans_iter_exit(&trans, &iter); - - bch2_trans_exit(&trans); - return ret; -} - void bch2_fs_quota_exit(struct bch_fs *c) { unsigned i; @@ -426,12 +512,32 @@ void bch2_fs_quota_init(struct bch_fs *c) mutex_init(&c->quotas[i].lock); } +static struct bch_sb_field_quota *bch2_sb_get_or_create_quota(struct bch_sb_handle *sb) +{ + struct bch_sb_field_quota *sb_quota = bch2_sb_field_get(sb->sb, quota); + + if (sb_quota) + return sb_quota; + + sb_quota = bch2_sb_field_resize(sb, quota, sizeof(*sb_quota) / sizeof(u64)); + if (sb_quota) { + unsigned qtype, qc; + + for (qtype = 0; qtype < QTYP_NR; qtype++) + for (qc = 0; qc < Q_COUNTERS; qc++) + sb_quota->q[qtype].c[qc].timelimit = + cpu_to_le32(7 * 24 * 60 * 60); + } + + return sb_quota; +} + static void bch2_sb_quota_read(struct bch_fs *c) { struct bch_sb_field_quota *sb_quota; unsigned i, j; - sb_quota = bch2_sb_get_quota(c->disk_sb.sb); + sb_quota = bch2_sb_field_get(c->disk_sb.sb, quota); if (!sb_quota) return; @@ -448,36 +554,37 @@ 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 btree_iter *iter, + struct bkey_s_c k) { struct bch_fs *c = trans->c; struct bch_inode_unpacked u; - struct bch_subvolume subvolume; - struct bkey_s_c k; + struct bch_snapshot_tree s_t; int ret; - k = bch2_btree_iter_peek(iter); - ret = bkey_err(k); + ret = bch2_snapshot_tree_lookup(trans, + bch2_snapshot_tree(c, k.k->p.snapshot), &s_t); + bch2_fs_inconsistent_on(bch2_err_matches(ret, ENOENT), c, + "%s: snapshot tree %u not found", __func__, + snapshot_t(c, k.k->p.snapshot)->tree); if (ret) return ret; - if (!k.k) - return 1; - - ret = bch2_snapshot_get_subvol(trans, k.k->p.snapshot, &subvolume); - if (ret) - return ret; + if (!s_t.master_subvol) + goto advance; + ret = bch2_inode_find_by_inum_nowarn_trans(trans, + (subvol_inum) { + le32_to_cpu(s_t.master_subvol), + k.k->p.offset, + }, &u); /* - * We don't do quota accounting in snapshots: + * Inode might be deleted in this snapshot - the easiest way to handle + * that is to just skip it here: */ - if (BCH_SUBVOLUME_SNAP(&subvolume)) - goto advance; - - if (!bkey_is_inode(k.k)) + if (bch2_err_matches(ret, ENOENT)) goto advance; - ret = bch2_inode_unpack(k, &u); if (ret) return ret; @@ -486,42 +593,32 @@ static int bch2_fs_quota_read_inode(struct btree_trans *trans, 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)); + bch2_btree_iter_set_pos(iter, bpos_nosnap_successor(iter->pos)); 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; - int ret; mutex_lock(&c->sb_lock); - bch2_sb_quota_read(c); - mutex_unlock(&c->sb_lock); - - for_each_set_qtype(c, i, q, qtypes) { - ret = bch2_quota_init_type(c, i); - if (ret) - return ret; + struct bch_sb_field_quota *sb_quota = bch2_sb_get_or_create_quota(&c->disk_sb); + if (!sb_quota) { + mutex_unlock(&c->sb_lock); + return -BCH_ERR_ENOSPC_sb_quota; } - bch2_trans_init(&trans, c, 0, 0); - - 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_sb_quota_read(c); + mutex_unlock(&c->sb_lock); - bch2_trans_exit(&trans); - return ret < 0 ? ret : 0; + int ret = bch2_trans_run(c, + for_each_btree_key(trans, iter, BTREE_ID_quotas, POS_MIN, + BTREE_ITER_PREFETCH, k, + __bch2_quota_set(c, k, NULL)) ?: + for_each_btree_key(trans, iter, BTREE_ID_inodes, POS_MIN, + BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k, + bch2_fs_quota_read_inode(trans, &iter, k))); + bch_err_fn(c, ret); + return ret; } /* Enable/disable/delete quotas for an entire filesystem: */ @@ -529,6 +626,8 @@ int bch2_fs_quota_read(struct bch_fs *c) static int bch2_quota_enable(struct super_block *sb, unsigned uflags) { struct bch_fs *c = sb->s_fs_info; + struct bch_sb_field_quota *sb_quota; + int ret = 0; if (sb->s_flags & SB_RDONLY) return -EROFS; @@ -548,6 +647,12 @@ static int bch2_quota_enable(struct super_block *sb, unsigned uflags) return -EINVAL; mutex_lock(&c->sb_lock); + sb_quota = bch2_sb_get_or_create_quota(&c->disk_sb); + if (!sb_quota) { + ret = -BCH_ERR_ENOSPC_sb_quota; + goto unlock; + } + if (uflags & FS_QUOTA_UDQ_ENFD) SET_BCH_SB_USRQUOTA(c->disk_sb.sb, true); @@ -558,9 +663,10 @@ static int bch2_quota_enable(struct super_block *sb, unsigned uflags) SET_BCH_SB_PRJQUOTA(c->disk_sb.sb, true); bch2_write_super(c); +unlock: mutex_unlock(&c->sb_lock); - return 0; + return bch2_err_class(ret); } static int bch2_quota_disable(struct super_block *sb, unsigned uflags) @@ -600,7 +706,7 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags) ret = bch2_btree_delete_range(c, BTREE_ID_quotas, POS(QTYP_USR, 0), - POS(QTYP_USR + 1, 0), + POS(QTYP_USR, U64_MAX), 0, NULL); if (ret) return ret; @@ -612,7 +718,7 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags) ret = bch2_btree_delete_range(c, BTREE_ID_quotas, POS(QTYP_GRP, 0), - POS(QTYP_GRP + 1, 0), + POS(QTYP_GRP, U64_MAX), 0, NULL); if (ret) return ret; @@ -624,7 +730,7 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags) ret = bch2_btree_delete_range(c, BTREE_ID_quotas, POS(QTYP_PRJ, 0), - POS(QTYP_PRJ + 1, 0), + POS(QTYP_PRJ, U64_MAX), 0, NULL); if (ret) return ret; @@ -671,7 +777,15 @@ static int bch2_quota_set_info(struct super_block *sb, int type, { struct bch_fs *c = sb->s_fs_info; struct bch_sb_field_quota *sb_quota; - struct bch_memquota_type *q; + int ret = 0; + + if (0) { + struct printbuf buf = PRINTBUF; + + qc_info_to_text(&buf, info); + pr_info("setting:\n%s", buf.buf); + printbuf_exit(&buf); + } if (sb->s_flags & SB_RDONLY) return -EROFS; @@ -686,15 +800,11 @@ static int bch2_quota_set_info(struct super_block *sb, int type, ~(QC_SPC_TIMER|QC_INO_TIMER|QC_SPC_WARNS|QC_INO_WARNS)) return -EINVAL; - q = &c->quotas[type]; - mutex_lock(&c->sb_lock); - sb_quota = bch2_sb_get_quota(c->disk_sb.sb); + sb_quota = bch2_sb_get_or_create_quota(&c->disk_sb); if (!sb_quota) { - sb_quota = bch2_sb_resize_quota(&c->disk_sb, - sizeof(*sb_quota) / sizeof(u64)); - if (!sb_quota) - return -ENOSPC; + ret = -BCH_ERR_ENOSPC_sb_quota; + goto unlock; } if (info->i_fieldmask & QC_SPC_TIMER) @@ -716,9 +826,10 @@ static int bch2_quota_set_info(struct super_block *sb, int type, bch2_sb_quota_read(c); bch2_write_super(c); +unlock: mutex_unlock(&c->sb_lock); - return 0; + return bch2_err_class(ret); } /* Get/set individual quotas: */ @@ -779,7 +890,7 @@ static int bch2_get_next_quota(struct super_block *sb, struct kqid *kqid, ret = -ENOENT; found: mutex_unlock(&q->lock); - return ret; + return bch2_err_class(ret); } static int bch2_set_quota_trans(struct btree_trans *trans, @@ -790,10 +901,8 @@ static int bch2_set_quota_trans(struct btree_trans *trans, struct bkey_s_c k; int ret; - bch2_trans_iter_init(trans, &iter, BTREE_ID_quotas, new_quota->k.p, - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - k = bch2_btree_iter_peek_slot(&iter); - + k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_quotas, new_quota->k.p, + BTREE_ITER_SLOTS|BTREE_ITER_INTENT); ret = bkey_err(k); if (unlikely(ret)) return ret; @@ -823,6 +932,14 @@ static int bch2_set_quota(struct super_block *sb, struct kqid qid, struct bkey_i_quota new_quota; int ret; + if (0) { + struct printbuf buf = PRINTBUF; + + qc_dqblk_to_text(&buf, qdq); + pr_info("setting:\n%s", buf.buf); + printbuf_exit(&buf); + } + if (sb->s_flags & SB_RDONLY) return -EROFS; @@ -830,10 +947,10 @@ static int bch2_set_quota(struct super_block *sb, struct kqid qid, new_quota.k.p = POS(qid.type, from_kqid(&init_user_ns, qid)); ret = bch2_trans_do(c, NULL, NULL, 0, - bch2_set_quota_trans(&trans, &new_quota, qdq)) ?: - __bch2_quota_set(c, bkey_i_to_s_c(&new_quota.k_i)); + bch2_set_quota_trans(trans, &new_quota, qdq)) ?: + __bch2_quota_set(c, bkey_i_to_s_c(&new_quota.k_i), qdq); - return ret; + return bch2_err_class(ret); } const struct quotactl_ops bch2_quotactl_operations = {