diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2024-06-07 16:54:21 +0300 |
---|---|---|
committer | Andreas Gruenbacher <agruenba@redhat.com> | 2024-06-20 17:38:15 +0300 |
commit | d9a75a60699dedaac17d2b5170bb2e3cdc03481e (patch) | |
tree | a554fea688629faec66b724cde4f40ee73019ecc /fs/gfs2 | |
parent | 8d89e068deccb4f34d412df4042f37a75e126259 (diff) | |
download | linux-d9a75a60699dedaac17d2b5170bb2e3cdc03481e.tar.xz |
gfs2: Be more careful with the quota sync generation
The quota sync generation is only ever updated under sd_quota_sync_mutex
by gfs2_quota_sync(), but its current value is fetched ouside of that
mutex, so use WRITE_ONCE() and READ_ONCE() when accessing it without
holding that mutex.
Pass the current sync generation to do_sync() from its callers to ensure
that we're not recording the wrong generation when the syncing is
done. Also, make sure that qd->qd_sync_gen only ever moves forward.
In gfs2_quota_sync(), only write the new sync generation when we know
that there are changes. This eliminates the need for function
sd_changed(), which we will remove in the next commit.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/quota.c | 27 |
1 files changed, 19 insertions, 8 deletions
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 931a133a5f96..4f2caa06ca93 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -891,7 +891,8 @@ static int gfs2_adjust_quota(struct gfs2_sbd *sdp, loff_t loc, return err; } -static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) +static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda, + u64 sync_gen) { struct gfs2_sbd *sdp = (*qda)->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); @@ -982,8 +983,13 @@ out_dq: gfs2_log_flush(ip->i_gl->gl_name.ln_sbd, ip->i_gl, GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_DO_SYNC); if (!error) { - for (x = 0; x < num_qd; x++) - qda[x]->qd_sync_gen = sdp->sd_quota_sync_gen; + for (x = 0; x < num_qd; x++) { + qd = qda[x]; + spin_lock(&qd->qd_lockref.lock); + if (qd->qd_sync_gen < sync_gen) + qd->qd_sync_gen = sync_gen; + spin_unlock(&qd->qd_lockref.lock); + } } return error; } @@ -1177,7 +1183,9 @@ void gfs2_quota_unlock(struct gfs2_inode *ip) } if (count) { - do_sync(count, qda); + u64 sync_gen = READ_ONCE(sdp->sd_quota_sync_gen); + + do_sync(count, qda, sync_gen); for (x = 0; x < count; x++) qd_unlock(qda[x]); } @@ -1323,6 +1331,7 @@ int gfs2_quota_sync(struct super_block *sb, int type) struct gfs2_sbd *sdp = sb->s_fs_info; struct gfs2_quota_data **qda; unsigned int max_qd = PAGE_SIZE / sizeof(struct gfs2_holder); + u64 sync_gen; int error = 0; if (sb_rdonly(sdp->sd_vfs)) @@ -1335,7 +1344,7 @@ int gfs2_quota_sync(struct super_block *sb, int type) return -ENOMEM; mutex_lock(&sdp->sd_quota_sync_mutex); - sdp->sd_quota_sync_gen++; + sync_gen = sdp->sd_quota_sync_gen + 1; do { struct gfs2_quota_data *iter; @@ -1344,7 +1353,7 @@ int gfs2_quota_sync(struct super_block *sb, int type) spin_lock(&qd_lock); list_for_each_entry(iter, &sdp->sd_quota_list, qd_list) { - if (qd_grab_sync(sdp, iter, sdp->sd_quota_sync_gen)) { + if (qd_grab_sync(sdp, iter, sync_gen)) { qda[num_qd++] = iter; if (num_qd == max_qd) break; @@ -1365,8 +1374,10 @@ int gfs2_quota_sync(struct super_block *sb, int type) break; } - if (!error) - error = do_sync(num_qd, qda); + if (!error) { + WRITE_ONCE(sdp->sd_quota_sync_gen, sync_gen); + error = do_sync(num_qd, qda, sync_gen); + } for (x = 0; x < num_qd; x++) qd_unlock(qda[x]); |