Whamcloud - gitweb
LU-1182 quota: quota accounting library
authorMikhail Pershin <tappro@whamcloud.com>
Tue, 19 Jun 2012 04:28:53 +0000 (08:28 +0400)
committerAndreas Dilger <adilger@whamcloud.com>
Thu, 28 Jun 2012 22:04:50 +0000 (18:04 -0400)
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>
lustre/include/Makefile.am
lustre/include/dt_object.h
lustre/include/lquota.h [new file with mode: 0644]
lustre/include/lustre_disk.h
lustre/obdclass/dt_object.c
lustre/quota/Makefile.in
lustre/quota/lproc_quota.c
lustre/quota/lquota_internal.h [new file with mode: 0644]
lustre/quota/lquota_lib.c [new file with mode: 0644]
lustre/quota/qsd_internal.h [new file with mode: 0644]
lustre/quota/qsd_lib.c [new file with mode: 0644]

index 56b2297..7d58aa4 100644 (file)
@@ -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
index ff143fe..f438bcb 100644 (file)
@@ -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 (file)
index 0000000..8d54c8f
--- /dev/null
@@ -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 <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 */
index 1f0f7f3..b5bd305 100644 (file)
@@ -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 *********************/
 
index 435f068..052cb4e 100644 (file)
@@ -49,6 +49,8 @@
 /* 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;
@@ -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);
index 50efef3..52c2e22 100644 (file)
@@ -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@
index d2319e0..b385d07 100644 (file)
 #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)
 {
diff --git a/lustre/quota/lquota_internal.h b/lustre/quota/lquota_internal.h
new file mode 100644 (file)
index 0000000..b39f633
--- /dev/null
@@ -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 <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 */
diff --git a/lustre/quota/lquota_lib.c b/lustre/quota/lquota_lib.c
new file mode 100644 (file)
index 0000000..dfffb86
--- /dev/null
@@ -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 <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);
diff --git a/lustre/quota/qsd_internal.h b/lustre/quota/qsd_internal.h
new file mode 100644 (file)
index 0000000..6a4aa6e
--- /dev/null
@@ -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 (file)
index 0000000..0ba8622
--- /dev/null
@@ -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 <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);