diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2017-12-08 15:11:39 -0500 |
---|---|---|
committer | Bob Peterson <rpeterso@redhat.com> | 2018-01-17 08:35:50 -0500 |
commit | c3ce5aa9b02dd912e381237e7f98d591d7358cdc (patch) | |
tree | 97db1b1f8a04435798d5c369ed2271d8921c7943 | |
parent | e8b43fe0c1e035a135be7ca3791d465fcb1b501e (diff) |
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>
-rw-r--r-- | fs/gfs2/bmap.c | 42 |
1 files changed, 25 insertions, 17 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 1110aa9f6f31..e983b5872679 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c | |||
@@ -279,14 +279,17 @@ static inline __be64 *metapointer(unsigned int height, const struct metapath *mp | |||
279 | return p + mp->mp_list[height]; | 279 | return p + mp->mp_list[height]; |
280 | } | 280 | } |
281 | 281 | ||
282 | static void gfs2_metapath_ra(struct gfs2_glock *gl, | 282 | static void gfs2_metapath_ra(struct gfs2_glock *gl, struct metapath *mp, |
283 | const struct buffer_head *bh, const __be64 *pos) | 283 | unsigned int height) |
284 | { | 284 | { |
285 | struct buffer_head *rabh; | 285 | struct buffer_head *bh = mp->mp_bh[height]; |
286 | const __be64 *pos = metapointer(height, mp); | ||
286 | const __be64 *endp = (const __be64 *)(bh->b_data + bh->b_size); | 287 | const __be64 *endp = (const __be64 *)(bh->b_data + bh->b_size); |
287 | const __be64 *t; | 288 | const __be64 *t; |
288 | 289 | ||
289 | for (t = pos; t < endp; t++) { | 290 | for (t = pos; t < endp; t++) { |
291 | struct buffer_head *rabh; | ||
292 | |||
290 | if (!*t) | 293 | if (!*t) |
291 | continue; | 294 | continue; |
292 | 295 | ||
@@ -353,12 +356,13 @@ static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp) | |||
353 | * | 356 | * |
354 | * Similar to lookup_metapath, but does lookups for a range of heights | 357 | * Similar to lookup_metapath, but does lookups for a range of heights |
355 | * | 358 | * |
356 | * Returns: error | 359 | * Returns: error or the number of buffers filled |
357 | */ | 360 | */ |
358 | 361 | ||
359 | static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h) | 362 | static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h) |
360 | { | 363 | { |
361 | unsigned int x = 0; | 364 | unsigned int x = 0; |
365 | int ret; | ||
362 | 366 | ||
363 | if (h) { | 367 | if (h) { |
364 | /* find the first buffer we need to look up. */ | 368 | /* 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) | |||
367 | break; | 371 | break; |
368 | } | 372 | } |
369 | } | 373 | } |
370 | return __fillup_metapath(ip, mp, x, h); | 374 | ret = __fillup_metapath(ip, mp, x, h); |
375 | if (ret) | ||
376 | return ret; | ||
377 | return mp->mp_aheight - x - 1; | ||
371 | } | 378 | } |
372 | 379 | ||
373 | static inline void release_metapath(struct metapath *mp) | 380 | static inline void release_metapath(struct metapath *mp) |
@@ -1309,7 +1316,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) | |||
1309 | u32 btotal = 0; | 1316 | u32 btotal = 0; |
1310 | int ret, state; | 1317 | int ret, state; |
1311 | int mp_h; /* metapath buffers are read in to this height */ | 1318 | int mp_h; /* metapath buffers are read in to this height */ |
1312 | sector_t last_ra = 0; | ||
1313 | u64 prev_bnr = 0; | 1319 | u64 prev_bnr = 0; |
1314 | bool preserve1; /* need to preserve the first meta pointer? */ | 1320 | bool preserve1; /* need to preserve the first meta pointer? */ |
1315 | 1321 | ||
@@ -1331,6 +1337,11 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) | |||
1331 | ret = lookup_metapath(ip, &mp); | 1337 | ret = lookup_metapath(ip, &mp); |
1332 | if (ret) | 1338 | if (ret) |
1333 | goto out_metapath; | 1339 | goto out_metapath; |
1340 | |||
1341 | /* issue read-ahead on metadata */ | ||
1342 | for (mp_h = 0; mp_h < mp.mp_aheight - 1; mp_h++) | ||
1343 | gfs2_metapath_ra(ip->i_gl, &mp, mp_h); | ||
1344 | |||
1334 | if (mp.mp_aheight == ip->i_height) | 1345 | if (mp.mp_aheight == ip->i_height) |
1335 | state = DEALLOC_MP_FULL; /* We have a complete metapath */ | 1346 | state = DEALLOC_MP_FULL; /* We have a complete metapath */ |
1336 | else | 1347 | else |
@@ -1352,16 +1363,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) | |||
1352 | /* Truncate a full metapath at the given strip height. | 1363 | /* Truncate a full metapath at the given strip height. |
1353 | * Note that strip_h == mp_h in order to be in this state. */ | 1364 | * Note that strip_h == mp_h in order to be in this state. */ |
1354 | case DEALLOC_MP_FULL: | 1365 | case DEALLOC_MP_FULL: |
1355 | if (mp_h > 0) { /* issue read-ahead on metadata */ | ||
1356 | __be64 *top; | ||
1357 | |||
1358 | bh = mp.mp_bh[mp_h - 1]; | ||
1359 | if (bh->b_blocknr != last_ra) { | ||
1360 | last_ra = bh->b_blocknr; | ||
1361 | top = metaptr1(mp_h - 1, &mp); | ||
1362 | gfs2_metapath_ra(ip->i_gl, bh, top); | ||
1363 | } | ||
1364 | } | ||
1365 | /* If we're truncating to a non-zero size and the mp is | 1366 | /* If we're truncating to a non-zero size and the mp is |
1366 | at the beginning of file for the strip height, we | 1367 | at the beginning of file for the strip height, we |
1367 | need to preserve the first metadata pointer. */ | 1368 | need to preserve the first metadata pointer. */ |
@@ -1427,9 +1428,16 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) | |||
1427 | case DEALLOC_FILL_MP: | 1428 | case DEALLOC_FILL_MP: |
1428 | /* Fill the buffers out to the current height. */ | 1429 | /* Fill the buffers out to the current height. */ |
1429 | ret = fillup_metapath(ip, &mp, mp_h); | 1430 | ret = fillup_metapath(ip, &mp, mp_h); |
1430 | if (ret) | 1431 | if (ret < 0) |
1431 | goto out; | 1432 | goto out; |
1432 | 1433 | ||
1434 | /* issue read-ahead on metadata */ | ||
1435 | if (mp.mp_aheight > 1) { | ||
1436 | for (; ret > 1; ret--) | ||
1437 | gfs2_metapath_ra(ip->i_gl, &mp, | ||
1438 | mp.mp_aheight - ret); | ||
1439 | } | ||
1440 | |||
1433 | /* If buffers found for the entire strip height */ | 1441 | /* If buffers found for the entire strip height */ |
1434 | if (mp.mp_aheight - 1 == strip_h) { | 1442 | if (mp.mp_aheight - 1 == strip_h) { |
1435 | state = DEALLOC_MP_FULL; | 1443 | state = DEALLOC_MP_FULL; |