Currently quotas are either disabled or suspended when the filesystem is mounted read only. This results in quota recovery failing when in happens on such mount and as a result quota accounting is wrong. Fix the problem by enabling quotas even for read-only mounts. We just don't start periodic flushing of our local changes to the global quota file since that is pointless for read-only mounts.
Signed-off-by: Jan Kara <j...@suse.cz> --- fs/ocfs2/super.c | 99 +++++++++++++++++++++++++------------------------------- 1 file changed, 44 insertions(+), 55 deletions(-) diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 39b62569e7ff..af4481b98c65 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -135,7 +135,8 @@ static int ocfs2_get_sector(struct super_block *sb, int sect_size); static struct inode *ocfs2_alloc_inode(struct super_block *sb); static void ocfs2_destroy_inode(struct inode *inode); -static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend); +static void ocfs2_enable_quota_sync(struct ocfs2_super *osb); +static void ocfs2_disable_quota_sync(struct ocfs2_super *osb); static int ocfs2_enable_quotas(struct ocfs2_super *osb); static void ocfs2_disable_quotas(struct ocfs2_super *osb); @@ -675,12 +676,9 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) /* We're going to/from readonly mode. */ if ((bool)(*flags & SB_RDONLY) != sb_rdonly(sb)) { - /* Disable quota accounting before remounting RO */ - if (*flags & SB_RDONLY) { - ret = ocfs2_susp_quotas(osb, 0); - if (ret < 0) - goto out; - } + /* Disable quota syncing before remounting RO */ + if (*flags & SB_RDONLY) + ocfs2_disable_quota_sync(osb); /* Lock here so the check of HARD_RO and the potential * setting of SOFT_RO is atomic. */ spin_lock(&osb->osb_lock); @@ -714,21 +712,9 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) trace_ocfs2_remount(sb->s_flags, osb->osb_flags, *flags); unlock_osb: spin_unlock(&osb->osb_lock); - /* Enable quota accounting after remounting RW */ - if (!ret && !(*flags & SB_RDONLY)) { - if (sb_any_quota_suspended(sb)) - ret = ocfs2_susp_quotas(osb, 1); - else - ret = ocfs2_enable_quotas(osb); - if (ret < 0) { - /* Return back changes... */ - spin_lock(&osb->osb_lock); - sb->s_flags |= SB_RDONLY; - osb->osb_flags |= OCFS2_OSB_SOFT_RO; - spin_unlock(&osb->osb_lock); - goto out; - } - } + /* Enable quota syncing after remounting RW */ + if (!ret && !(*flags & SB_RDONLY)) + ocfs2_enable_quota_sync(osb); } if (!ret) { @@ -902,38 +888,33 @@ static int ocfs2_verify_userspace_stack(struct ocfs2_super *osb, return 0; } -static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend) +static void ocfs2_enable_quota_sync(struct ocfs2_super *osb) { int type; struct super_block *sb = osb->sb; - unsigned int feature[OCFS2_MAXQUOTAS] = { - OCFS2_FEATURE_RO_COMPAT_USRQUOTA, - OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; - int status = 0; struct ocfs2_mem_dqinfo *oinfo; for (type = 0; type < OCFS2_MAXQUOTAS; type++) { - if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) + if (!sb_has_quota_active(sb, type)) continue; oinfo = sb_dqinfo(sb, type)->dqi_priv; - if (unsuspend) { - status = dquot_resume(sb, type); - if (status < 0) - break; - schedule_delayed_work(&oinfo->dqi_sync_work, - msecs_to_jiffies(oinfo->dqi_syncms)); - } else { - /* Cancel periodic syncing before suspending */ - cancel_delayed_work_sync(&oinfo->dqi_sync_work); - status = dquot_suspend(sb, type); - if (status < 0) - break; - } + schedule_delayed_work(&oinfo->dqi_sync_work, + msecs_to_jiffies(oinfo->dqi_syncms)); + } +} + +static void ocfs2_disable_quota_sync(struct ocfs2_super *osb) +{ + int type; + struct super_block *sb = osb->sb; + struct ocfs2_mem_dqinfo *oinfo; + + for (type = 0; type < OCFS2_MAXQUOTAS; type++) { + if (!sb_has_quota_active(sb, type)) + continue; + oinfo = sb_dqinfo(sb, type)->dqi_priv; + cancel_delayed_work_sync(&oinfo->dqi_sync_work); } - if (status < 0) - mlog(ML_ERROR, "Failed to suspend/unsuspend quotas on " - "remount (error = %d).\n", status); - return status; } static int ocfs2_enable_quotas(struct ocfs2_super *osb) @@ -946,11 +927,18 @@ static int ocfs2_enable_quotas(struct ocfs2_super *osb) unsigned int ino[OCFS2_MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, LOCAL_GROUP_QUOTA_SYSTEM_INODE }; - struct ocfs2_mem_dqinfo *oinfo; int status; int type; + bool ro; sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NEGATIVE_USAGE; + /* + * We temporarily switch sb to read-write to allow quota setup to pass + * cleanly + */ + ro = sb_rdonly(sb); + if (ro) + sb->s_flags &= ~SB_RDONLY; for (type = 0; type < OCFS2_MAXQUOTAS; type++) { if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) continue; @@ -964,16 +952,19 @@ static int ocfs2_enable_quotas(struct ocfs2_super *osb) DQUOT_USAGE_ENABLED); if (status < 0) goto out_quota_off; - oinfo = sb_dqinfo(sb, type)->dqi_priv; - schedule_delayed_work(&oinfo->dqi_sync_work, - msecs_to_jiffies(oinfo->dqi_syncms)); } + if (!ro) + ocfs2_enable_quota_sync(osb); + else + sb->s_flags |= SB_RDONLY; for (type = 0; type < OCFS2_MAXQUOTAS; type++) iput(inode[type]); return 0; out_quota_off: ocfs2_disable_quotas(osb); + if (ro) + sb->s_flags |= SB_RDONLY; for (type = 0; type < OCFS2_MAXQUOTAS; type++) iput(inode[type]); mlog_errno(status); @@ -985,15 +976,13 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb) int type; struct inode *inode; struct super_block *sb = osb->sb; - struct ocfs2_mem_dqinfo *oinfo; /* We mostly ignore errors in this function because there's not much * we can do when we see them */ + ocfs2_disable_quota_sync(osb); for (type = 0; type < OCFS2_MAXQUOTAS; type++) { if (!sb_has_quota_loaded(sb, type)) continue; - oinfo = sb_dqinfo(sb, type)->dqi_priv; - cancel_delayed_work_sync(&oinfo->dqi_sync_work); inode = igrab(sb->s_dquot.files[type]); /* Turn off quotas. This will remove all dquot structures from * memory and so they will be automatically synced to global @@ -1185,7 +1174,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) /* Now we can initialize quotas because we can afford to wait * for cluster locks recovery now. That also means that truncation * log recovery can happen but that waits for proper quota setup */ - if (!sb_rdonly(sb)) { + if (!ocfs2_is_hard_readonly(osb)) { status = ocfs2_enable_quotas(osb); if (status < 0) { /* We have to err-out specially here because @@ -1195,9 +1184,9 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) wake_up(&osb->osb_mount_event); return status; } - } - ocfs2_complete_quota_recovery(osb); + ocfs2_complete_quota_recovery(osb); + } /* Now we wake up again for processes waiting for quotas */ atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS); -- 2.13.6 _______________________________________________ Ocfs2-devel mailing list Ocfs2-devel@oss.oracle.com https://oss.oracle.com/mailman/listinfo/ocfs2-devel