Skip to content

Commit 296c24e

Browse files
sandeendchinner
authored andcommitted
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>
1 parent 8aa7d37 commit 296c24e

File tree

5 files changed

+142
-9
lines changed

5 files changed

+142
-9
lines changed

fs/xfs/libxfs/xfs_quota_defs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ typedef __uint16_t xfs_qwarncnt_t;
3737
#define XFS_DQ_PROJ 0x0002 /* project quota */
3838
#define XFS_DQ_GROUP 0x0004 /* a group quota */
3939
#define XFS_DQ_DIRTY 0x0008 /* dquot is dirty */
40-
#define XFS_DQ_FREEING 0x0010 /* dquot is beeing torn down */
40+
#define XFS_DQ_FREEING 0x0010 /* dquot is being torn down */
4141

4242
#define XFS_DQ_ALLTYPES (XFS_DQ_USER|XFS_DQ_PROJ|XFS_DQ_GROUP)
4343

@@ -116,6 +116,7 @@ typedef __uint16_t xfs_qwarncnt_t;
116116
#define XFS_QMOPT_DQREPAIR 0x0001000 /* repair dquot if damaged */
117117
#define XFS_QMOPT_GQUOTA 0x0002000 /* group dquot requested */
118118
#define XFS_QMOPT_ENOSPC 0x0004000 /* enospc instead of edquot (prj) */
119+
#define XFS_QMOPT_DQNEXT 0x0008000 /* return next dquot >= this ID */
119120

120121
/*
121122
* flags to xfs_trans_mod_dquot to indicate which field needs to be

fs/xfs/xfs_dquot.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,56 @@ xfs_qm_dqread(
685685
return error;
686686
}
687687

688+
/*
689+
* Advance to the next id in the current chunk, or if at the
690+
* end of the chunk, skip ahead to first id in next allocated chunk
691+
* using the SEEK_DATA interface.
692+
*/
693+
int
694+
xfs_dq_get_next_id(
695+
xfs_mount_t *mp,
696+
uint type,
697+
xfs_dqid_t *id,
698+
loff_t eof)
699+
{
700+
struct xfs_inode *quotip;
701+
xfs_fsblock_t start;
702+
loff_t offset;
703+
uint lock;
704+
xfs_dqid_t next_id;
705+
int error = 0;
706+
707+
/* Simple advance */
708+
next_id = *id + 1;
709+
710+
/* If new ID is within the current chunk, advancing it sufficed */
711+
if (next_id % mp->m_quotainfo->qi_dqperchunk) {
712+
*id = next_id;
713+
return 0;
714+
}
715+
716+
/* Nope, next_id is now past the current chunk, so find the next one */
717+
start = (xfs_fsblock_t)next_id / mp->m_quotainfo->qi_dqperchunk;
718+
719+
quotip = xfs_quota_inode(mp, type);
720+
lock = xfs_ilock_data_map_shared(quotip);
721+
722+
offset = __xfs_seek_hole_data(VFS_I(quotip), XFS_FSB_TO_B(mp, start),
723+
eof, SEEK_DATA);
724+
if (offset < 0)
725+
error = offset;
726+
727+
xfs_iunlock(quotip, lock);
728+
729+
/* -ENXIO is essentially "no more data" */
730+
if (error)
731+
return (error == -ENXIO ? -ENOENT: error);
732+
733+
/* Convert next data offset back to a quota id */
734+
*id = XFS_B_TO_FSB(mp, offset) * mp->m_quotainfo->qi_dqperchunk;
735+
return 0;
736+
}
737+
688738
/*
689739
* Given the file system, inode OR id, and type (UDQUOT/GDQUOT), return a
690740
* a locked dquot, doing an allocation (if requested) as needed.
@@ -705,6 +755,7 @@ xfs_qm_dqget(
705755
struct xfs_quotainfo *qi = mp->m_quotainfo;
706756
struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
707757
struct xfs_dquot *dqp;
758+
loff_t eof = 0;
708759
int error;
709760

710761
ASSERT(XFS_IS_QUOTA_RUNNING(mp));
@@ -732,6 +783,21 @@ xfs_qm_dqget(
732783
}
733784
#endif
734785

786+
/* Get the end of the quota file if we need it */
787+
if (flags & XFS_QMOPT_DQNEXT) {
788+
struct xfs_inode *quotip;
789+
xfs_fileoff_t last;
790+
uint lock_mode;
791+
792+
quotip = xfs_quota_inode(mp, type);
793+
lock_mode = xfs_ilock_data_map_shared(quotip);
794+
error = xfs_bmap_last_offset(quotip, &last, XFS_DATA_FORK);
795+
xfs_iunlock(quotip, lock_mode);
796+
if (error)
797+
return error;
798+
eof = XFS_FSB_TO_B(mp, last);
799+
}
800+
735801
restart:
736802
mutex_lock(&qi->qi_tree_lock);
737803
dqp = radix_tree_lookup(tree, id);
@@ -745,6 +811,18 @@ xfs_qm_dqget(
745811
goto restart;
746812
}
747813

814+
/* uninit / unused quota found in radix tree, keep looking */
815+
if (flags & XFS_QMOPT_DQNEXT) {
816+
if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
817+
xfs_dqunlock(dqp);
818+
mutex_unlock(&qi->qi_tree_lock);
819+
error = xfs_dq_get_next_id(mp, type, &id, eof);
820+
if (error)
821+
return error;
822+
goto restart;
823+
}
824+
}
825+
748826
dqp->q_nrefs++;
749827
mutex_unlock(&qi->qi_tree_lock);
750828

