* \retval 0 on success
* \retval negative negated errno on error
*/
- int (*dt_commit_async)(const struct lu_env *env,
- struct dt_device *dev);
+ int (*dt_commit_async)(const struct lu_env *env,
+ struct dt_device *dev);
+
+ /**
+ * The unit of \a count is byte for block or inodes for metadata.
+ *
+ * If \a count > 0, reserve quota in advance of an operation that
+ * changes the quota assignment, such as chgrp() or rename() into
+ * a directory with a different group ID.
+ *
+ * If \a count < 0, free the reserved quota previously.
+ *
+ * \param[in] env execution environment for this thread
+ * \param[in] dev the bottom OSD device to reserve quota
+ * \param[in] type quota type (LQUOTA_RES_DT or LQUOTA_RES_MD)
+ * \param[in] uid quota uid
+ * \param[in] gid quota gid
+ * \param[in] count space (bytes or inodes) to reserve or free
+ * \param[in] md true for inode, false for block
+ *
+ * \retval 0 on success
+ * \retval negative negated errno on error
+ */
+ int (*dt_reserve_or_free_quota)(const struct lu_env *env,
+ struct dt_device *dev,
+ enum quota_type type, __u64 uid,
+ __u64 gid, __s64 count, bool md);
};
struct dt_index_features {
struct dt_object, do_lu);
}
+struct dt_quota_reserve_rec {
+ enum quota_type qrr_type;
+ union lquota_id qrr_id;
+ __u64 qrr_count;
+};
+
/**
* This is the general purpose transaction handle.
* 1. Transaction Life Cycle
* callback mechanism */
struct thandle *th_top;
+ /* reserved quota for this handle */
+ struct dt_quota_reserve_rec th_reserved_quota;
+
/** the last operation result in this transaction.
* this value is used in recovery */
__s32 th_result;
return dev->dd_ops->dt_commit_async(env, dev);
}
+static inline int dt_reserve_or_free_quota(const struct lu_env *env,
+ struct dt_device *dev,
+ enum quota_type type, __u64 uid,
+ __u64 gid, int count, bool is_md)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_reserve_or_free_quota);
+ return dev->dd_ops->dt_reserve_or_free_quota(env, dev, type, uid, gid,
+ count, is_md);
+}
+
static inline int dt_lookup(const struct lu_env *env,
struct dt_object *dt,
struct dt_rec *rec,
struct lquota_trans *);
void qsd_op_adjust(const struct lu_env *, struct qsd_instance *,
union lquota_id *, int);
+int qsd_reserve_or_free_quota(const struct lu_env *env,
+ struct qsd_instance *qsd,
+ struct lquota_id_info *qi);
/*
* Quota information attached to a transaction
* on slave */
int lquotactl_slv(const struct lu_env *, struct dt_device *,
struct obd_quotactl *);
+
+static inline int quota_reserve_or_free(const struct lu_env *env,
+ struct qsd_instance *qsd,
+ struct lquota_id_info *qi,
+ enum quota_type type, __u64 uid,
+ __u64 gid, __s64 count, bool is_md)
+{
+ qi->lqi_type = type;
+ if (count > 0)
+ qi->lqi_space = toqb(count);
+ else
+ qi->lqi_space = -toqb(-count);
+
+ if (is_md)
+ qi->lqi_is_blk = false;
+ else
+ qi->lqi_is_blk = true;
+
+ qi->lqi_id.qid_uid = uid;
+ qi->lqi_id.qid_gid = gid;
+
+ return qsd_reserve_or_free_quota(env, qsd, qi);
+}
+
/** @} quota */
#endif /* _LUSTRE_QUOTA_H */
static int ll_md_setattr(struct dentry *dentry, struct md_op_data *op_data)
{
- struct lustre_md md;
- struct inode *inode = dentry->d_inode;
- struct ll_sb_info *sbi = ll_i2sbi(inode);
- struct ptlrpc_request *request = NULL;
- int rc, ia_valid;
- ENTRY;
+ struct lustre_md md;
+ struct inode *inode = dentry->d_inode;
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ptlrpc_request *request = NULL;
+ int rc, ia_valid;
+
+ ENTRY;
+
+ op_data = ll_prep_md_op_data(op_data, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ RETURN(PTR_ERR(op_data));
+
+ /* If this is a chgrp of a regular file, we want to reserve enough
+ * quota to cover the entire file size.
+ */
+ if (S_ISREG(inode->i_mode) && op_data->op_attr.ia_valid & ATTR_GID &&
+ from_kgid(&init_user_ns, op_data->op_attr.ia_gid) !=
+ from_kgid(&init_user_ns, inode->i_gid)) {
+ op_data->op_xvalid |= OP_XVALID_BLOCKS;
+ op_data->op_attr_blocks = inode->i_blocks;
+ }
- op_data = ll_prep_md_op_data(op_data, inode, NULL, NULL, 0, 0,
- LUSTRE_OPC_ANY, NULL);
- if (IS_ERR(op_data))
- RETURN(PTR_ERR(op_data));
rc = md_setattr(sbi->ll_md_exp, op_data, NULL, 0, &request);
if (rc) {
struct lu_attr *attr = MDD_ENV_VAR(env, cattr);
const struct lu_attr *la = &ma->ma_attr;
struct lu_ucred *uc;
+ bool quota_reserved = false;
bool chrgrp_by_unprivileged_user = false;
+ __s64 quota_size = 0;
int rc;
ENTRY;
uc = lu_ucred_check(env);
if (S_ISREG(attr->la_mode) && la->la_valid & LA_GID &&
la->la_gid != attr->la_gid && uc != NULL && uc->uc_fsuid != 0) {
- /* LU-10048: disable synchronous chgrp operation for it will
- * cause deadlock between MDT and OST.
- la_copy->la_valid |= LA_FLAGS;
- la_copy->la_flags |= LUSTRE_SET_SYNC_FL;
- */
+ CDEBUG(D_QUOTA, "%s: reserve quota for changing group: gid=%u size=%llu\n",
+ mdd2obd_dev(mdd)->obd_name, la->la_gid, la->la_size);
+
+ if (la->la_valid & LA_BLOCKS)
+ quota_size = la->la_blocks << 9;
+ else if (la->la_valid & LA_SIZE)
+ quota_size = la->la_size;
+ /* use local attr gotten above */
+ else if (attr->la_valid & LA_BLOCKS)
+ quota_size = attr->la_blocks << 9;
+ else if (attr->la_valid & LA_SIZE)
+ quota_size = attr->la_size;
+
+ if (quota_size > 0) {
+ rc = dt_reserve_or_free_quota(env, mdd->mdd_bottom,
+ GRPQUOTA, attr->la_uid,
+ la->la_gid, quota_size,
+ false);
+
+ if (rc) {
+ CDEBUG(D_QUOTA, "%s: failed to reserve quota for gid %d size %llu\n",
+ mdd2obd_dev(mdd)->obd_name,
+ la->la_gid, quota_size);
+
+ GOTO(out, rc);
+ }
+
+ quota_reserved = true;
+ la_copy->la_valid |= LA_FLAGS;
+ }
+
chrgrp_by_unprivileged_user = true;
/* Flush the possible existing client setattr requests to OSTs
rc = mdd_attr_set_changelog(env, obj, handle, &ma->ma_pfid,
la_copy->la_valid);
+ if (rc == 0 && quota_reserved) {
+ struct thandle *sub_th;
+
+ sub_th = thandle_get_sub_by_dt(env, handle, mdd->mdd_bottom);
+ if (unlikely(IS_ERR(sub_th))) {
+ dt_reserve_or_free_quota(env, mdd->mdd_bottom, GRPQUOTA,
+ attr->la_uid, la->la_gid,
+ -quota_size, false);
+ } else {
+ sub_th->th_reserved_quota.qrr_type = GRPQUOTA;
+ sub_th->th_reserved_quota.qrr_id.qid_gid = la->la_gid;
+ sub_th->th_reserved_quota.qrr_count = quota_size;
+ }
+ }
+
if (handle != NULL)
rc = mdd_trans_stop(env, mdd, rc, handle);
[DTO_ATTR_SET_CHOWN] = 0
};
+/* reserve or free quota for some operation */
+static int osd_reserve_or_free_quota(const struct lu_env *env,
+ struct dt_device *dev,
+ enum quota_type type, __u64 uid,
+ __u64 gid, __s64 count, bool is_md)
+{
+ int rc;
+ struct osd_device *osd = osd_dt_dev(dev);
+ struct osd_thread_info *info = osd_oti_get(env);
+ struct lquota_id_info *qi = &info->oti_qi;
+ struct qsd_instance *qsd = NULL;
+
+ ENTRY;
+
+ if (is_md)
+ qsd = osd->od_quota_slave_md;
+ else
+ qsd = osd->od_quota_slave_dt;
+
+ rc = quota_reserve_or_free(env, qsd, qi, type, uid, gid, count, is_md);
+ RETURN(rc);
+}
+
static const struct dt_device_operations osd_dt_ops = {
- .dt_root_get = osd_root_get,
- .dt_statfs = osd_statfs,
- .dt_trans_create = osd_trans_create,
- .dt_trans_start = osd_trans_start,
- .dt_trans_stop = osd_trans_stop,
- .dt_trans_cb_add = osd_trans_cb_add,
- .dt_conf_get = osd_conf_get,
- .dt_mnt_sb_get = osd_mnt_sb_get,
- .dt_sync = osd_sync,
- .dt_ro = osd_ro,
- .dt_commit_async = osd_commit_async,
+ .dt_root_get = osd_root_get,
+ .dt_statfs = osd_statfs,
+ .dt_trans_create = osd_trans_create,
+ .dt_trans_start = osd_trans_start,
+ .dt_trans_stop = osd_trans_stop,
+ .dt_trans_cb_add = osd_trans_cb_add,
+ .dt_conf_get = osd_conf_get,
+ .dt_mnt_sb_get = osd_mnt_sb_get,
+ .dt_sync = osd_sync,
+ .dt_ro = osd_ro,
+ .dt_commit_async = osd_commit_async,
+ .dt_reserve_or_free_quota = osd_reserve_or_free_quota,
};
static void osd_read_lock(const struct lu_env *env, struct dt_object *dt,
struct osd_object *obj,
struct osd_thandle *oh, long long bspace,
qid_t old_id, qid_t new_id, bool enforce,
- unsigned int type, bool ignore_edquot)
+ unsigned int type)
{
int rc;
struct osd_thread_info *info = osd_oti_get(env);
qi->lqi_space = 1;
/* Reserve credits for the new id */
rc = osd_declare_qid(env, oh, qi, NULL, enforce, NULL);
- if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
rc = 0;
if (rc)
RETURN(rc);
qi->lqi_id.qid_uid = old_id;
qi->lqi_space = -1;
rc = osd_declare_qid(env, oh, qi, obj, enforce, NULL);
- if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
rc = 0;
if (rc)
RETURN(rc);
* to save credit reservation.
*/
rc = osd_declare_qid(env, oh, qi, obj, enforce, NULL);
- if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
rc = 0;
if (rc)
RETURN(rc);
qi->lqi_id.qid_uid = old_id;
qi->lqi_space = -bspace;
rc = osd_declare_qid(env, oh, qi, obj, enforce, NULL);
- if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
rc = 0;
RETURN(rc);
* space adjustment once the operation is completed.
*/
if (attr->la_valid & LA_UID || attr->la_valid & LA_GID) {
- bool ignore_edquot = !(attr->la_flags & LUSTRE_SET_SYNC_FL);
-
- if (!ignore_edquot)
- CDEBUG(D_QUOTA,
- "%s: enforce quota on UID %u, GID %u (quota space is %lld)\n",
- osd_ino2name(obj->oo_inode), attr->la_uid,
- attr->la_gid, bspace);
-
/* USERQUOTA */
uid = i_uid_read(obj->oo_inode);
enforce = (attr->la_valid & LA_UID) && (attr->la_uid != uid);
rc = osd_declare_attr_qid(env, obj, oh, bspace, uid,
- attr->la_uid, enforce, USRQUOTA,
- true);
+ attr->la_uid, enforce, USRQUOTA);
if (rc)
RETURN(rc);
attr->la_uid, gid, attr->la_gid);
enforce = (attr->la_valid & LA_GID) && (attr->la_gid != gid);
rc = osd_declare_attr_qid(env, obj, oh, bspace, gid,
- attr->la_gid, enforce, GRPQUOTA,
- ignore_edquot);
+ attr->la_gid, enforce, GRPQUOTA);
if (rc)
RETURN(rc);
(attr->la_projid != projid);
rc = osd_declare_attr_qid(env, obj, oh, bspace,
(qid_t)projid, (qid_t)attr->la_projid,
- enforce, PRJQUOTA, true);
+ enforce, PRJQUOTA);
if (rc)
RETURN(rc);
}
i_projid_read(inode) != 0)
rc = osd_declare_attr_qid(env, osd_dt_obj(dt), oh,
0, i_projid_read(inode),
- 0, false, PRJQUOTA, true);
+ 0, false, PRJQUOTA);
#endif
}
* Author: Niu Yawei <niu@whamcloud.com>
*/
+#include <dt_object.h>
#include <lustre_quota.h>
#include "osd_internal.h"
RETURN(0);
}
+/* reserve or free quota for some operation */
+static int osd_reserve_or_free_quota(const struct lu_env *env,
+ struct dt_device *dev,
+ enum quota_type type, __u64 uid,
+ __u64 gid, __s64 count, bool is_md)
+{
+ int rc;
+ struct osd_device *osd = osd_dt_dev(dev);
+ struct osd_thread_info *info = osd_oti_get(env);
+ struct lquota_id_info *qi = &info->oti_qi;
+ struct qsd_instance *qsd = NULL;
+
+ if (is_md)
+ qsd = osd->od_quota_slave_md;
+ else
+ qsd = osd->od_quota_slave_dt;
+
+ rc = quota_reserve_or_free(env, qsd, qi, type, uid, gid, count, is_md);
+ RETURN(rc);
+}
+
static const struct dt_device_operations osd_dt_ops = {
- .dt_root_get = osd_root_get,
- .dt_statfs = osd_statfs,
- .dt_trans_create = osd_trans_create,
- .dt_trans_start = osd_trans_start,
- .dt_trans_stop = osd_trans_stop,
- .dt_trans_cb_add = osd_trans_cb_add,
- .dt_conf_get = osd_conf_get,
- .dt_sync = osd_sync,
- .dt_commit_async = osd_commit_async,
- .dt_ro = osd_ro,
+ .dt_root_get = osd_root_get,
+ .dt_statfs = osd_statfs,
+ .dt_trans_create = osd_trans_create,
+ .dt_trans_start = osd_trans_start,
+ .dt_trans_stop = osd_trans_stop,
+ .dt_trans_cb_add = osd_trans_cb_add,
+ .dt_conf_get = osd_conf_get,
+ .dt_sync = osd_sync,
+ .dt_commit_async = osd_commit_async,
+ .dt_ro = osd_ro,
+ .dt_reserve_or_free_quota = osd_reserve_or_free_quota,
};
/*
struct qsd_instance *qsd,
struct lquota_trans *trans, int qtype,
__u64 orig_id, __u64 new_id, __u64 bspace,
- struct lquota_id_info *qi, bool ignore_edquot)
+ struct lquota_id_info *qi)
{
int rc;
qi->lqi_id.qid_uid = new_id;
qi->lqi_space = 1;
rc = qsd_op_begin(env, qsd, trans, qi, NULL);
- if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
rc = 0;
if (rc)
return rc;
qi->lqi_id.qid_uid = new_id;
qi->lqi_space = bspace;
rc = qsd_op_begin(env, qsd, trans, qi, NULL);
- if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
rc = 0;
if (rc)
return rc;
rc = qsd_transfer(env, osd_def_qsd(osd),
&oh->ot_quota_trans, USRQUOTA,
obj->oo_attr.la_uid, attr->la_uid,
- bspace, &info->oti_qi, true);
+ bspace, &info->oti_qi);
if (rc)
GOTO(out, rc);
}
rc = qsd_transfer(env, osd_def_qsd(osd),
&oh->ot_quota_trans, GRPQUOTA,
obj->oo_attr.la_gid, attr->la_gid,
- bspace, &info->oti_qi,
- !(attr->la_flags &
- LUSTRE_SET_SYNC_FL));
+ bspace, &info->oti_qi);
if (rc)
GOTO(out, rc);
}
&oh->ot_quota_trans, PRJQUOTA,
obj->oo_attr.la_projid,
attr->la_projid, bspace,
- &info->oti_qi, true);
+ &info->oti_qi);
if (rc)
GOTO(out, rc);
}
* Author: Johann Lombardi <johann@whamcloud.com>
*/
+#include <dt_object.h>
#include <lustre_quota.h>
#include <obd.h>
#include "osd_internal.h"
EXIT;
}
EXPORT_SYMBOL(qsd_op_adjust);
+
+/**
+ * Reserve or free quota.
+ *
+ * Currently, It's used to reserve quota space before changing the file's group
+ * for normal user and free the reserved quota after the group change.
+ *
+ * \param env - the environment passed by the caller
+ * \param qsd - is the qsd instance associated with the device in charge of
+ * the operation.
+ * \param qi - qid & space required by current operation
+ *
+ * \retval 0 - success
+ * \retval -EDQUOT - out of quota
+ * \retval -EINPROGRESS - inform client to retry write
+ * \retval -ve - other appropriate errors
+ */
+int qsd_reserve_or_free_quota(const struct lu_env *env,
+ struct qsd_instance *qsd,
+ struct lquota_id_info *qi)
+{
+ struct lquota_entry *lqe;
+ struct qsd_qtype_info *qqi;
+ int rc = 0;
+ bool is_free = qi->lqi_space < 0;
+
+ ENTRY;
+
+ if (unlikely(qsd == NULL))
+ RETURN(0);
+
+ if (qsd->qsd_dev->dd_rdonly)
+ RETURN(0);
+
+ if (is_free)
+ qi->lqi_space *= -1;
+
+ /* We don't enforce quota until the qsd_instance is started */
+ read_lock(&qsd->qsd_lock);
+ if (!qsd->qsd_started) {
+ read_unlock(&qsd->qsd_lock);
+ RETURN(0);
+ }
+ read_unlock(&qsd->qsd_lock);
+
+ qqi = qsd->qsd_type_array[qi->lqi_type];
+ LASSERT(qqi);
+
+ CDEBUG(D_QUOTA, "type %s, acct %s, free %d, count %llu\n",
+ qsd_type_enabled(qsd, qi->lqi_type) ? "enabled" : "disabled",
+ (qsd->qsd_type_array[qi->lqi_type])->qqi_acct_failed ? "failed" :
+ "succeed", is_free, qi->lqi_space);
+
+ /* ignore quota enforcement request when:
+ * - quota isn't enforced for this quota type
+ * or - the user/group is root
+ * or - quota accounting isn't enabled
+ */
+ if (!qsd_type_enabled(qsd, qi->lqi_type) || qi->lqi_id.qid_uid == 0 ||
+ (qsd->qsd_type_array[qi->lqi_type])->qqi_acct_failed)
+ RETURN(0);
+
+ if (is_free) {
+ /* look up lquota entry associated with qid */
+ lqe = lqe_locate(env, qqi->qqi_site, &qi->lqi_id);
+ if (IS_ERR(lqe))
+ RETURN(PTR_ERR(lqe));
+ if (!lqe->lqe_enforced) {
+ lqe_putref(lqe);
+ RETURN(0);
+ }
+
+ qi->lqi_qentry = lqe;
+
+ /* lqe will be put in qsd_op_end0 */
+ qsd_op_end0(env, qsd->qsd_type_array[qi->lqi_type], qi);
+ qi->lqi_qentry = NULL;
+ } else {
+ /* manage quota enforcement for this ID */
+ rc = qsd_op_begin0(env, qsd->qsd_type_array[qi->lqi_type], qi,
+ qi->lqi_space, NULL);
+
+ if (qi->lqi_qentry != NULL) {
+ lqe_putref(qi->lqi_qentry);
+ qi->lqi_qentry = NULL;
+ }
+ }
+
+ CDEBUG(D_QUOTA, "%s quota: type %i, uid %llu, gid %llu, space %llu\n",
+ is_free ? "Free" : "Reserve", qi->lqi_type, qi->lqi_id.qid_uid,
+ qi->lqi_id.qid_gid, qi->lqi_space);
+
+ RETURN(rc);
+}
+EXPORT_SYMBOL(qsd_reserve_or_free_quota);
if (rc != 0)
return rc;
- if (attr->la_valid & LA_FLAGS &&
- attr->la_flags & LUSTRE_SET_SYNC_FL)
- th->th_sync |= 1;
-
arg = tx_add_exec(ta, out_tx_attr_set_exec, out_tx_attr_set_undo,
file, line);
if (IS_ERR(arg))
LASSERT(ccb->llcc_tgt != NULL);
LASSERT(ccb->llcc_exp->exp_obd == ccb->llcc_tgt->lut_obd);
+ if (th->th_reserved_quota.qrr_count > 0) {
+ struct lu_env temp_env;
+ int rc;
+
+ CDEBUG(D_QUOTA, "free quota %llu %llu\n",
+ th->th_reserved_quota.qrr_id.qid_gid,
+ th->th_reserved_quota.qrr_count);
+
+ rc = lu_env_init(&temp_env, LCT_DT_THREAD);
+ if (rc) {
+ CERROR("%s: can't initialize environment: rc = %d\n",
+ ccb->llcc_tgt->lut_obd->obd_name, rc);
+ goto out;
+ }
+
+ dt_reserve_or_free_quota(&temp_env, th->th_dev,
+ th->th_reserved_quota.qrr_type,
+ th->th_reserved_quota.qrr_id.qid_uid,
+ th->th_reserved_quota.qrr_id.qid_gid,
+ -th->th_reserved_quota.qrr_count,
+ false);
+ lu_env_fini(&temp_env);
+ }
+
/* error hit, don't update last committed to provide chance to
* replay data after fail */
if (err != 0)
init_logging
ALWAYS_EXCEPT="$SANITY_QUOTA_EXCEPT "
-# Bug number for skipped test: LU-5152
-ALWAYS_EXCEPT+=" 55"
+# Bug number for skipped test:
+ALWAYS_EXCEPT+=""
# UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT!
# Test duration: 30 min