X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Fquota%2Flproc_quota.c;h=6c8a5c86bf017dbdffa09d1d846d213da0efa74a;hb=b2287531b63ac56692746a67ea17f576a6d2ab81;hp=6ac0bc26b0711fa5a5e5990fbe7d13ff4b95101f;hpb=6e3ec5812ebd1b5ecf7cae584f429b013ffe7431;p=fs%2Flustre-release.git diff --git a/lustre/quota/lproc_quota.c b/lustre/quota/lproc_quota.c index 6ac0bc2..6c8a5c8 100644 --- a/lustre/quota/lproc_quota.c +++ b/lustre/quota/lproc_quota.c @@ -1,6 +1,4 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: - * +/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -26,8 +24,10 @@ * GPL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. + * + * Copyright (c) 2011, Whamcloud, Inc. */ /* * This file is part of Lustre, http://www.lustre.org/ @@ -42,11 +42,277 @@ #include #include +#include "lquota_internal.h" #include "quota_internal.h" -#ifdef HAVE_QUOTA_SUPPORT - #ifdef LPROCFS +/* structure allocated at seq_open time and release when seq_release is called. + * It is passed to seq_start/stop/next/show which can thus use the same lu_env + * to be used with the iterator API */ +struct lquota_procfs { + struct dt_object *lqp_obj; + struct lu_env lqp_env; +}; + +/* global shared environment */ +static void *lprocfs_quota_seq_start(struct seq_file *p, loff_t *pos) +{ + struct lquota_procfs *lqp = p->private; + const struct dt_it_ops *iops; + struct dt_it *it; + loff_t offset = *pos; + int rc; + + LASSERT(lqp); + + if (offset == 0) + return SEQ_START_TOKEN; + offset--; + + if (lqp->lqp_obj == NULL) + /* accounting not enabled. */ + return NULL; + + /* initialize iterator */ + iops = &lqp->lqp_obj->do_index_ops->dio_it; + it = iops->init(&lqp->lqp_env, lqp->lqp_obj, 0, BYPASS_CAPA); + if (IS_ERR(it)) { + CERROR("%s: failed to initialize iterator: rc = %ld\n", + lqp->lqp_obj->do_lu.lo_dev->ld_obd->obd_name, + PTR_ERR(it)); + return NULL; + } + + /* move on to the first valid record */ + rc = iops->load(&lqp->lqp_env, it, 0); + if (rc < 0) { /* Error */ + goto not_found; + } else if (rc == 0) { + /* + * Iterator didn't find record with exactly the key requested. + * + * It is currently either + * + * - positioned above record with key less than + * requested - skip it. + * - or not positioned at all (is in IAM_IT_SKEWED + * state) - position it on the next item. + */ + rc = iops->next(&lqp->lqp_env, it); + if (rc != 0) + goto not_found; + } + while (offset--) { + rc = iops->next(&lqp->lqp_env, it); + if (rc != 0) /* Error or reach the end */ + goto not_found; + } + return it; + +not_found: + iops->put(&lqp->lqp_env, it); + iops->fini(&lqp->lqp_env, it); + return NULL; +} + +static void lprocfs_quota_seq_stop(struct seq_file *p, void *v) +{ + struct lquota_procfs *lqp = p->private; + const struct dt_it_ops *iops; + struct dt_it *it; + + if (lqp->lqp_obj == NULL || v == NULL || v == SEQ_START_TOKEN) + return; + + iops = &lqp->lqp_obj->do_index_ops->dio_it; + it = (struct dt_it *)v; + /* if something wrong happened during ->seq_show, we need to release + * the iterator here */ + iops->put(&lqp->lqp_env, it); + iops->fini(&lqp->lqp_env, it); +} + +static void *lprocfs_quota_seq_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct lquota_procfs *lqp = p->private; + const struct dt_it_ops *iops; + struct dt_it *it; + int rc; + + LASSERT(lqp); + + ++*pos; + if (lqp->lqp_obj == NULL) + return NULL; + + if (v == SEQ_START_TOKEN) + return lprocfs_quota_seq_start(p, pos); + + iops = &lqp->lqp_obj->do_index_ops->dio_it; + it = (struct dt_it *)v; + + rc = iops->next(&lqp->lqp_env, it); + if (rc == 0) + return it; + + if (rc < 0) + CERROR("%s: seq_next failed: rc = %d\n", + lqp->lqp_obj->do_lu.lo_dev->ld_obd->obd_name, rc); + + /* Reach the end or error */ + iops->put(&lqp->lqp_env, it); + iops->fini(&lqp->lqp_env, it); + return NULL; +} + +/* + * Output example: + * + * user_accounting: + * - id: 0 + * usage: { inodes: 209, bytes: 26161152 } + * - id: 840000038 + * usage: { inodes: 1, bytes: 10485760 } + */ +static int lprocfs_quota_seq_show(struct seq_file *p, void *v) +{ + struct lquota_procfs *lqp = p->private; + const struct dt_it_ops *iops; + struct dt_it *it; + struct dt_key *key; + struct lquota_acct_rec rec; + int rc; + + LASSERT(lqp); + if (lqp->lqp_obj == NULL) { + seq_printf(p, "not supported\n"); + return 0; + } + + if (v == SEQ_START_TOKEN) { + const struct lu_fid *fid = lu_object_fid(&lqp->lqp_obj->do_lu); + + LASSERT(fid_is_acct(fid)); + if (fid_oid(fid) == ACCT_USER_OID) + seq_printf(p, "user_accounting:\n"); + else + seq_printf(p, "group_accounting:\n"); + return 0; + } + + iops = &lqp->lqp_obj->do_index_ops->dio_it; + it = (struct dt_it *)v; + + key = iops->key(&lqp->lqp_env, it); + if (IS_ERR(key)) { + CERROR("%s: failed to get key: rc = %ld\n", + lqp->lqp_obj->do_lu.lo_dev->ld_obd->obd_name, + PTR_ERR(key)); + return PTR_ERR(key); + } + + rc = iops->rec(&lqp->lqp_env, it, (struct dt_rec *)&rec, 0); + if (rc) { + CERROR("%s: failed to get rec: rc = %d\n", + lqp->lqp_obj->do_lu.lo_dev->ld_obd->obd_name, rc); + return rc; + } + + seq_printf(p, "- %-8s %llu\n", "id:", *((__u64 *)key)); + seq_printf(p, " %-8s { inodes: %20"LPF64"u, bytes: %20"LPF64"u }\n", + "usage:", rec.ispace, rec.bspace); + return 0; +} + +struct seq_operations lprocfs_quota_seq_sops = { + .start = lprocfs_quota_seq_start, + .stop = lprocfs_quota_seq_stop, + .next = lprocfs_quota_seq_next, + .show = lprocfs_quota_seq_show, +}; + +static int lprocfs_quota_seq_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *dp = PDE(inode); + struct seq_file *seq; + int rc; + struct lquota_procfs *lqp; + + /* Allocate quota procfs data. This structure will be passed to + * seq_start/stop/next/show via seq->private */ + OBD_ALLOC_PTR(lqp); + if (lqp == NULL) + return -ENOMEM; + + /* store pointer to object we would like to iterate over */ + lqp->lqp_obj = (struct dt_object *)dp->data; + + /* Initialize the common environment to be used in the seq operations */ + rc = lu_env_init(&lqp->lqp_env, LCT_LOCAL); + if (rc) { + CERROR("%s: error initializing procfs quota env: rc = %d\n", + lqp->lqp_obj->do_lu.lo_dev->ld_obd->obd_name, rc); + goto out_lqp; + } + + if (LPROCFS_ENTRY_AND_CHECK(dp)) { + rc = -ENOENT; + goto out_env; + } + + rc = seq_open(file, &lprocfs_quota_seq_sops); + if (rc) + goto out_lprocfs; + + seq = file->private_data; + seq->private = lqp; + return 0; + +out_lprocfs: + LPROCFS_EXIT(); +out_env: + lu_env_fini(&lqp->lqp_env); +out_lqp: + OBD_FREE_PTR(lqp); + return rc; +} + +static int lprocfs_quota_seq_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct lquota_procfs *lqp = seq->private; + + LPROCFS_EXIT(); + + LASSERT(lqp); + lu_env_fini(&lqp->lqp_env); + OBD_FREE_PTR(lqp); + + return seq_release(inode, file); +} + +struct file_operations lprocfs_quota_seq_fops = { + .owner = THIS_MODULE, + .open = lprocfs_quota_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = lprocfs_quota_seq_release, +}; + +int lprocfs_quota_rd_type_dumb(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return 0; +} +EXPORT_SYMBOL(lprocfs_quota_rd_type_dumb); + +int lprocfs_quota_wr_type_dumb(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return count; +} +EXPORT_SYMBOL(lprocfs_quota_wr_type_dumb); + int lprocfs_quota_rd_bunit(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -181,12 +447,9 @@ int lprocfs_quota_rd_type(char *page, char **start, off_t off, int count, struct obd_device *obd = (struct obd_device *)data; char stype[MAX_STYPE_SIZE + 1] = ""; int oq_type; - struct obd_device_target *obt; LASSERT(obd != NULL); - obt = &obd->u.obt; - /* Collect the needed information */ oq_type = obd->u.obt.obt_qctxt.lqc_flags; @@ -202,107 +465,101 @@ int lprocfs_quota_rd_type(char *page, char **start, off_t off, int count, } EXPORT_SYMBOL(lprocfs_quota_rd_type); -static int auto_quota_on(struct obd_device *obd, int type, - struct super_block *sb, int is_master) +/* + * 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_quotactl *oqctl; - struct lvfs_run_ctxt saved; - int rc = 0, rc1 = 0, id; struct obd_device_target *obt = &obd->u.obt; - struct lustre_quota_ctxt *qctxt = &obt->obt_qctxt; - struct mds_obd *mds = NULL; - ENTRY; - - LASSERT(type == USRQUOTA || type == GRPQUOTA || type == UGQUOTA); - - OBD_ALLOC_PTR(oqctl); - if (!oqctl) - RETURN(-ENOMEM); + struct lvfs_run_ctxt saved; + int id, is_master, rc = 0, local; /* means we need a local quotaon */ cfs_down(&obt->obt_quotachecking); - id = UGQUOTA2LQC(type); - /* quota already turned on */ - if ((obt->obt_qctxt.lqc_flags & id) == id) - GOTO(out, rc); - - if (obt->obt_qctxt.lqc_immutable) { - LCONSOLE_ERROR("Failed to turn Quota on, immutable mode " - "(is SOM enabled?)\n"); - GOTO(out, rc = -ECANCELED); - } + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); + id = UGQUOTA2LQC(oqctl->qc_type); + local = (obt->obt_qctxt.lqc_flags & id) != id; - oqctl->qc_type = type; oqctl->qc_cmd = Q_QUOTAON; oqctl->qc_id = obt->obt_qfmt; - push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); - + is_master = !strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME); if (is_master) { - mds = &obd->u.mds; - cfs_down(&mds->mds_qonoff_sem); - /* turn on cluster wide quota */ - rc1 = mds_admin_quota_on(obd, oqctl); - if (rc1 && rc1 != -EALREADY) { - CDEBUG_LIMIT(rc1 == -ENOENT ? D_QUOTA : D_ERROR, - "%s: auto-enable admin quota failed with " - "rc=%d\n", obd->obd_name, rc1); - GOTO(out_ctxt, rc1); + 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); } } - /* turn on local quota */ - rc = fsfilt_quotactl(obd, sb, oqctl); - if (!rc) { - obt->obt_qctxt.lqc_flags |= UGQUOTA2LQC(type); - build_lqs(obd); - } else if (rc == -EBUSY && quota_is_on(qctxt, oqctl)) { - CWARN("%s: mds local quota[%d] is on already\n", - obd->obd_name, oqctl->qc_type); - rc = -EALREADY; - } else { - CDEBUG_LIMIT(rc == -ENOENT ? D_QUOTA : D_ERROR, - "%s: auto-enable local quota failed with rc=%d\n", - obd->obd_name, rc); - if (rc1 == -EALREADY) { - oqctl->qc_cmd = Q_QUOTAOFF; - mds_admin_quota_off(obd, oqctl); + 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 == -ENOENT) - CWARN("%s: quotaon failed because quota files don't " - "exist, please run quotacheck firstly\n", - obd->obd_name); - GOTO(out_ctxt, rc); + if (rc == 0 && global && is_master) + rc = obd_quotactl(obd->u.mds.mds_lov_exp, oqctl); } - EXIT; + if (is_master) + cfs_up_write(&obd->u.mds.mds_qonoff_sem); -out_ctxt: - if (mds != NULL) - cfs_up(&mds->mds_qonoff_sem); pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); - -out: cfs_up(&obt->obt_quotachecking); - OBD_FREE_PTR(oqctl); + + CDEBUG(D_QUOTA, "%s: quotaon type:master:global:local:flags:rc " + "%u:%d:%d:%d:%lu:%d\n", + obd->obd_name, oqctl->qc_type, is_master, global, local, + obt->obt_qctxt.lqc_flags, rc); + return rc; } +static int auto_quota_on(struct obd_device *obd, int type) +{ + struct obd_quotactl *oqctl; + int rc; + ENTRY; + + LASSERT(type == USRQUOTA || type == GRPQUOTA || type == UGQUOTA); + + OBD_ALLOC_PTR(oqctl); + if (!oqctl) + RETURN(-ENOMEM); + + oqctl->qc_type = type; + + rc = generic_quota_on(obd, oqctl, 0); + + OBD_FREE_PTR(oqctl); + RETURN(rc); +} + int lprocfs_quota_wr_type(struct file *file, const char *buffer, unsigned long count, void *data) { struct obd_device *obd = (struct obd_device *)data; - struct obd_device_target *obt; - int type = 0, is_mds; + int type = 0; unsigned long i; char stype[MAX_STYPE_SIZE + 1] = ""; LASSERT(obd != NULL); - obt = &obd->u.obt; - - is_mds = !strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME); - if (count > MAX_STYPE_SIZE) return -EINVAL; @@ -329,7 +586,7 @@ int lprocfs_quota_wr_type(struct file *file, const char *buffer, } if (type != 0) { - int rc = auto_quota_on(obd, type - 1, obt->obt_sb, is_mds); + int rc = auto_quota_on(obd, type - 1); if (rc && rc != -EALREADY && rc != -ENOENT) return rc; @@ -690,4 +947,3 @@ int lquota_proc_cleanup(struct lustre_quota_ctxt *qctxt) } #endif /* LPROCFS */ -#endif