]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/gfs2/log.c
[GFS2] assertion failure after writing to journaled file, umount
[mirror_ubuntu-bionic-kernel.git] / fs / gfs2 / log.c
index fbdc0dc9923e93f58bc608030229badc829f54ae..8fcfb784f9067d45b304fc07dc7f3a9005b98ccb 100644 (file)
@@ -276,7 +276,7 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
  * @blks: The number of blocks to reserve
  *
  * Note that we never give out the last few blocks of the journal. Thats
- * due to the fact that there is are a small number of header blocks
+ * due to the fact that there is a small number of header blocks
  * associated with each log flush. The exact number can't be known until
  * flush time, so we ensure that we have just enough free blocks at all
  * times to avoid running out during a log flush.
@@ -371,6 +371,58 @@ static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer
        return dist;
 }
 
+/**
+ * calc_reserved - Calculate the number of blocks to reserve when
+ *                 refunding a transaction's unused buffers.
+ * @sdp: The GFS2 superblock
+ *
+ * This is complex.  We need to reserve room for all our currently used
+ * metadata buffers (e.g. normal file I/O rewriting file time stamps) and 
+ * all our journaled data buffers for journaled files (e.g. files in the 
+ * meta_fs like rindex, or files for which chattr +j was done.)
+ * If we don't reserve enough space, gfs2_log_refund and gfs2_log_flush
+ * will count it as free space (sd_log_blks_free) and corruption will follow.
+ *
+ * We can have metadata bufs and jdata bufs in the same journal.  So each
+ * type gets its own log header, for which we need to reserve a block.
+ * In fact, each type has the potential for needing more than one header 
+ * in cases where we have more buffers than will fit on a journal page.
+ * Metadata journal entries take up half the space of journaled buffer entries.
+ * Thus, metadata entries have buf_limit (502) and journaled buffers have
+ * databuf_limit (251) before they cause a wrap around.
+ *
+ * Also, we need to reserve blocks for revoke journal entries and one for an
+ * overall header for the lot.
+ *
+ * Returns: the number of blocks reserved
+ */
+static unsigned int calc_reserved(struct gfs2_sbd *sdp)
+{
+       unsigned int reserved = 0;
+       unsigned int mbuf_limit, metabufhdrs_needed;
+       unsigned int dbuf_limit, databufhdrs_needed;
+       unsigned int revokes = 0;
+
+       mbuf_limit = buf_limit(sdp);
+       metabufhdrs_needed = (sdp->sd_log_commited_buf +
+                             (mbuf_limit - 1)) / mbuf_limit;
+       dbuf_limit = databuf_limit(sdp);
+       databufhdrs_needed = (sdp->sd_log_commited_databuf +
+                             (dbuf_limit - 1)) / dbuf_limit;
+
+       if (sdp->sd_log_commited_revoke)
+               revokes = gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
+                                         sizeof(u64));
+
+       reserved = sdp->sd_log_commited_buf + metabufhdrs_needed +
+               sdp->sd_log_commited_databuf + databufhdrs_needed +
+               revokes;
+       /* One for the overall header */
+       if (reserved)
+               reserved++;
+       return reserved;
+}
+
 static unsigned int current_tail(struct gfs2_sbd *sdp)
 {
        struct gfs2_ail *ai;
@@ -461,14 +513,14 @@ struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
        return bh;
 }
 
-static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull)
+static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail)
 {
        unsigned int dist = log_distance(sdp, new_tail, sdp->sd_log_tail);
 
        ail2_empty(sdp, new_tail);
 
        gfs2_log_lock(sdp);
-       sdp->sd_log_blks_free += dist - (pull ? 1 : 0);
+       sdp->sd_log_blks_free += dist;
        gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks);
        gfs2_log_unlock(sdp);
 
@@ -518,7 +570,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull)
        brelse(bh);
 
        if (sdp->sd_log_tail != tail)
