Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 296c24e2 authored by Eric Sandeen's avatar Eric Sandeen Committed by Dave Chinner
Browse files

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: default avatarEric Sandeen <sandeen@redhat.com>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent 8aa7d37e
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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)

@@ -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
+96 −0
Original line number Diff line number Diff line
@@ -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.
@@ -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));
@@ -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);
@@ -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);

@@ -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;

@@ -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);
+2 −2
Original line number Diff line number Diff line
@@ -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);
+8 −4
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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));
@@ -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);
+34 −2
Original line number Diff line number Diff line
@@ -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
@@ -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,
};