Viewing: lquota_internal.h
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2012, 2017, Intel Corporation.
* Use is subject to license terms.
*/
#include <obd.h>
#include <dt_object.h>
#include <cfs_hash.h>
#include <lustre_quota.h>
#ifndef _LQUOTA_INTERNAL_H
#define _LQUOTA_INTERNAL_H
#define RES_NAME(res) ((res) == LQUOTA_RES_MD ? "md" : "dt")
#define QIF_IFLAGS (QIF_INODES | QIF_ITIME | QIF_ILIMITS)
#define QIF_BFLAGS (QIF_SPACE | QIF_BTIME | QIF_BLIMITS)
/* The biggest filename are the one used for slave index which are in the form
* of 0x%x-%s,glb_fid.f_oid,slv_uuid, that's to say:
* 2(0x) + 8(f_oid) + 1(-) + 40(UUID_MAX) which means 51 chars + '\0' */
#define LQUOTA_NAME_MAX 52
/* reserved OID in FID_SEQ_QUOTA for local objects */
enum lquota_local_oid {
LQUOTA_USR_OID = 1UL, /* slave index copy for user quota */
LQUOTA_GRP_OID = 2UL, /* slave index copy for group quota */
LQUOTA_PRJ_OID = 3UL, /* slave index copy for project quota */
/* all OIDs after this are allocated dynamically by the QMT */
LQUOTA_GENERATED_OID = 4096UL,
};
static inline __u32 qtype2slv_oid(int qtype)
{
switch (qtype) {
case USRQUOTA:
return LQUOTA_USR_OID;
case GRPQUOTA:
return LQUOTA_GRP_OID;
case PRJQUOTA:
return LQUOTA_PRJ_OID;
}
/* should not come here, just make compile happy */
return LQUOTA_USR_OID;
}
/*
* lquota_entry support
*/
/* Common operations supported by a lquota_entry */
struct lquota_entry_operations {
/* Initialize specific fields of a lquota entry */
void (*lqe_init)(struct lquota_entry *, void *arg);
/* Read quota settings from disk and update lquota entry */
int (*lqe_read)(const struct lu_env *, struct lquota_entry *,
void *arg, bool find);
/* Print debug information about a given lquota entry */
void (*lqe_debug)(struct lquota_entry *, void *,
struct libcfs_debug_msg_data *,
struct va_format *vaf);
};
/* Per-ID information specific to the quota master target */
struct lquota_mst_entry {
/* global hard limit, in inodes or kbytes */
__u64 lme_hardlimit;
/* global quota soft limit, in inodes or kbytes */
__u64 lme_softlimit;
/* grace time, in seconds */
__u64 lme_gracetime;
/* last time we glimpsed */
time64_t lme_revoke_time;
/* r/w semaphore used to protect concurrent access to the quota
* parameters which are stored on disk */
struct rw_semaphore lme_sem;
/* quota space that may be released after glimpse */
__u64 lme_may_rel;
};
/* Per-ID information specific to the quota slave */
struct lquota_slv_entry {
/* [ib]tune size, inodes or kbytes */
__u64 lse_qtune;
/* per-ID lock handle */
struct lustre_handle lse_lockh;
/* pending write which were granted quota space but haven't completed
* yet, in inodes or kbytes. */
__u64 lse_pending_write;
/* writes waiting for quota space, in inodes or kbytes. */
__u64 lse_waiting_write;
/* pending release, in inodes or kbytes */
__u64 lse_pending_rel;
/* pending dqacq/dqrel requests. */
unsigned int lse_pending_req;
/* rw spinlock protecting in-memory counters (i.e. lse_pending*) */
rwlock_t lse_lock;
/* waiter for pending request done */
wait_queue_head_t lse_waiters;
/* hint on current on-disk usage, in inodes or kbytes */
__u64 lse_usage;
/* time to trigger quota adjust */
time64_t lse_adjust_time;
/* return code of latest acquire RPC */
int lse_acq_rc;
/* when latest acquire RPC completed */
time64_t lse_acq_time;
/* when latest edquot set */
time64_t lse_edquot_time;
};
/* In-memory entry for each enforced quota id
* A lquota_entry structure belong to a single lquota_site */
struct lquota_entry {
/* link to site hash table */
struct hlist_node lqe_hash;
/* quota identifier associated with this entry */
union lquota_id lqe_id;
/* site this quota entry belongs to */
struct lquota_site *lqe_site;
/* reference counter */
struct kref lqe_ref;
/* linked to list of lqes which:
* - need quota space adjustment on slave
* - need glimpse to be sent on master */
struct list_head lqe_link;
/* current quota settings/usage of this ID */
__u64 lqe_granted; /* granted limit, inodes or kbytes */
/* used in quota pool recalc process (only on QMT) */
__u64 lqe_recalc_granted;
__u64 lqe_qunit; /* [ib]unit size, inodes or kbytes */
union {
struct lquota_mst_entry me; /* params specific to QMT */
struct lquota_slv_entry se; /* params specific to QSD */
} u;
/* flags describing the state of the lquota_entry */
unsigned long lqe_enforced:1, /* quota enforced or not */
lqe_uptodate:1, /* successfully read from disk */
lqe_edquot:1, /* id out of quota space on QMT */
lqe_gl:1, /* glimpse is in progress */
lqe_nopreacq:1, /* pre-acquire disabled */
lqe_is_default:1, /* the default quota is used */
lqe_is_global:1, /* lqe belongs to global pool "0x0"*/
lqe_is_deleted:1, /* lqe will be deleted soon */
lqe_is_reset:1, /* lqe has been reset */
lqe_revoke:1; /* all extra grant will be revoked */
/* the lock to protect lqe_glbl_data */
struct mutex lqe_glbl_data_lock;
struct lqe_glbl_data *lqe_glbl_data;
struct work_struct lqe_work; /* workitem to free lvbo */
};
#define lqe_qtype(lqe) (lqe->lqe_site->lqs_qtype)
#define lqe_rtype(lqe) (lqe2qpi(lqe)->qpi_rtype)
struct lqe_glbl_entry {
__u64 lge_qunit;
unsigned long lge_idx:16,
/* index of target */
lge_edquot:1,
/* true when minimum qunit is set */
lge_qunit_set:1,
/* qunit or edquot is changed - need
* to send glimpse to appropriate slave */
lge_qunit_nu:1,
lge_edquot_nu:1;
};
struct lqe_glbl_data {
struct lqe_glbl_entry *lqeg_arr;
/* number of initialised entries */
int lqeg_num_used;
/* number of allocated entries */
int lqeg_num_alloc;
};
/* Compartment within which lquota_entry are unique.
* lquota_entry structures are kept in a hash table and read from disk if not
* present. */
struct lquota_site {
/* Hash table storing lquota_entry structures */
struct cfs_hash *lqs_hash;
/* Quota type, either user or group. */
int lqs_qtype;
/* Record whether this site is for a QMT or a slave */
int lqs_is_mst;
/* Vector of operations which can be done on lquota entry belonging to
* this quota site */
const struct lquota_entry_operations *lqs_ops;
/* Backpointer to parent structure, either QMT pool info for master or
* QSD for slave */
void *lqs_parent;
};
#define lqe_hardlimit u.me.lme_hardlimit
#define lqe_softlimit u.me.lme_softlimit
#define lqe_gracetime u.me.lme_gracetime
#define lqe_revoke_time u.me.lme_revoke_time
#define lqe_sem u.me.lme_sem
#define lqe_may_rel u.me.lme_may_rel
#define lqe_qtune u.se.lse_qtune
#define lqe_pending_write u.se.lse_pending_write
#define lqe_waiting_write u.se.lse_waiting_write
#define lqe_pending_rel u.se.lse_pending_rel
#define lqe_pending_req u.se.lse_pending_req
#define lqe_waiters u.se.lse_waiters
#define lqe_lock u.se.lse_lock
#define lqe_usage u.se.lse_usage
#define lqe_adjust_time u.se.lse_adjust_time
#define lqe_lockh u.se.lse_lockh
#define lqe_acq_rc u.se.lse_acq_rc
#define lqe_acq_time u.se.lse_acq_time
#define lqe_edquot_time u.se.lse_edquot_time
#define LQUOTA_BUMP_VER 0x1
#define LQUOTA_SET_VER 0x2
extern struct kmem_cache *lqe_kmem;
/* lquota_lib.c */
void lqe_ref_free(struct kref *kref);
/* helper routine to get/put reference on lquota_entry */
static inline void lqe_getref(struct lquota_entry *lqe)
{
LASSERT(lqe != NULL);
kref_get(&lqe->lqe_ref);
}
static inline void lqe_putref(struct lquota_entry *lqe)
{
LASSERT(lqe != NULL);
LASSERT(kref_read(&lqe->lqe_ref) > 0);
kref_put(&lqe->lqe_ref, lqe_ref_free);
}
static inline int lqe_is_master(struct lquota_entry *lqe)
{
return lqe->lqe_site->lqs_is_mst;
}
/* lqe locking helpers */
static inline void lqe_write_lock(struct lquota_entry *lqe)
{
if (lqe_is_master(lqe))
down_write(&lqe->lqe_sem);
else
write_lock(&lqe->lqe_lock);
}
static inline void lqe_write_unlock(struct lquota_entry *lqe)
{
if (lqe_is_master(lqe))
up_write(&lqe->lqe_sem);
else
write_unlock(&lqe->lqe_lock);
}
static inline void lqe_read_lock(struct lquota_entry *lqe)
{
if (lqe_is_master(lqe))
down_read(&lqe->lqe_sem);
else
read_lock(&lqe->lqe_lock);
}
static inline void lqe_read_unlock(struct lquota_entry *lqe)
{
if (lqe_is_master(lqe))
up_read(&lqe->lqe_sem);
else
read_unlock(&lqe->lqe_lock);
}
/*
* Helper functions & prototypes
*/
/* minimum qunit size, 1K inode for metadata pool and 1MB for data pool */
#define LQUOTA_LEAST_QUNIT(type) \
(type == LQUOTA_RES_MD ? (1 << 10) : stoqb(OFD_MAX_BRW_SIZE))
static inline enum osd_quota_local_flags lquota_over_fl(int qtype)
{
switch (qtype) {
case USRQUOTA:
return QUOTA_FL_OVER_USRQUOTA;
case GRPQUOTA:
return QUOTA_FL_OVER_GRPQUOTA;
case PRJQUOTA:
return QUOTA_FL_OVER_PRJQUOTA;
}
/* should not come here, just make compile happy */
return QUOTA_FL_OVER_USRQUOTA;
}
/* Common data shared by quota-level handlers. This is allocated per-thread to
* reduce stack consumption */
struct lquota_thread_info {
union lquota_rec qti_rec;
struct lu_buf qti_lb;
struct lu_attr qti_attr;
struct dt_object_format qti_dof;
struct lu_fid qti_fid;
char qti_buf[LQUOTA_NAME_MAX];
struct lu_nodemap *nodemap;
};
#define qti_glb_rec qti_rec.lqr_glb_rec
#define qti_acct_rec qti_rec.lqr_acct_rec
#define qti_slv_rec qti_rec.lqr_slv_rec
#define LQUOTA_BUMP_VER 0x1
#define LQUOTA_SET_VER 0x2
extern struct lu_context_key lquota_thread_key;
/* extract lquota_threa_info context from environment */
static inline
struct lquota_thread_info *lquota_info(const struct lu_env *env)
{
return lu_env_info(env, &lquota_thread_key);
}
#define req_is_acq(flags) ((flags & QUOTA_DQACQ_FL_ACQ) != 0)
#define req_is_preacq(flags) ((flags & QUOTA_DQACQ_FL_PREACQ) != 0)
#define req_is_rel(flags) ((flags & QUOTA_DQACQ_FL_REL) != 0)
#define req_has_rep(flags) ((flags & QUOTA_DQACQ_FL_REPORT) != 0)
/* debugging macros */
#ifdef LIBCFS_DEBUG
#define lquota_lqe_debug(msgdata, mask, cdls, lqe, fmt, a...) do { \
if (((mask) & D_CANTMASK) != 0 || \
((libcfs_debug & (mask)) != 0 && \
(libcfs_subsystem_debug & DEBUG_SUBSYSTEM) != 0)) \
lquota_lqe_debug0(lqe, msgdata, fmt, ##a); \
} while (0)
void lquota_lqe_debug0(struct lquota_entry *lqe,
struct libcfs_debug_msg_data *data, const char *fmt, ...)
__printf(3, 4);
#define LQUOTA_DEBUG_LIMIT(mask, lqe, fmt, a...) do { \
static struct cfs_debug_limit_state _lquota_cdls; \
LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, mask, &_lquota_cdls); \
lquota_lqe_debug(&msgdata, mask, &_lquota_cdls, lqe, "$$$ "fmt" ", \
##a); \
} while (0)
#define LQUOTA_ERROR(lqe, fmt, a...) LQUOTA_DEBUG_LIMIT(D_ERROR, lqe, fmt, ## a)
#define LQUOTA_WARN(lqe, fmt, a...) \
LQUOTA_DEBUG_LIMIT(D_WARNING, lqe, fmt, ## a)
#define LQUOTA_CONSOLE(lqe, fmt, a...) \
LQUOTA_DEBUG_LIMIT(D_CONSOLE, lqe, fmt, ## a)
#define LQUOTA_ELEVEL_LQES(level, env, fmt, a...) do { \
int i; \
for (i = 0; i < qti_lqes_cnt(env); i++) { \
LQUOTA_##level(qti_lqes(env)[i], fmt, ##a); \
} \
} while (0)
#define LQUOTA_WARN_LQES(lqe, fmt, a...) \
LQUOTA_ELEVEL_LQES(WARN, env, fmt, ##a)
#define LQUOTA_CONSOLE_LQES(lqe, fmt, a...) \
LQUOTA_ELEVEL_LQES(CONSOLE, env, fmt, ##a)
#define LQUOTA_ERROR_LQES(lqe, fmt, a...) \
LQUOTA_ELEVEL_LQES(ERROR, env, fmt, ##a)
#define LQUOTA_DEBUG(lqe, fmt, a...) do { \
LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, D_QUOTA, NULL); \
lquota_lqe_debug(&msgdata, D_QUOTA, NULL, lqe, "$$$ "fmt" ", ##a); \
} while (0)
#define LQUOTA_DEBUG_LQES(env, fmt, a...) do { \
int i; \
for (i = 0; i < qti_lqes_cnt(env); i++) { \
LQUOTA_DEBUG(qti_lqes(env)[i], fmt, ##a); \
} \
} while (0)
#else /* !LIBCFS_DEBUG */
# define LQUOTA_DEBUG_LQES(lqe, fmt, a...) ((void)0)
# define LQUOTA_ERROR_LQES(lqe, fmt, a...) ((void)0)
# define LQUOTA_WARN_LQES(lqe, fmt, a...) ((void)0)
# define LQUOTA_CONSOLE_LQES(lqe, fmt, a...) ((void)0)
# define LQUOTA_DEBUG(lqe, fmt, a...) ((void)0)
# define LQUOTA_ERROR(lqe, fmt, a...) ((void)0)
# define LQUOTA_WARN(lqe, fmt, a...) ((void)0)
# define LQUOTA_CONSOLE(lqe, fmt, a...) ((void)0)
# define lquota_lqe_debug(cdls, level, lqe, file, func, line, fmt, a...) \
((void)0)
#endif
/* lquota_lib.c */
struct dt_object *acct_obj_lookup(const struct lu_env *, struct dt_device *,
int);
void lquota_generate_fid(struct lu_fid *, int, int);
int lquota_extract_fid(const struct lu_fid *, enum lquota_res_type *,
enum lquota_type *);
const struct dt_index_features *glb_idx_feature(struct lu_fid *);
int lquota_obj_iter(const struct lu_env *env, struct dt_device *dev,
struct dt_object *obj, struct lu_nodemap *nm,
struct lquota_entry *lqe_def, struct obd_quotactl *oqctl,
char *buffer, int size, bool is_glb, bool is_md);
/* lquota_entry.c */
/* site create/destroy */
struct lquota_site *lquota_site_alloc(const struct lu_env *env, void *parent,
bool master, short qtype,
const struct lquota_entry_operations *op);
void lquota_site_free(const struct lu_env *, struct lquota_site *);
/* quota entry operations */
#define lqe_locate(env, site, id) lqe_locate_find(env, site, id, false)
#define lqe_find(env, site, id) lqe_locate_find(env, site, id, true)
struct lquota_entry *lqe_locate_find(const struct lu_env *,
struct lquota_site *,
const union lquota_id *, bool);
static inline void lqe_set_deleted(struct lquota_entry *lqe)
{
lqe_write_lock(lqe);
lqe->lqe_enforced = 0;
lqe->lqe_edquot = 0;
lqe->lqe_is_default = 0;
lqe->lqe_hardlimit = 0;
lqe->lqe_softlimit = 0;
lqe->lqe_gracetime = 0;
lqe->lqe_is_deleted = 1;
lqe_write_unlock(lqe);
}
/* lquota_disk.c */
struct dt_object *lquota_disk_dir_find_create(const struct lu_env *,
struct dt_device *,
struct dt_object *, const char *);
struct dt_object *lquota_disk_glb_find_create(const struct lu_env *,
struct dt_device *,
struct dt_object *,
struct lu_fid *, bool);
struct dt_object *lquota_disk_slv_find_create(const struct lu_env *,
struct dt_device *,
struct dt_object *,
struct lu_fid *,
struct obd_uuid *, bool);
typedef int (*lquota_disk_slv_cb_t) (const struct lu_env *, struct lu_fid *,
char *, struct lu_fid *, void *);
int lquota_disk_for_each_slv(const struct lu_env *, struct dt_object *,
struct lu_fid *, lquota_disk_slv_cb_t, void *);
struct dt_object *lquota_disk_slv_find(const struct lu_env *,
struct dt_device *, struct dt_object *,
const struct lu_fid *,
struct obd_uuid *);
int lquota_disk_read(const struct lu_env *, struct dt_object *,
union lquota_id *, struct dt_rec *);
int lquota_disk_declare_write(const struct lu_env *, struct thandle *,
struct dt_object *, union lquota_id *);
int lquota_disk_write(const struct lu_env *, struct thandle *,
struct dt_object *, union lquota_id *, struct dt_rec *,
__u32, __u64 *);
int lquota_disk_delete(const struct lu_env *env, struct thandle *th,
struct dt_object *obj, __u64 qid, __u64 *ver);
int lquota_disk_update_ver(const struct lu_env *, struct dt_device *,
struct dt_object *, __u64);
int lquota_disk_write_glb(const struct lu_env *, struct dt_object *, __u64,
struct lquota_glb_rec *);
/* qmt_dev.c */
int qmt_glb_init(void);
void qmt_glb_fini(void);
/* lproc_quota.c */
extern const struct proc_ops lprocfs_quota_seq_fops;
/* qsd_lib.c */
int qsd_glb_init(void);
void qsd_glb_fini(void);
#endif /* _LQUOTA_INTERNAL_H */