/* * generic_quota_on is very lazy and tolerant about current quota settings * @global means to turn on quotas on each OST additionally to local quotas; * should not be called from filter_quota_ctl on MDS nodes (as it starts * admin quotas on MDS nodes). */ int generic_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl, int global) { struct obd_device_target *obt = &obd->u.obt; struct lvfs_run_ctxt saved; int id, is_master, rc = 0, local; /* means we need a local quotaon */ cfs_down(&obt->obt_quotachecking); push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); id = UGQUOTA2LQC(oqctl->qc_type); local = (obt->obt_qctxt.lqc_flags & id) != id; oqctl->qc_cmd = Q_QUOTAON; oqctl->qc_id = obt->obt_qfmt; is_master = !strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME); if (is_master) { cfs_down_write(&obd->u.mds.mds_qonoff_sem); if (local) { /* turn on cluster wide quota */ rc = mds_admin_quota_on(obd, oqctl); if (rc && rc != -ENOENT) CERROR("%s: %s admin quotaon failed. rc=%d\n", obd->obd_name, global ? "global":"local", rc); } } if (rc == 0) { if (local) { rc = fsfilt_quotactl(obd, obt->obt_sb, oqctl); if (rc) { if (rc != -ENOENT) CERROR("%s: %s quotaon failed with" " rc=%d\n", obd->obd_name, global ? "global" : "local", rc); } else { obt->obt_qctxt.lqc_flags |= UGQUOTA2LQC(oqctl->qc_type); build_lqs(obd); } } if (rc == 0 && global && is_master) rc = obd_quotactl(obd->u.mds.mds_lov_exp, oqctl); } if (is_master) cfs_up_write(&obd->u.mds.mds_qonoff_sem); pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); cfs_up(&obt->obt_quotachecking); return rc; }
int filter_quota_ctl(struct obd_device *unused, struct obd_export *exp, struct obd_quotactl *oqctl) { struct obd_device *obd = exp->exp_obd; struct obd_device_target *obt = &obd->u.obt; struct lvfs_run_ctxt saved; struct lustre_quota_ctxt *qctxt = &obt->obt_qctxt; struct lustre_qunit_size *lqs; void *handle = NULL; struct timeval work_start; struct timeval work_end; long timediff; int rc = 0; ENTRY; cfs_gettimeofday(&work_start); switch (oqctl->qc_cmd) { case Q_QUOTAON: oqctl->qc_id = obt->obt_qfmt; rc = generic_quota_on(obd, oqctl, 0); break; case Q_FINVALIDATE: case Q_QUOTAOFF: cfs_down(&obt->obt_quotachecking); if (oqctl->qc_cmd == Q_FINVALIDATE && (obt->obt_qctxt.lqc_flags & UGQUOTA2LQC(oqctl->qc_type))) { CWARN("quota[%u] is on yet\n", oqctl->qc_type); cfs_up(&obt->obt_quotachecking); rc = -EBUSY; break; } oqctl->qc_id = obt->obt_qfmt; /* override qfmt version */ case Q_GETOINFO: case Q_GETOQUOTA: case Q_GETQUOTA: /* In recovery scenario, this pending dqacq/dqrel might have * been processed by master successfully before it's dquot * on master enter recovery mode. We must wait for this * dqacq/dqrel done then return the correct limits to master */ if (oqctl->qc_stat == QUOTA_RECOVERING) handle = quota_barrier(&obd->u.obt.obt_qctxt, oqctl, 1); push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = fsfilt_quotactl(obd, obt->obt_sb, oqctl); pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (oqctl->qc_stat == QUOTA_RECOVERING) quota_unbarrier(handle); if (oqctl->qc_cmd == Q_QUOTAOFF || oqctl->qc_cmd == Q_FINVALIDATE) { if (oqctl->qc_cmd == Q_QUOTAOFF) { if (!rc) obt->obt_qctxt.lqc_flags &= ~UGQUOTA2LQC(oqctl->qc_type); else if (quota_is_off(qctxt, oqctl)) rc = -EALREADY; CDEBUG(D_QUOTA, "%s: quotaoff type:flags:rc " "%u:%lu:%d\n", obd->obd_name, oqctl->qc_type, qctxt->lqc_flags, rc); } cfs_up(&obt->obt_quotachecking); } break; case Q_SETQUOTA: /* currently, it is only used for nullifying the quota */ handle = quota_barrier(&obd->u.obt.obt_qctxt, oqctl, 1); push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl); if (!rc) { oqctl->qc_cmd = Q_SYNC; fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl); oqctl->qc_cmd = Q_SETQUOTA; } pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); quota_unbarrier(handle); lqs = quota_search_lqs(LQS_KEY(oqctl->qc_type, oqctl->qc_id), qctxt, 0); if (lqs == NULL || IS_ERR(lqs)){ CERROR("fail to create lqs during setquota operation " "for %sid %u\n", oqctl->qc_type ? "g" : "u", oqctl->qc_id); } else { lqs->lqs_flags &= ~QB_SET; lqs_putref(lqs); } break; case Q_INITQUOTA: { unsigned int id[MAXQUOTAS] = { 0, 0 }; /* Initialize quota limit to MIN_QLIMIT */ LASSERT(oqctl->qc_dqblk.dqb_valid == QIF_BLIMITS); LASSERT(oqctl->qc_dqblk.dqb_bsoftlimit == 0); if (!oqctl->qc_dqblk.dqb_bhardlimit) goto adjust; /* There might be a pending dqacq/dqrel (which is going to * clear stale limits on slave). we should wait for it's * completion then initialize limits */ handle = quota_barrier(&obd->u.obt.obt_qctxt, oqctl, 1); LASSERT(oqctl->qc_dqblk.dqb_bhardlimit == MIN_QLIMIT); push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl); /* Update on-disk quota, in case of lose the changed limits * (MIN_QLIMIT) on crash, which cannot be recovered.*/ if (!rc) { oqctl->qc_cmd = Q_SYNC; fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl); oqctl->qc_cmd = Q_INITQUOTA; } pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); quota_unbarrier(handle); if (rc) RETURN(rc); adjust: lqs = quota_search_lqs(LQS_KEY(oqctl->qc_type, oqctl->qc_id), qctxt, 1); if (lqs == NULL || IS_ERR(lqs)){ CERROR("fail to create lqs during setquota operation " "for %sid %u\n", oqctl->qc_type ? "g" : "u", oqctl->qc_id); break; } else { lqs->lqs_flags |= QB_SET; lqs_putref(lqs); } /* Trigger qunit pre-acquire */ if (oqctl->qc_type == USRQUOTA) id[USRQUOTA] = oqctl->qc_id; else id[GRPQUOTA] = oqctl->qc_id; rc = qctxt_adjust_qunit(obd, &obd->u.obt.obt_qctxt, id, 1, 0, NULL); if (rc == -EDQUOT || rc == -EBUSY) { CDEBUG(D_QUOTA, "rc: %d.\n", rc); rc = 0; } break; } default: CERROR("%s: unsupported filter_quotactl command: %d\n", obd->obd_name, oqctl->qc_cmd); RETURN(-EFAULT); } cfs_gettimeofday(&work_end); timediff = cfs_timeval_sub(&work_end, &work_start, NULL); lprocfs_counter_add(qctxt->lqc_stats, LQUOTA_QUOTA_CTL, timediff); RETURN(rc); }
static int filter_quota_getflag(struct obd_device *obd, struct obdo *oa) { struct obd_device_target *obt = &obd->u.obt; struct lustre_quota_ctxt *qctxt = &obt->obt_qctxt; int err, cnt, rc = 0; struct obd_quotactl *oqctl; ENTRY; if (!ll_sb_any_quota_active(obt->obt_sb)) RETURN(0); OBD_ALLOC_PTR(oqctl); if (!oqctl) RETURN(-ENOMEM); /* set over quota flags for a uid/gid */ oa->o_valid |= OBD_MD_FLUSRQUOTA | OBD_MD_FLGRPQUOTA; oa->o_flags &= ~(OBD_FL_NO_USRQUOTA | OBD_FL_NO_GRPQUOTA); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { struct lustre_qunit_size *lqs = NULL; lqs = quota_search_lqs(LQS_KEY(cnt, GET_OA_ID(cnt, oa)), qctxt, 0); if (lqs == NULL || IS_ERR(lqs)) { rc = PTR_ERR(lqs); if (rc) CDEBUG(D_QUOTA, "search lqs for %s %d failed, " "(rc = %d)\n", cnt == USRQUOTA ? "user" : "group", cnt == USRQUOTA ? oa->o_uid : oa->o_gid, rc); break; } else { cfs_spin_lock(&lqs->lqs_lock); if (lqs->lqs_bunit_sz <= qctxt->lqc_sync_blk) { oa->o_flags |= (cnt == USRQUOTA) ? OBD_FL_NO_USRQUOTA : OBD_FL_NO_GRPQUOTA; cfs_spin_unlock(&lqs->lqs_lock); CDEBUG(D_QUOTA, "set sync flag: bunit(%lu), " "sync_blk(%d)\n", lqs->lqs_bunit_sz, qctxt->lqc_sync_blk); /* this is for quota_search_lqs */ lqs_putref(lqs); continue; } cfs_spin_unlock(&lqs->lqs_lock); /* this is for quota_search_lqs */ lqs_putref(lqs); } memset(oqctl, 0, sizeof(*oqctl)); oqctl->qc_cmd = Q_GETQUOTA; oqctl->qc_type = cnt; oqctl->qc_id = (cnt == USRQUOTA) ? oa->o_uid : oa->o_gid; err = fsfilt_quotactl(obd, obt->obt_sb, oqctl); if (err) { if (!rc) rc = err; oa->o_valid &= ~((cnt == USRQUOTA) ? OBD_MD_FLUSRQUOTA : OBD_MD_FLGRPQUOTA); CDEBUG(D_QUOTA, "fsfilt getquota for %s %d failed, " "(rc = %d)\n", cnt == USRQUOTA ? "user" : "group", cnt == USRQUOTA ? oa->o_uid : oa->o_gid, err); continue; } if (oqctl->qc_dqblk.dqb_bhardlimit && (toqb(oqctl->qc_dqblk.dqb_curspace) >= oqctl->qc_dqblk.dqb_bhardlimit)) { oa->o_flags |= (cnt == USRQUOTA) ? OBD_FL_NO_USRQUOTA : OBD_FL_NO_GRPQUOTA; CDEBUG(D_QUOTA, "out of quota for %s %d\n", cnt == USRQUOTA ? "user" : "group", cnt == USRQUOTA ? oa->o_uid : oa->o_gid); } } OBD_FREE_PTR(oqctl); RETURN(rc); }
int target_quota_check(struct obd_device *obd, struct obd_export *exp, struct obd_quotactl *oqctl) { struct obd_device_target *obt = &obd->u.obt; struct quotacheck_thread_args *qta; int rc = 0; ENTRY; OBD_ALLOC_PTR(qta); if (!qta) RETURN(ENOMEM); cfs_down(&obt->obt_quotachecking); qta->qta_exp = exp; qta->qta_obd = obd; qta->qta_oqctl = *oqctl; qta->qta_oqctl.qc_id = obt->obt_qfmt; /* override qfmt version */ qta->qta_sb = obt->obt_sb; qta->qta_sem = &obt->obt_quotachecking; /* quotaoff firstly */ oqctl->qc_cmd = Q_QUOTAOFF; if (!strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME)) { rc = do_mds_quota_off(obd, oqctl); if (rc && rc != -EALREADY) { CERROR("off quota on MDS failed: %d\n", rc); GOTO(out, rc); } /* quota master */ rc = init_admin_quotafiles(obd, &qta->qta_oqctl); if (rc) { CERROR("init_admin_quotafiles failed: %d\n", rc); GOTO(out, rc); } } else { struct lvfs_run_ctxt saved; struct lustre_quota_ctxt *qctxt = &obt->obt_qctxt; push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = fsfilt_quotactl(obd, obt->obt_sb, oqctl); pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (!rc) { qctxt->lqc_flags &= ~UGQUOTA2LQC(oqctl->qc_type); } else if (!quota_is_off(qctxt, oqctl)) { CERROR("off quota on OSS failed: %d\n", rc); GOTO(out, rc); } } /* we get ref for exp because target_quotacheck_callback() will use this * export later b=18126 */ class_export_get(exp); rc = cfs_kernel_thread(target_quotacheck_thread, qta, CLONE_VM|CLONE_FILES); if (rc >= 0) { /* target_quotacheck_thread will drop the ref on exp and release * obt_quotachecking */ CDEBUG(D_INFO, "%s: target_quotacheck_thread: %d\n", obd->obd_name, rc); RETURN(0); } else { CERROR("%s: error starting quotacheck_thread: %d\n", obd->obd_name, rc); class_export_put(exp); EXIT; } out: cfs_up(&obt->obt_quotachecking); OBD_FREE_PTR(qta); return rc; }