Re: [Cluster-devel] [PATCH v3 08/20] gfs2: Get rid of on-stack transactions

2021-01-28 Thread Steven Whitehouse

Hi,

On 27/01/2021 21:07, Andreas Gruenbacher wrote:

On-stack transactions were introduced to work around a transaction glock
deadlock in gfs2_trans_begin in commit d8348de06f70 ("GFS2: Fix deadlock
on journal flush").  Subsequently, transaction glocks were eliminated in
favor of the more efficient freeze glocks in commit 24972557b12c ("GFS2:
remove transaction glock") without also removing the on-stack
transactions.

It has now turned out that committing on-stack transactions
significantly complicates journal free space accounting when no system
transaction (sdp->sd_log_tr) is active at the time.  It doesn't seem
that on-stack transactions provide a significant benefit beyond their
original purpose (as an optimization), so remove them to allow fixing
the journal free space accounting in a reasonable way in a subsequent
patch.

FIXME: Can we better handle a gfs2_trans_begin failure in gfs2_ail_empty_gl?
If we skip the __gfs2_ail_flush, we'll just end up with leftover items on
gl_ail_list.


The reason for the on-stack allocation is to avoid the GFP_NOFAIL 
allocation here. Please don't add it back, we have gradually been 
working to eliminate those. Thoes allocations may not fail, but they 
might also take a long enough time that it would make little difference 
if they did. So perhaps we need to look at another solution?


Steve.




Signed-off-by: Andreas Gruenbacher 
---
  fs/gfs2/glops.c  | 29 +++--
  fs/gfs2/incore.h |  1 -
  fs/gfs2/log.c|  1 -
  fs/gfs2/trans.c  | 25 +
  fs/gfs2/trans.h  |  2 ++
  5 files changed, 22 insertions(+), 36 deletions(-)

diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 3faa421568b0..853e590ccc15 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -84,18 +84,11 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool 
fsync,
  
  static int gfs2_ail_empty_gl(struct gfs2_glock *gl)

  {
+   unsigned int revokes = atomic_read(>gl_ail_count);
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
-   struct gfs2_trans tr;
int ret;
  
-	memset(, 0, sizeof(tr));

-   INIT_LIST_HEAD(_buf);
-   INIT_LIST_HEAD(_databuf);
-   INIT_LIST_HEAD(_ail1_list);
-   INIT_LIST_HEAD(_ail2_list);
-   tr.tr_revokes = atomic_read(>gl_ail_count);
-
-   if (!tr.tr_revokes) {
+   if (!revokes) {
bool have_revokes;
bool log_in_flight;
  
@@ -122,20 +115,12 @@ static int gfs2_ail_empty_gl(struct gfs2_glock *gl)

return 0;
}
  
-	/* A shortened, inline version of gfs2_trans_begin()

- * tr->alloced is not set since the transaction structure is
- * on the stack */
-   tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes);
-   tr.tr_ip = _RET_IP_;
-   ret = gfs2_log_reserve(sdp, tr.tr_reserved);
-   if (ret < 0)
-   return ret;
-   WARN_ON_ONCE(current->journal_info);
-   current->journal_info = 
-
-   __gfs2_ail_flush(gl, 0, tr.tr_revokes);
-
+   ret = __gfs2_trans_begin(sdp, 0, revokes, GFP_NOFS | __GFP_NOFAIL);
+   if (ret)
+   goto flush;
+   __gfs2_ail_flush(gl, 0, revokes);
gfs2_trans_end(sdp);
+
  flush:
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
   GFS2_LFC_AIL_EMPTY_GL);
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 8e1ab8ed4abc..958810e533ad 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -490,7 +490,6 @@ struct gfs2_quota_data {
  enum {
TR_TOUCHED = 1,
TR_ATTACHED = 2,
-   TR_ALLOCED = 3,
  };
  
  struct gfs2_trans {

diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index e4dc23a24569..721d2d7f0efd 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -1114,7 +1114,6 @@ static void log_refund(struct gfs2_sbd *sdp, struct 
gfs2_trans *tr)
if (sdp->sd_log_tr) {
gfs2_merge_trans(sdp, tr);
} else if (tr->tr_num_buf_new || tr->tr_num_databuf_new) {
-   gfs2_assert_withdraw(sdp, test_bit(TR_ALLOCED, >tr_flags));
sdp->sd_log_tr = tr;
set_bit(TR_ATTACHED, >tr_flags);
}
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index 7705f04621f4..4f461ab37ced 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -37,8 +37,8 @@ static void gfs2_print_trans(struct gfs2_sbd *sdp, const 
struct gfs2_trans *tr)
tr->tr_num_revoke, tr->tr_num_revoke_rm);
  }
  
-int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,

-unsigned int revokes)
+int __gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
+  unsigned int revokes, gfp_t gfp_mask)
  {
struct gfs2_trans *tr;
int error;
@@ -52,7 +52,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int 
blocks,
if (!test_bit(SDF_JOURNAL_LIVE, >sd_flags))
return -EROFS;
  
-	tr = kmem_cache_zalloc(gfs2_trans_cachep, GFP_NOFS);

+   tr = 

[Cluster-devel] [PATCH v3 08/20] gfs2: Get rid of on-stack transactions

2021-01-27 Thread Andreas Gruenbacher
On-stack transactions were introduced to work around a transaction glock
deadlock in gfs2_trans_begin in commit d8348de06f70 ("GFS2: Fix deadlock
on journal flush").  Subsequently, transaction glocks were eliminated in
favor of the more efficient freeze glocks in commit 24972557b12c ("GFS2:
remove transaction glock") without also removing the on-stack
transactions.

It has now turned out that committing on-stack transactions
significantly complicates journal free space accounting when no system
transaction (sdp->sd_log_tr) is active at the time.  It doesn't seem
that on-stack transactions provide a significant benefit beyond their
original purpose (as an optimization), so remove them to allow fixing
the journal free space accounting in a reasonable way in a subsequent
patch.

FIXME: Can we better handle a gfs2_trans_begin failure in gfs2_ail_empty_gl?
If we skip the __gfs2_ail_flush, we'll just end up with leftover items on
gl_ail_list.

Signed-off-by: Andreas Gruenbacher 
---
 fs/gfs2/glops.c  | 29 +++--
 fs/gfs2/incore.h |  1 -
 fs/gfs2/log.c|  1 -
 fs/gfs2/trans.c  | 25 +
 fs/gfs2/trans.h  |  2 ++
 5 files changed, 22 insertions(+), 36 deletions(-)

diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 3faa421568b0..853e590ccc15 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -84,18 +84,11 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool 
fsync,
 
 static int gfs2_ail_empty_gl(struct gfs2_glock *gl)
 {
+   unsigned int revokes = atomic_read(>gl_ail_count);
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
-   struct gfs2_trans tr;
int ret;
 
-   memset(, 0, sizeof(tr));
-   INIT_LIST_HEAD(_buf);
-   INIT_LIST_HEAD(_databuf);
-   INIT_LIST_HEAD(_ail1_list);
-   INIT_LIST_HEAD(_ail2_list);
-   tr.tr_revokes = atomic_read(>gl_ail_count);
-
-   if (!tr.tr_revokes) {
+   if (!revokes) {
bool have_revokes;
bool log_in_flight;
 
@@ -122,20 +115,12 @@ static int gfs2_ail_empty_gl(struct gfs2_glock *gl)
return 0;
}
 
-   /* A shortened, inline version of gfs2_trans_begin()
- * tr->alloced is not set since the transaction structure is
- * on the stack */
-   tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes);
-   tr.tr_ip = _RET_IP_;
-   ret = gfs2_log_reserve(sdp, tr.tr_reserved);
-   if (ret < 0)
-   return ret;
-   WARN_ON_ONCE(current->journal_info);
-   current->journal_info = 
-
-   __gfs2_ail_flush(gl, 0, tr.tr_revokes);
-
+   ret = __gfs2_trans_begin(sdp, 0, revokes, GFP_NOFS | __GFP_NOFAIL);
+   if (ret)
+   goto flush;
+   __gfs2_ail_flush(gl, 0, revokes);
gfs2_trans_end(sdp);
+
 flush:
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
   GFS2_LFC_AIL_EMPTY_GL);
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 8e1ab8ed4abc..958810e533ad 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -490,7 +490,6 @@ struct gfs2_quota_data {
 enum {
TR_TOUCHED = 1,
TR_ATTACHED = 2,
-   TR_ALLOCED = 3,
 };
 
 struct gfs2_trans {
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index e4dc23a24569..721d2d7f0efd 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -1114,7 +1114,6 @@ static void log_refund(struct gfs2_sbd *sdp, struct 
gfs2_trans *tr)
if (sdp->sd_log_tr) {
gfs2_merge_trans(sdp, tr);
} else if (tr->tr_num_buf_new || tr->tr_num_databuf_new) {
-   gfs2_assert_withdraw(sdp, test_bit(TR_ALLOCED, >tr_flags));
sdp->sd_log_tr = tr;
set_bit(TR_ATTACHED, >tr_flags);
}
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index 7705f04621f4..4f461ab37ced 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -37,8 +37,8 @@ static void gfs2_print_trans(struct gfs2_sbd *sdp, const 
struct gfs2_trans *tr)
tr->tr_num_revoke, tr->tr_num_revoke_rm);
 }
 
-int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
-unsigned int revokes)
+int __gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
+  unsigned int revokes, gfp_t gfp_mask)
 {
struct gfs2_trans *tr;
int error;
@@ -52,7 +52,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int 
blocks,
if (!test_bit(SDF_JOURNAL_LIVE, >sd_flags))
return -EROFS;
 
-   tr = kmem_cache_zalloc(gfs2_trans_cachep, GFP_NOFS);
+   tr = kmem_cache_zalloc(gfs2_trans_cachep, gfp_mask);
if (!tr)
return -ENOMEM;
 
@@ -60,7 +60,6 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int 
blocks,
tr->tr_blocks = blocks;
tr->tr_revokes = revokes;
tr->tr_reserved = 1;
-   set_bit(TR_ALLOCED, >tr_flags);
if (blocks)
tr->tr_reserved += 6 + blocks;
if (revokes)
@@ -88,20 +87,23 @@ int