/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is included * in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA * * GPL HEADER END */ /* * Copyright (c) 2012, 2014, Intel Corporation. * Use is subject to license terms. * * Author: Johann Lombardi * Author: Niu Yawei */ #define DEBUG_SUBSYSTEM S_LQUOTA #include #include #include "qsd_internal.h" static LIST_HEAD(qfs_list); /* protect the qfs_list */ static DEFINE_SPINLOCK(qfs_list_lock); /* * Put reference of qsd_fsinfo. * * \param qfs - the qsd_fsinfo to be put */ void qsd_put_fsinfo(struct qsd_fsinfo *qfs) { ENTRY; LASSERT(qfs != NULL); spin_lock(&qfs_list_lock); LASSERT(qfs->qfs_ref > 0); qfs->qfs_ref--; if (qfs->qfs_ref == 0) { LASSERT(list_empty(&qfs->qfs_qsd_list)); list_del(&qfs->qfs_link); OBD_FREE_PTR(qfs); } spin_unlock(&qfs_list_lock); EXIT; } /* * Find or create a qsd_fsinfo * * \param name - filesystem name * \param create - when @create is non-zero, create new one if fail to * find existing qfs by @name * * \retval qsd_fsinfo - success * \retval NULL - failure */ struct qsd_fsinfo *qsd_get_fsinfo(char *name, bool create) { struct qsd_fsinfo *qfs, *new = NULL; ENTRY; if (name == NULL || strlen(name) >= MTI_NAME_MAXLEN) RETURN(NULL); if (create) { /* pre-allocate a qsd_fsinfo in case there isn't one already. * we can afford the extra cost since qsd_get_fsinfo() isn't * called very often with create = true */ OBD_ALLOC_PTR(new); if (new == NULL) RETURN(NULL); mutex_init(&new->qfs_mutex); INIT_LIST_HEAD(&new->qfs_qsd_list); strcpy(new->qfs_name, name); new->qfs_ref = 1; } /* search in the fsinfo list */ spin_lock(&qfs_list_lock); list_for_each_entry(qfs, &qfs_list, qfs_link) { if (!strcmp(qfs->qfs_name, name)) { qfs->qfs_ref++; goto out; } } qfs = NULL; /* not found */ if (new) { /* not found, but we were asked to create a new one */ list_add_tail(&new->qfs_link, &qfs_list); qfs = new; new = NULL; } out: spin_unlock(&qfs_list_lock); if (new) OBD_FREE_PTR(new); RETURN(qfs); } int qsd_config(char *valstr, char *fsname, int pool) { struct qsd_fsinfo *qfs; int rc, enabled = 0, old_enabled = 0; bool reint = false; ENTRY; qfs = qsd_get_fsinfo(fsname, 0); if (qfs == NULL) { CERROR("failed to find quota filesystem information for %s\n", fsname); RETURN(-ENOENT); } if (strchr(valstr, 'u')) enabled |= BIT(USRQUOTA); if (strchr(valstr, 'g')) enabled |= BIT(GRPQUOTA); if (strchr(valstr, 'p')) enabled |= BIT(PRJQUOTA); mutex_lock(&qfs->qfs_mutex); if (qfs->qfs_enabled[pool - LQUOTA_FIRST_RES] == enabled) /* no change required */ GOTO(out, rc = 0); if ((qfs->qfs_enabled[pool - LQUOTA_FIRST_RES] & enabled) != enabled) reint = true; old_enabled = qfs->qfs_enabled[pool - LQUOTA_FIRST_RES]; qfs->qfs_enabled[pool - LQUOTA_FIRST_RES] = enabled; /* trigger reintegration for all qsd */ if (reint) { struct qsd_instance *qsd; struct qsd_qtype_info *qqi; list_for_each_entry(qsd, &qfs->qfs_qsd_list, qsd_link) { bool skip = false; int type; /* start reintegration only if qsd_prepare() was * successfully called */ read_lock(&qsd->qsd_lock); if (!qsd->qsd_prepared) skip = true; read_unlock(&qsd->qsd_lock); if (skip) continue; for (type = USRQUOTA; type < LL_MAXQUOTAS; type++) { qqi = qsd->qsd_type_array[type]; /* only trigger reintegration if this * type of quota is not enabled before */ if ((old_enabled & BIT(type)) || !(enabled & BIT(type))) continue; if (qqi->qqi_acct_failed) { LCONSOLE_ERROR("%s: can't enable quota " "enforcement since space " "accounting isn't functional. " "Please run tunefs.lustre " "--quota on an unmounted " "filesystem if not done already" "\n", qsd->qsd_svname); continue; } qsd_start_reint_thread(qqi); } } } out: mutex_unlock(&qfs->qfs_mutex); qsd_put_fsinfo(qfs); RETURN(0); } /* * Quota configuration handlers in charge of processing all per-filesystem quota * parameters set via conf_param. * * \param lcfg - quota configuration log to be processed */ int qsd_process_config(struct lustre_cfg *lcfg) { char *fsname = lustre_cfg_string(lcfg, 0); char *cfgstr = lustre_cfg_string(lcfg, 1); char *keystr, *valstr; int pool; ENTRY; CDEBUG(D_QUOTA, "processing quota parameter: fs:%s cfgstr:%s\n", fsname, cfgstr); if (class_match_param(cfgstr, PARAM_QUOTA, &keystr) != 0) RETURN(-EINVAL); if (!class_match_param(keystr, QUOTA_METAPOOL_NAME, &valstr)) pool = LQUOTA_RES_MD; else if (!class_match_param(keystr, QUOTA_DATAPOOL_NAME, &valstr)) pool = LQUOTA_RES_DT; else RETURN(-EINVAL); return qsd_config(valstr, fsname, pool); }