@@ -771,6 +849,13 @@ xfs_qm_dqget(
771849
if (ip)
772850
xfs_ilock(ip, XFS_ILOCK_EXCL);
773851

852+
/* If we are asked to find next active id, keep looking */
853+
if (error == -ENOENT && (flags & XFS_QMOPT_DQNEXT)) {
854+
error = xfs_dq_get_next_id(mp, type, &id, eof);
855+
if (!error)
856+
goto restart;
857+
}
858+
774859
if (error)
775860
return error;
776861

@@ -821,6 +906,17 @@ xfs_qm_dqget(
821906
qi->qi_dquots++;
822907
mutex_unlock(&qi->qi_tree_lock);
823908

909+
/* If we are asked to find next active id, keep looking */
910+
if (flags & XFS_QMOPT_DQNEXT) {
911+
if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
912+
xfs_qm_dqput(dqp);
913+
error = xfs_dq_get_next_id(mp, type, &id, eof);
914+
if (error)
915+
return error;
916+
goto restart;
917+
}
918+
}
919+
824920
dqret:
825921
ASSERT((ip == NULL) || xfs_isilocked(ip, XFS_ILOCK_EXCL));
826922
trace_xfs_dqget_miss(dqp);

fs/xfs/xfs_qm.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ extern void xfs_qm_dqrele_all_inodes(struct xfs_mount *, uint);
164164

165165
/* quota ops */
166166
extern int xfs_qm_scall_trunc_qfiles(struct xfs_mount *, uint);
167-
extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t,
168-
uint, struct qc_dqblk *);
167+
extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t *,
168+
uint, struct qc_dqblk *, uint);
169169
extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint,
170170
struct qc_dqblk *);
171171
extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint);

fs/xfs/xfs_qm_syscalls.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -635,9 +635,10 @@ xfs_qm_log_quotaoff(
635635
int
636636
xfs_qm_scall_getquota(
637637
struct xfs_mount *mp,
638-
xfs_dqid_t id,
638+
xfs_dqid_t *id,
639639
uint type,
640-
struct qc_dqblk *dst)
640+
struct qc_dqblk *dst,
641+
uint dqget_flags)
641642
{
642643
struct xfs_dquot *dqp;
643644
int error;
@@ -647,7 +648,7 @@ xfs_qm_scall_getquota(
647648
* we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't
648649
* exist, we'll get ENOENT back.
649650
*/
650-
error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp);
651+
error = xfs_qm_dqget(mp, NULL, *id, type, dqget_flags, &dqp);
651652
if (error)
652653
return error;
653654

@@ -660,6 +661,9 @@ xfs_qm_scall_getquota(
660661
goto out_put;
661662
}
662663

664+
/* Fill in the ID we actually read from disk */
665+
*id = be32_to_cpu(dqp->q_core.d_id);
666+
663667
memset(dst, 0, sizeof(*dst));
664668
dst->d_spc_hardlimit =
665669
XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit));
@@ -701,7 +705,7 @@ xfs_qm_scall_getquota(
701705
if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) ||
702706
(XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) ||
703707
(XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) &&
704-
id != 0) {
708+
*id != 0) {
705709
if ((dst->d_space > dst->d_spc_softlimit) &&
706710
(dst->d_spc_softlimit > 0)) {
707711
ASSERT(dst->d_spc_timer != 0);

fs/xfs/xfs_quotaops.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,45 @@ xfs_fs_get_dqblk(
231231
struct qc_dqblk *qdq)
232232
{
233233
struct xfs_mount *mp = XFS_M(sb);
234+
xfs_dqid_t id;
234235

235236
if (!XFS_IS_QUOTA_RUNNING(mp))
236237
return -ENOSYS;
237238
if (!XFS_IS_QUOTA_ON(mp))
238239
return -ESRCH;
239240

240-
return xfs_qm_scall_getquota(mp, from_kqid(&init_user_ns, qid),
241-
xfs_quota_type(qid.type), qdq);
241+
id = from_kqid(&init_user_ns, qid);
242+
return xfs_qm_scall_getquota(mp, &id,
243+
xfs_quota_type(qid.type), qdq, 0);
244+
}
245+
246+
/* Return quota info for active quota >= this qid */
247+
STATIC int
248+
xfs_fs_get_nextdqblk(
249+
struct super_block *sb,
250+
struct kqid *qid,
251+
struct qc_dqblk *qdq)
252+
{
253+
int ret;
254+
struct xfs_mount *mp = XFS_M(sb);
255+
xfs_dqid_t id;
256+
257+
if (!XFS_IS_QUOTA_RUNNING(mp))
258+
return -ENOSYS;
259+
if (!XFS_IS_QUOTA_ON(mp))
260+
return -ESRCH;
261+
262+
id = from_kqid(&init_user_ns, *qid);
263+
ret = xfs_qm_scall_getquota(mp, &id,
264+
xfs_quota_type(qid->type), qdq,
265+
XFS_QMOPT_DQNEXT);
266+
if (ret)
267+
return ret;
268+
269+
/* ID may be different, so convert back what we got */
270+
*qid = make_kqid(current_user_ns(), qid->type, id);
271+
return 0;
272+
242273
}
243274

244275
STATIC int
@@ -267,5 +298,6 @@ const struct quotactl_ops xfs_quotactl_operations = {
267298
.quota_disable = xfs_quota_disable,
268299
.rm_xquota = xfs_fs_rm_xquota,
269300
.get_dqblk = xfs_fs_get_dqblk,
301+
.get_nextdqblk = xfs_fs_get_nextdqblk,
270302
.set_dqblk = xfs_fs_set_dqblk,
271303
};

0 commit comments

Comments
 (0)