X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs%2Fquota.c;h=4f0654ff816f35e6c83e0ade9c2c37e6f839ca30;hb=15b24c732749339e3f65f030e7e68624b1b4bfbd;hp=75104ea98027f449196420148578438a022eb05f;hpb=bdba182d105b1eaa9776c78a094b0b0192046651;p=bcachefs-tools-debian diff --git a/libbcachefs/quota.c b/libbcachefs/quota.c index 75104ea..4f0654f 100644 --- a/libbcachefs/quota.c +++ b/libbcachefs/quota.c @@ -1,67 +1,88 @@ +// 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 "super-io.h" -static const char *bch2_sb_validate_quota(struct bch_sb *sb, - struct bch_sb_field *f) +static const char * const bch2_quota_types[] = { + "user", + "group", + "project", +}; + +static const char * const bch2_quota_counters[] = { + "space", + "inodes", +}; + +static int bch2_sb_quota_validate(struct bch_sb *sb, struct bch_sb_field *f, + struct printbuf *err) { struct bch_sb_field_quota *q = field_to_type(f, quota); - if (vstruct_bytes(&q->field) != sizeof(*q)) - return "invalid field quota: wrong size"; + if (vstruct_bytes(&q->field) < sizeof(*q)) { + prt_printf(err, "wrong size (got %zu should be %zu)", + vstruct_bytes(&q->field), sizeof(*q)); + return -BCH_ERR_invalid_sb_quota; + } - return NULL; + return 0; } -const struct bch_sb_field_ops bch_sb_field_ops_quota = { - .validate = bch2_sb_validate_quota, -}; - -const char *bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k) +static void bch2_sb_quota_to_text(struct printbuf *out, struct bch_sb *sb, + struct bch_sb_field *f) { - struct bkey_s_c_quota dq; - - if (k.k->p.inode >= QTYP_NR) - return "invalid quota type"; + struct bch_sb_field_quota *q = field_to_type(f, quota); + unsigned qtyp, counter; - switch (k.k->type) { - case BCH_QUOTA: { - dq = bkey_s_c_to_quota(k); + for (qtyp = 0; qtyp < ARRAY_SIZE(q->q); qtyp++) { + prt_printf(out, "%s: flags %llx", + bch2_quota_types[qtyp], + le64_to_cpu(q->q[qtyp].flags)); - if (bkey_val_bytes(k.k) != sizeof(struct bch_quota)) - return "incorrect value size"; + for (counter = 0; counter < Q_COUNTERS; counter++) + 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)); - return NULL; - } - default: - return "invalid type"; + prt_newline(out); } } -static const char * const bch2_quota_counters[] = { - "space", - "inodes", +const struct bch_sb_field_ops bch_sb_field_ops_quota = { + .validate = bch2_sb_quota_validate, + .to_text = bch2_sb_quota_to_text, }; +int bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k, + enum bkey_invalid_flags flags, + struct printbuf *err) +{ + if (k.k->p.inode >= QTYP_NR) { + prt_printf(err, "invalid quota type (%llu >= %u)", + k.k->p.inode, QTYP_NR); + return -BCH_ERR_invalid_bkey; + } + + return 0; +} + void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k) { - struct bkey_s_c_quota dq; + struct bkey_s_c_quota dq = bkey_s_c_to_quota(k); unsigned i; - switch (k.k->type) { - case BCH_QUOTA: - dq = bkey_s_c_to_quota(k); - - for (i = 0; i < Q_COUNTERS; i++) - pr_buf(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)); - break; - } + for (i = 0; i < Q_COUNTERS; i++) + 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)); } #ifdef CONFIG_BCACHEFS_QUOTA @@ -70,6 +91,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; @@ -177,7 +305,7 @@ static int bch2_quota_check_limit(struct bch_fs *c, BUG_ON((s64) n < 0); - if (mode == BCH_QUOTA_NOCHECK) + if (mode == KEY_TYPE_QUOTA_NOCHECK) return 0; if (v <= 0) { @@ -200,34 +328,20 @@ static int bch2_quota_check_limit(struct bch_fs *c, if (qc->hardlimit && qc->hardlimit < n && !ignore_hardlimit(q)) { - if (mode == BCH_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 == BCH_QUOTA_PREALLOC) - return -EDQUOT; - - prepare_warning(qc, qtype, counter, msgs, SOFTLONGWARN); - } - - if (qc->softlimit && - qc->softlimit < n && - qc->timer == 0) { - if (mode == BCH_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; @@ -246,16 +360,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; @@ -285,7 +399,8 @@ static void __bch2_quota_transfer(struct bch_memquota *src_q, int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes, struct bch_qid dst, - struct bch_qid src, u64 space) + struct bch_qid src, u64 space, + enum quota_acct_mode mode) { struct bch_memquota_type *q; struct bch_memquota *src_q[3], *dst_q[3]; @@ -297,27 +412,26 @@ 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, - BCH_QUOTA_PREALLOC); + mode); if (ret) goto err; ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_INO, dst_q[i]->c[Q_INO].v + 1, - BCH_QUOTA_PREALLOC); + mode); if (ret) goto err; } @@ -336,7 +450,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; @@ -345,8 +460,11 @@ 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 BCH_QUOTA: + case KEY_TYPE_quota: dq = bkey_s_c_to_quota(k); q = &c->quotas[k.k->p.inode]; @@ -362,31 +480,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_iter iter; - struct bkey_s_c k; - int ret = 0; - - for_each_btree_key(&iter, c, BTREE_ID_QUOTAS, POS(type, 0), - BTREE_ITER_PREFETCH, k) { - if (k.k->p.inode != type) - break; - - ret = __bch2_quota_set(c, k); - if (ret) - break; - } - - return bch2_btree_iter_unlock(&iter) ?: ret; -} - void bch2_fs_quota_exit(struct bch_fs *c) { unsigned i; @@ -403,6 +511,26 @@ 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_get_quota(sb->sb); + + if (sb_quota) + return sb_quota; + + sb_quota = bch2_sb_resize_quota(sb, 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; @@ -424,40 +552,82 @@ 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 bkey_s_c k) +{ + struct bch_fs *c = trans->c; + struct bch_inode_unpacked u; + struct bch_snapshot_tree s_t; + int ret; + + 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 (!s_t.master_subvol) + goto advance; + + ret = bch2_inode_find_by_inum_trans(trans, + (subvol_inum) { + le32_to_cpu(s_t.master_subvol), + k.k->p.offset, + }, &u); + /* + * Inode might be deleted in this snapshot - the easiest way to handle + * that is to just skip it here: + */ + if (bch2_err_matches(ret, ENOENT)) + goto advance; + + 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, 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 bch_sb_field_quota *sb_quota; + 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); + 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_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; - } + bch2_trans_init(&trans, c, 0, 0); - for_each_btree_key(&iter, c, BTREE_ID_INODES, POS_MIN, - BTREE_ITER_PREFETCH, k) { - switch (k.k->type) { - case BCH_INODE_FS: - ret = bch2_inode_unpack(bkey_s_c_to_inode(k), &u); - if (ret) - return ret; - - bch2_quota_acct(c, bch_qid(&u), Q_SPC, u.bi_sectors, - BCH_QUOTA_NOCHECK); - bch2_quota_acct(c, bch_qid(&u), Q_INO, 1, - BCH_QUOTA_NOCHECK); - } - } - return bch2_btree_iter_unlock(&iter) ?: ret; + ret = for_each_btree_key2(&trans, iter, BTREE_ID_quotas, + POS_MIN, BTREE_ITER_PREFETCH, k, + __bch2_quota_set(c, k, NULL)) ?: + for_each_btree_key2(&trans, iter, BTREE_ID_inodes, + POS_MIN, BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k, + bch2_fs_quota_read_inode(&trans, &iter, k)); + + bch2_trans_exit(&trans); + + if (ret) + bch_err_fn(c, ret); + return ret; } /* Enable/disable/delete quotas for an entire filesystem: */ @@ -465,8 +635,10 @@ 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 & MS_RDONLY) + if (sb->s_flags & SB_RDONLY) return -EROFS; /* Accounting must be enabled at mount time: */ @@ -484,6 +656,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); @@ -494,16 +672,17 @@ 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) { struct bch_fs *c = sb->s_fs_info; - if (sb->s_flags & MS_RDONLY) + if (sb->s_flags & SB_RDONLY) return -EROFS; mutex_lock(&c->sb_lock); @@ -527,17 +706,17 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags) struct bch_fs *c = sb->s_fs_info; int ret; - if (sb->s_flags & MS_RDONLY) + if (sb->s_flags & SB_RDONLY) return -EROFS; if (uflags & FS_USER_QUOTA) { if (c->opts.usrquota) return -EINVAL; - ret = bch2_btree_delete_range(c, BTREE_ID_QUOTAS, + ret = bch2_btree_delete_range(c, BTREE_ID_quotas, POS(QTYP_USR, 0), - POS(QTYP_USR + 1, 0), - NULL); + POS(QTYP_USR, U64_MAX), + 0, NULL); if (ret) return ret; } @@ -546,10 +725,10 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags) if (c->opts.grpquota) return -EINVAL; - ret = bch2_btree_delete_range(c, BTREE_ID_QUOTAS, + ret = bch2_btree_delete_range(c, BTREE_ID_quotas, POS(QTYP_GRP, 0), - POS(QTYP_GRP + 1, 0), - NULL); + POS(QTYP_GRP, U64_MAX), + 0, NULL); if (ret) return ret; } @@ -558,10 +737,10 @@ static int bch2_quota_remove(struct super_block *sb, unsigned uflags) if (c->opts.prjquota) return -EINVAL; - ret = bch2_btree_delete_range(c, BTREE_ID_QUOTAS, + ret = bch2_btree_delete_range(c, BTREE_ID_quotas, POS(QTYP_PRJ, 0), - POS(QTYP_PRJ + 1, 0), - NULL); + POS(QTYP_PRJ, U64_MAX), + 0, NULL); if (ret) return ret; } @@ -608,8 +787,17 @@ 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; - if (sb->s_flags & MS_RDONLY) + qc_info_to_text(&buf, info); + pr_info("setting:\n%s", buf.buf); + printbuf_exit(&buf); + } + + if (sb->s_flags & SB_RDONLY) return -EROFS; if (type >= QTYP_NR) @@ -625,12 +813,10 @@ static int bch2_quota_set_info(struct super_block *sb, int type, 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) @@ -652,9 +838,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: */ @@ -699,77 +886,83 @@ static int bch2_get_next_quota(struct super_block *sb, struct kqid *kqid, struct bch_fs *c = sb->s_fs_info; struct bch_memquota_type *q = &c->quotas[kqid->type]; qid_t qid = from_kqid(&init_user_ns, *kqid); - struct genradix_iter iter = genradix_iter_init(&q->table, qid); + struct genradix_iter iter; struct bch_memquota *mq; int ret = 0; mutex_lock(&q->lock); - while ((mq = genradix_iter_peek(&iter, &q->table))) { + genradix_for_each_from(&q->table, iter, mq, qid) if (memcmp(mq, page_address(ZERO_PAGE(0)), sizeof(*mq))) { __bch2_quota_get(qdq, mq); *kqid = make_kqid(current_user_ns(), kqid->type, iter.pos); goto found; } - genradix_iter_advance(&iter, &q->table); - } - ret = -ENOENT; found: mutex_unlock(&q->lock); - return ret; + return bch2_err_class(ret); } -static int bch2_set_quota(struct super_block *sb, struct kqid qid, - struct qc_dqblk *qdq) +static int bch2_set_quota_trans(struct btree_trans *trans, + struct bkey_i_quota *new_quota, + struct qc_dqblk *qdq) { - struct bch_fs *c = sb->s_fs_info; struct btree_iter iter; struct bkey_s_c k; - struct bkey_i_quota new_quota; int ret; - if (sb->s_flags & MS_RDONLY) - return -EROFS; - - bkey_quota_init(&new_quota.k_i); - new_quota.k.p = POS(qid.type, from_kqid(&init_user_ns, qid)); - - bch2_btree_iter_init(&iter, c, BTREE_ID_QUOTAS, new_quota.k.p, - BTREE_ITER_SLOTS|BTREE_ITER_INTENT); - k = bch2_btree_iter_peek_slot(&iter); - - ret = btree_iter_err(k); + 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; - switch (k.k->type) { - case BCH_QUOTA: - new_quota.v = *bkey_s_c_to_quota(k).v; - break; - } + if (k.k->type == KEY_TYPE_quota) + new_quota->v = *bkey_s_c_to_quota(k).v; if (qdq->d_fieldmask & QC_SPC_SOFT) - new_quota.v.c[Q_SPC].softlimit = cpu_to_le64(qdq->d_spc_softlimit >> 9); + new_quota->v.c[Q_SPC].softlimit = cpu_to_le64(qdq->d_spc_softlimit >> 9); if (qdq->d_fieldmask & QC_SPC_HARD) - new_quota.v.c[Q_SPC].hardlimit = cpu_to_le64(qdq->d_spc_hardlimit >> 9); + new_quota->v.c[Q_SPC].hardlimit = cpu_to_le64(qdq->d_spc_hardlimit >> 9); if (qdq->d_fieldmask & QC_INO_SOFT) - new_quota.v.c[Q_INO].softlimit = cpu_to_le64(qdq->d_ino_softlimit); + new_quota->v.c[Q_INO].softlimit = cpu_to_le64(qdq->d_ino_softlimit); if (qdq->d_fieldmask & QC_INO_HARD) - new_quota.v.c[Q_INO].hardlimit = cpu_to_le64(qdq->d_ino_hardlimit); + new_quota->v.c[Q_INO].hardlimit = cpu_to_le64(qdq->d_ino_hardlimit); - ret = bch2_btree_insert_at(c, NULL, NULL, 0, - BTREE_INSERT_ENTRY(&iter, &new_quota.k_i)); - bch2_btree_iter_unlock(&iter); + ret = bch2_trans_update(trans, &iter, &new_quota->k_i, 0); + bch2_trans_iter_exit(trans, &iter); + return ret; +} - if (ret) - return ret; +static int bch2_set_quota(struct super_block *sb, struct kqid qid, + struct qc_dqblk *qdq) +{ + struct bch_fs *c = sb->s_fs_info; + struct bkey_i_quota new_quota; + int ret; - ret = __bch2_quota_set(c, bkey_i_to_s_c(&new_quota.k_i)); + if (0) { + struct printbuf buf = PRINTBUF; - return ret; + qc_dqblk_to_text(&buf, qdq); + pr_info("setting:\n%s", buf.buf); + printbuf_exit(&buf); + } + + if (sb->s_flags & SB_RDONLY) + return -EROFS; + + bkey_quota_init(&new_quota.k_i); + 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), qdq); + + return bch2_err_class(ret); } const struct quotactl_ops bch2_quotactl_operations = {