Skip to content

Commit

Permalink
gfs2: read journal in large chunks
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Abhi Das authored and Andreas Gruenbacher committed May 7, 2019
1 parent d0a22a4 commit f4686c2
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 139 deletions.
3 changes: 2 additions & 1 deletion fs/gfs2/glops.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "util.h"
#include "trans.h"
#include "dir.h"
#include "lops.h"

struct workqueue_struct *gfs2_freeze_wq;

Expand Down Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions fs/gfs2/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down
212 changes: 206 additions & 6 deletions fs/gfs2/lops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 at 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)
{
Expand Down
4 changes: 3 additions & 1 deletion fs/gfs2/lops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
3 changes: 2 additions & 1 deletion fs/gfs2/ops_fstype.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "dir.h"
#include "meta_io.h"
#include "trace_gfs2.h"
#include "lops.h"

#define DO 0
#define UNDO 1
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit f4686c2

Please sign in to comment.