Signed-off-by: Abhi Das <a...@redhat.com>
---
fs/gfs2/incore.h | 7 +++-
fs/gfs2/log.c | 2 +-
fs/gfs2/log.h | 1 +
fs/gfs2/lops.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++----
fs/gfs2/lops.h | 1 +
fs/gfs2/recovery.c | 118 ++++-------------------------------------------------
fs/gfs2/recovery.h | 1 +
7 files changed, 127 insertions(+), 118 deletions(-)
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index f303616..82d50a0 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -494,18 +494,23 @@ struct gfs2_journal_extent {
u64 blocks;
};
+enum {
+ JDF_RECOVERY = 1,
+ JDF_JHEAD = 2,
+};
+
struct gfs2_jdesc {
struct list_head jd_list;
struct list_head extent_list;
struct work_struct jd_work;
struct inode *jd_inode;
unsigned long jd_flags;
-#define JDF_RECOVERY 1
unsigned int jd_jid;
unsigned int jd_blocks;
int jd_recover_error;
/* Replay stuff */
+ struct gfs2_log_header_host jd_jhead;
unsigned int jd_found_blocks;
unsigned int jd_found_revokes;
unsigned int jd_replayed_blocks;
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index 15a3a8c..523cf79 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -469,7 +469,7 @@ static void log_pull_tail(struct gfs2_sbd *sdp, unsigned
int new_tail)
}
-static void log_flush_wait(struct gfs2_sbd *sdp)
+void log_flush_wait(struct gfs2_sbd *sdp)
{
DEFINE_WAIT(wait);
diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h
index 19c93df..95b1ea3 100644
--- a/fs/gfs2/log.h
+++ b/fs/gfs2/log.h
@@ -65,6 +65,7 @@ extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp,
unsigned int nstruct,
extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks);
extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl);
+extern void log_flush_wait(struct gfs2_sbd *sdp);
extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd);
extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control
*wbc);
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 4da6055..48d7e6d 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -16,6 +16,7 @@
#include <linux/gfs2_ondisk.h>
#include <linux/bio.h>
#include <linux/fs.h>
+#include <linux/crc32.h>
#include "gfs2.h"
#include "incore.h"
@@ -228,6 +229,60 @@ static void gfs2_end_log_write(struct bio *bio, int error)
wake_up(&sdp->sd_log_flush_wait);
}
+static void gfs2_end_log_read(struct bio *bio, int error)
+{
+ struct gfs2_jdesc *jd = bio->bi_private;
+ struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+ struct page *page;
+ struct bio_vec *bvec;
+ int i, last;
+
+ if (error) {
+ sdp->sd_log_error = error;
+ fs_err(sdp, "Error %d reading from journal, jid=%u\n", error,
+ jd->jd_jid);
+ wake_up(&sdp->sd_logd_waitq);
+ }
+
+ bio_for_each_segment_all(bvec, bio, i) {
+ struct gfs2_log_header_host uninitialized_var(lh);
+ void *ptr;
+ const u32 nothing = 0;
+ u32 hash;
+
+ page = bvec->bv_page;
+ ptr = page_address(page);
+ hash = crc32_le((u32)~0, ptr, sizeof(struct gfs2_log_header) -
sizeof(u32));
+ hash = crc32_le(hash, (unsigned char const *)¬hing,
sizeof(nothing));
+ hash ^= (u32)~0;
+ error = gfs2_log_header_in(&lh, ptr);
+ last = page_private(page);
+ mempool_free(page, gfs2_page_pool);
+
+ if (!test_bit(JDF_JHEAD, &jd->jd_flags))
+ continue;
+
+ if (!error && lh.lh_hash == hash) {
+ if (lh.lh_sequence > jd->jd_jhead.lh_sequence)
+ jd->jd_jhead = lh;
+ else
+ goto found;
+ }
+
+ if (last) {
+ found:
+ clear_bit(JDF_JHEAD, &jd->jd_flags);
+ smp_mb__after_clear_bit();
+ wake_up_bit(&jd->jd_flags, JDF_JHEAD);
+ }
+ }
+
+ bio_put(bio);
+
+ if (atomic_dec_and_test(&sdp->sd_log_in_flight))
+ wake_up(&sdp->sd_log_flush_wait);
+}
+
/**
* gfs2_log_flush_bio - Submit any pending log bio
* @sdp: The superblock
@@ -259,7 +314,8 @@ void gfs2_log_flush_bio(struct gfs2_sbd *sdp, int rw)
* Returns: Newly allocated bio
*/
-static struct bio *gfs2_log_alloc_bio(struct gfs2_sbd *sdp, u64 blkno)
+static struct bio *gfs2_log_alloc_bio(struct gfs2_sbd *sdp, u64 blkno,
+ bio_end_io_t *end_io)
{
struct super_block *sb = sdp->sd_vfs;
unsigned nrvecs = bio_get_nr_vecs(sb->s_bdev);
@@ -276,7 +332,7 @@ static struct bio *gfs2_log_alloc_bio(struct gfs2_sbd *sdp,
u64 blkno)
bio->bi_sector = blkno * (sb->s_blocksize >> 9);
bio->bi_bdev = sb->s_bdev;
- bio->bi_end_io = gfs2_end_log_write;
+ bio->bi_end_io = end_io;
bio->bi_private = sdp;
sdp->sd_log_bio = bio;
@@ -297,7 +353,7 @@ static struct bio *gfs2_log_alloc_bio(struct gfs2_sbd *sdp,
u64 blkno)
* Returns: The bio to use for log writes
*/
-static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno)
+static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno, int rw)
{
struct bio *bio = sdp->sd_log_bio;
u64 nblk;
@@ -307,10 +363,11 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp,
u64 blkno)
nblk >>= sdp->sd_fsb2bb_shift;
if (blkno == nblk)
return bio;
- gfs2_log_flush_bio(sdp, WRITE);
+ gfs2_log_flush_bio(sdp, rw);
}
- return gfs2_log_alloc_bio(sdp, blkno);
+ return gfs2_log_alloc_bio(sdp, blkno, rw == WRITE ? gfs2_end_log_write :
+ gfs2_end_log_read);
}
@@ -333,11 +390,11 @@ static void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
struct bio *bio;
int ret;
- bio = gfs2_log_get_bio(sdp, blkno);
+ bio = gfs2_log_get_bio(sdp, blkno, WRITE);
ret = bio_add_page(bio, page, size, offset);
if (ret == 0) {
gfs2_log_flush_bio(sdp, WRITE);
- bio = gfs2_log_alloc_bio(sdp, blkno);
+ bio = gfs2_log_alloc_bio(sdp, blkno, gfs2_end_log_write);
ret = bio_add_page(bio, page, size, offset);
WARN_ON(ret == 0);
}
@@ -375,6 +432,50 @@ void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page
*page)
gfs2_log_write(sdp, page, sb->s_blocksize, 0);
}
+void gfs2_log_read_extent(struct gfs2_jdesc *jd, u64 dblock,
+ unsigned int blocks, int last)
+{
+ struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+ struct page *page;
+ int i, ret;
+ struct bio *bio;
+ struct super_block *sb = sdp->sd_vfs;
+
+ for (i=0; i<blocks; i++) {
+ page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
+ /* flag the last page of the journal we plan to read in */
+ page_private(page) = (last && i == (blocks - 1));
+
+ bio = gfs2_log_get_bio(sdp, dblock + i, READ);
+ ret = bio_add_page(bio, page, sb->s_blocksize, 0);
+ if (ret == 0) {
+ gfs2_log_flush_bio(sdp, READ);
+ log_flush_wait(sdp);
+ bio = gfs2_log_alloc_bio(sdp, dblock + i,
gfs2_end_log_read);
+ ret = bio_add_page(bio, page, sb->s_blocksize, 0);
+ WARN_ON(ret == 0);
+ }
+ bio->bi_private = jd;
+ }
+}
+
+void gfs2_log_read(struct gfs2_jdesc *jd)
+{
+ struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+ int last = 0;
+ struct gfs2_journal_extent *je;
+
+ if (list_empty(&jd->extent_list))
+ map_journal_extents(sdp, jd);
+
+ list_for_each_entry(je, &jd->extent_list, extent_list) {
+ last = list_is_last(&je->extent_list, &jd->extent_list);
+ gfs2_log_read_extent(jd, je->dblock, je->blocks, last);
+ gfs2_log_flush_bio(sdp, READ);
+ log_flush_wait(sdp);
+ }
+}
+
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 06793e3..b8a3886 100644
--- a/fs/gfs2/lops.h
+++ b/fs/gfs2/lops.h
@@ -30,6 +30,7 @@ extern const struct gfs2_log_operations *gfs2_log_ops[];
extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page);
extern void gfs2_log_flush_bio(struct gfs2_sbd *sdp, int rw);
extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh);
+extern void gfs2_log_read(struct gfs2_jdesc *jd);
static inline unsigned int buf_limit(struct gfs2_sbd *sdp)
{
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index 4b042db..4fd2b23 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -118,7 +118,7 @@ void gfs2_revoke_clean(struct gfs2_jdesc *jd)
}
}
-static int gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf)
+int gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf)
{
const struct gfs2_log_header *str = buf;
@@ -177,126 +177,26 @@ static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
}
/**
- * 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
+ * Do a 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;
+ memset(&jd->jd_jhead, 0, sizeof(struct gfs2_log_header_host));
+ set_bit(JDF_JHEAD, &jd->jd_flags);
+ gfs2_log_read(jd);
- blk_1 = 0;
- blk_2 = jd->jd_blocks - 1;
+ if (test_bit(JDF_JHEAD, &jd->jd_flags))
+ wait_on_bit(&jd->jd_flags, JDF_JHEAD, TASK_INTERRUPTIBLE);
- 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;
+ *head = jd->jd_jhead;
+ return 0;
}
/**
diff --git a/fs/gfs2/recovery.h b/fs/gfs2/recovery.h
index 11fdfab..cd691ff 100644
--- a/fs/gfs2/recovery.h
+++ b/fs/gfs2/recovery.h
@@ -29,6 +29,7 @@ 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_log_header_in(struct gfs2_log_header_host *lh, const void
*buf);
extern int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd, bool wait);
extern void gfs2_recover_func(struct work_struct *work);