Commit 7bbbf2c2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'gfs2-4.21.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2

Pull gfs2 updates from Bob Peterson:

 - Enhancements and performance improvements to journal replay (Abhi
   Das)

 - Cleanup of gfs2_is_ordered and gfs2_is_writeback (Andreas
   Gruenbacher)

 - Fix a potential double-free in inode creation (Andreas Gruenbacher)

 - Fix the bitmap search loop that was searching too far (Andreas
   Gruenbacher)

 - Various cleanups (Andreas Gruenbacher, Bob Peterson)

 - Implement Steve Whitehouse's patch to dump nrpages for inodes (Bob
   Peterson)

 - Fix a withdraw bug where stuffed journaled data files didn't allocate
   enough journal space to be grown (Bob Peterson)

* tag 'gfs2-4.21.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: take jdata unstuff into account in do_grow
  gfs2: Dump nrpages for inodes and their glocks
  gfs2: Fix loop in gfs2_rbm_find
  gfs2: Get rid of potential double-freeing in gfs2_create_inode
  gfs2: Remove vestigial bd_ops
  gfs2: read journal in large chunks to locate the head
  gfs2: add a helper function to get_log_header that can be used elsewhere
  gfs2: changes to gfs2_log_XXX_bio
  gfs2: add more timing info to journal recovery process
  gfs2: Fix the gfs2_invalidatepage description
  gfs2: Clean up gfs2_is_{ordered,writeback}
