summaryrefslogtreecommitdiff
path: root/fs/xfs/xfs_qm.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/xfs_qm.c')
-rw-r--r--fs/xfs/xfs_qm.c94
1 files changed, 59 insertions, 35 deletions
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 91c72b4958ff..9c37599d63c7 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -1130,11 +1130,12 @@ xfs_qm_dqusage_adjust(
struct xfs_mount *mp,
struct xfs_trans *tp,
xfs_ino_t ino,
- void *data)
+ void *need_ilocks)
{
struct xfs_inode *ip;
xfs_qcnt_t nblks;
xfs_filblks_t rtblks = 0; /* total rt blks */
+ uint ilock_flags = 0;
int error;
ASSERT(XFS_IS_QUOTA_RUNNING(mp));
@@ -1146,16 +1147,19 @@ xfs_qm_dqusage_adjust(
if (xfs_is_quota_inode(&mp->m_sb, ino))
return 0;
- /*
- * We don't _need_ to take the ilock EXCL here because quotacheck runs
- * at mount time and therefore nobody will be racing chown/chproj.
- */
+ /* Grab inode and lock it if needed. */
error = xfs_iget(mp, tp, ino, XFS_IGET_DONTCACHE, 0, &ip);
if (error == -EINVAL || error == -ENOENT)
return 0;
if (error)
return error;
+ if (need_ilocks) {
+ ilock_flags = XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED;
+ xfs_ilock(ip, ilock_flags);
+ ilock_flags |= xfs_ilock_data_map_shared(ip);
+ }
+
ASSERT(ip->i_delayed_blks == 0);
if (XFS_IS_REALTIME_INODE(ip)) {
@@ -1206,6 +1210,8 @@ xfs_qm_dqusage_adjust(
}
error0:
+ if (ilock_flags)
+ xfs_iunlock(ip, ilock_flags);
xfs_irele(ip);
return error;
}
@@ -1263,16 +1269,60 @@ out_unlock:
}
/*
+ * Walk the inodes and adjust quota usage. Caller must have previously
+ * zeroed all dquots.
+ */
+int
+xfs_qm_quotacheck_walk_and_flush(
+ struct xfs_mount *mp,
+ bool need_ilocks,
+ struct list_head *buffer_list)
+{
+ int error, error2;
+
+ error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0,
+ !need_ilocks, NULL);
+ if (error)
+ return error;
+
+ /*
+ * We've made all the changes that we need to make incore. Flush them
+ * down to disk buffers if everything was updated successfully.
+ */
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ error = xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_flush_one,
+ buffer_list);
+ }
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ error2 = xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_flush_one,
+ buffer_list);
+ if (!error)
+ error = error2;
+ }
+ if (XFS_IS_PQUOTA_ON(mp)) {
+ error2 = xfs_qm_dquot_walk(mp, XFS_DQ_PROJ, xfs_qm_flush_one,
+ buffer_list);
+ if (!error)
+ error = error2;
+ }
+
+ error2 = xfs_buf_delwri_submit(buffer_list);
+ if (!error)
+ error = error2;
+ return error;
+}
+
+/*
* Walk thru all the filesystem inodes and construct a consistent view
* of the disk quota world. If the quotacheck fails, disable quotas.
*/
STATIC int
xfs_qm_quotacheck(
- xfs_mount_t *mp)
+ struct xfs_mount *mp)
{
- int error, error2;
- uint flags;
+ int error;
LIST_HEAD (buffer_list);
+ uint flags;
struct xfs_inode *uip = mp->m_quotainfo->qi_uquotaip;
struct xfs_inode *gip = mp->m_quotainfo->qi_gquotaip;
struct xfs_inode *pip = mp->m_quotainfo->qi_pquotaip;
@@ -1313,37 +1363,11 @@ xfs_qm_quotacheck(
flags |= XFS_PQUOTA_CHKD;
}
- error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true,
- NULL);
+ error = xfs_qm_quotacheck_walk_and_flush(mp, false, &buffer_list);
if (error)
goto error_return;
/*
- * We've made all the changes that we need to make incore. Flush them
- * down to disk buffers if everything was updated successfully.
- */
- if (XFS_IS_UQUOTA_ON(mp)) {
- error = xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_flush_one,
- &buffer_list);
- }
- if (XFS_IS_GQUOTA_ON(mp)) {
- error2 = xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_flush_one,
- &buffer_list);
- if (!error)
- error = error2;
- }
- if (XFS_IS_PQUOTA_ON(mp)) {
- error2 = xfs_qm_dquot_walk(mp, XFS_DQ_PROJ, xfs_qm_flush_one,
- &buffer_list);
- if (!error)
- error = error2;
- }
-
- error2 = xfs_buf_delwri_submit(&buffer_list);
- if (!error)
- error = error2;
-
- /*
* We can get this error if we couldn't do a dquot allocation inside
* xfs_qm_dqusage_adjust (via bulkstat). We don't care about the
* dirty dquots that might be cached, we just want to get rid of them