-               log_pull_tail(sdp, tail, pull);
+               log_pull_tail(sdp, tail);
        else
                gfs2_assert_withdraw(sdp, !pull);
 
@@ -579,7 +631,10 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
        INIT_LIST_HEAD(&ai->ai_ail1_list);
        INIT_LIST_HEAD(&ai->ai_ail2_list);
 
-       gfs2_assert_withdraw(sdp, sdp->sd_log_num_buf + sdp->sd_log_num_jdata == sdp->sd_log_commited_buf);
+       gfs2_assert_withdraw(sdp,
+                            sdp->sd_log_num_buf + sdp->sd_log_num_jdata ==
+                            sdp->sd_log_commited_buf +
+                            sdp->sd_log_commited_databuf);
        gfs2_assert_withdraw(sdp,
                        sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke);
 
@@ -590,16 +645,19 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
        lops_before_commit(sdp);
        if (!list_empty(&sdp->sd_log_flush_list))
                log_flush_commit(sdp);
-       else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle)
+       else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
+               gfs2_log_lock(sdp);
+               sdp->sd_log_blks_free--; /* Adjust for unreserved buffer */
+               gfs2_log_unlock(sdp);
                log_write_header(sdp, 0, PULL);
+       }
        lops_after_commit(sdp, ai);
 
        gfs2_log_lock(sdp);
        sdp->sd_log_head = sdp->sd_log_flush_head;
-       sdp->sd_log_blks_free -= sdp->sd_log_num_hdrs;
        sdp->sd_log_blks_reserved = 0;
        sdp->sd_log_commited_buf = 0;
-       sdp->sd_log_num_hdrs = 0;
+       sdp->sd_log_commited_databuf = 0;
        sdp->sd_log_commited_revoke = 0;
 
        if (!list_empty(&ai->ai_ail1_list)) {
@@ -616,32 +674,26 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
 
 static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
 {
-       unsigned int reserved = 0;
+       unsigned int reserved;
        unsigned int old;
 
        gfs2_log_lock(sdp);
 
        sdp->sd_log_commited_buf += tr->tr_num_buf_new - tr->tr_num_buf_rm;
-       gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_buf) >= 0);
+       sdp->sd_log_commited_databuf += tr->tr_num_databuf_new -
+               tr->tr_num_databuf_rm;
+       gfs2_assert_withdraw(sdp, (((int)sdp->sd_log_commited_buf) >= 0) ||
+                            (((int)sdp->sd_log_commited_databuf) >= 0));
        sdp->sd_log_commited_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm;
        gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_revoke) >= 0);
-
-       if (sdp->sd_log_commited_buf)
-               reserved += sdp->sd_log_commited_buf;
-       if (sdp->sd_log_commited_revoke)
-               reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
-                                           sizeof(u64));
-       if (reserved)
-               reserved++;
-
+       reserved = calc_reserved(sdp);
        old = sdp->sd_log_blks_free;
        sdp->sd_log_blks_free += tr->tr_reserved -
                                 (reserved - sdp->sd_log_blks_reserved);
 
        gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free >= old);
-       gfs2_assert_withdraw(sdp,
-                            sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks +
-                            sdp->sd_log_num_hdrs);
+       gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <=
+                            sdp->sd_jdesc->jd_blocks);
 
        sdp->sd_log_blks_reserved = reserved;
 
@@ -687,13 +739,13 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf);
-       gfs2_assert_withdraw(sdp, !sdp->sd_log_num_hdrs);
        gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list));
 
        sdp->sd_log_flush_head = sdp->sd_log_head;
        sdp->sd_log_flush_wrapped = 0;
 
-       log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, 0);
+       log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT,
+                        (sdp->sd_log_tail == current_tail(sdp)) ? 0 : PULL);
 
        gfs2_assert_warn(sdp, sdp->sd_log_blks_free == sdp->sd_jdesc->jd_blocks);
        gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail);