gfs2: read journal in large chunks to locate the head
authorAbhi Das <adas@redhat.com>
Fri, 9 Nov 2018 15:57:20 +0000 (09:57 -0600)
committerAndreas Gruenbacher <agruenba@redhat.com>
Tue, 11 Dec 2018 16:50:36 +0000 (17:50 +0100)
Use bio(s) to read in the journal sequentially in large chunks and
locate the head of the journal.

This version addresses the issues Christoph pointed out w.r.t error handling
and using deprecated API.

Signed-off-by: Abhi Das <adas@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Cc: Christoph Hellwig <hch@infradead.org>
fs/gfs2/glops.c
fs/gfs2/log.c
fs/gfs2/lops.c
fs/gfs2/lops.h
fs/gfs2/ops_fstype.c
fs/gfs2/recovery.c
fs/gfs2/recovery.h
fs/gfs2/super.c

index c63bee9adb6a8e175d8e2cfc78abe197264acf5e..f79ef9525e33ac2f508fac41977699dc31365590 100644 (file)
@@ -28,6 +28,7 @@
 #include "util.h"
 #include "trans.h"
 #include "dir.h"
+#include "lops.h"
 
 struct workqueue_struct *gfs2_freeze_wq;
 
index e7c6e9fc62b5a6539c2095e4bdacafef789a7700..4dcd2b48189e91b8fd31b2f00bd74f52fd2ed92c 100644 (file)
@@ -734,7 +734,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, addr);
-       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);
 }
 
@@ -811,7 +811,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);
index 2295042bc6259887c87bfcb786e5d4bfc511662c..94dcab655bc0211ddffd934886850e3c808903c3 100644 (file)
@@ -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"
@@ -193,7 +195,6 @@ static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp, struct bio_vec *bvec,
 /**
  * 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
@@ -230,20 +231,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;
        }
@@ -304,7 +304,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);
@@ -375,6 +375,184 @@ 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;
+
+       bio_for_each_segment_all(bvec, bio, i) {
+               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)
+{
+       struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+       struct address_space *mapping = jd->jd_inode->i_mapping;
+       struct gfs2_journal_extent *je;
+       u32 block, read_idx = 0, submit_idx = 0, index = 0;
+       int shift = PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift;
+       int blocks_per_page = 1 << shift, sz, ret = 0;
+       struct bio *bio = NULL;
+       struct page *page;
+       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 = 0; block < je->blocks; block += blocks_per_page) {
+                       index = (je->lblock + block) >> shift;
+
+                       page = find_or_create_page(mapping, index, GFP_NOFS);
+                       if (!page) {
+                               ret = -ENOMEM;
+                               done = true;
+                               goto out;
+                       }
+
+                       if (bio) {
+                               sz = bio_add_page(bio, page, PAGE_SIZE, 0);
+                               if (sz == PAGE_SIZE)
+                                       goto page_added;
+                               submit_idx = index;
+                               submit_bio(bio);
+                               bio = NULL;
+                       }
+
+                       bio = gfs2_log_alloc_bio(sdp,
+                                                je->dblock + (index << shift),
+                                                gfs2_end_log_read);
+                       bio->bi_opf = REQ_OP_READ;
+                       sz = bio_add_page(bio, page, PAGE_SIZE, 0);
+                       gfs2_assert_warn(sdp, sz == PAGE_SIZE);
+
+page_added:
+                       if (submit_idx <= read_idx + BIO_MAX_PAGES) {
+                               /* Keep at least one bio in flight */
+                               continue;
+                       }
+
+                       gfs2_jhead_process_page(jd, read_idx++, head, &done);
+                       if (done)
+                               goto out;  /* found */
+               }
+       }
+
+out:
+       if (bio)
+               submit_bio(bio);
+       while (read_idx <= index)
+               gfs2_jhead_process_page(jd, read_idx++, head, &done);
+
+       if (!ret)
+               ret = filemap_check_wb_err(mapping, since);
+
+       return ret;
+}
+
 static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type,
                                      u32 ld_length, u32 ld_data1)
 {
index 711c4d89c0639cd0a3b70142070dbcde745ba3f6..331160fc568b87ce8fdcc11585542639a8a4b6f6 100644 (file)
@@ -30,8 +30,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);
 
 static inline unsigned int buf_limit(struct gfs2_sbd *sdp)
 {
index b041cb8ae383d8fd3642f00e9f2985594573e044..1179763f6370f5064efd6c6001f9b9b9387cc8ef 100644 (file)
@@ -41,6 +41,7 @@
 #include "dir.h"
 #include "meta_io.h"
 #include "trace_gfs2.h"
+#include "lops.h"
 
 #define DO 0
 #define UNDO 1
index 2dac430653826b15a1d4f158c167eabdf437b2c3..7389e445a7a7b82bbb9b0b3f14e65fbf8212c346 100644 (file)
@@ -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
index 11d81248be85c001a3f51bc04d5654ce41360e7c..99575ab8120294481d7222bbf859adbe92207d12 100644 (file)
@@ -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,
index ca71163ff7cfda20234c434a1932ecbff72ed267..d4b11c903971cf48ff3c6f1e62cca9e76ad4e884 100644 (file)
@@ -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)