From fe927599b62b60f14f657984599c2c6aed9beb05 Mon Sep 17 00:00:00 2001 From: Mikhail Pershin Date: Tue, 19 Jun 2012 08:28:53 +0400 Subject: [PATCH 1/1] LU-1182 quota: quota accounting library Library functions for quota accounting. Introduce basic support for Quota Slave Driver (aka QSD) which just registers procfs file to dump the accounting data for the time being. Signed-off-by: Johann Lombardi Change-Id: I35495062b82f43f2bc26c263e08a656f9cbd9c2b Reviewed-on: http://review.whamcloud.com/3147 Tested-by: Hudson Reviewed-by: Niu Yawei Reviewed-by: Andreas Dilger Tested-by: Maloo --- lustre/include/Makefile.am | 2 +- lustre/include/dt_object.h | 3 + lustre/include/lquota.h | 84 +++++++++++++ lustre/include/lustre_disk.h | 3 +- lustre/obdclass/dt_object.c | 17 +++ lustre/quota/Makefile.in | 12 +- lustre/quota/lproc_quota.c | 268 +++++++++++++++++++++++++++++++++++++++++ lustre/quota/lquota_internal.h | 43 +++++++ lustre/quota/lquota_lib.c | 144 ++++++++++++++++++++++ lustre/quota/qsd_internal.h | 80 ++++++++++++ lustre/quota/qsd_lib.c | 246 +++++++++++++++++++++++++++++++++++++ 11 files changed, 896 insertions(+), 6 deletions(-) create mode 100644 lustre/include/lquota.h create mode 100644 lustre/quota/lquota_internal.h create mode 100644 lustre/quota/lquota_lib.c create mode 100644 lustre/quota/qsd_internal.h create mode 100644 lustre/quota/qsd_lib.c diff --git a/lustre/include/Makefile.am b/lustre/include/Makefile.am index 56b2297..7d58aa4 100644 --- a/lustre/include/Makefile.am +++ b/lustre/include/Makefile.am @@ -48,4 +48,4 @@ EXTRA_DIST = ioctl.h liblustre.h lprocfs_status.h lustre_cfg.h \ md_object.h dt_object.h lustre_param.h lustre_mdt.h \ lustre_fid.h lustre_fld.h lustre_req_layout.h lustre_capa.h \ lustre_idmap.h lustre_eacl.h interval_tree.h obd_cksum.h \ - lu_ref.h cl_object.h lustre_acl.h lclient.h lu_target.h + lu_ref.h cl_object.h lustre_acl.h lclient.h lu_target.h lquota.h diff --git a/lustre/include/dt_object.h b/lustre/include/dt_object.h index ff143fe..f438bcb 100644 --- a/lustre/include/dt_object.h +++ b/lustre/include/dt_object.h @@ -215,6 +215,9 @@ enum dt_index_flags { extern const struct dt_index_features dt_directory_features; extern const struct dt_index_features dt_otable_features; +/* index features supported by the accounting objects */ +extern const struct dt_index_features dt_acct_features; + /** * This is a general purpose dt allocation hint. * It now contains the parent object. diff --git a/lustre/include/lquota.h b/lustre/include/lquota.h new file mode 100644 index 0000000..8d54c8f --- /dev/null +++ b/lustre/include/lquota.h @@ -0,0 +1,84 @@ +/* + * 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) 2011, 2012, Whamcloud, Inc. + * Use is subject to license terms. + */ + +#include +#include +#include + +#ifndef _LUSTRE_LQUOTA_H +#define _LUSTRE_LQUOTA_H + +/* + * Space accounting support + * Format of an accounting record, providing disk usage information for a given + * user or group + */ +struct acct_rec { /* 16 bytes */ + __u64 bspace; /* current space in use */ + __u64 ispace; /* current # inodes in use */ +}; + +/* + * Quota enforcement support on slaves + */ + +struct qsd_instance; + +/* The quota slave feature is implemented under the form of a library. + * The API is the following: + * + * - qsd_init(): the user (mostly the OSD layer) should first allocate a qsd + * instance via qsd_init(). This sets up on-disk objects + * associated with the quota slave feature and initiates the quota + * reintegration procedure if needed. qsd_init() should typically + * be called when ->ldo_start is invoked. + * + * - qsd_fini(): is used to release a qsd_instance structure allocated with + * qsd_init(). This releases all quota slave objects and frees the + * structures associated with the qsd_instance. + * + * Below are the function prototypes to be used by OSD layer to manage quota + * enforcement. Arguments are documented where each function is defined. */ + +struct qsd_instance *qsd_init(const struct lu_env *, char *, struct dt_device *, + cfs_proc_dir_entry_t *); + +void qsd_fini(const struct lu_env *, struct qsd_instance *); + +/* helper function used by MDT & OFD to retrieve quota accounting information + * on slave */ +int lquotactl_slv(const struct lu_env *, struct dt_device *, + struct obd_quotactl *); + +#ifdef LPROCFS +/* dumb procfs handler which always report success, for backward compatibility + * purpose */ +int lprocfs_quota_rd_type_dumb(char *, char **, off_t, int, int *, void *); +int lprocfs_quota_wr_type_dumb(struct file *, const char *, unsigned long, + void *); +#endif /* LPROCFS */ +#endif /* _LUSTRE_LQUOTA_H */ diff --git a/lustre/include/lustre_disk.h b/lustre/include/lustre_disk.h index 1f0f7f3..b5bd305 100644 --- a/lustre/include/lustre_disk.h +++ b/lustre/include/lustre_disk.h @@ -64,7 +64,8 @@ #define CAPA_KEYS "capa_keys" #define CHANGELOG_USERS "changelog_users" #define MGS_NIDTBL_DIR "NIDTBL_VERSIONS" - +#define QMT_DIR "quota_master" +#define QSD_DIR "quota_slave" /****************** persistent mount data *********************/ diff --git a/lustre/obdclass/dt_object.c b/lustre/obdclass/dt_object.c index 435f068..052cb4e 100644 --- a/lustre/obdclass/dt_object.c +++ b/lustre/obdclass/dt_object.c @@ -49,6 +49,8 @@ /* fid_be_to_cpu() */ #include +#include + struct dt_find_hint { struct lu_fid *dfh_fid; struct dt_device *dfh_dt; @@ -559,8 +561,23 @@ dt_obj_version_t dt_version_get(const struct lu_env *env, struct dt_object *o) } EXPORT_SYMBOL(dt_version_get); +/* list of all supported index types */ + +/* directories */ const struct dt_index_features dt_directory_features; EXPORT_SYMBOL(dt_directory_features); +/* scrub iterator */ const struct dt_index_features dt_otable_features; EXPORT_SYMBOL(dt_otable_features); + +/* accounting indexes */ +const struct dt_index_features dt_acct_features = { + .dif_flags = DT_IND_UPDATE, + .dif_keysize_min = sizeof(__u64), /* 64-bit uid/gid */ + .dif_keysize_max = sizeof(__u64), /* 64-bit uid/gid */ + .dif_recsize_min = sizeof(struct acct_rec), /* 32 bytes */ + .dif_recsize_max = sizeof(struct acct_rec), /* 32 bytes */ + .dif_ptrsize = 4 +}; +EXPORT_SYMBOL(dt_acct_features); diff --git a/lustre/quota/Makefile.in b/lustre/quota/Makefile.in index 50efef3..52c2e22 100644 --- a/lustre/quota/Makefile.in +++ b/lustre/quota/Makefile.in @@ -1,9 +1,13 @@ MODULES := lquota -lquota-objs := quota_check.o quota_context.o quota_ctl.o quota_interface.o -lquota-objs += quota_master.o quota_adjust_qunit.o lproc_quota.o +quota-objs := quota_check.o quota_context.o quota_ctl.o quota_interface.o +quota-objs += quota_master.o quota_adjust_qunit.o lproc_quota.o lquota_lib.o -EXTRA_DIST := $(lquota-objs:%.o=%.c) $(quotactl-objs:%.o=%.c) $(quotacheck-objs:%.o=%.c) quota_internal.h +qsd-objs := qsd_lib.o -@INCLUDE_RULES@ +lquota-objs := $(quota-objs) $(qsd-objs) + +EXTRA_DIST := $(lquota-objs:%.o=%.c) lquota_internal.h qsd_internal.h +EXTRA_DIST += quota_internal.h +@INCLUDE_RULES@ diff --git a/lustre/quota/lproc_quota.c b/lustre/quota/lproc_quota.c index d2319e0..b385d07 100644 --- a/lustre/quota/lproc_quota.c +++ b/lustre/quota/lproc_quota.c @@ -42,9 +42,277 @@ #include #include +#include "lquota_internal.h" #include "quota_internal.h" #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 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) { diff --git a/lustre/quota/lquota_internal.h b/lustre/quota/lquota_internal.h new file mode 100644 index 0000000..b39f633 --- /dev/null +++ b/lustre/quota/lquota_internal.h @@ -0,0 +1,43 @@ +/* + * 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 Whamcloud, Inc. + * Use is subject to license terms. + */ + +#include +#include + +#ifndef _LQUOTA_INTERNAL_H +#define _LQUOTA_INTERNAL_H + +#define QTYPE_NAME(qtype) ((qtype) == USRQUOTA ? "usr" : "grp") + +/* lquota_lib.c */ +struct dt_object *acct_obj_lookup(const struct lu_env *, struct dt_device *, + __u32); + +/* lproc_quota.c */ +extern struct file_operations lprocfs_quota_seq_fops; + +#endif /* _LQUOTA_INTERNAL_H */ diff --git a/lustre/quota/lquota_lib.c b/lustre/quota/lquota_lib.c new file mode 100644 index 0000000..dfffb86 --- /dev/null +++ b/lustre/quota/lquota_lib.c @@ -0,0 +1,144 @@ +/* + * + * 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) 2011, 2012, Whamcloud, Inc. + * Use is subject to license terms. + * + * Author: Johann Lombardi + * Author: Niu Yawei + */ + +#ifndef EXPORT_SYMTAB +# define EXPORT_SYMTAB +#endif + +#define DEBUG_SUBSYSTEM S_LQUOTA + +#include "lquota_internal.h" + +static struct dt_object_format dt_acct_format = { + .dof_type = DFT_INDEX, + .u.dof_idx.di_feat = &dt_acct_features +}; + +/** + * Look-up accounting object to collect space usage information for user + * or group. + * + * \param env - is the environment passed by the caller + * \param dev - is the dt_device storing the accounting object + * \param oid - is the object id of the accounting object to initialize, must be + * either ACCT_USER_OID or ACCT_GROUP_OID. + */ +struct dt_object *acct_obj_lookup(const struct lu_env *env, + struct dt_device *dev, __u32 oid) +{ + struct dt_object *obj = NULL; + struct lu_fid fid; + struct lu_attr attr; + int rc; + ENTRY; + + memset(&attr, 0, sizeof(attr)); + attr.la_valid = LA_MODE; + attr.la_mode = S_IFREG | S_IRUGO | S_IWUSR; + lu_local_obj_fid(&fid, oid); + + /* lookup/create the accounting object */ + obj = dt_find_or_create(env, dev, &fid, &dt_acct_format, &attr); + if (IS_ERR(obj)) + RETURN(obj); + + if (obj->do_index_ops == NULL) { + /* set up indexing operations */ + rc = obj->do_ops->do_index_try(env, obj, &dt_acct_features); + if (rc) { + lu_object_put(env, &obj->do_lu); + RETURN(ERR_PTR(rc)); + } + } + RETURN(obj); +} + +/* + * Helper routine to retrieve slave information. + * This function converts a quotactl request into quota/accounting object + * operations. It is independant of the slave stack which is only accessible + * from the OSD layer. + * + * \param env - is the environment passed by the caller + * \param dev - is the dt_device this quotactl is executed on + * \param oqctl - is the quotactl request + */ +int lquotactl_slv(const struct lu_env *env, struct dt_device *dev, + struct obd_quotactl *oqctl) +{ + struct acct_rec rec; + __u64 key; + struct dt_object *obj; + int rc = 0; + ENTRY; + + if (oqctl->qc_cmd != Q_GETOQUOTA) { + /* as in many other places, dev->dd_lu_dev.ld_obd->obd_name + * point to a valid obd_name, to be fixed in LU-1574 */ + CERROR("%s: Unsupported quotactl command: %x\n", + dev->dd_lu_dev.ld_obd->obd_name, oqctl->qc_cmd); + RETURN(-EOPNOTSUPP); + } + + if (oqctl->qc_type == USRQUOTA) + obj = acct_obj_lookup(env, dev, ACCT_USER_OID); + else if (oqctl->qc_type == GRPQUOTA) + obj = acct_obj_lookup(env, dev, ACCT_GROUP_OID); + else + /* no support for directory quota yet */ + RETURN(-EOPNOTSUPP); + + if (IS_ERR(obj)) + RETURN(-EOPNOTSUPP); + if (obj->do_index_ops == NULL) + GOTO(out, rc = -EINVAL); + + /* qc_id is a 32-bit field while a key has 64 bits */ + key = oqctl->qc_id; + + /* lookup record storing space accounting information for this ID */ + rc = dt_lookup(env, obj, (struct dt_rec *)&rec, (struct dt_key *)&key, + BYPASS_CAPA); + if (rc < 0) + GOTO(out, rc); + + memset(&oqctl->qc_dqblk, 0, sizeof(struct obd_dqblk)); + oqctl->qc_dqblk.dqb_curspace = rec.bspace; + oqctl->qc_dqblk.dqb_curinodes = rec.ispace; + oqctl->qc_dqblk.dqb_valid = QIF_USAGE; + /* TODO: must set {hard,soft}limit and grace time */ + + EXIT; +out: + lu_object_put(env, &obj->do_lu); + return rc; +} +EXPORT_SYMBOL(lquotactl_slv); diff --git a/lustre/quota/qsd_internal.h b/lustre/quota/qsd_internal.h new file mode 100644 index 0000000..6a4aa6e --- /dev/null +++ b/lustre/quota/qsd_internal.h @@ -0,0 +1,80 @@ +/* + * 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 Whamcloud, Inc. + * Use is subject to license terms. + */ + +#include "lquota_internal.h" + +#ifndef _QSD_INTERNAL_H +#define _QSD_INTERNAL_H + +struct qsd_type_info; + +/* + * A QSD instance implements quota enforcement support for a given OSD. + * The instance can be created via qsd_init() and then freed with qsd_fini(). + * This structure gathers all quota parameters and pointers to on-disk indexes + * required on quota slave to: + * i. acquire/release quota space from the QMT; + * ii. allocate this quota space to local requests. + */ +struct qsd_instance { + /* name of service which created this qsd instance */ + char qsd_svname[MAX_OBD_NAME]; + + /* dt_device associated with this qsd instance */ + struct dt_device *qsd_dev; + + /* procfs directory where information related to the underlying slaves + * are exported */ + cfs_proc_dir_entry_t *qsd_proc; + + /* We create 2 quota slave instances: + * - one for user quota + * - one for group quota + * + * This will have to be revisited if new quota types are added in the + * future. For the time being, we can just use an array. */ + struct qsd_qtype_info *qsd_type_array[MAXQUOTAS]; +}; + +/* + * Per-type quota information. + * Quota slave instance for a specific quota type. The qsd instance has one such + * structure for each quota type (i.e. user & group). + */ +struct qsd_qtype_info { + /* quota type, either USRQUOTA or GRPQUOTA + * immutable after creation. */ + int qqi_qtype; + + /* back pointer to qsd device + * immutable after creation. */ + struct qsd_instance *qqi_qsd; + + /* Local index files storing quota settings for this quota type */ + struct dt_object *qqi_acct_obj; /* accounting object */ +}; +#endif /* _QSD_INTERNAL_H */ diff --git a/lustre/quota/qsd_lib.c b/lustre/quota/qsd_lib.c new file mode 100644 index 0000000..0ba8622 --- /dev/null +++ b/lustre/quota/qsd_lib.c @@ -0,0 +1,246 @@ +/* + * 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 Whamcloud, Inc. + * Use is subject to license terms. + * + * Author: Johann Lombardi + * Author: Niu Yawei + */ + +/* + * Quota Slave Driver (QSD) management. + */ + +#ifndef EXPORT_SYMTAB +# define EXPORT_SYMTAB +#endif + +#define DEBUG_SUBSYSTEM S_LQUOTA + +#include "qsd_internal.h" + +/* some procfs helpers */ +static int lprocfs_qsd_rd_state(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct qsd_instance *qsd = (struct qsd_instance *)data; + + LASSERT(qsd != NULL); + + return snprintf(page, count, + "target name: %s\n" + "quota enabled: none\n", + qsd->qsd_svname); +} + +static struct lprocfs_vars lprocfs_quota_qsd_vars[] = { + { "info", lprocfs_qsd_rd_state, 0, 0}, + { NULL } +}; + +/* + * Release qsd_qtype_info structure which contains data associated with a + * given quota type. This releases the accounting objects. + * It's called on OSD cleanup when the qsd instance is released. + * + * \param env - is the environment passed by the caller + * \param qsd - is the qsd instance managing the qsd_qtype_info structure + * to be released + * \param qtype - is the quota type to be shutdown + */ +static void qsd_qtype_fini(const struct lu_env *env, struct qsd_instance *qsd, + int qtype) +{ + struct qsd_qtype_info *qqi; + ENTRY; + + if (qsd->qsd_type_array[qtype] == NULL) + RETURN_EXIT; + qqi = qsd->qsd_type_array[qtype]; + qsd->qsd_type_array[qtype] = NULL; + + /* release accounting object */ + if (qqi->qqi_acct_obj != NULL && !IS_ERR(qqi->qqi_acct_obj)) { + lu_object_put(env, &qqi->qqi_acct_obj->do_lu); + qqi->qqi_acct_obj = NULL; + } + + OBD_FREE_PTR(qqi); + EXIT; +} + +/* + * Allocate and initialize a qsd_qtype_info structure for quota type \qtype. + * This opens the accounting object and initializes the proc file. + * It's called on OSD start when the qsd instance is created. + * + * \param env - the environment passed by the caller + * \param qsd - is the qsd instance which will be in charge of the new + * qsd_qtype_info instance. + * \param qtype - is quota type to set up + * + * \retval - 0 on success and qsd->qsd_type_array[qtype] is allocated, + * appropriate error on failure + */ +static int qsd_qtype_init(const struct lu_env *env, struct qsd_instance *qsd, + int qtype) +{ + struct qsd_qtype_info *qqi; + int rc; + ENTRY; + + LASSERT(qsd->qsd_type_array[qtype] == NULL); + + /* allocate structure for this quota type */ + OBD_ALLOC_PTR(qqi); + if (qqi == NULL) + RETURN(-ENOMEM); + qsd->qsd_type_array[qtype] = qqi; + + /* set backpointer and other parameters */ + qqi->qqi_qsd = qsd; + qqi->qqi_qtype = qtype; + + /* open accounting object */ + LASSERT(qqi->qqi_acct_obj == NULL); + qqi->qqi_acct_obj = acct_obj_lookup(env, qsd->qsd_dev, + qtype == USRQUOTA ? ACCT_USER_OID + : ACCT_GROUP_OID); + /* don't print any error message on failure in order not to confuse + * non-OFD user (e.g. 2.3 MDT stack) */ + if (IS_ERR(qqi->qqi_acct_obj)) + qqi->qqi_acct_obj = NULL; + + /* register proc entry for accounting object */ + rc = lprocfs_seq_create(qsd->qsd_proc, + qtype == USRQUOTA ? "acct_user" : "acct_group", + 0444, &lprocfs_quota_seq_fops, + qqi->qqi_acct_obj); + if (rc) { + CWARN("%s: can't add procfs entry for accounting file %d\n", + qsd->qsd_svname, rc); + GOTO(out, rc); + } + + EXIT; +out: + if (rc) + qsd_qtype_fini(env, qsd, qtype); + return rc; +} + +/* + * Release a qsd_instance. Companion of qsd_init(). This releases all data + * structures associated with the quota slave. + * This function should be called when the OSD is shutting down. + * + * \param env - is the environment passed by the caller + * \param qsd - is the qsd instance to shutdown + */ +void qsd_fini(const struct lu_env *env, struct qsd_instance *qsd) +{ + int qtype; + ENTRY; + + /* remove qsd proc entry */ + if (qsd->qsd_proc != NULL && !IS_ERR(qsd->qsd_proc)) { + lprocfs_remove(&qsd->qsd_proc); + qsd->qsd_proc = NULL; + } + + /* free per-quota type data */ + for (qtype = USRQUOTA; qtype < MAXQUOTAS; qtype++) + qsd_qtype_fini(env, qsd, qtype); + + /* release reference on dt_device */ + if (qsd->qsd_dev != NULL) { + lu_ref_del(&qsd->qsd_dev->dd_lu_dev.ld_reference, "qsd", qsd); + lu_device_put(&qsd->qsd_dev->dd_lu_dev); + qsd->qsd_dev = NULL; + } + + OBD_FREE_PTR(qsd); + EXIT; +} +EXPORT_SYMBOL(qsd_fini); + +/* + * Create a new qsd_instance to be associated with backend osd device + * identified by \dev. For now, this function just create procfs files which + * dumps the accounting information + * + * \param env - the environment passed by the caller + * \param svname - is the service name of the OSD device creating this instance + * \param dev - is the dt_device where to store quota index files + * \param osd_proc - is the procfs parent directory where to create procfs file + * related to this new qsd instance + * + * \retval - pointer to new qsd_instance associated with dev \dev on success, + * appropriate error on failure + */ +struct qsd_instance *qsd_init(const struct lu_env *env, char *svname, + struct dt_device *dev, + cfs_proc_dir_entry_t *osd_proc) +{ + struct qsd_instance *qsd; + int rc, qtype; + ENTRY; + + /* allocate qsd instance */ + OBD_ALLOC_PTR(qsd); + if (qsd == NULL) + RETURN(ERR_PTR(-ENOMEM)); + + /* copy service name */ + strncpy(qsd->qsd_svname, svname, MAX_OBD_NAME); + + /* grab reference on osd device */ + lu_device_get(&dev->dd_lu_dev); + lu_ref_add(&dev->dd_lu_dev.ld_reference, "qsd", qsd); + qsd->qsd_dev = dev; + + /* register procfs directory */ + qsd->qsd_proc = lprocfs_register(QSD_DIR, osd_proc, + lprocfs_quota_qsd_vars, qsd); + if (IS_ERR(qsd->qsd_proc)) { + rc = PTR_ERR(qsd->qsd_proc); + CERROR("%s: fail to create quota slave proc entry (%d)\n", + svname, rc); + GOTO(out, rc); + } + + /* initialize per-quota type data */ + for (qtype = USRQUOTA; qtype < MAXQUOTAS; qtype++) { + rc = qsd_qtype_init(env, qsd, qtype); + if (rc) + GOTO(out, rc); + } +out: + if (rc) { + qsd_fini(env, qsd); + return ERR_PTR(rc); + } + RETURN(qsd); +} +EXPORT_SYMBOL(qsd_init); -- 1.8.3.1