Viewing: lproc_quota.c
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2011, 2017, Intel Corporation.
* Use is subject to license terms.
*/
/*
* Author: Johann Lombardi <johann.lombardi@intel.com>
* Author: Niu Yawei <yawei.niu@intel.com>
*/
#define DEBUG_SUBSYSTEM S_LQUOTA
#include <linux/version.h>
#include <lprocfs_status.h>
#include <obd.h>
#include <linux/seq_file.h>
#include "lquota_internal.h"
#ifdef CONFIG_PROC_FS
/* 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;
struct dt_it *lqp_it;
__u64 lqp_cookie;
__u64 lqp_first_cookie;
};
/* 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;
if (lqp->lqp_obj == NULL)
/* accounting not enabled. */
return NULL;
if (lqp->lqp_it == NULL) /* reach the end */
return NULL;
offset--;
/* move on to the last processed entry */
iops = &lqp->lqp_obj->do_index_ops->dio_it;
it = lqp->lqp_it;
rc = iops->load(&lqp->lqp_env, it, lqp->lqp_cookie);
if (rc == 0 && offset == 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;
lqp->lqp_cookie = iops->store(&lqp->lqp_env, it);
} else if (rc <= 0) {
goto not_found;
}
/* The id entry could be deleted while iteration, and above ->load()
* operation will reset cursor to the first cookie (ldiskfs), we
* need to break in such situation. */
if (offset == 0)
lqp->lqp_first_cookie = lqp->lqp_cookie;
else if (lqp->lqp_cookie == lqp->lqp_first_cookie)
goto not_found;
return it;
not_found:
iops->put(&lqp->lqp_env, it);
iops->fini(&lqp->lqp_env, it);
lqp->lqp_it = NULL;
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);
}
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);
if (lqp->lqp_it == NULL) /* reach the end */
return NULL;
iops = &lqp->lqp_obj->do_index_ops->dio_it;
it = (struct dt_it *)v;
rc = iops->next(&lqp->lqp_env, it);
if (rc == 0) {
lqp->lqp_cookie = iops->store(&lqp->lqp_env, it);
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);
lqp->lqp_it = NULL;
return NULL;
}
static inline const char *oid2name(__u32 oid)
{
switch (oid) {
case ACCT_USER_OID:
return "usr_accounting";
case ACCT_GROUP_OID:
return "grp_accounting";
case ACCT_PROJECT_OID:
return "prj_accounting";
default:
return "unknown_accounting";
}
}
/*
* Output example:
*
* usr_accounting:
* - id: 0
* usage: { inodes: 209, kbytes: 2616 }
* - id: 840000038
* usage: { inodes: 1, kbytes: 1048 }
*/
static int lprocfs_quota_seq_show(struct seq_file *p, void *v)
{
struct lquota_procfs *lqp = p->private;
struct lquota_thread_info *qti = lquota_info(&lqp->lqp_env);
const struct dt_it_ops *iops;
struct dt_it *it;
struct dt_key *key;
struct dt_rec *rec = (struct dt_rec *)&qti->qti_rec;
const struct lu_fid *fid;
int rc;
LASSERT(lqp);
if (lqp->lqp_obj == NULL) {
seq_puts(p, "not supported\n");
return 0;
}
fid = lu_object_fid(&lqp->lqp_obj->do_lu);
if (v == SEQ_START_TOKEN) {
if (fid_is_acct(fid)) {
seq_printf(p, "%s:\n", oid2name(fid_oid(fid)));
} else if (fid_seq(fid) == FID_SEQ_QUOTA_GLB) {
enum lquota_res_type rtype;
enum lquota_type qtype;
rc = lquota_extract_fid(fid, &rtype, &qtype);
if (rc)
return rc;
seq_printf(p, "global_pool%d_%s_%s\n", 0,
RES_NAME(rtype), qtype_name(qtype));
} else if (fid_seq(fid) == FID_SEQ_LOCAL_NAME) {
/* global index copy object */
seq_puts(p, "global_index_copy:\n");
} else {
return -ENOTSUPP;
}
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, 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));
if (fid_is_acct(fid))
seq_printf(p, " %-8s { inodes: %12llu, kbytes: %12llu }\n",
"usage:", ((struct lquota_acct_rec *)rec)->ispace,
stoqb(((struct lquota_acct_rec *)rec)->bspace));
else if (fid_seq(fid) == FID_SEQ_QUOTA_GLB ||
fid_seq(fid) == FID_SEQ_LOCAL_NAME)
seq_printf(p, " %-8s { hard: %12llu, soft: %12llu, granted: %12llu, time: %15llu }\n",
"limits:",
((struct lquota_glb_rec *)rec)->qbr_hardlimit,
((struct lquota_glb_rec *)rec)->qbr_softlimit,
((struct lquota_glb_rec *)rec)->qbr_granted,
((struct lquota_glb_rec *)rec)->qbr_time);
return 0;
}
static const 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 seq_file *seq;
int rc;
struct lquota_procfs *lqp;
const struct dt_it_ops *iops;
struct dt_it *it;
/* 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 *)pde_data(inode);
/* Initialize the common environment to be used in the seq operations */
rc = lu_env_init(&lqp->lqp_env, LCT_LOCAL);
if (rc) {
char *obd_name = "quota";
if (lqp->lqp_obj != NULL)
obd_name = lqp->lqp_obj->do_lu.lo_dev->ld_obd->obd_name;
CERROR("%s: error initializing procfs quota env: rc = %d\n",
obd_name, rc);
goto out_lqp;
}
rc = seq_open(file, &lprocfs_quota_seq_sops);
if (rc)
goto out_env;
if (!lqp->lqp_obj) {
lqp->lqp_it = NULL;
goto out_seq;
}
/* initialize iterator */
iops = &lqp->lqp_obj->do_index_ops->dio_it;
it = iops->init(&lqp->lqp_env, lqp->lqp_obj, 0);
if (IS_ERR(it)) {
rc = PTR_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));
seq_release(inode, file);
goto out_env;
}
lqp->lqp_it = it;
lqp->lqp_cookie = 0;
out_seq:
seq = file->private_data;
seq->private = lqp;
return 0;
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;
const struct dt_it_ops *iops;
LASSERT(lqp);
if (lqp->lqp_it != NULL) {
iops = &lqp->lqp_obj->do_index_ops->dio_it;
iops->fini(&lqp->lqp_env, lqp->lqp_it);
}
lu_env_fini(&lqp->lqp_env);
OBD_FREE_PTR(lqp);
return seq_release(inode, file);
}
const struct proc_ops lprocfs_quota_seq_fops = {
PROC_OWNER(THIS_MODULE)
.proc_open = lprocfs_quota_seq_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = lprocfs_quota_seq_release,
};
#endif /* CONFIG_PROC_FS */