Commit 7276b3b0 authored by Steven Whitehouse's avatar Steven Whitehouse
Browse files

[GFS2] Tidy up meta_io code



Fix a bug in the directory reading code, where we might have dereferenced
a NULL pointer in case of OOM. Updated the directory code to use the new
& improved version of gfs2_meta_ra() which now returns the first block
that was being read. Previously it was releasing it requiring following
code to grab the block again at each point it was called.

Also turned off readahead on directory lookups since we are reading a
hash table, and therefore reading the entries in order is very
unlikely. Readahead is still used for all other calls to the
directory reading function (e.g. when growing the hash table).

Removed the DIO_START constant. Everywhere this was used, it was
used to unconditionally start i/o aside from a couple of places, so
I've removed it and made the couple of exceptions to this rule into
separate functions.

Also hunted through the other DIO flags and removed them as arguments
from functions which were always called with the same combination of
arguments.

Updated gfs2_meta_indirect_buffer to be a bit more efficient and
hopefully also be a bit easier to read.
Signed-off-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
parent 91fa4796
......@@ -448,6 +448,8 @@ static int gfs2_block_pointers(struct inode *inode, u64 lblock, int create,
u64 dblock = 0;
int boundary;
BUG_ON(maxlen == 0);
if (gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip)))
return 0;
......@@ -561,7 +563,7 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi
BUG_ON(!new);
bmap_lock(inode, create);
ret = gfs2_block_pointers(inode, lblock, create, &bh, &mp, *extlen);
ret = gfs2_block_pointers(inode, lblock, create, &bh, &mp, 32);
bmap_unlock(inode, create);
*extlen = bh.b_size >> inode->i_blkbits;
*dblock = bh.b_blocknr;
......
......@@ -106,7 +106,7 @@ static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, u64 block,
struct buffer_head *bh;
int error;
error = gfs2_meta_read(ip->i_gl, block, DIO_START | DIO_WAIT, &bh);
error = gfs2_meta_read(ip->i_gl, block, DIO_WAIT, &bh);
if (error)
return error;
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_JD)) {
......@@ -246,7 +246,7 @@ fail:
}
static int gfs2_dir_read_stuffed(struct gfs2_inode *ip, char *buf,
unsigned int offset, unsigned int size)
u64 offset, unsigned int size)
{
struct buffer_head *dibh;
int error;
......@@ -271,8 +271,8 @@ static int gfs2_dir_read_stuffed(struct gfs2_inode *ip, char *buf,
*
* Returns: The amount of data actually copied or the error
*/
static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf,
u64 offset, unsigned int size)
static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, u64 offset,
unsigned int size, unsigned ra)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
u64 lblock, dblock;
......@@ -291,8 +291,7 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf,
return 0;
if (gfs2_is_stuffed(ip))
return gfs2_dir_read_stuffed(ip, buf, (unsigned int)offset,
size);
return gfs2_dir_read_stuffed(ip, buf, offset, size);
if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip)))
return -EINVAL;
......@@ -313,34 +312,31 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf,
new = 0;
error = gfs2_extent_map(&ip->i_inode, lblock, &new,
&dblock, &extlen);
if (error)
if (error || !dblock)
goto fail;
BUG_ON(extlen < 1);
if (!ra)
extlen = 1;
bh = gfs2_meta_ra(ip->i_gl, dblock, extlen);
}
if (extlen > 1)
gfs2_meta_ra(ip->i_gl, dblock, extlen);
if (dblock) {
if (new)
error = gfs2_dir_get_new_buffer(ip, dblock, &bh);
else
error = gfs2_dir_get_existing_buffer(ip, dblock, &bh);
if (!bh) {
error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, &bh);
if (error)
goto fail;
dblock++;
extlen--;
} else
bh = NULL;
}
error = gfs2_metatype_check(sdp, bh, GFS2_METATYPE_JD);
if (error) {
brelse(bh);
goto fail;
}
dblock++;
extlen--;
memcpy(buf, bh->b_data + o, amount);
brelse(bh);
if (error)
goto fail;
bh = NULL;
buf += amount;
copied += amount;
lblock++;
o = sizeof(struct gfs2_meta_header);
}
......@@ -701,7 +697,7 @@ static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
{
int error;
error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_START | DIO_WAIT, bhp);
error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, bhp);
if (!error && gfs2_metatype_check(GFS2_SB(&dip->i_inode), *bhp, GFS2_METATYPE_LF)) {
/* printk(KERN_INFO "block num=%llu\n", leaf_no); */
error = -EIO;
......@@ -727,7 +723,7 @@ static int get_leaf_nr(struct gfs2_inode *dip, u32 index,
error = gfs2_dir_read_data(dip, (char *)&leaf_no,
index * sizeof(u64),
sizeof(u64));
sizeof(u64), 0);
if (error != sizeof(u64))
return (error < 0) ? error : -EIO;
......@@ -1095,7 +1091,7 @@ static int dir_double_exhash(struct gfs2_inode *dip)
for (block = dip->i_di.di_size >> sdp->sd_hash_bsize_shift; block--;) {
error = gfs2_dir_read_data(dip, (char *)buf,
block * sdp->sd_hash_bsize,
sdp->sd_hash_bsize);
sdp->sd_hash_bsize, 1);
if (error != sdp->sd_hash_bsize) {
if (error >= 0)
error = -EIO;
......@@ -1375,7 +1371,7 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
if (ht_offset_cur != ht_offset) {
error = gfs2_dir_read_data(dip, (char *)lp,
ht_offset * sizeof(u64),
sdp->sd_hash_bsize);
sdp->sd_hash_bsize, 1);
if (error != sdp->sd_hash_bsize) {
if (error >= 0)
error = -EIO;
......@@ -1745,7 +1741,7 @@ static int foreach_leaf(struct gfs2_inode *dip, leaf_call_t lc, void *data)
if (ht_offset_cur != ht_offset) {
error = gfs2_dir_read_data(dip, (char *)lp,
ht_offset * sizeof(u64),
sdp->sd_hash_bsize);
sdp->sd_hash_bsize, 1);
if (error != sdp->sd_hash_bsize) {
if (error >= 0)
error = -EIO;
......
......@@ -115,7 +115,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
u64 *eablk, *end;
int error;
error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr, DIO_START | DIO_WAIT, &bh);
error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr, DIO_WAIT, &bh);
if (error)
return error;
......@@ -139,7 +139,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
break;
bn = be64_to_cpu(*eablk);
error = gfs2_meta_read(ip->i_gl, bn, DIO_START | DIO_WAIT, &eabh);
error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, &eabh);
if (error)
break;
error = ea_foreach_i(ip, eabh, ea_call, data);
......@@ -453,8 +453,8 @@ static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
return -ENOMEM;
for (x = 0; x < nptrs; x++) {
error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs),
DIO_START, bh + x);
error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0,
bh + x);
if (error) {
while (x--)
brelse(bh[x]);
......@@ -464,7 +464,7 @@ static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
}
for (x = 0; x < nptrs; x++) {
error = gfs2_meta_reread(sdp, bh[x], DIO_WAIT);
error = gfs2_meta_wait(sdp, bh[x]);
if (error) {
for (; x < nptrs; x++)
brelse(bh[x]);
......@@ -938,8 +938,8 @@ static int ea_set_block(struct gfs2_inode *ip, struct gfs2_ea_request *er,
if (ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT) {
u64 *end;
error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr,
DIO_START | DIO_WAIT, &indbh);
error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr, DIO_WAIT,
&indbh);
if (error)
return error;
......@@ -1215,8 +1215,8 @@ static int ea_acl_chmod_unstuffed(struct gfs2_inode *ip,
goto out;
for (x = 0; x < nptrs; x++) {
error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs),
DIO_START, bh + x);
error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0,
bh + x);
if (error) {
while (x--)
brelse(bh[x]);
......@@ -1226,7 +1226,7 @@ static int ea_acl_chmod_unstuffed(struct gfs2_inode *ip,
}
for (x = 0; x < nptrs; x++) {
error = gfs2_meta_reread(sdp, bh[x], DIO_WAIT);
error = gfs2_meta_wait(sdp, bh[x]);
if (error) {
for (; x < nptrs; x++)
brelse(bh[x]);
......@@ -1310,8 +1310,7 @@ static int ea_dealloc_indirect(struct gfs2_inode *ip)
memset(&rlist, 0, sizeof(struct gfs2_rgrp_list));
error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr,
DIO_START | DIO_WAIT, &indbh);
error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr, DIO_WAIT, &indbh);
if (error)
return error;
......
......@@ -77,32 +77,24 @@ static void gfs2_page_inval(struct gfs2_glock *gl)
}
/**
* gfs2_page_sync - Sync the data pages (not metadata) associated with a glock
* gfs2_page_wait - Wait for writeback of data
* @gl: the glock
* @flags: DIO_START | DIO_WAIT
*
* Syncs data (not metadata) for a regular file.
* No-op for all other types.
*/
static void gfs2_page_sync(struct gfs2_glock *gl, int flags)
static void gfs2_page_wait(struct gfs2_glock *gl)
{
struct gfs2_inode *ip;
struct inode *inode;
struct address_space *mapping;
int error = 0;
struct gfs2_inode *ip = gl->gl_object;
struct inode *inode = &ip->i_inode;
struct address_space *mapping = inode->i_mapping;
int error;
ip = gl->gl_object;
inode = &ip->i_inode;
if (!ip || !S_ISREG(ip->i_di.di_mode))
if (!S_ISREG(ip->i_di.di_mode))
return;
mapping = inode->i_mapping;
if (flags & DIO_START)
filemap_fdatawrite(mapping);
if (!error && (flags & DIO_WAIT))
error = filemap_fdatawait(mapping);
error = filemap_fdatawait(mapping);
/* Put back any errors cleared by filemap_fdatawait()
so they can be caught by someone who can pass them
......@@ -115,6 +107,18 @@ static void gfs2_page_sync(struct gfs2_glock *gl, int flags)
}
static void gfs2_page_writeback(struct gfs2_glock *gl)
{
struct gfs2_inode *ip = gl->gl_object;
struct inode *inode = &ip->i_inode;
struct address_space *mapping = inode->i_mapping;
if (!S_ISREG(ip->i_di.di_mode))
return;
filemap_fdatawrite(mapping);
}
/**
* meta_go_sync - sync out the metadata for this glock
* @gl: the glock
......@@ -132,7 +136,7 @@ static void meta_go_sync(struct gfs2_glock *gl, int flags)
if (test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) {
gfs2_log_flush(gl->gl_sbd, gl);
gfs2_meta_sync(gl, flags | DIO_START | DIO_WAIT);
gfs2_meta_sync(gl);
if (flags & DIO_RELEASE)
gfs2_ail_empty_gl(gl);
}
......@@ -185,8 +189,7 @@ static void inode_go_xmote_bh(struct gfs2_glock *gl)
if (gl->gl_state != LM_ST_UNLOCKED &&
(!gh || !(gh->gh_flags & GL_SKIP))) {
error = gfs2_meta_read(gl, gl->gl_name.ln_number, DIO_START,
&bh);
error = gfs2_meta_read(gl, gl->gl_name.ln_number, 0, &bh);
if (!error)
brelse(bh);
}
......@@ -221,16 +224,18 @@ static void inode_go_sync(struct gfs2_glock *gl, int flags)
if (test_bit(GLF_DIRTY, &gl->gl_flags)) {
if (meta && data) {
gfs2_page_sync(gl, flags | DIO_START);
gfs2_page_writeback(gl);
gfs2_log_flush(gl->gl_sbd, gl);
gfs2_meta_sync(gl, flags | DIO_START | DIO_WAIT);
gfs2_page_sync(gl, flags | DIO_WAIT);
gfs2_meta_sync(gl);
gfs2_page_wait(gl);
clear_bit(GLF_DIRTY, &gl->gl_flags);
} else if (meta) {
gfs2_log_flush(gl->gl_sbd, gl);
gfs2_meta_sync(gl, flags | DIO_START | DIO_WAIT);
} else if (data)
gfs2_page_sync(gl, flags | DIO_START | DIO_WAIT);
gfs2_meta_sync(gl);
} else if (data) {
gfs2_page_writeback(gl);
gfs2_page_wait(gl);
}
if (flags & DIO_RELEASE)
gfs2_ail_empty_gl(gl);
}
......
......@@ -12,7 +12,6 @@
#include <linux/fs.h>
#define DIO_START 0x00000008
#define DIO_WAIT 0x00000010
#define DIO_METADATA 0x00000020
#define DIO_DATA 0x00000040
......
......@@ -248,13 +248,13 @@ static void buf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
if (error) {
gfs2_meta_sync(ip->i_gl, DIO_START | DIO_WAIT);
gfs2_meta_sync(ip->i_gl);
return;
}
if (pass != 1)
return;
gfs2_meta_sync(ip->i_gl, DIO_START | DIO_WAIT);
gfs2_meta_sync(ip->i_gl);
fs_info(sdp, "jid=%u: Replayed %u of %u blocks\n",
jd->jd_jid, sdp->sd_replayed_blocks, sdp->sd_found_blocks);
......@@ -726,15 +726,14 @@ static void databuf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
if (error) {
gfs2_meta_sync(ip->i_gl,
DIO_START | DIO_WAIT);
gfs2_meta_sync(ip->i_gl);
return;
}
if (pass != 1)
return;
/* data sync? */
gfs2_meta_sync(ip->i_gl, DIO_START | DIO_WAIT);
gfs2_meta_sync(ip->i_gl);
fs_info(sdp, "jid=%u: Replayed %u of %u data blocks\n",
jd->jd_jid, sdp->sd_replayed_blocks, sdp->sd_found_blocks);
......
......@@ -273,19 +273,16 @@ void gfs2_meta_inval(struct gfs2_glock *gl)
/**
* gfs2_meta_sync - Sync all buffers associated with a glock
* @gl: The glock
* @flags: DIO_START | DIO_WAIT
*
*/
void gfs2_meta_sync(struct gfs2_glock *gl, int flags)
void gfs2_meta_sync(struct gfs2_glock *gl)
{
struct address_space *mapping = gl->gl_aspace->i_mapping;
int error = 0;
int error;
if (flags & DIO_START)
filemap_fdatawrite(mapping);
if (!error && (flags & DIO_WAIT))
error = filemap_fdatawait(mapping);
filemap_fdatawrite(mapping);
error = filemap_fdatawait(mapping);
if (error)
gfs2_io_error(gl->gl_sbd);
......@@ -377,7 +374,7 @@ struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno)
* gfs2_meta_read - Read a block from disk
* @gl: The glock covering the block
* @blkno: The block number
* @flags: flags to gfs2_dreread()
* @flags: flags
* @bhp: the place where the buffer is returned (NULL on failure)
*
* Returns: errno
......@@ -386,45 +383,43 @@ struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno)
int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
struct buffer_head **bhp)
{
int error;
*bhp = getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE);
error = gfs2_meta_reread(gl->gl_sbd, *bhp, flags);
if (error)
brelse(*bhp);
if (!buffer_uptodate(*bhp))
ll_rw_block(READ, 1, bhp);
if (flags & DIO_WAIT) {
int error = gfs2_meta_wait(gl->gl_sbd, *bhp);
if (error) {
brelse(*bhp);
return error;
}
}
return error;
return 0;
}
/**
* gfs2_meta_reread - Reread a block from disk
* gfs2_meta_wait - Reread a block from disk
* @sdp: the filesystem
* @bh: The block to read
* @flags: Flags that control the read
* @bh: The block to wait for
*
* Returns: errno
*/
int gfs2_meta_reread(struct gfs2_sbd *sdp, struct buffer_head *bh, int flags)
int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
{
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
return -EIO;
if ((flags & DIO_START) && !buffer_uptodate(bh))
ll_rw_block(READ, 1, &bh);
if (flags & DIO_WAIT) {
wait_on_buffer(bh);
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
struct gfs2_trans *tr = current->journal_info;
if (tr && tr->tr_touched)
gfs2_io_error_bh(sdp, bh);
return -EIO;
}
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
return -EIO;
if (!buffer_uptodate(bh)) {
struct gfs2_trans *tr = current->journal_info;
if (tr && tr->tr_touched)
gfs2_io_error_bh(sdp, bh);
return -EIO;
}
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
return -EIO;
return 0;
}
......@@ -635,67 +630,57 @@ void gfs2_meta_cache_flush(struct gfs2_inode *ip)
int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, u64 num,
int new, struct buffer_head **bhp)
{
struct buffer_head *bh, **bh_slot = ip->i_cache + height;
int error;
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_glock *gl = ip->i_gl;
struct buffer_head *bh = NULL, **bh_slot = ip->i_cache + height;
int in_cache = 0;
spin_lock(&ip->i_spin);
bh = *bh_slot;
if (bh) {
if (bh->b_blocknr == num)
get_bh(bh);
else
bh = NULL;
if (*bh_slot && (*bh_slot)->b_blocknr == num) {
bh = *bh_slot;
get_bh(bh);
in_cache = 1;
}
spin_unlock(&ip->i_spin);
if (bh) {
if (new)
meta_prep_new(bh);
else {
error = gfs2_meta_reread(GFS2_SB(&ip->i_inode), bh,
DIO_START | DIO_WAIT);
if (error) {
brelse(bh);
return error;
}
}
} else {
if (new)
bh = gfs2_meta_new(ip->i_gl, num);
else {
error = gfs2_meta_read(ip->i_gl, num,
DIO_START | DIO_WAIT, &bh);
if (error)
return error;
}
if (!bh)
bh = getbuf(gl->gl_sbd, gl->gl_aspace, num, CREATE);
spin_lock(&ip->i_spin);
if (*bh_slot != bh) {
brelse(*bh_slot);
*bh_slot = bh;
get_bh(bh);
}
spin_unlock(&ip->i_spin);
}
if (!bh)
return -ENOBUFS;
if (new) {
if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), height)) {
brelse(bh);
return -EIO;
}
if (gfs2_assert_warn(sdp, height))
goto err;
meta_prep_new(bh);
gfs2_trans_add_bh(ip->i_gl, bh, 1);
gfs2_metatype_set(bh, GFS2_METATYPE_IN, GFS2_FORMAT_IN);
gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
} else {
u32 mtype = height ? GFS2_METATYPE_IN : GFS2_METATYPE_DI;
if (!buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
if (gfs2_meta_wait(sdp, bh))
goto err;
}
if (gfs2_metatype_check(sdp, bh, mtype))
goto err;
}
} else if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh,
(height) ? GFS2_METATYPE_IN : GFS2_METATYPE_DI)) {
brelse(bh);
return -EIO;
if (!in_cache) {
spin_lock(&ip->i_spin);
if (*bh_slot)
brelse(*bh_slot);
*bh_slot = bh;
get_bh(bh);
spin_unlock(&ip->i_spin);
}
*bhp = bh;
return 0;
err:
brelse(bh);
return -EIO;
}
/**
......@@ -704,19 +689,21 @@ int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, u64 num,
* @dblock: the starting disk block
* @extlen: the number of blocks in the extent
*
* returns: the first buffer in the extent
*/
void gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct inode *aspace = gl->gl_aspace;
struct buffer_head *first_bh, *bh;
u32 max_ra = gfs2_tune_get(sdp, gt_max_readahead) >>
sdp->sd_sb.sb_bsize_shift;
int error;
if (!extlen || !max_ra)
return;
BUG_ON(!extlen);
if (max_ra < 1)
max_ra = 1;
if (extlen > max_ra)
extlen = max_ra;
......@@ -724,11 +711,8 @@ void gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
if (buffer_uptodate(first_bh))
goto out;
if (!buffer_locked(first_bh)) {
error = gfs2_meta_reread(sdp, first_bh, DIO_START);
if (error)
goto out;
}
if (!buffer_locked(first_bh))
ll_rw_block(READ, 1, &first_bh);
dblock++;
extlen--;
......@@ -736,23 +720,18 @@ void gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen)
while (extlen) {
bh = getbuf(sdp, aspace, dblock, CREATE);
if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
error = gfs2_meta_reread(sdp, bh, DIO_START);
brelse(bh);
if (error)
goto out;
} else
brelse(