parents b71acb0e bc020561
...@@ -820,10 +820,10 @@ static void gfs2_invalidatepage(struct page *page, unsigned int offset, ...@@ -820,10 +820,10 @@ static void gfs2_invalidatepage(struct page *page, unsigned int offset,
* @page: the page that's being released * @page: the page that's being released
* @gfp_mask: passed from Linux VFS, ignored by us * @gfp_mask: passed from Linux VFS, ignored by us
* *
* Call try_to_free_buffers() if the buffers in this page can be * Calls try_to_free_buffers() to free the buffers and put the page if the
* released. * buffers can be released.
* *
* Returns: 0 * Returns: 1 if the page was put or else 0
*/ */
int gfs2_releasepage(struct page *page, gfp_t gfp_mask) int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
...@@ -930,14 +930,14 @@ static const struct address_space_operations gfs2_jdata_aops = { ...@@ -930,14 +930,14 @@ static const struct address_space_operations gfs2_jdata_aops = {
void gfs2_set_aops(struct inode *inode) void gfs2_set_aops(struct inode *inode)
{ {
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode);
if (gfs2_is_writeback(ip)) if (gfs2_is_jdata(ip))
inode->i_mapping->a_ops = &gfs2_jdata_aops;
else if (gfs2_is_writeback(sdp))
inode->i_mapping->a_ops = &gfs2_writeback_aops; inode->i_mapping->a_ops = &gfs2_writeback_aops;
else if (gfs2_is_ordered(ip)) else if (gfs2_is_ordered(sdp))
inode->i_mapping->a_ops = &gfs2_ordered_aops; inode->i_mapping->a_ops = &gfs2_ordered_aops;
else if (gfs2_is_jdata(ip))
inode->i_mapping->a_ops = &gfs2_jdata_aops;
else else
BUG(); BUG();
} }
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/gfs2_ondisk.h> #include <linux/gfs2_ondisk.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/iomap.h> #include <linux/iomap.h>
#include <linux/ktime.h>
#include "gfs2.h" #include "gfs2.h"
#include "incore.h" #include "incore.h"
...@@ -2083,6 +2084,8 @@ static int do_grow(struct inode *inode, u64 size) ...@@ -2083,6 +2084,8 @@ static int do_grow(struct inode *inode, u64 size)
} }
error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT + error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT +
(unstuff &&
gfs2_is_jdata(ip) ? RES_JDATA : 0) +
(sdp->sd_args.ar_quota == GFS2_QUOTA_OFF ? (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF ?
0 : RES_QUOTA), 0); 0 : RES_QUOTA), 0);
if (error) if (error)
...@@ -2248,7 +2251,9 @@ int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd) ...@@ -2248,7 +2251,9 @@ int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd)
unsigned int shift = sdp->sd_sb.sb_bsize_shift; unsigned int shift = sdp->sd_sb.sb_bsize_shift;
u64 size; u64 size;
int rc; int rc;
ktime_t start, end;
start = ktime_get();
lblock_stop = i_size_read(jd->jd_inode) >> shift; lblock_stop = i_size_read(jd->jd_inode) >> shift;
size = (lblock_stop - lblock) << shift; size = (lblock_stop - lblock) << shift;
jd->nr_extents = 0; jd->nr_extents = 0;
...@@ -2268,8 +2273,9 @@ int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd) ...@@ -2268,8 +2273,9 @@ int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd)
lblock += (bh.b_size >> ip->i_inode.i_blkbits); lblock += (bh.b_size >> ip->i_inode.i_blkbits);
} while(size > 0); } while(size > 0);
fs_info(sdp, "journal %d mapped with %u extents\n", jd->jd_jid, end = ktime_get();
jd->nr_extents); fs_info(sdp, "journal %d mapped with %u extents in %lldms\n", jd->jd_jid,
jd->nr_extents, ktime_ms_delta(end, start));
return 0; return 0;
fail: fail:
......
...@@ -1777,7 +1777,7 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl) ...@@ -1777,7 +1777,7 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
* *
*/ */
void gfs2_dump_glock(struct seq_file *seq, const struct gfs2_glock *gl) void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl)
{ {
const struct gfs2_glock_operations *glops = gl->gl_ops; const struct gfs2_glock_operations *glops = gl->gl_ops;
unsigned long long dtime; unsigned long long dtime;
......
...@@ -202,7 +202,7 @@ extern int gfs2_glock_nq_num(struct gfs2_sbd *sdp, u64 number, ...@@ -202,7 +202,7 @@ extern int gfs2_glock_nq_num(struct gfs2_sbd *sdp, u64 number,
struct gfs2_holder *gh); struct gfs2_holder *gh);
extern int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs); extern int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs);
extern void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs); extern void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs);
extern void gfs2_dump_glock(struct seq_file *seq, const struct gfs2_glock *gl); extern void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl);
#define GLOCK_BUG_ON(gl,x) do { if (unlikely(x)) { gfs2_dump_glock(NULL, gl); BUG(); } } while(0) #define GLOCK_BUG_ON(gl,x) do { if (unlikely(x)) { gfs2_dump_glock(NULL, gl); BUG(); } } while(0)
extern __printf(2, 3) extern __printf(2, 3)
void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...); void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "util.h" #include "util.h"
#include "trans.h" #include "trans.h"
#include "dir.h" #include "dir.h"
#include "lops.h"
struct workqueue_struct *gfs2_freeze_wq; struct workqueue_struct *gfs2_freeze_wq;
...@@ -466,17 +467,25 @@ static int inode_go_lock(struct gfs2_holder *gh) ...@@ -466,17 +467,25 @@ static int inode_go_lock(struct gfs2_holder *gh)
* *
*/ */
static void inode_go_dump(struct seq_file *seq, const struct gfs2_glock *gl) static void inode_go_dump(struct seq_file *seq, struct gfs2_glock *gl)
{ {
const struct gfs2_inode *ip = gl->gl_object; struct gfs2_inode *ip = gl->gl_object;
struct inode *inode = &ip->i_inode;
unsigned long nrpages;
if (ip == NULL) if (ip == NULL)
return; return;
gfs2_print_dbg(seq, " I: n:%llu/%llu t:%u f:0x%02lx d:0x%08x s:%llu\n",
xa_lock_irq(&inode->i_data.i_pages);
nrpages = inode->i_data.nrpages;
xa_unlock_irq(&inode->i_data.i_pages);
gfs2_print_dbg(seq, " I: n:%llu/%llu t:%u f:0x%02lx d:0x%08x s:%llu p:%lu\n",
(unsigned long long)ip->i_no_formal_ino, (unsigned long long)ip->i_no_formal_ino,
(unsigned long long)ip->i_no_addr, (unsigned long long)ip->i_no_addr,
IF2DT(ip->i_inode.i_mode), ip->i_flags, IF2DT(ip->i_inode.i_mode), ip->i_flags,
(unsigned int)ip->i_diskflags, (unsigned int)ip->i_diskflags,
(unsigned long long)i_size_read(&ip->i_inode)); (unsigned long long)i_size_read(inode), nrpages);
} }
/** /**
......
...@@ -165,7 +165,6 @@ struct gfs2_bufdata { ...@@ -165,7 +165,6 @@ struct gfs2_bufdata {
u64 bd_blkno; u64 bd_blkno;
struct list_head bd_list; struct list_head bd_list;
const struct gfs2_log_operations *bd_ops;
struct gfs2_trans *bd_tr; struct gfs2_trans *bd_tr;
struct list_head bd_ail_st_list; struct list_head bd_ail_st_list;
...@@ -244,7 +243,7 @@ struct gfs2_glock_operations { ...@@ -244,7 +243,7 @@ struct gfs2_glock_operations {
int (*go_demote_ok) (const struct gfs2_glock *gl); int (*go_demote_ok) (const struct gfs2_glock *gl);
int (*go_lock) (struct gfs2_holder *gh); int (*go_lock) (struct gfs2_holder *gh);
void (*go_unlock) (struct gfs2_holder *gh); void (*go_unlock) (struct gfs2_holder *gh);
void (*go_dump)(struct seq_file *seq, const struct gfs2_glock *gl); void (*go_dump)(struct seq_file *seq, struct gfs2_glock *gl);
void (*go_callback)(struct gfs2_glock *gl, bool remote); void (*go_callback)(struct gfs2_glock *gl, bool remote);
const int go_type; const int go_type;
const unsigned long go_flags; const unsigned long go_flags;
......
...@@ -744,17 +744,19 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, ...@@ -744,17 +744,19 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
the gfs2 structures. */ the gfs2 structures. */
if (default_acl) { if (default_acl) {
error = __gfs2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT); error = __gfs2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
if (error)
goto fail_gunlock3;
posix_acl_release(default_acl); posix_acl_release(default_acl);
default_acl = NULL;
} }
if (acl) { if (acl) {
if (!error) error = __gfs2_set_acl(inode, acl, ACL_TYPE_ACCESS);
error = __gfs2_set_acl(inode, acl, ACL_TYPE_ACCESS); if (error)
goto fail_gunlock3;
posix_acl_release(acl); posix_acl_release(acl);
acl = NULL;
} }
if (error)
goto fail_gunlock3;
error = security_inode_init_security(&ip->i_inode, &dip->i_inode, name, error = security_inode_init_security(&ip->i_inode, &dip->i_inode, name,
&gfs2_initxattrs, NULL); &gfs2_initxattrs, NULL);
if (error) if (error)
...@@ -789,10 +791,8 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, ...@@ -789,10 +791,8 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
} }
gfs2_rsqa_delete(ip, NULL); gfs2_rsqa_delete(ip, NULL);
fail_free_acls: fail_free_acls:
if (default_acl) posix_acl_release(default_acl);
posix_acl_release(default_acl); posix_acl_release(acl);
if (acl)
posix_acl_release(acl);
fail_gunlock: fail_gunlock:
gfs2_dir_no_add(&da); gfs2_dir_no_add(&da);
gfs2_glock_dq_uninit(ghs); gfs2_glock_dq_uninit(ghs);
......
...@@ -30,16 +30,14 @@ static inline int gfs2_is_jdata(const struct gfs2_inode *ip) ...@@ -30,16 +30,14 @@ static inline int gfs2_is_jdata(const struct gfs2_inode *ip)
return ip->i_diskflags & GFS2_DIF_JDATA; return ip->i_diskflags & GFS2_DIF_JDATA;
} }
static inline int gfs2_is_writeback(const struct gfs2_inode *ip) static inline bool gfs2_is_ordered(const struct gfs2_sbd *sdp)
{ {
const struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); return sdp->sd_args.ar_data == GFS2_DATA_ORDERED;
return (sdp->sd_args.ar_data == GFS2_DATA_WRITEBACK) && !gfs2_is_jdata(ip);
} }
static inline int gfs2_is_ordered(const struct gfs2_inode *ip) static inline bool gfs2_is_writeback(const struct gfs2_sbd *sdp)
{ {
const struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); return sdp->sd_args.ar_data == GFS2_DATA_WRITEBACK;
return (sdp->sd_args.ar_data == GFS2_DATA_ORDERED) && !gfs2_is_jdata(ip);
} }
static inline int gfs2_is_dir(const struct gfs2_inode *ip) static inline int gfs2_is_dir(const struct gfs2_inode *ip)
......
...@@ -605,7 +605,6 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd) ...@@ -605,7 +605,6 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
bd->bd_blkno = bh->b_blocknr; bd->bd_blkno = bh->b_blocknr;
gfs2_remove_from_ail(bd); /* drops ref on bh */ gfs2_remove_from_ail(bd); /* drops ref on bh */
bd->bd_bh = NULL; bd->bd_bh = NULL;
bd->bd_ops = &gfs2_revoke_lops;
sdp->sd_log_num_revoke++; sdp->sd_log_num_revoke++;
atomic_inc(&gl->gl_revokes); atomic_inc(&gl->gl_revokes);
set_bit(GLF_LFLUSH, &gl->gl_flags); set_bit(GLF_LFLUSH, &gl->gl_flags);
...@@ -734,7 +733,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, ...@@ -734,7 +733,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
lh->lh_crc = cpu_to_be32(crc); lh->lh_crc = cpu_to_be32(crc);
gfs2_log_write(sdp, page, sb->s_blocksize, 0, addr); gfs2_log_write(sdp, page, sb->s_blocksize, 0, addr);
gfs2_log_flush_bio(sdp, REQ_OP_WRITE, op_flags); gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE | op_flags);
log_flush_wait(sdp); log_flush_wait(sdp);
} }
...@@ -811,7 +810,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) ...@@ -811,7 +810,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
gfs2_ordered_write(sdp); gfs2_ordered_write(sdp);
lops_before_commit(sdp, tr); lops_before_commit(sdp, tr);
gfs2_log_flush_bio(sdp, 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) { if (sdp->sd_log_head != sdp->sd_log_flush_head) {
log_flush_wait(sdp); log_flush_wait(sdp);
......
...@@ -51,12 +51,11 @@ static inline void gfs2_log_pointers_init(struct gfs2_sbd *sdp, ...@@ -51,12 +51,11 @@ static inline void gfs2_log_pointers_init(struct gfs2_sbd *sdp,
static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip) static inline void gfs2_ordered_add_inode(struct gfs2_inode *ip)
{ {
struct gfs2_sbd *sdp; struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
if (!gfs2_is_ordered(ip)) if (gfs2_is_jdata(ip) || !gfs2_is_ordered(sdp))
return; return;
sdp = GFS2_SB(&ip->i_inode);
if (!test_bit(GIF_ORDERED, &ip->i_flags)) { if (!test_bit(GIF_ORDERED, &ip->i_flags)) {
spin_lock(&sdp->sd_ordered_lock); spin_lock(&sdp->sd_ordered_lock);
if (!test_and_set_bit(GIF_ORDERED, &ip->i_flags)) if (!test_and_set_bit(GIF_ORDERED, &ip->i_flags))
......
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/list_sort.h> #include <linux/list_sort.h>
#include <linux/blkdev.h>
#include "bmap.h"
#include "dir.h" #include "dir.h"
#include "gfs2.h" #include "gfs2.h"
#include "incore.h" #include "incore.h"
...@@ -193,7 +195,6 @@ static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp, struct bio_vec *bvec, ...@@ -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 * gfs2_end_log_write - end of i/o to the log
* @bio: The bio * @bio: The bio
* @error: Status of i/o request
* *
* Each bio_vec contains either data from the pagecache or data * Each bio_vec contains either data from the pagecache or data
* relating to the log itself. Here we iterate over the bio_vec * relating to the log itself. Here we iterate over the bio_vec
...@@ -228,83 +229,86 @@ static void gfs2_end_log_write(struct bio *bio) ...@@ -228,83 +229,86 @@ static void gfs2_end_log_write(struct bio *bio)
} }
/** /**
* gfs2_log_flush_bio - Submit any pending log bio * gfs2_log_submit_bio - Submit any pending log bio
* @sdp: The superblock * @biop: Address of the bio pointer
* @op: REQ_OP * @opf: REQ_OP | op_flags
* @op_flags: req_flag_bits
* *
* Submit any pending part-built or full bio to the block device. If * Submit any pending part-built or full bio to the block device. If
* there is no pending bio, then this is a no-op. * there is no pending bio, then this is a no-op.
*/ */
void gfs2_log_flush_bio(struct gfs2_sbd *sdp, int op, int op_flags) void gfs2_log_submit_bio(struct bio **biop, int opf)
{ {
if (sdp->sd_log_bio) { struct bio *bio = *biop;
if (bio) {
struct gfs2_sbd *sdp = bio->bi_private;
atomic_inc(&sdp->sd_log_in_flight); atomic_inc(&sdp->sd_log_in_flight);
bio_set_op_attrs(sdp->sd_log_bio, op, op_flags); bio->bi_opf = opf;
submit_bio(sdp->sd_log_bio); submit_bio(bio);
sdp->sd_log_bio = NULL; *biop = NULL;
} }
} }
/** /**
* gfs2_log_alloc_bio - Allocate a new bio for log writing * gfs2_log_alloc_bio - Allocate a bio
* @sdp: The superblock * @sdp: The super block
* @blkno: The next device block number we want to write to * @blkno: The device block number we want to write to
* @end_io: The bi_end_io callback
* *
* This should never be called when there is a cached bio in the * Allocate a new bio, initialize it with the given parameters and return it.
* super block. When it returns, there will be a cached bio in the
* super block which will have as many bio_vecs as the device is
* happy to handle.
* *
* Returns: Newly allocated bio * Returns: The 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; struct super_block *sb = sdp->sd_vfs;
struct bio *bio; struct bio *bio = bio_alloc(GFP_NOIO, BIO_MAX_PAGES);
BUG_ON(sdp->sd_log_bio);
bio = bio_alloc(GFP_NOIO, BIO_MAX_PAGES);
bio->bi_iter.bi_sector = blkno * (sb->s_blocksize >> 9); bio->bi_iter.bi_sector = blkno * (sb->s_blocksize >> 9);
bio_set_dev(bio, sb->s_bdev); bio_set_dev(bio, sb->s_bdev);
bio->bi_end_io = gfs2_end_log_write; bio->bi_end_io = end_io;
bio->bi_private = sdp; bio->bi_private = sdp;
sdp->sd_log_bio = bio;
return bio; return bio;
} }
/** /**
* gfs2_log_get_bio - Get cached log bio, or allocate a new one * gfs2_log_get_bio - Get cached log bio, or allocate a new one
* @sdp: The superblock * @sdp: The super block
* @blkno: The device block number we want to write to * @blkno: The device block number we want to write to
* @bio: The bio to get or allocate
* @op: REQ_OP
* @end_io: The bi_end_io callback
* @flush: Always flush the current bio and allocate a new one?
* *
* If there is a cached bio, then if the next block number is sequential * If there is a cached bio, then if the next block number is sequential
* with the previous one, return it, otherwise flush the bio to the * with the previous one, return it, otherwise flush the bio to the
* device. If there is not a cached bio, or we just flushed it, then * device. If there is no cached bio, or we just flushed it, then
* allocate a new one. * allocate a new one.
* *
* Returns: The bio to use for log writes * 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,
struct bio **biop, int op,
bio_end_io_t *end_io, bool flush)
{ {
struct bio *bio = sdp->sd_log_bio; struct bio *bio = *biop;
u64 nblk;
if (bio) { if (bio) {
u64 nblk;
nblk = bio_end_sector(bio); nblk = bio_end_sector(bio);
nblk >>= sdp->sd_fsb2bb_shift; nblk >>= sdp->sd_fsb2bb_shift;
if (blkno == nblk) if (blkno == nblk && !flush)
return bio; return bio;
gfs2_log_flush_bio(sdp, REQ_OP_WRITE, 0); gfs2_log_submit_bio(biop, op);
} }
return gfs2_log_alloc_bio(sdp, blkno); *biop = gfs2_log_alloc_bio(sdp, blkno, end_io);
return *biop;
} }
/** /**
...@@ -326,11 +330,12 @@ void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page, ...@@ -326,11 +330,12 @@ void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
struct bio *bio; struct bio *bio;
int ret; int ret;
bio = gfs2_log_get_bio(sdp, blkno); bio = gfs2_log_get_bio(sdp, blkno, &sdp->sd_log_bio, REQ_OP_WRITE,
gfs2_end_log_write, false);
ret = bio_add_page(bio, page, size, offset); ret = bio_add_page(bio, page, size, offset);
if (ret == 0) { if (ret == 0) {
gfs2_log_flush_bio(sdp, REQ_OP_WRITE, 0); bio = gfs2_log_get_bio(sdp, blkno, &sdp->sd_log_bio,
bio = gfs2_log_alloc_bio(sdp, blkno); REQ_OP_WRITE, gfs2_end_log_write, true);
ret = bio_add_page(bio, page, size, offset); ret = bio_add_page(bio, page, size, offset);
WARN_ON(ret == 0); WARN_ON(ret == 0);
} }
...@@ -370,6 +375,184 @@ void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page) ...@@ -370,6 +375,184 @@ void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page)
gfs2_log_bmap(sdp)); 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, static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type,
u32 ld_length, u32 ld_data1) u32 ld_length, u32 ld_data1)
{ {
......
...@@ -30,8 +30,10 @@ extern u64 gfs2_log_bmap(struct gfs2_sbd *sdp); ...@@ -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, extern void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
unsigned size, unsigned offset, u64 blkno); unsigned size, unsigned offset, u64 blkno);
extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page); extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page);
extern void gfs2_log_flush_bio(struct gfs2_sbd *sdp, 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 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) static inline unsigned int buf_limit(struct gfs2_sbd *sdp)
{ {
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "dir.h" #include "dir.h"
#include "meta_io.h" #include "meta_io.h"
#include "trace_gfs2.h" #include "trace_gfs2.h"
#include "lops.h"
#define DO 0 #define DO 0
#define UNDO 1 #define UNDO 1
......
...@@ -120,6 +120,35 @@ void gfs2_revoke_clean(struct gfs2_jdesc *jd) ...@@ -120,6 +120,35 @@ void gfs2_revoke_clean(struct gfs2_jdesc *jd)
} }
} }
int __get_log_header(struct gfs2_sbd *sdp, const struct gfs2_log_header *lh,
unsigned int blkno, struct gfs2_log_header_host *head)
{
u32 hash, crc;
if (lh->lh_header.mh_magic != cpu_to_be32(GFS2_MAGIC) ||
lh->lh_header.mh_type != cpu_to_be32(GFS2_METATYPE_LH) ||
(blkno && be32_to_cpu(lh->lh_blkno) != blkno))
return 1;
hash = crc32(~0, lh, LH_V1_SIZE - 4);
hash = ~crc32_le_shift(hash, 4); /* assume lh_hash is zero */
if (be32_to_cpu(lh->lh_hash) != hash)
return 1;
crc = crc32c(~0, (void *)lh + LH_V1_SIZE + 4,
sdp->sd_sb.sb_bsize - LH_V1_SIZE - 4);
if ((lh->lh_crc != 0 && be32_to_cpu(lh->lh_crc) != crc))
return 1;
head->lh_sequence = be64_to_cpu(lh->lh_sequence);
head->lh_flags = be32_to_cpu(lh->lh_flags);
head->lh_tail = be32_to_cpu(lh->lh_tail);
head->lh_blkno = be32_to_cpu(lh->lh_blkno);
return 0;
}
/** /**
* get_log_header - read the log header for a given segment * get_log_header - read the log header for a given segment
* @jd: the journal * @jd: the journal
...@@ -137,159 +166,18 @@ void gfs2_revoke_clean(struct gfs2_jdesc *jd) ...@@ -137,159 +166,18 @@ void gfs2_revoke_clean(struct gfs2_jdesc *jd)
static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk, static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
struct gfs2_log_header_host *head) struct gfs2_log_header_host *head)
{ {
struct gfs2_log_header *lh; struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
struct buffer_head *bh; struct buffer_head *bh;
u32 hash, crc;
int error; int error;
error = gfs2_replay_read_block(jd, blk, &bh); error = gfs2_replay_read_block(jd, blk, &bh);
if (error) if (error)
return error; return error;
lh = (void *)bh->b_data;
hash = crc32(~0, lh, LH_V1_SIZE - 4);
hash = ~crc32_le_shift(hash, 4); /* assume lh_hash is zero */
crc = crc32c(~0, (void *)lh + LH_V1_SIZE + 4,
bh->b_size - LH_V1_SIZE - 4);
error = lh->lh_header.mh_magic != cpu_to_be32(GFS2_MAGIC) ||
lh->lh_header.mh_type != cpu_to_be32(GFS2_METATYPE_LH) ||
be32_to_cpu(lh->lh_blkno) != blk ||
be32_to_cpu(lh->lh_hash) != hash ||
(lh->lh_crc != 0 && be32_to_cpu(lh->lh_crc) != crc);
error = __get_log_header(sdp, (const struct gfs2_log_header *)bh->b_data,
blk, head);
brelse(bh); brelse(bh);
if (!error) {
head->lh_sequence = be64_to_cpu(lh->lh_sequence);
head->lh_flags = be32_to_cpu(lh->lh_flags);
head->lh_tail = be32_to_cpu(lh->lh_tail);
head->lh_blkno = be32_to_cpu(lh->lh_blkno);
}
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; return error;
} }
...@@ -460,6 +348,8 @@ void gfs2_recover_func(struct work_struct *work) ...@@ -460,6 +348,8 @@ void gfs2_recover_func(struct work_struct *work)
if (error) if (error)
goto fail_gunlock_ji; goto fail_gunlock_ji;
t_jhd = ktime_get(); t_jhd = ktime_get();
fs_info(sdp, "jid=%u: Journal head lookup took %lldms\n", jd->jd_jid,
ktime_ms_delta(t_jhd, t_jlck));
if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) {
fs_info(sdp, "jid=%u: Acquiring the transaction lock...\n", fs_info(sdp, "jid=%u: Acquiring the transaction lock...\n",
......
...@@ -27,10 +27,11 @@ extern int gfs2_revoke_add(struct gfs2_jdesc *jd, u64 blkno, unsigned int where) ...@@ -27,10 +27,11 @@ 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 int gfs2_revoke_check(struct gfs2_jdesc *jd, u64 blkno, unsigned int where);
extern void gfs2_revoke_clean(struct gfs2_jdesc *jd); 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 int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd, bool wait);
extern void gfs2_recover_func(struct work_struct *work); extern void gfs2_recover_func(struct work_struct *work);
extern int __get_log_header(struct gfs2_sbd *sdp,
const struct gfs2_log_header *lh, unsigned int blkno,
struct gfs2_log_header_host *head);
#endif /* __RECOVERY_DOT_H__ */ #endif /* __RECOVERY_DOT_H__ */
...@@ -1780,9 +1780,9 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext, ...@@ -1780,9 +1780,9 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext,
goto next_iter; goto next_iter;
} }
if (ret == -E2BIG) { if (ret == -E2BIG) {
n += rbm->bii - initial_bii;
rbm->bii = 0; rbm->bii = 0;
rbm->offset = 0; rbm->offset = 0;
n += (rbm->bii - initial_bii);
goto res_covered_end_of_rgrp; goto res_covered_end_of_rgrp;
} }
return ret; return ret;
...@@ -2256,7 +2256,7 @@ static void rgblk_free(struct gfs2_sbd *sdp, struct gfs2_rgrpd *rgd, ...@@ -2256,7 +2256,7 @@ static void rgblk_free(struct gfs2_sbd *sdp, struct gfs2_rgrpd *rgd,
* *
*/ */
void gfs2_rgrp_dump(struct seq_file *seq, const struct gfs2_glock *gl) void gfs2_rgrp_dump(struct seq_file *seq, struct gfs2_glock *gl)
{ {
struct gfs2_rgrpd *rgd = gl->gl_object; struct gfs2_rgrpd *rgd = gl->gl_object;
struct gfs2_blkreserv *trs; struct gfs2_blkreserv *trs;
......
...@@ -72,7 +72,7 @@ extern void gfs2_rlist_add(struct gfs2_inode *ip, struct gfs2_rgrp_list *rlist, ...@@ -72,7 +72,7 @@ extern void gfs2_rlist_add(struct gfs2_inode *ip, struct gfs2_rgrp_list *rlist,
extern void gfs2_rlist_alloc(struct gfs2_rgrp_list *rlist); extern void gfs2_rlist_alloc(struct gfs2_rgrp_list *rlist);
extern void gfs2_rlist_free(struct gfs2_rgrp_list *rlist); extern void gfs2_rlist_free(struct gfs2_rgrp_list *rlist);
extern u64 gfs2_ri_total(struct gfs2_sbd *sdp); extern u64 gfs2_ri_total(struct gfs2_sbd *sdp);
extern void gfs2_rgrp_dump(struct seq_file *seq, const struct gfs2_glock *gl); extern void gfs2_rgrp_dump(struct seq_file *seq, struct gfs2_glock *gl);
extern int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset, extern int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
struct buffer_head *bh, struct buffer_head *bh,
const struct gfs2_bitmap *bi, unsigned minlen, u64 *ptrimmed); const struct gfs2_bitmap *bi, unsigned minlen, u64 *ptrimmed);
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "util.h" #include "util.h"
#include "sys.h" #include "sys.h"
#include "xattr.h" #include "xattr.h"
#include "lops.h"
#define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x) #define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x)
......
...@@ -124,15 +124,13 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) ...@@ -124,15 +124,13 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
} }
static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl, static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl,
struct buffer_head *bh, struct buffer_head *bh)
const struct gfs2_log_operations *lops)
{ {
struct gfs2_bufdata *bd; struct gfs2_bufdata *bd;
bd = kmem_cache_zalloc(gfs2_bufdata_cachep, GFP_NOFS | __GFP_NOFAIL); bd = kmem_cache_zalloc(gfs2_bufdata_cachep, GFP_NOFS | __GFP_NOFAIL);
bd->bd_bh = bh; bd->bd_bh = bh;
bd->bd_gl = gl; bd->bd_gl = gl;
bd->bd_ops = lops;
INIT_LIST_HEAD(&bd->bd_list); INIT_LIST_HEAD(&bd->bd_list);
bh->b_private = bd; bh->b_private = bd;
return bd; return bd;
...@@ -169,7 +167,7 @@ void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh) ...@@ -169,7 +167,7 @@ void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh)
gfs2_log_unlock(sdp); gfs2_log_unlock(sdp);
unlock_buffer(bh); unlock_buffer(bh);
if (bh->b_private == NULL) if (bh->b_private == NULL)
bd = gfs2_alloc_bufdata(gl, bh, &gfs2_databuf_lops); bd = gfs2_alloc_bufdata(gl, bh);
else else
bd = bh->b_private; bd = bh->b_private;
lock_buffer(bh); lock_buffer(bh);
...@@ -210,7 +208,7 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) ...@@ -210,7 +208,7 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh)
unlock_buffer(bh); unlock_buffer(bh);
lock_page(bh->b_page); lock_page(bh->b_page);
if (bh->b_private == NULL) if (bh->b_private == NULL)
bd = gfs2_alloc_bufdata(gl, bh, &gfs2_buf_lops); bd = gfs2_alloc_bufdata(gl, bh);
else else
bd = bh->b_private; bd = bh->b_private;
unlock_page(bh->b_page); unlock_page(bh->b_page);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment