* [Cluster-devel] [GFS2 PATCH 02/12] gfs2: Fix lru_count going negative
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
@ 2019-05-07 20:31 ` Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 03/12] gfs2: clean_journal improperly set sd_log_flush_head Andreas Gruenbacher
` (9 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:31 UTC (permalink / raw)
To: cluster-devel.redhat.com
From: Ross Lagerwall <ross.lagerwall@citrix.com>
Under certain conditions, lru_count may drop below zero resulting in
a large amount of log spam like this:
vmscan: shrink_slab: gfs2_dump_glock+0x3b0/0x630 [gfs2] \
negative objects to delete nr=-1
This happens as follows:
1) A glock is moved from lru_list to the dispose list and lru_count is
decremented.
2) The dispose function calls cond_resched() and drops the lru lock.
3) Another thread takes the lru lock and tries to add the same glock to
lru_list, checking if the glock is on an lru list.
4) It is on a list (actually the dispose list) and so it avoids
incrementing lru_count.
5) The glock is moved to lru_list.
5) The original thread doesn't dispose it because it has been re-added
to the lru list but the lru_count has still decreased by one.
Fix by checking if the LRU flag is set on the glock rather than checking
if the glock is on some list and rearrange the code so that the LRU flag
is added/removed precisely when the glock is added/removed from lru_list.
Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/glock.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index d32964cd1117..e4f6d39500bc 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -183,15 +183,19 @@ static int demote_ok(const struct gfs2_glock *gl)
void gfs2_glock_add_to_lru(struct gfs2_glock *gl)
{
+ if (!(gl->gl_ops->go_flags & GLOF_LRU))
+ return;
+
spin_lock(&lru_lock);
- if (!list_empty(&gl->gl_lru))
- list_del_init(&gl->gl_lru);
- else
+ list_del(&gl->gl_lru);
+ list_add_tail(&gl->gl_lru, &lru_list);
+
+ if (!test_bit(GLF_LRU, &gl->gl_flags)) {
+ set_bit(GLF_LRU, &gl->gl_flags);
atomic_inc(&lru_count);
+ }
- list_add_tail(&gl->gl_lru, &lru_list);
- set_bit(GLF_LRU, &gl->gl_flags);
spin_unlock(&lru_lock);
}
@@ -201,7 +205,7 @@ static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl)
return;
spin_lock(&lru_lock);
- if (!list_empty(&gl->gl_lru)) {
+ if (test_bit(GLF_LRU, &gl->gl_flags)) {
list_del_init(&gl->gl_lru);
atomic_dec(&lru_count);
clear_bit(GLF_LRU, &gl->gl_flags);
@@ -1159,8 +1163,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
!test_bit(GLF_DEMOTE, &gl->gl_flags))
fast_path = 1;
}
- if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl) &&
- (glops->go_flags & GLOF_LRU))
+ if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl))
gfs2_glock_add_to_lru(gl);
trace_gfs2_glock_queue(gh, 0);
@@ -1456,6 +1459,7 @@ __acquires(&lru_lock)
if (!spin_trylock(&gl->gl_lockref.lock)) {
add_back_to_lru:
list_add(&gl->gl_lru, &lru_list);
+ set_bit(GLF_LRU, &gl->gl_flags);
atomic_inc(&lru_count);
continue;
}
@@ -1463,7 +1467,6 @@ __acquires(&lru_lock)
spin_unlock(&gl->gl_lockref.lock);
goto add_back_to_lru;
}
- clear_bit(GLF_LRU, &gl->gl_flags);
gl->gl_lockref.count++;
if (demote_ok(gl))
handle_callback(gl, LM_ST_UNLOCKED, 0, false);
@@ -1498,6 +1501,7 @@ static long gfs2_scan_glock_lru(int nr)
if (!test_bit(GLF_LOCK, &gl->gl_flags)) {
list_move(&gl->gl_lru, &dispose);
atomic_dec(&lru_count);
+ clear_bit(GLF_LRU, &gl->gl_flags);
freed++;
continue;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 03/12] gfs2: clean_journal improperly set sd_log_flush_head
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 02/12] gfs2: Fix lru_count going negative Andreas Gruenbacher
@ 2019-05-07 20:31 ` Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 04/12] gfs2: Fix occasional glock use-after-free Andreas Gruenbacher
` (8 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:31 UTC (permalink / raw)
To: cluster-devel.redhat.com
From: Bob Peterson <rpeterso@redhat.com>
This patch fixes regressions in 588bff95c94efc05f9e1a0b19015c9408ed7c0ef.
Due to that patch, function clean_journal was setting the value of
sd_log_flush_head, but that's only valid if it is replaying the node's
own journal. If it's replaying another node's journal, that's completely
wrong and will lead to multiple problems. This patch tries to clean up
the mess by passing the value of the logical journal block number into
gfs2_write_log_header so the function can treat non-owned journals
generically. For the local journal, the journal extent map is used for
best performance. For other nodes from other journals, new function
gfs2_lblk_to_dblk is called to figure it out using gfs2_iomap_get.
This patch also tries to establish more consistency when passing journal
block parameters by changing several unsigned int types to a consistent
u32.
Fixes: 588bff95c94e ("GFS2: Reduce code redundancy writing log headers")
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Reviewed-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/bmap.c | 26 ++++++++++++++++++++++++++
fs/gfs2/bmap.h | 1 +
fs/gfs2/incore.h | 2 +-
fs/gfs2/log.c | 24 ++++++++++++++++--------
fs/gfs2/log.h | 3 ++-
fs/gfs2/lops.c | 6 +++---
fs/gfs2/lops.h | 2 +-
fs/gfs2/recovery.c | 10 ++++++----
fs/gfs2/recovery.h | 2 +-
9 files changed, 57 insertions(+), 19 deletions(-)
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 02b2646d84b3..e95b33b65d89 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -925,6 +925,32 @@ static int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length,
goto out;
}
+/**
+ * gfs2_lblk_to_dblk - convert logical block to disk block
+ * @inode: the inode of the file we're mapping
+ * @lblock: the block relative to the start of the file
+ * @dblock: the returned dblock, if no error
+ *
+ * This function maps a single block from a file logical block (relative to
+ * the start of the file) to a file system absolute block using iomap.
+ *
+ * Returns: the absolute file system block, or an error
+ */
+int gfs2_lblk_to_dblk(struct inode *inode, u32 lblock, u64 *dblock)
+{
+ struct iomap iomap = { };
+ struct metapath mp = { .mp_aheight = 1, };
+ loff_t pos = (loff_t)lblock << inode->i_blkbits;
+ int ret;
+
+ ret = gfs2_iomap_get(inode, pos, i_blocksize(inode), 0, &iomap, &mp);
+ release_metapath(&mp);
+ if (ret == 0)
+ *dblock = iomap.addr >> inode->i_blkbits;
+
+ return ret;
+}
+
static int gfs2_write_lock(struct inode *inode)
{
struct gfs2_inode *ip = GFS2_I(inode);
diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h
index 6b18fb323f0a..19a1fd772c61 100644
--- a/fs/gfs2/bmap.h
+++ b/fs/gfs2/bmap.h
@@ -64,5 +64,6 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd);
extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd);
extern int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length);
+extern int gfs2_lblk_to_dblk(struct inode *inode, u32 lblock, u64 *dblock);
#endif /* __BMAP_DOT_H__ */
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index cdf07b408f54..86840a70ee1a 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -535,7 +535,7 @@ struct gfs2_jdesc {
unsigned long jd_flags;
#define JDF_RECOVERY 1
unsigned int jd_jid;
- unsigned int jd_blocks;
+ u32 jd_blocks;
int jd_recover_error;
/* Replay stuff */
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index b8830fda51e8..ebbc68dca145 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -666,11 +666,12 @@ void gfs2_write_revokes(struct gfs2_sbd *sdp)
}
/**
- * write_log_header - Write a journal log header buffer at sd_log_flush_head
+ * gfs2_write_log_header - Write a journal log header buffer at lblock
* @sdp: The GFS2 superblock
* @jd: journal descriptor of the journal to which we are writing
* @seq: sequence number
* @tail: tail of the log
+ * @lblock: value for lh_blkno (block number relative to start of journal)
* @flags: log header flags GFS2_LOG_HEAD_*
* @op_flags: flags to pass to the bio
*
@@ -678,7 +679,8 @@ void gfs2_write_revokes(struct gfs2_sbd *sdp)
*/
void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
- u64 seq, u32 tail, u32 flags, int op_flags)
+ u64 seq, u32 tail, u32 lblock, u32 flags,
+ int op_flags)
{
struct gfs2_log_header *lh;
u32 hash, crc;
@@ -686,7 +688,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local;
struct timespec64 tv;
struct super_block *sb = sdp->sd_vfs;
- u64 addr;
+ u64 dblock;
lh = page_address(page);
clear_page(lh);
@@ -699,15 +701,21 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
lh->lh_sequence = cpu_to_be64(seq);
lh->lh_flags = cpu_to_be32(flags);
lh->lh_tail = cpu_to_be32(tail);
- lh->lh_blkno = cpu_to_be32(sdp->sd_log_flush_head);
+ lh->lh_blkno = cpu_to_be32(lblock);
hash = ~crc32(~0, lh, LH_V1_SIZE);
lh->lh_hash = cpu_to_be32(hash);
ktime_get_coarse_real_ts64(&tv);
lh->lh_nsec = cpu_to_be32(tv.tv_nsec);
lh->lh_sec = cpu_to_be64(tv.tv_sec);
- addr = gfs2_log_bmap(sdp);
- lh->lh_addr = cpu_to_be64(addr);
+ if (!list_empty(&jd->extent_list))
+ dblock = gfs2_log_bmap(sdp);
+ else {
+ int ret = gfs2_lblk_to_dblk(jd->jd_inode, lblock, &dblock);
+ if (gfs2_assert_withdraw(sdp, ret == 0))
+ return;
+ }
+ lh->lh_addr = cpu_to_be64(dblock);
lh->lh_jinode = cpu_to_be64(GFS2_I(jd->jd_inode)->i_no_addr);
/* We may only write local statfs, quota, etc., when writing to our
@@ -732,7 +740,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
sb->s_blocksize - LH_V1_SIZE - 4);
lh->lh_crc = cpu_to_be32(crc);
- gfs2_log_write(sdp, page, sb->s_blocksize, 0, addr);
+ gfs2_log_write(sdp, page, sb->s_blocksize, 0, dblock);
gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE, op_flags);
log_flush_wait(sdp);
}
@@ -761,7 +769,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags)
}
sdp->sd_log_idle = (tail == sdp->sd_log_flush_head);
gfs2_write_log_header(sdp, sdp->sd_jdesc, sdp->sd_log_sequence++, tail,
- flags, op_flags);
+ sdp->sd_log_flush_head, flags, op_flags);
if (sdp->sd_log_tail != tail)
log_pull_tail(sdp, tail);
diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h
index 1bc9bd444b28..86d07d436cdf 100644
--- a/fs/gfs2/log.h
+++ b/fs/gfs2/log.h
@@ -70,7 +70,8 @@ extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct,
extern void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks);
extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks);
extern void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
- u64 seq, u32 tail, u32 flags, int op_flags);
+ u64 seq, u32 tail, u32 lblock, u32 flags,
+ int op_flags);
extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
u32 type);
extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 8722c60b11fe..aef21b6a608f 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -530,7 +530,7 @@ static void buf_lo_before_scan(struct gfs2_jdesc *jd,
jd->jd_replayed_blocks = 0;
}
-static int buf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
+static int buf_lo_scan_elements(struct gfs2_jdesc *jd, u32 start,
struct gfs2_log_descriptor *ld, __be64 *ptr,
int pass)
{
@@ -685,7 +685,7 @@ static void revoke_lo_before_scan(struct gfs2_jdesc *jd,
jd->jd_replay_tail = head->lh_tail;
}
-static int revoke_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
+static int revoke_lo_scan_elements(struct gfs2_jdesc *jd, u32 start,
struct gfs2_log_descriptor *ld, __be64 *ptr,
int pass)
{
@@ -767,7 +767,7 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr
gfs2_before_commit(sdp, limit, nbuf, &tr->tr_databuf, 1);
}
-static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
+static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, u32 start,
struct gfs2_log_descriptor *ld,
__be64 *ptr, int pass)
{
diff --git a/fs/gfs2/lops.h b/fs/gfs2/lops.h
index 711c4d89c063..4e81742de7a0 100644
--- a/fs/gfs2/lops.h
+++ b/fs/gfs2/lops.h
@@ -77,7 +77,7 @@ static inline void lops_before_scan(struct gfs2_jdesc *jd,
gfs2_log_ops[x]->lo_before_scan(jd, head, pass);
}
-static inline int lops_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
+static inline int lops_scan_elements(struct gfs2_jdesc *jd, u32 start,
struct gfs2_log_descriptor *ld,
__be64 *ptr,
unsigned int pass)
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index 2dac43065382..fa575d1676b9 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -316,7 +316,7 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
* Returns: errno
*/
-static int foreach_descriptor(struct gfs2_jdesc *jd, unsigned int start,
+static int foreach_descriptor(struct gfs2_jdesc *jd, u32 start,
unsigned int end, int pass)
{
struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
@@ -386,10 +386,12 @@ static void clean_journal(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head)
{
struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+ u32 lblock = head->lh_blkno;
- sdp->sd_log_flush_head = head->lh_blkno;
- gfs2_replay_incr_blk(jd, &sdp->sd_log_flush_head);
- gfs2_write_log_header(sdp, jd, head->lh_sequence + 1, 0,
+ gfs2_replay_incr_blk(jd, &lblock);
+ if (jd->jd_jid == sdp->sd_lockstruct.ls_jid)
+ sdp->sd_log_flush_head = lblock;
+ gfs2_write_log_header(sdp, jd, head->lh_sequence + 1, 0, lblock,
GFS2_LOG_HEAD_UNMOUNT | GFS2_LOG_HEAD_RECOVERY,
REQ_PREFLUSH | REQ_FUA | REQ_META | REQ_SYNC);
}
diff --git a/fs/gfs2/recovery.h b/fs/gfs2/recovery.h
index 11d81248be85..5932d4b6f43e 100644
--- a/fs/gfs2/recovery.h
+++ b/fs/gfs2/recovery.h
@@ -14,7 +14,7 @@
extern struct workqueue_struct *gfs_recovery_wq;
-static inline void gfs2_replay_incr_blk(struct gfs2_jdesc *jd, unsigned int *blk)
+static inline void gfs2_replay_incr_blk(struct gfs2_jdesc *jd, u32 *blk)
{
if (++*blk == jd->jd_blocks)
*blk = 0;
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 04/12] gfs2: Fix occasional glock use-after-free
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 02/12] gfs2: Fix lru_count going negative Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 03/12] gfs2: clean_journal improperly set sd_log_flush_head Andreas Gruenbacher
@ 2019-05-07 20:31 ` Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 05/12] gfs2: Replace gl_revokes with a GLF flag Andreas Gruenbacher
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:31 UTC (permalink / raw)
To: cluster-devel.redhat.com
This patch has to do with the life cycle of glocks and buffers. When
gfs2 metadata or journaled data is queued to be written, a gfs2_bufdata
object is assigned to track the buffer, and that is queued to various
lists, including the glock's gl_ail_list to indicate it's on the active
items list. Once the page associated with the buffer has been written,
it is removed from the ail list, but its life isn't over until a revoke
has been successfully written.
So after the block is written, its bufdata object is moved from the
glock's gl_ail_list to a file-system-wide list of pending revokes,
sd_log_le_revoke. At that point the glock still needs to track how many
revokes it contributed to that list (in gl_revokes) so that things like
glock go_sync can ensure all the metadata has been not only written, but
also revoked before the glock is granted to a different node. This is
to guarantee journal replay doesn't replay the block once the glock has
been granted to another node.
Ross Lagerwall recently discovered a race in which an inode could be
evicted, and its glock freed after its ail list had been synced, but
while it still had unwritten revokes on the sd_log_le_revoke list. The
evict decremented the glock reference count to zero, which allowed the
glock to be freed. After the revoke was written, function
revoke_lo_after_commit tried to adjust the glock's gl_revokes counter
and clear its GLF_LFLUSH flag, at which time it referenced the freed
glock.
This patch fixes the problem by incrementing the glock reference count
in gfs2_add_revoke when the glock's first bufdata object is moved from
the glock to the global revokes list. Later, when the glock's last such
bufdata object is freed, the reference count is decremented. This
guarantees that whichever process finishes last (the revoke writing or
the evict) will properly free the glock, and neither will reference the
glock after it has been freed.
Reported-by: Ross Lagerwall <ross.lagerwall@citrix.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
---
fs/gfs2/glock.c | 1 +
fs/gfs2/log.c | 3 ++-
fs/gfs2/lops.c | 6 ++++--
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index e4f6d39500bc..71c28ff98b56 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -140,6 +140,7 @@ void gfs2_glock_free(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+ BUG_ON(atomic_read(&gl->gl_revokes));
rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms);
smp_mb();
wake_up_glock(gl);
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index ebbc68dca145..7ba94b66c91b 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -606,7 +606,8 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
gfs2_remove_from_ail(bd); /* drops ref on bh */
bd->bd_bh = NULL;
sdp->sd_log_num_revoke++;
- atomic_inc(&gl->gl_revokes);
+ if (atomic_inc_return(&gl->gl_revokes) == 1)
+ gfs2_glock_hold(gl);
set_bit(GLF_LFLUSH, &gl->gl_flags);
list_add(&bd->bd_list, &sdp->sd_log_le_revoke);
}
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index aef21b6a608f..b4f0a6a3ba59 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -669,8 +669,10 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
bd = list_entry(head->next, struct gfs2_bufdata, bd_list);
list_del_init(&bd->bd_list);
gl = bd->bd_gl;
- atomic_dec(&gl->gl_revokes);
- clear_bit(GLF_LFLUSH, &gl->gl_flags);
+ if (atomic_dec_return(&gl->gl_revokes) == 0) {
+ clear_bit(GLF_LFLUSH, &gl->gl_flags);
+ gfs2_glock_queue_put(gl);
+ }
kmem_cache_free(gfs2_bufdata_cachep, bd);
}
}
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 05/12] gfs2: Replace gl_revokes with a GLF flag
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (2 preceding siblings ...)
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 04/12] gfs2: Fix occasional glock use-after-free Andreas Gruenbacher
@ 2019-05-07 20:31 ` Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 06/12] gfs2: Remove misleading comments in gfs2_evict_inode Andreas Gruenbacher
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:31 UTC (permalink / raw)
To: cluster-devel.redhat.com
From: Bob Peterson <rpeterso@redhat.com>
The gl_revokes value determines how many outstanding revokes a glock has
on the superblock revokes list; this is used to avoid unnecessary log
flushes. However, gl_revokes is only ever tested for being zero, and it's
only decremented in revoke_lo_after_commit, which removes all revokes
from the list, so we know that the gl_revoke values of all the glocks on
the list will reach zero. Therefore, we can replace gl_revokes with a
bit flag. This saves an atomic counter in struct gfs2_glock.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/glock.c | 4 ++--
fs/gfs2/incore.h | 2 +-
fs/gfs2/log.c | 4 +++-
fs/gfs2/lops.c | 33 ++++++++++++++++++++++++---------
fs/gfs2/main.c | 1 -
fs/gfs2/super.c | 2 +-
6 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 71c28ff98b56..15c605cfcfc8 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -140,7 +140,7 @@ void gfs2_glock_free(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
- BUG_ON(atomic_read(&gl->gl_revokes));
+ BUG_ON(test_bit(GLF_REVOKES, &gl->gl_flags));
rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms);
smp_mb();
wake_up_glock(gl);
@@ -1801,7 +1801,7 @@ void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl)
state2str(gl->gl_target),
state2str(gl->gl_demote_state), dtime,
atomic_read(&gl->gl_ail_count),
- atomic_read(&gl->gl_revokes),
+ test_bit(GLF_REVOKES, &gl->gl_flags) ? 1 : 0,
(int)gl->gl_lockref.count, gl->gl_hold_time);
list_for_each_entry(gh, &gl->gl_holders, gh_list)
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 86840a70ee1a..6a94b094a904 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -345,6 +345,7 @@ enum {
GLF_OBJECT = 14, /* Used only for tracing */
GLF_BLOCKING = 15,
GLF_INODE_CREATING = 16, /* Inode creation occurring */
+ GLF_REVOKES = 17, /* Glock has revokes in queue */
};
struct gfs2_glock {
@@ -374,7 +375,6 @@ struct gfs2_glock {
struct list_head gl_lru;
struct list_head gl_ail_list;
atomic_t gl_ail_count;
- atomic_t gl_revokes;
struct delayed_work gl_work;
union {
/* For inode and iopen glocks only */
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index 7ba94b66c91b..d55315a46ece 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -606,8 +606,10 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
gfs2_remove_from_ail(bd); /* drops ref on bh */
bd->bd_bh = NULL;
sdp->sd_log_num_revoke++;
- if (atomic_inc_return(&gl->gl_revokes) == 1)
+ if (!test_bit(GLF_REVOKES, &gl->gl_flags)) {
+ set_bit(GLF_REVOKES, &gl->gl_flags);
gfs2_glock_hold(gl);
+ }
set_bit(GLF_LFLUSH, &gl->gl_flags);
list_add(&bd->bd_list, &sdp->sd_log_le_revoke);
}
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index b4f0a6a3ba59..2fd61853ba63 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -662,19 +662,34 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
struct list_head *head = &sdp->sd_log_le_revoke;
- struct gfs2_bufdata *bd;
- struct gfs2_glock *gl;
+ struct gfs2_bufdata *bd, *tmp;
- while (!list_empty(head)) {
- bd = list_entry(head->next, struct gfs2_bufdata, bd_list);
- list_del_init(&bd->bd_list);
- gl = bd->bd_gl;
- if (atomic_dec_return(&gl->gl_revokes) == 0) {
- clear_bit(GLF_LFLUSH, &gl->gl_flags);
- gfs2_glock_queue_put(gl);
+ /*
+ * Glocks can be referenced repeatedly on the revoke list, but the list
+ * only holds one reference. All glocks on the list will have the
+ * GLF_REVOKES flag set initially.
+ */
+
+ list_for_each_entry_safe(bd, tmp, head, bd_list) {
+ struct gfs2_glock *gl = bd->bd_gl;
+
+ if (test_bit(GLF_REVOKES, &gl->gl_flags)) {
+ /* Keep each glock on the list exactly once. */
+ clear_bit(GLF_REVOKES, &gl->gl_flags);
+ continue;
}
+ list_del(&bd->bd_list);
+ kmem_cache_free(gfs2_bufdata_cachep, bd);
+ }
+ list_for_each_entry_safe(bd, tmp, head, bd_list) {
+ struct gfs2_glock *gl = bd->bd_gl;
+
+ list_del(&bd->bd_list);
kmem_cache_free(gfs2_bufdata_cachep, bd);
+ clear_bit(GLF_LFLUSH, &gl->gl_flags);
+ gfs2_glock_queue_put(gl);
}
+ /* the list is empty now */
}
static void revoke_lo_before_scan(struct gfs2_jdesc *jd,
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index 136484ef35d3..c700738de1f7 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -59,7 +59,6 @@ static void gfs2_init_glock_once(void *foo)
INIT_LIST_HEAD(&gl->gl_lru);
INIT_LIST_HEAD(&gl->gl_ail_list);
atomic_set(&gl->gl_ail_count, 0);
- atomic_set(&gl->gl_revokes, 0);
}
static void gfs2_init_gl_aspace_once(void *foo)
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index ca71163ff7cf..e20fa55715e2 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -1474,7 +1474,7 @@ static void gfs2_final_release_pages(struct gfs2_inode *ip)
truncate_inode_pages(gfs2_glock2aspace(ip->i_gl), 0);
truncate_inode_pages(&inode->i_data, 0);
- if (atomic_read(&gl->gl_revokes) == 0) {
+ if (!test_bit(GLF_REVOKES, &gl->gl_flags)) {
clear_bit(GLF_LFLUSH, &gl->gl_flags);
clear_bit(GLF_DIRTY, &gl->gl_flags);
}
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 06/12] gfs2: Remove misleading comments in gfs2_evict_inode
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (3 preceding siblings ...)
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 05/12] gfs2: Replace gl_revokes with a GLF flag Andreas Gruenbacher
@ 2019-05-07 20:31 ` Andreas Gruenbacher
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 07/12] gfs2: Remove unnecessary extern declarations Andreas Gruenbacher
` (5 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:31 UTC (permalink / raw)
To: cluster-devel.redhat.com
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/super.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index e20fa55715e2..a6a325b2a78b 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -1630,8 +1630,6 @@ static void gfs2_evict_inode(struct inode *inode)
goto out_truncate;
}
- /* Case 1 starts here */
-
if (S_ISDIR(inode->i_mode) &&
(ip->i_diskflags & GFS2_DIF_EXHASH)) {
error = gfs2_dir_exhash_dealloc(ip);
@@ -1670,7 +1668,6 @@ static void gfs2_evict_inode(struct inode *inode)
write_inode_now(inode, 1);
gfs2_ail_flush(ip->i_gl, 0);
- /* Case 2 starts here */
error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks);
if (error)
goto out_unlock;
@@ -1680,7 +1677,6 @@ static void gfs2_evict_inode(struct inode *inode)
gfs2_trans_end(sdp);
out_unlock:
- /* Error path for case 1 */
if (gfs2_rs_active(&ip->i_res))
gfs2_rs_deltree(&ip->i_res);
@@ -1699,7 +1695,6 @@ static void gfs2_evict_inode(struct inode *inode)
if (error && error != GLR_TRYFAILED && error != -EROFS)
fs_warn(sdp, "gfs2_evict_inode: %d\n", error);
out:
- /* Case 3 starts here */
truncate_inode_pages_final(&inode->i_data);
gfs2_rsqa_delete(ip, NULL);
gfs2_ordered_del_inode(ip);
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 07/12] gfs2: Remove unnecessary extern declarations
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (4 preceding siblings ...)
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 06/12] gfs2: Remove misleading comments in gfs2_evict_inode Andreas Gruenbacher
@ 2019-05-07 20:31 ` Andreas Gruenbacher
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 08/12] gfs2: Rename sd_log_le_{revoke, ordered} Andreas Gruenbacher
` (4 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:31 UTC (permalink / raw)
To: cluster-devel.redhat.com
Make log operations statuc; they are only used locally.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/lops.c | 6 +++---
fs/gfs2/lops.h | 5 -----
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 2fd61853ba63..6c1ec6c60639 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -870,7 +870,7 @@ static void databuf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
}
-const struct gfs2_log_operations gfs2_buf_lops = {
+static const struct gfs2_log_operations gfs2_buf_lops = {
.lo_before_commit = buf_lo_before_commit,
.lo_after_commit = buf_lo_after_commit,
.lo_before_scan = buf_lo_before_scan,
@@ -879,7 +879,7 @@ const struct gfs2_log_operations gfs2_buf_lops = {
.lo_name = "buf",
};
-const struct gfs2_log_operations gfs2_revoke_lops = {
+static const struct gfs2_log_operations gfs2_revoke_lops = {
.lo_before_commit = revoke_lo_before_commit,
.lo_after_commit = revoke_lo_after_commit,
.lo_before_scan = revoke_lo_before_scan,
@@ -888,7 +888,7 @@ const struct gfs2_log_operations gfs2_revoke_lops = {
.lo_name = "revoke",
};
-const struct gfs2_log_operations gfs2_databuf_lops = {
+static const struct gfs2_log_operations gfs2_databuf_lops = {
.lo_before_commit = databuf_lo_before_commit,
.lo_after_commit = databuf_lo_after_commit,
.lo_scan_elements = databuf_lo_scan_elements,
diff --git a/fs/gfs2/lops.h b/fs/gfs2/lops.h
index 4e81742de7a0..320fbf28d2fb 100644
--- a/fs/gfs2/lops.h
+++ b/fs/gfs2/lops.h
@@ -20,11 +20,6 @@
((sizeof(struct gfs2_log_descriptor) + (2 * sizeof(__be64) - 1)) & \
~(2 * sizeof(__be64) - 1))
-extern const struct gfs2_log_operations gfs2_glock_lops;
-extern const struct gfs2_log_operations gfs2_buf_lops;
-extern const struct gfs2_log_operations gfs2_revoke_lops;
-extern const struct gfs2_log_operations gfs2_databuf_lops;
-
extern const struct gfs2_log_operations *gfs2_log_ops[];
extern u64 gfs2_log_bmap(struct gfs2_sbd *sdp);
extern void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 08/12] gfs2: Rename sd_log_le_{revoke, ordered}
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (5 preceding siblings ...)
2019-05-07 20:31 ` [Cluster-devel] [GFS2 PATCH 07/12] gfs2: Remove unnecessary extern declarations Andreas Gruenbacher
@ 2019-05-07 20:32 ` Andreas Gruenbacher
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 09/12] gfs2: Rename gfs2_trans_{add_unrevoke => remove_revoke} Andreas Gruenbacher
` (3 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:32 UTC (permalink / raw)
To: cluster-devel.redhat.com
Rename sd_log_le_revoke to sd_log_revokes and sd_log_le_ordered to
sd_log_ordered: not sure what le stands for here, but it doesn't add
clarity, and if it stands for list entry, it's actually confusing as
those are both list heads but not list entries.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/incore.h | 4 ++--
fs/gfs2/log.c | 14 +++++++-------
fs/gfs2/log.h | 2 +-
fs/gfs2/lops.c | 4 ++--
fs/gfs2/ops_fstype.c | 4 ++--
fs/gfs2/trans.c | 2 +-
6 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 6a94b094a904..78c8e761b321 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -809,8 +809,8 @@ struct gfs2_sbd {
atomic_t sd_log_pinned;
unsigned int sd_log_num_revoke;
- struct list_head sd_log_le_revoke;
- struct list_head sd_log_le_ordered;
+ struct list_head sd_log_revokes;
+ struct list_head sd_log_ordered;
spinlock_t sd_ordered_lock;
atomic_t sd_log_thresh1;
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index d55315a46ece..a7febb4bd400 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -551,9 +551,9 @@ static void gfs2_ordered_write(struct gfs2_sbd *sdp)
LIST_HEAD(written);
spin_lock(&sdp->sd_ordered_lock);
- list_sort(NULL, &sdp->sd_log_le_ordered, &ip_cmp);
- while (!list_empty(&sdp->sd_log_le_ordered)) {
- ip = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_inode, i_ordered);
+ list_sort(NULL, &sdp->sd_log_ordered, &ip_cmp);
+ while (!list_empty(&sdp->sd_log_ordered)) {
+ ip = list_entry(sdp->sd_log_ordered.next, struct gfs2_inode, i_ordered);
if (ip->i_inode.i_mapping->nrpages == 0) {
test_and_clear_bit(GIF_ORDERED, &ip->i_flags);
list_del(&ip->i_ordered);
@@ -564,7 +564,7 @@ static void gfs2_ordered_write(struct gfs2_sbd *sdp)
filemap_fdatawrite(ip->i_inode.i_mapping);
spin_lock(&sdp->sd_ordered_lock);
}
- list_splice(&written, &sdp->sd_log_le_ordered);
+ list_splice(&written, &sdp->sd_log_ordered);
spin_unlock(&sdp->sd_ordered_lock);
}
@@ -573,8 +573,8 @@ static void gfs2_ordered_wait(struct gfs2_sbd *sdp)
struct gfs2_inode *ip;
spin_lock(&sdp->sd_ordered_lock);
- while (!list_empty(&sdp->sd_log_le_ordered)) {
- ip = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_inode, i_ordered);
+ while (!list_empty(&sdp->sd_log_ordered)) {
+ ip = list_entry(sdp->sd_log_ordered.next, struct gfs2_inode, i_ordered);
list_del(&ip->i_ordered);
WARN_ON(!test_and_clear_bit(GIF_ORDERED, &ip->i_flags));
if (ip->i_inode.i_mapping->nrpages == 0)
@@ -611,7 +611,7 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
gfs2_glock_hold(gl);
}
set_bit(GLF_LFLUSH, &gl->gl_flags);
- list_add(&bd->bd_list, &sdp->sd_log_le_revoke);
+ list_add(&bd->bd_list, &sdp->sd_log_revokes);
}
void gfs2_write_revokes(struct gfs2_sbd *sdp)
diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h
index 86d07d436cdf..7a34a3234266 100644
--- a/fs/gfs2/log.h
+++ b/fs/gfs2/log.h
@@ -59,7 +59,7 @@ static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip)
if (!test_bit(GIF_ORDERED, &ip->i_flags)) {
spin_lock(&sdp->sd_ordered_lock);
if (!test_and_set_bit(GIF_ORDERED, &ip->i_flags))
- list_add(&ip->i_ordered, &sdp->sd_log_le_ordered);
+ list_add(&ip->i_ordered, &sdp->sd_log_ordered);
spin_unlock(&sdp->sd_ordered_lock);
}
}
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 6c1ec6c60639..6af6a3cea967 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -623,7 +623,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
struct gfs2_meta_header *mh;
unsigned int offset;
- struct list_head *head = &sdp->sd_log_le_revoke;
+ struct list_head *head = &sdp->sd_log_revokes;
struct gfs2_bufdata *bd;
struct page *page;
unsigned int length;
@@ -661,7 +661,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
- struct list_head *head = &sdp->sd_log_le_revoke;
+ struct list_head *head = &sdp->sd_log_revokes;
struct gfs2_bufdata *bd, *tmp;
/*
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index b041cb8ae383..abfaecde0e3d 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -117,8 +117,8 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
spin_lock_init(&sdp->sd_log_lock);
atomic_set(&sdp->sd_log_pinned, 0);
- INIT_LIST_HEAD(&sdp->sd_log_le_revoke);
- INIT_LIST_HEAD(&sdp->sd_log_le_ordered);
+ INIT_LIST_HEAD(&sdp->sd_log_revokes);
+ INIT_LIST_HEAD(&sdp->sd_log_ordered);
spin_lock_init(&sdp->sd_ordered_lock);
init_waitqueue_head(&sdp->sd_log_waitq);
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index cd9a94a6b5bb..5c4eae3b69fb 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -260,7 +260,7 @@ void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
unsigned int n = len;
gfs2_log_lock(sdp);
- list_for_each_entry_safe(bd, tmp, &sdp->sd_log_le_revoke, bd_list) {
+ list_for_each_entry_safe(bd, tmp, &sdp->sd_log_revokes, bd_list) {
if ((bd->bd_blkno >= blkno) && (bd->bd_blkno < (blkno + len))) {
list_del_init(&bd->bd_list);
gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke);
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 09/12] gfs2: Rename gfs2_trans_{add_unrevoke => remove_revoke}
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (6 preceding siblings ...)
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 08/12] gfs2: Rename sd_log_le_{revoke, ordered} Andreas Gruenbacher
@ 2019-05-07 20:32 ` Andreas Gruenbacher
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 10/12] gfs2: fix race between gfs2_freeze_func and unmount Andreas Gruenbacher
` (2 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:32 UTC (permalink / raw)
To: cluster-devel.redhat.com
Rename gfs2_trans_add_unrevoke to gfs2_trans_remove_revoke: there is no
such thing as an "unrevoke" object; all this function does is remove
existing revoke objects plus some bookkeeping.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/bmap.c | 4 ++--
fs/gfs2/dir.c | 2 +-
fs/gfs2/rgrp.c | 2 +-
fs/gfs2/trans.c | 2 +-
fs/gfs2/trans.h | 2 +-
fs/gfs2/xattr.c | 6 +++---
6 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index e95b33b65d89..5da4ca9041c0 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -142,7 +142,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page)
if (error)
goto out_brelse;
if (isdir) {
- gfs2_trans_add_unrevoke(GFS2_SB(&ip->i_inode), block, 1);
+ gfs2_trans_remove_revoke(GFS2_SB(&ip->i_inode), block, 1);
error = gfs2_dir_get_new_buffer(ip, block, &bh);
if (error)
goto out_brelse;
@@ -676,7 +676,7 @@ static int gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap,
goto out;
alloced += n;
if (state != ALLOC_DATA || gfs2_is_jdata(ip))
- gfs2_trans_add_unrevoke(sdp, bn, n);
+ gfs2_trans_remove_revoke(sdp, bn, n);
switch (state) {
/* Growing height of tree */
case ALLOC_GROW_HEIGHT:
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index daa14ab4e31b..db9a05244a35 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -883,7 +883,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
if (!bh)
return NULL;
- gfs2_trans_add_unrevoke(GFS2_SB(inode), bn, 1);
+ gfs2_trans_remove_revoke(GFS2_SB(inode), bn, 1);
gfs2_trans_add_meta(ip->i_gl, bh);
gfs2_metatype_set(bh, GFS2_METATYPE_LF, GFS2_FORMAT_LF);
leaf = (struct gfs2_leaf *)bh->b_data;
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 52a4f340a867..15d6e32de55f 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -2440,7 +2440,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
gfs2_statfs_change(sdp, 0, -(s64)*nblocks, dinode ? 1 : 0);
if (dinode)
- gfs2_trans_add_unrevoke(sdp, block, *nblocks);
+ gfs2_trans_remove_revoke(sdp, block, *nblocks);
gfs2_quota_change(ip, *nblocks, ip->i_inode.i_uid, ip->i_inode.i_gid);
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index 5c4eae3b69fb..d328da7cde36 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -253,7 +253,7 @@ void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
tr->tr_num_revoke++;
}
-void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
+void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
{
struct gfs2_bufdata *bd, *tmp;
struct gfs2_trans *tr = current->journal_info;
diff --git a/fs/gfs2/trans.h b/fs/gfs2/trans.h
index ad70087d0597..1e39f056ccb7 100644
--- a/fs/gfs2/trans.h
+++ b/fs/gfs2/trans.h
@@ -44,6 +44,6 @@ extern void gfs2_trans_end(struct gfs2_sbd *sdp);
extern void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh);
extern void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh);
extern void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
-extern void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len);
+extern void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len);
#endif /* __TRANS_DOT_H__ */
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index 996c915a9c97..675e704830df 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -631,7 +631,7 @@ static int ea_alloc_blk(struct gfs2_inode *ip, struct buffer_head **bhp)
error = gfs2_alloc_blocks(ip, &block, &n, 0, NULL);
if (error)
return error;
- gfs2_trans_add_unrevoke(sdp, block, 1);
+ gfs2_trans_remove_revoke(sdp, block, 1);
*bhp = gfs2_meta_new(ip->i_gl, block);
gfs2_trans_add_meta(ip->i_gl, *bhp);
gfs2_metatype_set(*bhp, GFS2_METATYPE_EA, GFS2_FORMAT_EA);
@@ -693,7 +693,7 @@ static int ea_write(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
error = gfs2_alloc_blocks(ip, &block, &n, 0, NULL);
if (error)
return error;
- gfs2_trans_add_unrevoke(sdp, block, 1);
+ gfs2_trans_remove_revoke(sdp, block, 1);
bh = gfs2_meta_new(ip->i_gl, block);
gfs2_trans_add_meta(ip->i_gl, bh);
gfs2_metatype_set(bh, GFS2_METATYPE_ED, GFS2_FORMAT_ED);
@@ -997,7 +997,7 @@ static int ea_set_block(struct gfs2_inode *ip, struct gfs2_ea_request *er,
error = gfs2_alloc_blocks(ip, &blk, &n, 0, NULL);
if (error)
return error;
- gfs2_trans_add_unrevoke(sdp, blk, 1);
+ gfs2_trans_remove_revoke(sdp, blk, 1);
indbh = gfs2_meta_new(ip->i_gl, blk);
gfs2_trans_add_meta(ip->i_gl, indbh);
gfs2_metatype_set(indbh, GFS2_METATYPE_IN, GFS2_FORMAT_IN);
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 10/12] gfs2: fix race between gfs2_freeze_func and unmount
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (7 preceding siblings ...)
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 09/12] gfs2: Rename gfs2_trans_{add_unrevoke => remove_revoke} Andreas Gruenbacher
@ 2019-05-07 20:32 ` Andreas Gruenbacher
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 11/12] gfs2: Fix iomap write page reclaim deadlock Andreas Gruenbacher
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 12/12] gfs2: read journal in large chunks Andreas Gruenbacher
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:32 UTC (permalink / raw)
To: cluster-devel.redhat.com
From: Abhi Das <adas@redhat.com>
As part of the freeze operation, gfs2_freeze_func() is left blocking
on a request to hold the sd_freeze_gl in SH. This glock is held in EX
by the gfs2_freeze() code.
A subsequent call to gfs2_unfreeze() releases the EXclusively held
sd_freeze_gl, which allows gfs2_freeze_func() to acquire it in SH and
resume its operation.
gfs2_unfreeze(), however, doesn't wait for gfs2_freeze_func() to complete.
If a umount is issued right after unfreeze, it could result in an
inconsistent filesystem because some journal data (statfs update) isn't
written out.
Refer to commit 24972557b12c for a more detailed explanation of how
freeze/unfreeze work.
This patch causes gfs2_unfreeze() to wait for gfs2_freeze_func() to
complete before returning to the user.
Signed-off-by: Abhi Das <adas@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/incore.h | 1 +
fs/gfs2/super.c | 8 +++++---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 78c8e761b321..b15755068593 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -621,6 +621,7 @@ enum {
SDF_SKIP_DLM_UNLOCK = 8,
SDF_FORCE_AIL_FLUSH = 9,
SDF_AIL1_IO_ERROR = 10,
+ SDF_FS_FROZEN = 11,
};
enum gfs2_freeze_state {
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index a6a325b2a78b..ceec631efa49 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -973,8 +973,7 @@ void gfs2_freeze_func(struct work_struct *work)
if (error) {
printk(KERN_INFO "GFS2: couldn't get freeze lock : %d\n", error);
gfs2_assert_withdraw(sdp, 0);
- }
- else {
+ } else {
atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN);
error = thaw_super(sb);
if (error) {
@@ -987,6 +986,8 @@ void gfs2_freeze_func(struct work_struct *work)
gfs2_glock_dq_uninit(&freeze_gh);
}
deactivate_super(sb);
+ clear_bit_unlock(SDF_FS_FROZEN, &sdp->sd_flags);
+ wake_up_bit(&sdp->sd_flags, SDF_FS_FROZEN);
return;
}
@@ -1029,6 +1030,7 @@ static int gfs2_freeze(struct super_block *sb)
msleep(1000);
}
error = 0;
+ set_bit(SDF_FS_FROZEN, &sdp->sd_flags);
out:
mutex_unlock(&sdp->sd_freeze_mutex);
return error;
@@ -1053,7 +1055,7 @@ static int gfs2_unfreeze(struct super_block *sb)
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
mutex_unlock(&sdp->sd_freeze_mutex);
- return 0;
+ return wait_on_bit(&sdp->sd_flags, SDF_FS_FROZEN, TASK_INTERRUPTIBLE);
}
/**
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 11/12] gfs2: Fix iomap write page reclaim deadlock
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (8 preceding siblings ...)
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 10/12] gfs2: fix race between gfs2_freeze_func and unmount Andreas Gruenbacher
@ 2019-05-07 20:32 ` Andreas Gruenbacher
2019-06-07 16:19 ` Ross Lagerwall
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 12/12] gfs2: read journal in large chunks Andreas Gruenbacher
10 siblings, 1 reply; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:32 UTC (permalink / raw)
To: cluster-devel.redhat.com
Since commit 64bc06bb32ee ("gfs2: iomap buffered write support"), gfs2 is doing
buffered writes by starting a transaction in iomap_begin, writing a range of
pages, and ending that transaction in iomap_end. This approach suffers from
two problems:
(1) Any allocations necessary for the write are done in iomap_begin, so when
the data aren't journaled, there is no need for keeping the transaction open
until iomap_end.
(2) Transactions keep the gfs2 log flush lock held. When
iomap_file_buffered_write calls balance_dirty_pages, this can end up calling
gfs2_write_inode, which will try to flush the log. This requires taking the
log flush lock which is already held, resulting in a deadlock.
Fix both of these issues by not keeping transactions open from iomap_begin to
iomap_end. Instead, start a small transaction in page_prepare and end it in
page_done when necessary.
Reported-by: Edwin T?r?k <edvin.torok@citrix.com>
Fixes: 64bc06bb32ee ("gfs2: iomap buffered write support")
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
---
fs/gfs2/aops.c | 14 +++++---
fs/gfs2/bmap.c | 88 +++++++++++++++++++++++++++-----------------------
2 files changed, 58 insertions(+), 44 deletions(-)
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 05dd78f4b2b3..6210d4429d84 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -649,7 +649,7 @@ static int gfs2_readpages(struct file *file, struct address_space *mapping,
*/
void adjust_fs_space(struct inode *inode)
{
- struct gfs2_sbd *sdp = inode->i_sb->s_fs_info;
+ struct gfs2_sbd *sdp = GFS2_SB(inode);
struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode);
struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode);
struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master;
@@ -657,10 +657,13 @@ void adjust_fs_space(struct inode *inode)
struct buffer_head *m_bh, *l_bh;
u64 fs_total, new_free;
+ if (gfs2_trans_begin(sdp, 2 * RES_STATFS, 0) != 0)
+ return;
+
/* Total up the file system space, according to the latest rindex. */
fs_total = gfs2_ri_total(sdp);
if (gfs2_meta_inode_buffer(m_ip, &m_bh) != 0)
- return;
+ goto out;
spin_lock(&sdp->sd_statfs_spin);
gfs2_statfs_change_in(m_sc, m_bh->b_data +
@@ -675,11 +678,14 @@ void adjust_fs_space(struct inode *inode)
gfs2_statfs_change(sdp, new_free, new_free, 0);
if (gfs2_meta_inode_buffer(l_ip, &l_bh) != 0)
- goto out;
+ goto out2;
update_statfs(sdp, m_bh, l_bh);
brelse(l_bh);
-out:
+out2:
brelse(m_bh);
+out:
+ sdp->sd_rindex_uptodate = 0;
+ gfs2_trans_end(sdp);
}
/**
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index aa014725f84a..27c82f4aaf32 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -991,17 +991,28 @@ static void gfs2_write_unlock(struct inode *inode)
gfs2_glock_dq_uninit(&ip->i_gh);
}
+static int gfs2_iomap_page_prepare(struct inode *inode, loff_t pos,
+ unsigned len, struct iomap *iomap)
+{
+ struct gfs2_sbd *sdp = GFS2_SB(inode);
+
+ return gfs2_trans_begin(sdp, RES_DINODE + (len >> inode->i_blkbits), 0);
+}
+
static void gfs2_iomap_page_done(struct inode *inode, loff_t pos,
unsigned copied, struct page *page,
struct iomap *iomap)
{
struct gfs2_inode *ip = GFS2_I(inode);
+ struct gfs2_sbd *sdp = GFS2_SB(inode);
- if (page)
+ if (page && !gfs2_is_stuffed(ip))
gfs2_page_add_databufs(ip, page, offset_in_page(pos), copied);
+ gfs2_trans_end(sdp);
}
static const struct iomap_page_ops gfs2_iomap_page_ops = {
+ .page_prepare = gfs2_iomap_page_prepare,
.page_done = gfs2_iomap_page_done,
};
@@ -1057,31 +1068,45 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
if (alloc_required)
rblocks += gfs2_rg_blocks(ip, data_blocks + ind_blocks);
- ret = gfs2_trans_begin(sdp, rblocks, iomap->length >> inode->i_blkbits);
- if (ret)
- goto out_trans_fail;
+ if (unstuff || iomap->type == IOMAP_HOLE) {
+ struct gfs2_trans *tr;
- if (unstuff) {
- ret = gfs2_unstuff_dinode(ip, NULL);
+ ret = gfs2_trans_begin(sdp, rblocks,
+ iomap->length >> inode->i_blkbits);
if (ret)
- goto out_trans_end;
- release_metapath(mp);
- ret = gfs2_iomap_get(inode, iomap->offset, iomap->length,
- flags, iomap, mp);
- if (ret)
- goto out_trans_end;
- }
+ goto out_trans_fail;
- if (iomap->type == IOMAP_HOLE) {
- ret = gfs2_iomap_alloc(inode, iomap, flags, mp);
- if (ret) {
- gfs2_trans_end(sdp);
- gfs2_inplace_release(ip);
- punch_hole(ip, iomap->offset, iomap->length);
- goto out_qunlock;
+ if (unstuff) {
+ ret = gfs2_unstuff_dinode(ip, NULL);
+ if (ret)
+ goto out_trans_end;
+ release_metapath(mp);
+ ret = gfs2_iomap_get(inode, iomap->offset,
+ iomap->length, flags, iomap, mp);
+ if (ret)
+ goto out_trans_end;
+ }
+
+ if (iomap->type == IOMAP_HOLE) {
+ ret = gfs2_iomap_alloc(inode, iomap, flags, mp);
+ if (ret) {
+ gfs2_trans_end(sdp);
+ gfs2_inplace_release(ip);
+ punch_hole(ip, iomap->offset, iomap->length);
+ goto out_qunlock;
+ }
}
+
+ tr = current->journal_info;
+ if (tr->tr_num_buf_new)
+ __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
+ else
+ gfs2_trans_add_meta(ip->i_gl, mp->mp_bh[0]);
+
+ gfs2_trans_end(sdp);
}
- if (!gfs2_is_stuffed(ip) && gfs2_is_jdata(ip))
+
+ if (gfs2_is_stuffed(ip) || gfs2_is_jdata(ip))
iomap->page_ops = &gfs2_iomap_page_ops;
return 0;
@@ -1121,10 +1146,6 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
iomap->type != IOMAP_MAPPED)
ret = -ENOTBLK;
}
- if (!ret) {
- get_bh(mp.mp_bh[0]);
- iomap->private = mp.mp_bh[0];
- }
release_metapath(&mp);
trace_gfs2_iomap_end(ip, iomap, ret);
return ret;
@@ -1135,27 +1156,16 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length,
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode);
- struct gfs2_trans *tr = current->journal_info;
- struct buffer_head *dibh = iomap->private;
if ((flags & (IOMAP_WRITE | IOMAP_DIRECT)) != IOMAP_WRITE)
goto out;
- if (iomap->type != IOMAP_INLINE) {
+ if (!gfs2_is_stuffed(ip))
gfs2_ordered_add_inode(ip);
- if (tr->tr_num_buf_new)
- __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
- else
- gfs2_trans_add_meta(ip->i_gl, dibh);
- }
-
- if (inode == sdp->sd_rindex) {
+ if (inode == sdp->sd_rindex)
adjust_fs_space(inode);
- sdp->sd_rindex_uptodate = 0;
- }
- gfs2_trans_end(sdp);
gfs2_inplace_release(ip);
if (length != written && (iomap->flags & IOMAP_F_NEW)) {
@@ -1175,8 +1185,6 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length,
gfs2_write_unlock(inode);
out:
- if (dibh)
- brelse(dibh);
return 0;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 11/12] gfs2: Fix iomap write page reclaim deadlock
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 11/12] gfs2: Fix iomap write page reclaim deadlock Andreas Gruenbacher
@ 2019-06-07 16:19 ` Ross Lagerwall
2019-06-08 12:16 ` Andreas Gruenbacher
0 siblings, 1 reply; 15+ messages in thread
From: Ross Lagerwall @ 2019-06-07 16:19 UTC (permalink / raw)
To: cluster-devel.redhat.com
On 5/7/19 9:32 PM, Andreas Gruenbacher wrote:
> Since commit 64bc06bb32ee ("gfs2: iomap buffered write support"), gfs2 is doing
> buffered writes by starting a transaction in iomap_begin, writing a range of
> pages, and ending that transaction in iomap_end. This approach suffers from
> two problems:
>
> (1) Any allocations necessary for the write are done in iomap_begin, so when
> the data aren't journaled, there is no need for keeping the transaction open
> until iomap_end.
>
> (2) Transactions keep the gfs2 log flush lock held. When
> iomap_file_buffered_write calls balance_dirty_pages, this can end up calling
> gfs2_write_inode, which will try to flush the log. This requires taking the
> log flush lock which is already held, resulting in a deadlock.
>
> Fix both of these issues by not keeping transactions open from iomap_begin to
> iomap_end. Instead, start a small transaction in page_prepare and end it in
> page_done when necessary.
>
Unfortunately, this patch broke growing gfs2 filesystems. It is easy to
reproduce:
$ mkfs.gfs2 -t xxx:yyy /dev/xvdb 4369065
$ mount /dev/xvdb /mnt
$ gfs2_grow /mnt (doesn't finish)
FS: Mount point: /mnt
FS: Device: /dev/xvdb
FS: Size: 4369062 (0x42aaa6)
DEV: Length: 13107200 (0xc80000)
The file system will grow by 34133MB.
Looking at the kernel log, I see it hits the following assertion and
then hangs trying to withdraw the filesystem (which is a separate
problem, presumably):
gfs2: fsid=xxx:yyy.0: fatal: assertion "(nbuf <= tr->tr_blocks) &&
(tr->tr_num_revoke <= tr->tr_revokes)" failed
function = gfs2_trans_end, file = fs/gfs2/trans.c, line = 117
gfs2: fsid=xxx:yyy.0: about to withdraw this file system
Rearranging the code so that it prints information about the transaction
before the failed withdrawal attempt shows:
gfs2: fsid=xxx:yyy.0: Transaction created at:
iomap_write_begin.constprop.45+0xbc/0x380
gfs2: fsid=xxx:yyy.0: blocks=1 revokes=0 reserved=8 touched=1
gfs2: fsid=xxx:yyy.0: Buf 1/0 Databuf 1/0 Revoke 0/0
Reverting this commit fixes the issue. Tested with git master as of
today (16d72dd4891fe).
Thanks,
--
Ross Lagerwall
^ permalink raw reply [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 11/12] gfs2: Fix iomap write page reclaim deadlock
2019-06-07 16:19 ` Ross Lagerwall
@ 2019-06-08 12:16 ` Andreas Gruenbacher
2019-06-11 8:29 ` Ross Lagerwall
0 siblings, 1 reply; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-06-08 12:16 UTC (permalink / raw)
To: cluster-devel.redhat.com
Hi Ross,
On Fri, 7 Jun 2019 at 18:21, Ross Lagerwall <ross.lagerwall@citrix.com> wrote:
> On 5/7/19 9:32 PM, Andreas Gruenbacher wrote:
> > Since commit 64bc06bb32ee ("gfs2: iomap buffered write support"), gfs2 is doing
> > buffered writes by starting a transaction in iomap_begin, writing a range of
> > pages, and ending that transaction in iomap_end. This approach suffers from
> > two problems:
> >
> > (1) Any allocations necessary for the write are done in iomap_begin, so when
> > the data aren't journaled, there is no need for keeping the transaction open
> > until iomap_end.
> >
> > (2) Transactions keep the gfs2 log flush lock held. When
> > iomap_file_buffered_write calls balance_dirty_pages, this can end up calling
> > gfs2_write_inode, which will try to flush the log. This requires taking the
> > log flush lock which is already held, resulting in a deadlock.
> >
> > Fix both of these issues by not keeping transactions open from iomap_begin to
> > iomap_end. Instead, start a small transaction in page_prepare and end it in
> > page_done when necessary.
> >
> Unfortunately, this patch broke growing gfs2 filesystems. It is easy to
> reproduce:
>
> $ mkfs.gfs2 -t xxx:yyy /dev/xvdb 4369065
> $ mount /dev/xvdb /mnt
> $ gfs2_grow /mnt (doesn't finish)
> FS: Mount point: /mnt
> FS: Device: /dev/xvdb
> FS: Size: 4369062 (0x42aaa6)
> DEV: Length: 13107200 (0xc80000)
> The file system will grow by 34133MB.
>
> Looking at the kernel log, I see it hits the following assertion and
> then hangs trying to withdraw the filesystem (which is a separate
> problem, presumably):
>
> gfs2: fsid=xxx:yyy.0: fatal: assertion "(nbuf <= tr->tr_blocks) &&
> (tr->tr_num_revoke <= tr->tr_revokes)" failed
> function = gfs2_trans_end, file = fs/gfs2/trans.c, line = 117
> gfs2: fsid=xxx:yyy.0: about to withdraw this file system
>
> Rearranging the code so that it prints information about the transaction
> before the failed withdrawal attempt shows:
> gfs2: fsid=xxx:yyy.0: Transaction created at:
> iomap_write_begin.constprop.45+0xbc/0x380
> gfs2: fsid=xxx:yyy.0: blocks=1 revokes=0 reserved=8 touched=1
> gfs2: fsid=xxx:yyy.0: Buf 1/0 Databuf 1/0 Revoke 0/0
>
> Reverting this commit fixes the issue. Tested with git master as of
> today (16d72dd4891fe).
thanks for the error report. This turns out to be a rounding error in
gfs2_iomap_page_prepare; the attached patch should help.
Thanks,
Andreas
---
fs/gfs2/bmap.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index f42718d..d2a3f038 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -994,9 +994,11 @@ static void gfs2_write_unlock(struct inode *inode)
static int gfs2_iomap_page_prepare(struct inode *inode, loff_t pos,
unsigned len, struct iomap *iomap)
{
+ unsigned int blocks;
struct gfs2_sbd *sdp = GFS2_SB(inode);
- return gfs2_trans_begin(sdp, RES_DINODE + (len >> inode->i_blkbits), 0);
+ blocks = (len + i_blocksize(inode) - 1) >> inode->i_blkbits;
+ return gfs2_trans_begin(sdp, RES_DINODE + blocks, 0);
}
static void gfs2_iomap_page_done(struct inode *inode, loff_t pos,
--
1.8.3.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 11/12] gfs2: Fix iomap write page reclaim deadlock
2019-06-08 12:16 ` Andreas Gruenbacher
@ 2019-06-11 8:29 ` Ross Lagerwall
0 siblings, 0 replies; 15+ messages in thread
From: Ross Lagerwall @ 2019-06-11 8:29 UTC (permalink / raw)
To: cluster-devel.redhat.com
On 6/8/19 1:16 PM, Andreas Gruenbacher wrote:
> Hi Ross,
>
> On Fri, 7 Jun 2019 at 18:21, Ross Lagerwall <ross.lagerwall@citrix.com> wrote:
>> On 5/7/19 9:32 PM, Andreas Gruenbacher wrote:
>>> Since commit 64bc06bb32ee ("gfs2: iomap buffered write support"), gfs2 is doing
>>> buffered writes by starting a transaction in iomap_begin, writing a range of
>>> pages, and ending that transaction in iomap_end. This approach suffers from
>>> two problems:
>>>
>>> (1) Any allocations necessary for the write are done in iomap_begin, so when
>>> the data aren't journaled, there is no need for keeping the transaction open
>>> until iomap_end.
>>>
>>> (2) Transactions keep the gfs2 log flush lock held. When
>>> iomap_file_buffered_write calls balance_dirty_pages, this can end up calling
>>> gfs2_write_inode, which will try to flush the log. This requires taking the
>>> log flush lock which is already held, resulting in a deadlock.
>>>
>>> Fix both of these issues by not keeping transactions open from iomap_begin to
>>> iomap_end. Instead, start a small transaction in page_prepare and end it in
>>> page_done when necessary.
>>>
>> Unfortunately, this patch broke growing gfs2 filesystems. It is easy to
>> reproduce:
>>
>> $ mkfs.gfs2 -t xxx:yyy /dev/xvdb 4369065
>> $ mount /dev/xvdb /mnt
>> $ gfs2_grow /mnt (doesn't finish)
>> FS: Mount point: /mnt
>> FS: Device: /dev/xvdb
>> FS: Size: 4369062 (0x42aaa6)
>> DEV: Length: 13107200 (0xc80000)
>> The file system will grow by 34133MB.
>>
>> Looking at the kernel log, I see it hits the following assertion and
>> then hangs trying to withdraw the filesystem (which is a separate
>> problem, presumably):
>>
>> gfs2: fsid=xxx:yyy.0: fatal: assertion "(nbuf <= tr->tr_blocks) &&
>> (tr->tr_num_revoke <= tr->tr_revokes)" failed
>> function = gfs2_trans_end, file = fs/gfs2/trans.c, line = 117
>> gfs2: fsid=xxx:yyy.0: about to withdraw this file system
>>
>> Rearranging the code so that it prints information about the transaction
>> before the failed withdrawal attempt shows:
>> gfs2: fsid=xxx:yyy.0: Transaction created at:
>> iomap_write_begin.constprop.45+0xbc/0x380
>> gfs2: fsid=xxx:yyy.0: blocks=1 revokes=0 reserved=8 touched=1
>> gfs2: fsid=xxx:yyy.0: Buf 1/0 Databuf 1/0 Revoke 0/0
>>
>> Reverting this commit fixes the issue. Tested with git master as of
>> today (16d72dd4891fe).
>
> thanks for the error report. This turns out to be a rounding error in
> gfs2_iomap_page_prepare; the attached patch should help.
>
Yes, that fixes the problem. Thanks for the quick response.
--
Ross Lagerwall
^ permalink raw reply [flat|nested] 15+ messages in thread
* [Cluster-devel] [GFS2 PATCH 12/12] gfs2: read journal in large chunks
2019-05-07 20:31 [Cluster-devel] [GFS2 PATCH 01/12] gfs2: Fix loop in gfs2_rbm_find (v2) Andreas Gruenbacher
` (9 preceding siblings ...)
2019-05-07 20:32 ` [Cluster-devel] [GFS2 PATCH 11/12] gfs2: Fix iomap write page reclaim deadlock Andreas Gruenbacher
@ 2019-05-07 20:32 ` Andreas Gruenbacher
10 siblings, 0 replies; 15+ messages in thread
From: Andreas Gruenbacher @ 2019-05-07 20:32 UTC (permalink / raw)
To: cluster-devel.redhat.com
From: Abhi Das <adas@redhat.com>
Use bios to read in the journal into the address space of the journal inode
(jd_inode), sequentially and in large chunks. This is faster for locating the
journal head that the previous binary search approach. When performing
recovery, we keep the journal in the address space until recovery is done,
which further speeds up things.
Signed-off-by: Abhi Das <adas@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/glops.c | 3 +-
fs/gfs2/log.c | 4 +-
fs/gfs2/lops.c | 212 +++++++++++++++++++++++++++++++++++++++++--
fs/gfs2/lops.h | 4 +-
fs/gfs2/ops_fstype.c | 3 +-
fs/gfs2/recovery.c | 125 +------------------------
fs/gfs2/recovery.h | 2 -
fs/gfs2/super.c | 5 +-
8 files changed, 219 insertions(+), 139 deletions(-)
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 78510ab91835..24ada3ccc525 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -28,6 +28,7 @@
#include "util.h"
#include "trans.h"
#include "dir.h"
+#include "lops.h"
struct workqueue_struct *gfs2_freeze_wq;
@@ -531,7 +532,7 @@ static int freeze_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh)
if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA);
- error = gfs2_find_jhead(sdp->sd_jdesc, &head);
+ error = gfs2_find_jhead(sdp->sd_jdesc, &head, false);
if (error)
gfs2_consist(sdp);
if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT))
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index a7febb4bd400..a2e1df488df0 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -744,7 +744,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
lh->lh_crc = cpu_to_be32(crc);
gfs2_log_write(sdp, page, sb->s_blocksize, 0, dblock);
- gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE, op_flags);
+ gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE | op_flags);
log_flush_wait(sdp);
}
@@ -821,7 +821,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
gfs2_ordered_write(sdp);
lops_before_commit(sdp, tr);
- gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE, 0);
+ gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE);
if (sdp->sd_log_head != sdp->sd_log_flush_head) {
log_flush_wait(sdp);
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 6af6a3cea967..ce048a9e058d 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -17,7 +17,9 @@
#include <linux/bio.h>
#include <linux/fs.h>
#include <linux/list_sort.h>
+#include <linux/blkdev.h>
+#include "bmap.h"
#include "dir.h"
#include "gfs2.h"
#include "incore.h"
@@ -194,7 +196,6 @@ static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp,
/**
* gfs2_end_log_write - end of i/o to the log
* @bio: The bio
- * @error: Status of i/o request
*
* Each bio_vec contains either data from the pagecache or data
* relating to the log itself. Here we iterate over the bio_vec
@@ -232,20 +233,19 @@ static void gfs2_end_log_write(struct bio *bio)
/**
* gfs2_log_submit_bio - Submit any pending log bio
* @biop: Address of the bio pointer
- * @op: REQ_OP
- * @op_flags: req_flag_bits
+ * @opf: REQ_OP | op_flags
*
* Submit any pending part-built or full bio to the block device. If
* there is no pending bio, then this is a no-op.
*/
-void gfs2_log_submit_bio(struct bio **biop, int op, int op_flags)
+void gfs2_log_submit_bio(struct bio **biop, int opf)
{
struct bio *bio = *biop;
if (bio) {
struct gfs2_sbd *sdp = bio->bi_private;
atomic_inc(&sdp->sd_log_in_flight);
- bio_set_op_attrs(bio, op, op_flags);
+ bio->bi_opf = opf;
submit_bio(bio);
*biop = NULL;
}
@@ -306,7 +306,7 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno,
nblk >>= sdp->sd_fsb2bb_shift;
if (blkno == nblk && !flush)
return bio;
- gfs2_log_submit_bio(biop, op, 0);
+ gfs2_log_submit_bio(biop, op);
}
*biop = gfs2_log_alloc_bio(sdp, blkno, end_io);
@@ -377,6 +377,206 @@ void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page)
gfs2_log_bmap(sdp));
}
+/**
+ * gfs2_end_log_read - end I/O callback for reads from the log
+ * @bio: The bio
+ *
+ * Simply unlock the pages in the bio. The main thread will wait on them and
+ * process them in order as necessary.
+ */
+
+static void gfs2_end_log_read(struct bio *bio)
+{
+ struct page *page;
+ struct bio_vec *bvec;
+ int i;
+ struct bvec_iter_all iter_all;
+
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
+ page = bvec->bv_page;
+ if (bio->bi_status) {
+ int err = blk_status_to_errno(bio->bi_status);
+
+ SetPageError(page);
+ mapping_set_error(page->mapping, err);
+ }
+ unlock_page(page);
+ }
+
+ bio_put(bio);
+}
+
+/**
+ * gfs2_jhead_pg_srch - Look for the journal head in a given page.
+ * @jd: The journal descriptor
+ * @page: The page to look in
+ *
+ * Returns: 1 if found, 0 otherwise.
+ */
+
+static bool gfs2_jhead_pg_srch(struct gfs2_jdesc *jd,
+ struct gfs2_log_header_host *head,
+ struct page *page)
+{
+ struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+ struct gfs2_log_header_host uninitialized_var(lh);
+ void *kaddr = kmap_atomic(page);
+ unsigned int offset;
+ bool ret = false;
+
+ for (offset = 0; offset < PAGE_SIZE; offset += sdp->sd_sb.sb_bsize) {
+ if (!__get_log_header(sdp, kaddr + offset, 0, &lh)) {
+ if (lh.lh_sequence > head->lh_sequence)
+ *head = lh;
+ else {
+ ret = true;
+ break;
+ }
+ }
+ }
+ kunmap_atomic(kaddr);
+ return ret;
+}
+
+/**
+ * gfs2_jhead_process_page - Search/cleanup a page
+ * @jd: The journal descriptor
+ * @index: Index of the page to look into
+ * @done: If set, perform only cleanup, else search and set if found.
+ *
+ * Find the page with 'index' in the journal's mapping. Search the page for
+ * the journal head if requested (cleanup == false). Release refs on the
+ * page so the page cache can reclaim it (put_page() twice). We grabbed a
+ * reference on this page two times, first when we did a find_or_create_page()
+ * to obtain the page to add it to the bio and second when we do a
+ * find_get_page() here to get the page to wait on while I/O on it is being
+ * completed.
+ * This function is also used to free up a page we might've grabbed but not
+ * used. Maybe we added it to a bio, but not submitted it for I/O. Or we
+ * submitted the I/O, but we already found the jhead so we only need to drop
+ * our references to the page.
+ */
+
+static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index,
+ struct gfs2_log_header_host *head,
+ bool *done)
+{
+ struct page *page;
+
+ page = find_get_page(jd->jd_inode->i_mapping, index);
+ wait_on_page_locked(page);
+
+ if (PageError(page))
+ *done = true;
+
+ if (!*done)
+ *done = gfs2_jhead_pg_srch(jd, head, page);
+
+ put_page(page); /* Once for find_get_page */
+ put_page(page); /* Once more for find_or_create_page */
+}
+
+/**
+ * gfs2_find_jhead - find the head of a log
+ * @jd: The journal descriptor
+ * @head: The log descriptor for the head of the log is returned here
+ *
+ * Do a search of a journal by reading it in large chunks using bios and find
+ * the valid log entry with the highest sequence number. (i.e. the log head)
+ *
+ * Returns: 0 on success, errno otherwise
+ */
+int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head,
+ bool keep_cache)
+{
+ struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+ struct address_space *mapping = jd->jd_inode->i_mapping;
+ unsigned int block = 0, blocks_submitted = 0, blocks_read = 0;
+ unsigned int bsize = sdp->sd_sb.sb_bsize;
+ unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift;
+ unsigned int shift = PAGE_SHIFT - bsize_shift;
+ unsigned int readhead_blocks = BIO_MAX_PAGES << shift;
+ struct gfs2_journal_extent *je;
+ int sz, ret = 0;
+ struct bio *bio = NULL;
+ struct page *page = NULL;
+ bool done = false;
+ errseq_t since;
+
+ memset(head, 0, sizeof(*head));
+ if (list_empty(&jd->extent_list))
+ gfs2_map_journal_extents(sdp, jd);
+
+ since = filemap_sample_wb_err(mapping);
+ list_for_each_entry(je, &jd->extent_list, list) {
+ for (; block < je->lblock + je->blocks; block++) {
+ u64 dblock;
+
+ if (!page) {
+ page = find_or_create_page(mapping,
+ block >> shift, GFP_NOFS);
+ if (!page) {
+ ret = -ENOMEM;
+ done = true;
+ goto out;
+ }
+ }
+
+ if (bio) {
+ unsigned int off;
+
+ off = (block << bsize_shift) & ~PAGE_MASK;
+ sz = bio_add_page(bio, page, bsize, off);
+ if (sz == bsize) { /* block added */
+ if (off + bsize == PAGE_SIZE) {
+ page = NULL;
+ goto page_added;
+ }
+ continue;
+ }
+ blocks_submitted = block + 1;
+ submit_bio(bio);
+ bio = NULL;
+ }
+
+ dblock = je->dblock + (block - je->lblock);
+ bio = gfs2_log_alloc_bio(sdp, dblock, gfs2_end_log_read);
+ bio->bi_opf = REQ_OP_READ;
+ sz = bio_add_page(bio, page, bsize, 0);
+ gfs2_assert_warn(sdp, sz == bsize);
+ if (bsize == PAGE_SIZE)
+ page = NULL;
+
+page_added:
+ if (blocks_submitted < blocks_read + readhead_blocks) {
+ /* Keep@least one bio in flight */
+ continue;
+ }
+
+ gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+ blocks_read += PAGE_SIZE >> bsize_shift;
+ if (done)
+ goto out; /* found */
+ }
+ }
+
+out:
+ if (bio)
+ submit_bio(bio);
+ while (blocks_read < block) {
+ gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+ blocks_read += PAGE_SIZE >> bsize_shift;
+ }
+
+ if (!ret)
+ ret = filemap_check_wb_err(mapping, since);
+
+ if (!keep_cache)
+ truncate_inode_pages(mapping, 0);
+
+ return ret;
+}
+
static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type,
u32 ld_length, u32 ld_data1)
{
diff --git a/fs/gfs2/lops.h b/fs/gfs2/lops.h
index 320fbf28d2fb..f195ffb435ac 100644
--- a/fs/gfs2/lops.h
+++ b/fs/gfs2/lops.h
@@ -25,8 +25,10 @@ extern u64 gfs2_log_bmap(struct gfs2_sbd *sdp);
extern void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
unsigned size, unsigned offset, u64 blkno);
extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page);
-extern void gfs2_log_submit_bio(struct bio **biop, int op, int op_flags);
+extern void gfs2_log_submit_bio(struct bio **biop, int opf);
extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh);
+extern int gfs2_find_jhead(struct gfs2_jdesc *jd,
+ struct gfs2_log_header_host *head, bool keep_cache);
static inline unsigned int buf_limit(struct gfs2_sbd *sdp)
{
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index abfaecde0e3d..46f6615eaf12 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -41,6 +41,7 @@
#include "dir.h"
#include "meta_io.h"
#include "trace_gfs2.h"
+#include "lops.h"
#define DO 0
#define UNDO 1
@@ -616,7 +617,7 @@ static int check_journal_clean(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd)
fs_err(sdp, "Error checking journal for spectator mount.\n");
goto out_unlock;
}
- error = gfs2_find_jhead(jd, &head);
+ error = gfs2_find_jhead(jd, &head, false);
if (error) {
fs_err(sdp, "Error parsing journal for spectator mount.\n");
goto out_unlock;
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index fa575d1676b9..389b3ef77e20 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -181,129 +181,6 @@ static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
return error;
}
-/**
- * find_good_lh - find a good log header
- * @jd: the journal
- * @blk: the segment to start searching from
- * @lh: the log header to fill in
- * @forward: if true search forward in the log, else search backward
- *
- * Call get_log_header() to get a log header for a segment, but if the
- * segment is bad, either scan forward or backward until we find a good one.
- *
- * Returns: errno
- */
-
-static int find_good_lh(struct gfs2_jdesc *jd, unsigned int *blk,
- struct gfs2_log_header_host *head)
-{
- unsigned int orig_blk = *blk;
- int error;
-
- for (;;) {
- error = get_log_header(jd, *blk, head);
- if (error <= 0)
- return error;
-
- if (++*blk == jd->jd_blocks)
- *blk = 0;
-
- if (*blk == orig_blk) {
- gfs2_consist_inode(GFS2_I(jd->jd_inode));
- return -EIO;
- }
- }
-}
-
-/**
- * jhead_scan - make sure we've found the head of the log
- * @jd: the journal
- * @head: this is filled in with the log descriptor of the head
- *
- * At this point, seg and lh should be either the head of the log or just
- * before. Scan forward until we find the head.
- *
- * Returns: errno
- */
-
-static int jhead_scan(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
-{
- unsigned int blk = head->lh_blkno;
- struct gfs2_log_header_host lh;
- int error;
-
- for (;;) {
- if (++blk == jd->jd_blocks)
- blk = 0;
-
- error = get_log_header(jd, blk, &lh);
- if (error < 0)
- return error;
- if (error == 1)
- continue;
-
- if (lh.lh_sequence == head->lh_sequence) {
- gfs2_consist_inode(GFS2_I(jd->jd_inode));
- return -EIO;
- }
- if (lh.lh_sequence < head->lh_sequence)
- break;
-
- *head = lh;
- }
-
- return 0;
-}
-
-/**
- * gfs2_find_jhead - find the head of a log
- * @jd: the journal
- * @head: the log descriptor for the head of the log is returned here
- *
- * Do a binary search of a journal and find the valid log entry with the
- * highest sequence number. (i.e. the log head)
- *
- * Returns: errno
- */
-
-int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
-{
- struct gfs2_log_header_host lh_1, lh_m;
- u32 blk_1, blk_2, blk_m;
- int error;
-
- blk_1 = 0;
- blk_2 = jd->jd_blocks - 1;
-
- for (;;) {
- blk_m = (blk_1 + blk_2) / 2;
-
- error = find_good_lh(jd, &blk_1, &lh_1);
- if (error)
- return error;
-
- error = find_good_lh(jd, &blk_m, &lh_m);
- if (error)
- return error;
-
- if (blk_1 == blk_m || blk_m == blk_2)
- break;
-
- if (lh_1.lh_sequence <= lh_m.lh_sequence)
- blk_1 = blk_m;
- else
- blk_2 = blk_m;
- }
-
- error = jhead_scan(jd, &lh_1);
- if (error)
- return error;
-
- *head = lh_1;
-
- return error;
-}
-
/**
* foreach_descriptor - go through the active part of the log
* @jd: the journal
@@ -469,7 +346,7 @@ void gfs2_recover_func(struct work_struct *work)
if (error)
goto fail_gunlock_ji;
- error = gfs2_find_jhead(jd, &head);
+ error = gfs2_find_jhead(jd, &head, true);
if (error)
goto fail_gunlock_ji;
t_jhd = ktime_get();
diff --git a/fs/gfs2/recovery.h b/fs/gfs2/recovery.h
index 5932d4b6f43e..1831a1974c8c 100644
--- a/fs/gfs2/recovery.h
+++ b/fs/gfs2/recovery.h
@@ -27,8 +27,6 @@ extern int gfs2_revoke_add(struct gfs2_jdesc *jd, u64 blkno, unsigned int where)
extern int gfs2_revoke_check(struct gfs2_jdesc *jd, u64 blkno, unsigned int where);
extern void gfs2_revoke_clean(struct gfs2_jdesc *jd);
-extern int gfs2_find_jhead(struct gfs2_jdesc *jd,
- struct gfs2_log_header_host *head);
extern int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd, bool wait);
extern void gfs2_recover_func(struct work_struct *work);
extern int __get_log_header(struct gfs2_sbd *sdp,
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index ceec631efa49..43e7c2c87014 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -45,6 +45,7 @@
#include "util.h"
#include "sys.h"
#include "xattr.h"
+#include "lops.h"
#define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x)
@@ -425,7 +426,7 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA);
- error = gfs2_find_jhead(sdp->sd_jdesc, &head);
+ error = gfs2_find_jhead(sdp->sd_jdesc, &head, false);
if (error)
goto fail;
@@ -680,7 +681,7 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
error = gfs2_jdesc_check(jd);
if (error)
break;
- error = gfs2_find_jhead(jd, &lh);
+ error = gfs2_find_jhead(jd, &lh, false);
if (error)
break;
if (!(lh.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) {
--
2.20.1
^ permalink raw reply related [flat|nested] 15+ messages in thread