gfs2: Fix metadata read-ahead during truncate

The metadata read-ahead algorithm broke when switching from recursive to
non-recursive delete: the current algorithm reads ahead blocks at height
N - 1 while deallocating the blocks at hight N.  However, deallocating
the blocks at height N requires a complete walk of the metadata tree,
not only down to height N - 1.  Consequently, all blocks below height
N - 1 will be accessed without read-ahead.

Fix this by issuing read-aheads as early as possible, after each
metapath lookup.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
This commit is contained in:
Andreas Gruenbacher 2017-12-08 21:11:39 +01:00 committed by Bob Peterson
parent e8b43fe0c1
commit c3ce5aa9b0
1 changed files with 25 additions and 17 deletions

View File

@ -279,14 +279,17 @@ static inline __be64 *metapointer(unsigned int height, const struct metapath *mp
return p + mp->mp_list[height]; return p + mp->mp_list[height];
} }
static void gfs2_metapath_ra(struct gfs2_glock *gl, static void gfs2_metapath_ra(struct gfs2_glock *gl, struct metapath *mp,
const struct buffer_head *bh, const __be64 *pos) unsigned int height)
{ {
struct buffer_head *rabh; struct buffer_head *bh = mp->mp_bh[height];
const __be64 *pos = metapointer(height, mp);
const __be64 *endp = (const __be64 *)(bh->b_data + bh->b_size); const __be64 *endp = (const __be64 *)(bh->b_data + bh->b_size);
const __be64 *t; const __be64 *t;
for (t = pos; t < endp; t++) { for (t = pos; t < endp; t++) {
struct buffer_head *rabh;
if (!*t) if (!*t)
continue; continue;
@ -353,12 +356,13 @@ static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp)
* *
* Similar to lookup_metapath, but does lookups for a range of heights * Similar to lookup_metapath, but does lookups for a range of heights
* *
* Returns: error * Returns: error or the number of buffers filled
*/ */
static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h) static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h)
{ {
unsigned int x = 0; unsigned int x = 0;
int ret;
if (h) { if (h) {
/* find the first buffer we need to look up. */ /* find the first buffer we need to look up. */
@ -367,7 +371,10 @@ static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h)
break; break;
} }
} }
return __fillup_metapath(ip, mp, x, h); ret = __fillup_metapath(ip, mp, x, h);
if (ret)
return ret;
return mp->mp_aheight - x - 1;
} }
static inline void release_metapath(struct metapath *mp) static inline void release_metapath(struct metapath *mp)
@ -1309,7 +1316,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
u32 btotal = 0; u32 btotal = 0;
int ret, state; int ret, state;
int mp_h; /* metapath buffers are read in to this height */ int mp_h; /* metapath buffers are read in to this height */
sector_t last_ra = 0;
u64 prev_bnr = 0; u64 prev_bnr = 0;
bool preserve1; /* need to preserve the first meta pointer? */ bool preserve1; /* need to preserve the first meta pointer? */
@ -1331,6 +1337,11 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
ret = lookup_metapath(ip, &mp); ret = lookup_metapath(ip, &mp);
if (ret) if (ret)
goto out_metapath; goto out_metapath;
/* issue read-ahead on metadata */
for (mp_h = 0; mp_h < mp.mp_aheight - 1; mp_h++)
gfs2_metapath_ra(ip->i_gl, &mp, mp_h);
if (mp.mp_aheight == ip->i_height) if (mp.mp_aheight == ip->i_height)
state = DEALLOC_MP_FULL; /* We have a complete metapath */ state = DEALLOC_MP_FULL; /* We have a complete metapath */
else else
@ -1352,16 +1363,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
/* Truncate a full metapath at the given strip height. /* Truncate a full metapath at the given strip height.
* Note that strip_h == mp_h in order to be in this state. */ * Note that strip_h == mp_h in order to be in this state. */
case DEALLOC_MP_FULL: case DEALLOC_MP_FULL:
if (mp_h > 0) { /* issue read-ahead on metadata */
__be64 *top;
bh = mp.mp_bh[mp_h - 1];
if (bh->b_blocknr != last_ra) {
last_ra = bh->b_blocknr;
top = metaptr1(mp_h - 1, &mp);
gfs2_metapath_ra(ip->i_gl, bh, top);
}
}
/* If we're truncating to a non-zero size and the mp is /* If we're truncating to a non-zero size and the mp is
at the beginning of file for the strip height, we at the beginning of file for the strip height, we
need to preserve the first metadata pointer. */ need to preserve the first metadata pointer. */
@ -1427,9 +1428,16 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
case DEALLOC_FILL_MP: case DEALLOC_FILL_MP:
/* Fill the buffers out to the current height. */ /* Fill the buffers out to the current height. */
ret = fillup_metapath(ip, &mp, mp_h); ret = fillup_metapath(ip, &mp, mp_h);
if (ret) if (ret < 0)
goto out; goto out;
/* issue read-ahead on metadata */
if (mp.mp_aheight > 1) {
for (; ret > 1; ret--)
gfs2_metapath_ra(ip->i_gl, &mp,
mp.mp_aheight - ret);
}
/* If buffers found for the entire strip height */ /* If buffers found for the entire strip height */
if (mp.mp_aheight - 1 == strip_h) { if (mp.mp_aheight - 1 == strip_h) {
state = DEALLOC_MP_FULL; state = DEALLOC_MP_FULL;