Skip to content

Commit

Permalink
xfs: wire up Q_XGETNEXTQUOTA / get_nextdqblk
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Sandeen authored and Dave Chinner committed Feb 8, 2016
1 parent 8aa7d37 commit 296c24e
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 9 deletions.
3 changes: 2 additions & 1 deletion fs/xfs/libxfs/xfs_quota_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ typedef __uint16_t xfs_qwarncnt_t;
#define XFS_DQ_PROJ 0x0002 /* project quota */
#define XFS_DQ_GROUP 0x0004 /* a group quota */
#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)

Expand Down Expand Up @@ -116,6 +116,7 @@ typedef __uint16_t xfs_qwarncnt_t;
#define XFS_QMOPT_DQREPAIR 0x0001000 /* repair dquot if damaged */
#define XFS_QMOPT_GQUOTA 0x0002000 /* group dquot requested */
#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
Expand Down
96 changes: 96 additions & 0 deletions fs/xfs/xfs_dquot.c
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,56 @@ xfs_qm_dqread(
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
* a locked dquot, doing an allocation (if requested) as needed.
Expand All @@ -705,6 +755,7 @@ xfs_qm_dqget(
struct xfs_quotainfo *qi = mp->m_quotainfo;
struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
struct xfs_dquot *dqp;
loff_t eof = 0;
int error;

ASSERT(XFS_IS_QUOTA_RUNNING(mp));
Expand Down Expand Up @@ -732,6 +783,21 @@ xfs_qm_dqget(
}
#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:
mutex_lock(&qi->qi_tree_lock);
dqp = radix_tree_lookup(tree, id);
Expand All @@ -745,6 +811,18 @@ xfs_qm_dqget(
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++;
mutex_unlock(&qi->qi_tree_lock);

Expand All @@ -771,6 +849,13 @@ xfs_qm_dqget(
if (ip)
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)
return error;

Expand Down Expand Up @@ -821,6 +906,17 @@ xfs_qm_dqget(
qi->qi_dquots++;
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:
ASSERT((ip == NULL) || xfs_isilocked(ip, XFS_ILOCK_EXCL));
trace_xfs_dqget_miss(dqp);
Expand Down
4 changes: 2 additions & 2 deletions fs/xfs/xfs_qm.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ extern void xfs_qm_dqrele_all_inodes(struct xfs_mount *, uint);

/* quota ops */
extern int xfs_qm_scall_trunc_qfiles(struct xfs_mount *, uint);
extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t,
uint, struct qc_dqblk *);
extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t *,
uint, struct qc_dqblk *, uint);
extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint,
struct qc_dqblk *);
extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint);
Expand Down
12 changes: 8 additions & 4 deletions fs/xfs/xfs_qm_syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -635,9 +635,10 @@ xfs_qm_log_quotaoff(
int
xfs_qm_scall_getquota(
struct xfs_mount *mp,
xfs_dqid_t id,
xfs_dqid_t *id,
uint type,
struct qc_dqblk *dst)
struct qc_dqblk *dst,
uint dqget_flags)
{
struct xfs_dquot *dqp;
int error;
Expand All @@ -647,7 +648,7 @@ xfs_qm_scall_getquota(
* we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't
* 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)
return error;

Expand All @@ -660,6 +661,9 @@ xfs_qm_scall_getquota(
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));
dst->d_spc_hardlimit =
XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit));
Expand Down Expand Up @@ -701,7 +705,7 @@ xfs_qm_scall_getquota(
if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) ||
(XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) ||
(XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) &&
id != 0) {
*id != 0) {
if ((dst->d_space > dst->d_spc_softlimit) &&
(dst->d_spc_softlimit > 0)) {
ASSERT(dst->d_spc_timer != 0);
Expand Down
36 changes: 34 additions & 2 deletions fs/xfs/xfs_quotaops.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,45 @@ xfs_fs_get_dqblk(
struct qc_dqblk *qdq)
{
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;

return xfs_qm_scall_getquota(mp, from_kqid(&init_user_ns, qid),
xfs_quota_type(qid.type), qdq);
id = from_kqid(&init_user_ns, qid);
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
Expand Down Expand Up @@ -267,5 +298,6 @@ const struct quotactl_ops xfs_quotactl_operations = {
.quota_disable = xfs_quota_disable,
.rm_xquota = xfs_fs_rm_xquota,
.get_dqblk = xfs_fs_get_dqblk,
.get_nextdqblk = xfs_fs_get_nextdqblk,
.set_dqblk = xfs_fs_set_dqblk,
};

0 comments on commit 296c24e

Please sign in to comment.