@@ -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);
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 <jack@suse.cz> --- fs/ocfs2/super.c | 99 +++++++++++++++++++++++++------------------------------- 1 file changed, 44 insertions(+), 55 deletions(-)