bool found = false;
ENTRY;
+ /* fast path, ignore quota enforcement request for root owned files */
+ if (qi->lqi_id.qid_uid == 0)
+ return 0;
+
if (unlikely(qsd == NULL))
RETURN(0);
* - 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 ||
+ if (!qsd_type_enabled(qsd, qi->lqi_type) ||
(qsd->qsd_type_array[qi->lqi_type])->qqi_acct_failed)
RETURN(0);
qsd_refresh_usage(env, lqe);
lqe_write_lock(lqe);
- if (qid->lqi_space > 0)
- lqe->lqe_pending_write -= qid->lqi_space;
+ if (qid->lqi_space > 0) {
+ if (lqe->lqe_pending_write < qid->lqi_space) {
+ LQUOTA_ERROR(lqe,
+ "More pending write quota to reduce (pending %llu, space %lld)\n",
+ lqe->lqe_pending_write, qid->lqi_space);
+ lqe->lqe_pending_write = 0;
+ } else {
+ lqe->lqe_pending_write -= qid->lqi_space;
+ }
+ }
if (env != NULL)
adjust = qsd_adjust_needed(lqe);
else
}
EXPORT_SYMBOL(qsd_op_end);
+/* Simple wrapper on top of qsd API which implement quota transfer for osd
+ * setattr needs. As a reminder, only the root user can change ownership of
+ * a file, that's why EDQUOT & EINPROGRESS errors are discarded
+ */
+int qsd_transfer(const struct lu_env *env, struct qsd_instance *qsd,
+ struct lquota_trans *trans, unsigned int qtype,
+ u64 orig_id, u64 new_id, u64 bspace,
+ struct lquota_id_info *qi)
+{
+ int rc;
+
+ if (unlikely(!qsd))
+ return 0;
+
+ LASSERT(qtype < LL_MAXQUOTAS);
+ if (qtype == PRJQUOTA)
+ if (!projid_valid(make_kprojid(&init_user_ns, new_id)))
+ return -EINVAL;
+
+ qi->lqi_type = qtype;
+
+ /* inode accounting */
+ qi->lqi_is_blk = false;
+
+ /* one more inode for the new owner ... */
+ qi->lqi_id.qid_uid = new_id;
+ qi->lqi_space = 1;
+ rc = qsd_op_begin(env, qsd, trans, qi, NULL);
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
+ rc = 0;
+ if (rc)
+ return rc;
+
+ /* and one less inode for the current id */
+ qi->lqi_id.qid_uid = orig_id;
+ qi->lqi_space = -1;
+ /* can't get EDQUOT when reducing usage */
+ rc = qsd_op_begin(env, qsd, trans, qi, NULL);
+ if (rc == -EINPROGRESS)
+ rc = 0;
+ if (rc)
+ return rc;
+
+ /* block accounting */
+ qi->lqi_is_blk = true;
+
+ /* more blocks for the new owner ... */
+ qi->lqi_id.qid_uid = new_id;
+ qi->lqi_space = bspace;
+ rc = qsd_op_begin(env, qsd, trans, qi, NULL);
+ if (rc == -EDQUOT || rc == -EINPROGRESS)
+ rc = 0;
+ if (rc)
+ return rc;
+
+ /* and finally less blocks for the current owner */
+ qi->lqi_id.qid_uid = orig_id;
+ qi->lqi_space = -bspace;
+ rc = qsd_op_begin(env, qsd, trans, qi, NULL);
+ /* can't get EDQUOT when reducing usage */
+ if (rc == -EINPROGRESS)
+ rc = 0;
+ return rc;
+}
+EXPORT_SYMBOL(qsd_transfer);
+
/**
* Trigger pre-acquire/release if necessary.
* It's only used by ldiskfs osd so far. When unlink a file in ldiskfs, the
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;
+ int rc = 0;
ENTRY;
* 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)
+ if (!is_free &&
+ (!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);
+ long long qspace = qi->lqi_space;
- if (qi->lqi_qentry != NULL) {
+ /* the acquired quota will add to lqi_space in qsd_op_begin0 */
+ qi->lqi_space = 0;
+ rc = qsd_op_begin0(env, qsd->qsd_type_array[qi->lqi_type], qi,
+ qspace, NULL);
+ if (rc && qi->lqi_qentry) {
lqe_putref(qi->lqi_qentry);
qi->lqi_qentry = NULL;
}