xfs: wire up Q_XGETNEXTQUOTA / get_nextdqblk
Add code to allow the Q_XGETNEXTQUOTA quotactl to quickly find all active quotas by examining the quota inode, and skipping over unallocated or uninitialized regions. Userspace can then use this interface rather than i.e. a getpwent() loop when asked to report all active quotas. Signed-off-by: Eric Sandeen <sandeen@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
parent
8aa7d37ebf
commit
296c24e26e
|
@ -37,7 +37,7 @@ typedef __uint16_t xfs_qwarncnt_t;
|
||||||
#define XFS_DQ_PROJ 0x0002 /* project quota */
|
#define XFS_DQ_PROJ 0x0002 /* project quota */
|
||||||
#define XFS_DQ_GROUP 0x0004 /* a group quota */
|
#define XFS_DQ_GROUP 0x0004 /* a group quota */
|
||||||
#define XFS_DQ_DIRTY 0x0008 /* dquot is dirty */
|
#define XFS_DQ_DIRTY 0x0008 /* dquot is dirty */
|
||||||
#define XFS_DQ_FREEING 0x0010 /* dquot is beeing torn down */
|
#define XFS_DQ_FREEING 0x0010 /* dquot is being torn down */
|
||||||
|
|
||||||
#define XFS_DQ_ALLTYPES (XFS_DQ_USER|XFS_DQ_PROJ|XFS_DQ_GROUP)
|
#define XFS_DQ_ALLTYPES (XFS_DQ_USER|XFS_DQ_PROJ|XFS_DQ_GROUP)
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ typedef __uint16_t xfs_qwarncnt_t;
|
||||||
#define XFS_QMOPT_DQREPAIR 0x0001000 /* repair dquot if damaged */
|
#define XFS_QMOPT_DQREPAIR 0x0001000 /* repair dquot if damaged */
|
||||||
#define XFS_QMOPT_GQUOTA 0x0002000 /* group dquot requested */
|
#define XFS_QMOPT_GQUOTA 0x0002000 /* group dquot requested */
|
||||||
#define XFS_QMOPT_ENOSPC 0x0004000 /* enospc instead of edquot (prj) */
|
#define XFS_QMOPT_ENOSPC 0x0004000 /* enospc instead of edquot (prj) */
|
||||||
|
#define XFS_QMOPT_DQNEXT 0x0008000 /* return next dquot >= this ID */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* flags to xfs_trans_mod_dquot to indicate which field needs to be
|
* flags to xfs_trans_mod_dquot to indicate which field needs to be
|
||||||
|
|
|
@ -685,6 +685,56 @@ error0:
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Advance to the next id in the current chunk, or if at the
|
||||||
|
* end of the chunk, skip ahead to first id in next allocated chunk
|
||||||
|
* using the SEEK_DATA interface.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xfs_dq_get_next_id(
|
||||||
|
xfs_mount_t *mp,
|
||||||
|
uint type,
|
||||||
|
xfs_dqid_t *id,
|
||||||
|
loff_t eof)
|
||||||
|
{
|
||||||
|
struct xfs_inode *quotip;
|
||||||
|
xfs_fsblock_t start;
|
||||||
|
loff_t offset;
|
||||||
|
uint lock;
|
||||||
|
xfs_dqid_t next_id;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
/* Simple advance */
|
||||||
|
next_id = *id + 1;
|
||||||
|
|
||||||
|
/* If new ID is within the current chunk, advancing it sufficed */
|
||||||
|
if (next_id % mp->m_quotainfo->qi_dqperchunk) {
|
||||||
|
*id = next_id;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nope, next_id is now past the current chunk, so find the next one */
|
||||||
|
start = (xfs_fsblock_t)next_id / mp->m_quotainfo->qi_dqperchunk;
|
||||||
|
|
||||||
|
quotip = xfs_quota_inode(mp, type);
|
||||||
|
lock = xfs_ilock_data_map_shared(quotip);
|
||||||
|
|
||||||
|
offset = __xfs_seek_hole_data(VFS_I(quotip), XFS_FSB_TO_B(mp, start),
|
||||||
|
eof, SEEK_DATA);
|
||||||
|
if (offset < 0)
|
||||||
|
error = offset;
|
||||||
|
|
||||||
|
xfs_iunlock(quotip, lock);
|
||||||
|
|
||||||
|
/* -ENXIO is essentially "no more data" */
|
||||||
|
if (error)
|
||||||
|
return (error == -ENXIO ? -ENOENT: error);
|
||||||
|
|
||||||
|
/* Convert next data offset back to a quota id */
|
||||||
|
*id = XFS_B_TO_FSB(mp, offset) * mp->m_quotainfo->qi_dqperchunk;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given the file system, inode OR id, and type (UDQUOT/GDQUOT), return a
|
* Given the file system, inode OR id, and type (UDQUOT/GDQUOT), return a
|
||||||
* a locked dquot, doing an allocation (if requested) as needed.
|
* a locked dquot, doing an allocation (if requested) as needed.
|
||||||
|
@ -705,6 +755,7 @@ xfs_qm_dqget(
|
||||||
struct xfs_quotainfo *qi = mp->m_quotainfo;
|
struct xfs_quotainfo *qi = mp->m_quotainfo;
|
||||||
struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
|
struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
|
||||||
struct xfs_dquot *dqp;
|
struct xfs_dquot *dqp;
|
||||||
|
loff_t eof = 0;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
ASSERT(XFS_IS_QUOTA_RUNNING(mp));
|
ASSERT(XFS_IS_QUOTA_RUNNING(mp));
|
||||||
|
@ -732,6 +783,21 @@ xfs_qm_dqget(
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Get the end of the quota file if we need it */
|
||||||
|
if (flags & XFS_QMOPT_DQNEXT) {
|
||||||
|
struct xfs_inode *quotip;
|
||||||
|
xfs_fileoff_t last;
|
||||||
|
uint lock_mode;
|
||||||
|
|
||||||
|
quotip = xfs_quota_inode(mp, type);
|
||||||
|
lock_mode = xfs_ilock_data_map_shared(quotip);
|
||||||
|
error = xfs_bmap_last_offset(quotip, &last, XFS_DATA_FORK);
|
||||||
|
xfs_iunlock(quotip, lock_mode);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
eof = XFS_FSB_TO_B(mp, last);
|
||||||
|
}
|
||||||
|
|
||||||
restart:
|
restart:
|
||||||
mutex_lock(&qi->qi_tree_lock);
|
mutex_lock(&qi->qi_tree_lock);
|
||||||
dqp = radix_tree_lookup(tree, id);
|
dqp = radix_tree_lookup(tree, id);
|
||||||
|
@ -745,6 +811,18 @@ restart:
|
||||||
goto restart;
|
goto restart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* uninit / unused quota found in radix tree, keep looking */
|
||||||
|
if (flags & XFS_QMOPT_DQNEXT) {
|
||||||
|
if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
|
||||||
|
xfs_dqunlock(dqp);
|
||||||
|
mutex_unlock(&qi->qi_tree_lock);
|
||||||
|
error = xfs_dq_get_next_id(mp, type, &id, eof);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dqp->q_nrefs++;
|
dqp->q_nrefs++;
|
||||||
mutex_unlock(&qi->qi_tree_lock);
|
mutex_unlock(&qi->qi_tree_lock);
|
||||||
|
|
||||||
|
@ -771,6 +849,13 @@ restart:
|
||||||
if (ip)
|
if (ip)
|
||||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||||
|
|
||||||
|
/* If we are asked to find next active id, keep looking */
|
||||||
|
if (error == -ENOENT && (flags & XFS_QMOPT_DQNEXT)) {
|
||||||
|
error = xfs_dq_get_next_id(mp, type, &id, eof);
|
||||||
|
if (!error)
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -821,6 +906,17 @@ restart:
|
||||||
qi->qi_dquots++;
|
qi->qi_dquots++;
|
||||||
mutex_unlock(&qi->qi_tree_lock);
|
mutex_unlock(&qi->qi_tree_lock);
|
||||||
|
|
||||||
|
/* If we are asked to find next active id, keep looking */
|
||||||
|
if (flags & XFS_QMOPT_DQNEXT) {
|
||||||
|
if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
|
||||||
|
xfs_qm_dqput(dqp);
|
||||||
|
error = xfs_dq_get_next_id(mp, type, &id, eof);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dqret:
|
dqret:
|
||||||
ASSERT((ip == NULL) || xfs_isilocked(ip, XFS_ILOCK_EXCL));
|
ASSERT((ip == NULL) || xfs_isilocked(ip, XFS_ILOCK_EXCL));
|
||||||
trace_xfs_dqget_miss(dqp);
|
trace_xfs_dqget_miss(dqp);
|
||||||
|
|
|
@ -164,8 +164,8 @@ extern void xfs_qm_dqrele_all_inodes(struct xfs_mount *, uint);
|
||||||
|
|
||||||
/* quota ops */
|
/* quota ops */
|
||||||
extern int xfs_qm_scall_trunc_qfiles(struct xfs_mount *, uint);
|
extern int xfs_qm_scall_trunc_qfiles(struct xfs_mount *, uint);
|
||||||
extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t,
|
extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t *,
|
||||||
uint, struct qc_dqblk *);
|
uint, struct qc_dqblk *, uint);
|
||||||
extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint,
|
extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint,
|
||||||
struct qc_dqblk *);
|
struct qc_dqblk *);
|
||||||
extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint);
|
extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint);
|
||||||
|
|
|
@ -635,9 +635,10 @@ out:
|
||||||
int
|
int
|
||||||
xfs_qm_scall_getquota(
|
xfs_qm_scall_getquota(
|
||||||
struct xfs_mount *mp,
|
struct xfs_mount *mp,
|
||||||
xfs_dqid_t id,
|
xfs_dqid_t *id,
|
||||||
uint type,
|
uint type,
|
||||||
struct qc_dqblk *dst)
|
struct qc_dqblk *dst,
|
||||||
|
uint dqget_flags)
|
||||||
{
|
{
|
||||||
struct xfs_dquot *dqp;
|
struct xfs_dquot *dqp;
|
||||||
int error;
|
int error;
|
||||||
|
@ -647,7 +648,7 @@ xfs_qm_scall_getquota(
|
||||||
* we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't
|
* we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't
|
||||||
* exist, we'll get ENOENT back.
|
* exist, we'll get ENOENT back.
|
||||||
*/
|
*/
|
||||||
error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp);
|
error = xfs_qm_dqget(mp, NULL, *id, type, dqget_flags, &dqp);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -660,6 +661,9 @@ xfs_qm_scall_getquota(
|
||||||
goto out_put;
|
goto out_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fill in the ID we actually read from disk */
|
||||||
|
*id = be32_to_cpu(dqp->q_core.d_id);
|
||||||
|
|
||||||
memset(dst, 0, sizeof(*dst));
|
memset(dst, 0, sizeof(*dst));
|
||||||
dst->d_spc_hardlimit =
|
dst->d_spc_hardlimit =
|
||||||
XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit));
|
XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit));
|
||||||
|
@ -701,7 +705,7 @@ xfs_qm_scall_getquota(
|
||||||
if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) ||
|
if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) ||
|
||||||
(XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) ||
|
(XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) ||
|
||||||
(XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) &&
|
(XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) &&
|
||||||
id != 0) {
|
*id != 0) {
|
||||||
if ((dst->d_space > dst->d_spc_softlimit) &&
|
if ((dst->d_space > dst->d_spc_softlimit) &&
|
||||||
(dst->d_spc_softlimit > 0)) {
|
(dst->d_spc_softlimit > 0)) {
|
||||||
ASSERT(dst->d_spc_timer != 0);
|
ASSERT(dst->d_spc_timer != 0);
|
||||||
|
|
|
@ -231,14 +231,45 @@ xfs_fs_get_dqblk(
|
||||||
struct qc_dqblk *qdq)
|
struct qc_dqblk *qdq)
|
||||||
{
|
{
|
||||||
struct xfs_mount *mp = XFS_M(sb);
|
struct xfs_mount *mp = XFS_M(sb);
|
||||||
|
xfs_dqid_t id;
|
||||||
|
|
||||||
if (!XFS_IS_QUOTA_RUNNING(mp))
|
if (!XFS_IS_QUOTA_RUNNING(mp))
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
if (!XFS_IS_QUOTA_ON(mp))
|
if (!XFS_IS_QUOTA_ON(mp))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
return xfs_qm_scall_getquota(mp, from_kqid(&init_user_ns, qid),
|
id = from_kqid(&init_user_ns, qid);
|
||||||
xfs_quota_type(qid.type), qdq);
|
return xfs_qm_scall_getquota(mp, &id,
|
||||||
|
xfs_quota_type(qid.type), qdq, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return quota info for active quota >= this qid */
|
||||||
|
STATIC int
|
||||||
|
xfs_fs_get_nextdqblk(
|
||||||
|
struct super_block *sb,
|
||||||
|
struct kqid *qid,
|
||||||
|
struct qc_dqblk *qdq)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct xfs_mount *mp = XFS_M(sb);
|
||||||
|
xfs_dqid_t id;
|
||||||
|
|
||||||
|
if (!XFS_IS_QUOTA_RUNNING(mp))
|
||||||
|
return -ENOSYS;
|
||||||
|
if (!XFS_IS_QUOTA_ON(mp))
|
||||||
|
return -ESRCH;
|
||||||
|
|
||||||
|
id = from_kqid(&init_user_ns, *qid);
|
||||||
|
ret = xfs_qm_scall_getquota(mp, &id,
|
||||||
|
xfs_quota_type(qid->type), qdq,
|
||||||
|
XFS_QMOPT_DQNEXT);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* ID may be different, so convert back what we got */
|
||||||
|
*qid = make_kqid(current_user_ns(), qid->type, id);
|
||||||
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC int
|
STATIC int
|
||||||
|
@ -267,5 +298,6 @@ const struct quotactl_ops xfs_quotactl_operations = {
|
||||||
.quota_disable = xfs_quota_disable,
|
.quota_disable = xfs_quota_disable,
|
||||||
.rm_xquota = xfs_fs_rm_xquota,
|
.rm_xquota = xfs_fs_rm_xquota,
|
||||||
.get_dqblk = xfs_fs_get_dqblk,
|
.get_dqblk = xfs_fs_get_dqblk,
|
||||||
|
.get_nextdqblk = xfs_fs_get_nextdqblk,
|
||||||
.set_dqblk = xfs_fs_set_dqblk,
|
.set_dqblk = xfs_fs_set_dqblk,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue