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 <johann@whamcloud.com>
Change-Id: I35495062b82f43f2bc26c263e08a656f9cbd9c2b
Reviewed-on: http://review.whamcloud.com/3147
Tested-by: Hudson
Reviewed-by: Niu Yawei <niu@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: Maloo <whamcloud.maloo@gmail.com>
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
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.
--- /dev/null
+/*
+ * 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 <dt_object.h>
+#include <lustre_fid.h>
+#include <lustre_dlm.h>
+
+#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 */
#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 *********************/
/* fid_be_to_cpu() */
#include <lustre_fid.h>
+#include <lquota.h>
+
struct dt_find_hint {
struct lu_fid *dfh_fid;
struct dt_device *dfh_dt;
}
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);
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@
#include <linux/seq_file.h>
#include <lustre_fsfilt.h>
+#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)
{
--- /dev/null
+/*
+ * 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 <obd.h>
+#include <lquota.h>
+
+#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 */
--- /dev/null
+/*
+ *
+ * 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 <johann@whamcloud.com>
+ * Author: Niu Yawei <niu@whamcloud.com>
+ */
+
+#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);
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 <johann@whamcloud.com>
+ * Author: Niu Yawei <niu@whamcloud.com>
+ */
+
+/*
+ * 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);