History

md/r5cache: improve recovery with read ahead page pool

In r5cache recovery, the journal device is scanned page by page. Currently, we use sync_page_io() to read journal device. This is not efficient when we have to recovery many stripes from the journal. To improve the speed of recovery, this patch introduces a read ahead page pool (ra_pool) to recovery_ctx. With ra_pool, multiple consecutive pages are read in one IO. Then the recovery code read the journal from ra_pool. With ra_pool, r5l_recovery_ctx has become much bigger. Therefore, r5l_recovery_log() is refactored so r5l_recovery_ctx is not using stack space.

Song Liu committed 9 years ago

Commit effe6ee1 parent aaf9f121 file · +175 −46Browse files

Unified · Split

drivers/md/raid5-cache.c+175−46
@@ -30,6 +30,7 @@ · 29 unmodified lines
30 * underneath hardware sector size. only works with PAGE_SIZE == 409630 * underneath hardware sector size. only works with PAGE_SIZE == 4096
31 */31 */
32#define BLOCK_SECTORS (8)32#define BLOCK_SECTORS (8)
33#define BLOCK_SECTOR_SHIFT (3)
33 34
34/*35/*
35 * log->max_free_space is min(1/4 disk size, 10G reclaimable space).36 * log->max_free_space is min(1/4 disk size, 10G reclaimable space).
@@ -1552,6 +1553,8 @@ bool r5l_log_disk_error(struct r5conf *conf) · 1516 unmodified lines
1552 return ret;1553 return ret;
1553}1554}
1554 1555
1556#define R5L_RECOVERY_PAGE_POOL_SIZE 256
1557
1555struct r5l_recovery_ctx {1558struct r5l_recovery_ctx {
1556 struct page *meta_page; /* current meta */1559 struct page *meta_page; /* current meta */
1557 sector_t meta_total_blocks; /* total size of current meta and data */1560 sector_t meta_total_blocks; /* total size of current meta and data */
@@ -1560,18 +1563,131 @@ struct r5l_recovery_ctx { · 2 unmodified lines
1560 int data_parity_stripes; /* number of data_parity stripes */1563 int data_parity_stripes; /* number of data_parity stripes */
1561 int data_only_stripes; /* number of data_only stripes */1564 int data_only_stripes; /* number of data_only stripes */
1562 struct list_head cached_list;1565 struct list_head cached_list;
1566
1567 /*
1568 * read ahead page pool (ra_pool)
1569 * in recovery, log is read sequentially. It is not efficient to
1570 * read every page with sync_page_io(). The read ahead page pool
1571 * reads multiple pages with one IO, so further log read can
1572 * just copy data from the pool.
1573 */
1574 struct page *ra_pool[R5L_RECOVERY_PAGE_POOL_SIZE];
1575 sector_t pool_offset; /* offset of first page in the pool */
1576 int total_pages; /* total allocated pages */
1577 int valid_pages; /* pages with valid data */
1578 struct bio *ra_bio; /* bio to do the read ahead */
1563};1579};
1564 1580
1581static int r5l_recovery_allocate_ra_pool(struct r5l_log *log,
1582 struct r5l_recovery_ctx *ctx)
1583{
1584 struct page *page;
1585
1586 ctx->ra_bio = bio_alloc_bioset(GFP_KERNEL, BIO_MAX_PAGES, log->bs);
1587 if (!ctx->ra_bio)
1588 return -ENOMEM;
1589
1590 ctx->valid_pages = 0;
1591 ctx->total_pages = 0;
1592 while (ctx->total_pages < R5L_RECOVERY_PAGE_POOL_SIZE) {
1593 page = alloc_page(GFP_KERNEL);
1594
1595 if (!page)
1596 break;
1597 ctx->ra_pool[ctx->total_pages] = page;
1598 ctx->total_pages += 1;
1599 }
1600
1601 if (ctx->total_pages == 0) {
1602 bio_put(ctx->ra_bio);
1603 return -ENOMEM;
1604 }
1605
1606 ctx->pool_offset = 0;
1607 return 0;
1608}
1609
1610static void r5l_recovery_free_ra_pool(struct r5l_log *log,
1611 struct r5l_recovery_ctx *ctx)
1612{
1613 int i;
1614
1615 for (i = 0; i < ctx->total_pages; ++i)
1616 put_page(ctx->ra_pool[i]);
1617 bio_put(ctx->ra_bio);
1618}
1619
1620/*
1621 * fetch ctx->valid_pages pages from offset
1622 * In normal cases, ctx->valid_pages == ctx->total_pages after the call.
1623 * However, if the offset is close to the end of the journal device,
1624 * ctx->valid_pages could be smaller than ctx->total_pages
1625 */
1626static int r5l_recovery_fetch_ra_pool(struct r5l_log *log,
1627 struct r5l_recovery_ctx *ctx,
1628 sector_t offset)
1629{
1630 bio_reset(ctx->ra_bio);
1631 ctx->ra_bio->bi_bdev = log->rdev->bdev;
1632 bio_set_op_attrs(ctx->ra_bio, REQ_OP_READ, 0);
1633 ctx->ra_bio->bi_iter.bi_sector = log->rdev->data_offset + offset;
1634
1635 ctx->valid_pages = 0;
1636 ctx->pool_offset = offset;
1637
1638 while (ctx->valid_pages < ctx->total_pages) {
1639 bio_add_page(ctx->ra_bio,
1640 ctx->ra_pool[ctx->valid_pages], PAGE_SIZE, 0);
1641 ctx->valid_pages += 1;
1642
1643 offset = r5l_ring_add(log, offset, BLOCK_SECTORS);
1644
1645 if (offset == 0) /* reached end of the device */
1646 break;
1647 }
1648
1649 return submit_bio_wait(ctx->ra_bio);
1650}
1651
1652/*
1653 * try read a page from the read ahead page pool, if the page is not in the
1654 * pool, call r5l_recovery_fetch_ra_pool
1655 */
1656static int r5l_recovery_read_page(struct r5l_log *log,
1657 struct r5l_recovery_ctx *ctx,
1658 struct page *page,
1659 sector_t offset)
1660{
1661 int ret;
1662
1663 if (offset < ctx->pool_offset ||
1664 offset >= ctx->pool_offset + ctx->valid_pages * BLOCK_SECTORS) {
1665 ret = r5l_recovery_fetch_ra_pool(log, ctx, offset);
1666 if (ret)
1667 return ret;
1668 }
1669
1670 BUG_ON(offset < ctx->pool_offset ||
1671 offset >= ctx->pool_offset + ctx->valid_pages * BLOCK_SECTORS);
1672
1673 memcpy(page_address(page),
1674 page_address(ctx->ra_pool[(offset - ctx->pool_offset) >>
1675 BLOCK_SECTOR_SHIFT]),
1676 PAGE_SIZE);
1677 return 0;
1678}
1679
1565static int r5l_recovery_read_meta_block(struct r5l_log *log,1680static int r5l_recovery_read_meta_block(struct r5l_log *log,
1566 struct r5l_recovery_ctx *ctx)1681 struct r5l_recovery_ctx *ctx)
1567{1682{
1568 struct page *page = ctx->meta_page;1683 struct page *page = ctx->meta_page;
1569 struct r5l_meta_block *mb;1684 struct r5l_meta_block *mb;
1570 u32 crc, stored_crc;1685 u32 crc, stored_crc;
1686 int ret;
1571 1687
1572 if (!sync_page_io(log->rdev, ctx->pos, PAGE_SIZE, page, REQ_OP_READ, 0,1688 ret = r5l_recovery_read_page(log, ctx, page, ctx->pos);
1573 false))1689 if (ret != 0)
1574 return -EIO;1690 return ret;
1575 1691
1576 mb = page_address(page);1692 mb = page_address(page);
1577 stored_crc = le32_to_cpu(mb->checksum);1693 stored_crc = le32_to_cpu(mb->checksum);
@@ -1653,8 +1769,7 @@ static void r5l_recovery_load_data(struct r5l_log *log, · 75 unmodified lines
1653 raid5_compute_sector(conf,1769 raid5_compute_sector(conf,
1654 le64_to_cpu(payload->location), 0,1770 le64_to_cpu(payload->location), 0,
1655 &dd_idx, sh);1771 &dd_idx, sh);
1656 sync_page_io(log->rdev, log_offset, PAGE_SIZE,1772 r5l_recovery_read_page(log, ctx, sh->dev[dd_idx].page, log_offset);
1657 sh->dev[dd_idx].page, REQ_OP_READ, 0, false);
1658 sh->dev[dd_idx].log_checksum =1773 sh->dev[dd_idx].log_checksum =
1659 le32_to_cpu(payload->checksum[0]);1774 le32_to_cpu(payload->checksum[0]);
1660 ctx->meta_total_blocks += BLOCK_SECTORS;1775 ctx->meta_total_blocks += BLOCK_SECTORS;
@@ -1673,17 +1788,15 @@ static void r5l_recovery_load_parity(struct r5l_log *log, · 12 unmodified lines
1673 struct r5conf *conf = mddev->private;1788 struct r5conf *conf = mddev->private;
1674 1789
1675 ctx->meta_total_blocks += BLOCK_SECTORS * conf->max_degraded;1790 ctx->meta_total_blocks += BLOCK_SECTORS * conf->max_degraded;
1676 sync_page_io(log->rdev, log_offset, PAGE_SIZE,1791 r5l_recovery_read_page(log, ctx, sh->dev[sh->pd_idx].page, log_offset);
1677 sh->dev[sh->pd_idx].page, REQ_OP_READ, 0, false);
1678 sh->dev[sh->pd_idx].log_checksum =1792 sh->dev[sh->pd_idx].log_checksum =
1679 le32_to_cpu(payload->checksum[0]);1793 le32_to_cpu(payload->checksum[0]);
1680 set_bit(R5_Wantwrite, &sh->dev[sh->pd_idx].flags);1794 set_bit(R5_Wantwrite, &sh->dev[sh->pd_idx].flags);
1681 1795
1682 if (sh->qd_idx >= 0) {1796 if (sh->qd_idx >= 0) {
1683 sync_page_io(log->rdev,1797 r5l_recovery_read_page(
1684 r5l_ring_add(log, log_offset, BLOCK_SECTORS),1798 log, ctx, sh->dev[sh->qd_idx].page,
1685 PAGE_SIZE, sh->dev[sh->qd_idx].page,1799 r5l_ring_add(log, log_offset, BLOCK_SECTORS));
1686 REQ_OP_READ, 0, false);
1687 sh->dev[sh->qd_idx].log_checksum =1800 sh->dev[sh->qd_idx].log_checksum =
1688 le32_to_cpu(payload->checksum[1]);1801 le32_to_cpu(payload->checksum[1]);
1689 set_bit(R5_Wantwrite, &sh->dev[sh->qd_idx].flags);1802 set_bit(R5_Wantwrite, &sh->dev[sh->qd_idx].flags);
@@ -1814,14 +1927,15 @@ r5c_recovery_replay_stripes(struct list_head *cached_stripe_list, · 124 unmodified lines
1814 1927
1815/* if matches return 0; otherwise return -EINVAL */1928/* if matches return 0; otherwise return -EINVAL */
1816static int1929static int
1817r5l_recovery_verify_data_checksum(struct r5l_log *log, struct page *page,1930r5l_recovery_verify_data_checksum(struct r5l_log *log,
1931 struct r5l_recovery_ctx *ctx,
1932 struct page *page,
1818 sector_t log_offset, __le32 log_checksum)1933 sector_t log_offset, __le32 log_checksum)
1819{1934{
1820 void *addr;1935 void *addr;
1821 u32 checksum;1936 u32 checksum;
1822 1937
1823 sync_page_io(log->rdev, log_offset, PAGE_SIZE,1938 r5l_recovery_read_page(log, ctx, page, log_offset);
1824 page, REQ_OP_READ, 0, false);
1825 addr = kmap_atomic(page);1939 addr = kmap_atomic(page);
1826 checksum = crc32c_le(log->uuid_checksum, addr, PAGE_SIZE);1940 checksum = crc32c_le(log->uuid_checksum, addr, PAGE_SIZE);
1827 kunmap_atomic(addr);1941 kunmap_atomic(addr);
@@ -1853,17 +1967,17 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log, · 25 unmodified lines
1853 1967
1854 if (payload->header.type == R5LOG_PAYLOAD_DATA) {1968 if (payload->header.type == R5LOG_PAYLOAD_DATA) {
1855 if (r5l_recovery_verify_data_checksum(1969 if (r5l_recovery_verify_data_checksum(
1856 log, page, log_offset,1970 log, ctx, page, log_offset,
1857 payload->checksum[0]) < 0)1971 payload->checksum[0]) < 0)
1858 goto mismatch;1972 goto mismatch;
1859 } else if (payload->header.type == R5LOG_PAYLOAD_PARITY) {1973 } else if (payload->header.type == R5LOG_PAYLOAD_PARITY) {
1860 if (r5l_recovery_verify_data_checksum(1974 if (r5l_recovery_verify_data_checksum(
1861 log, page, log_offset,1975 log, ctx, page, log_offset,
1862 payload->checksum[0]) < 0)1976 payload->checksum[0]) < 0)
1863 goto mismatch;1977 goto mismatch;
1864 if (conf->max_degraded == 2 && /* q for RAID 6 */1978 if (conf->max_degraded == 2 && /* q for RAID 6 */
1865 r5l_recovery_verify_data_checksum(1979 r5l_recovery_verify_data_checksum(
1866 log, page,1980 log, ctx, page,
1867 r5l_ring_add(log, log_offset,1981 r5l_ring_add(log, log_offset,
1868 BLOCK_SECTORS),1982 BLOCK_SECTORS),
1869 payload->checksum[1]) < 0)1983 payload->checksum[1]) < 0)
@@ -2241,55 +2355,70 @@ static void r5c_recovery_flush_data_only_stripes(struct r5l_log *log, · 371 unmodified lines
2241static int r5l_recovery_log(struct r5l_log *log)2355static int r5l_recovery_log(struct r5l_log *log)
2242{2356{
2243 struct mddev *mddev = log->rdev->mddev;2357 struct mddev *mddev = log->rdev->mddev;
2244 struct r5l_recovery_ctx ctx;2358 struct r5l_recovery_ctx *ctx;
2245 int ret;2359 int ret;
2246 sector_t pos;2360 sector_t pos;
2247 2361
2248 ctx.pos = log->last_checkpoint;2362 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
2249 ctx.seq = log->last_cp_seq;2363 if (!ctx)
2250 ctx.meta_page = alloc_page(GFP_KERNEL);
2251 ctx.data_only_stripes = 0;
2252 ctx.data_parity_stripes = 0;
2253 INIT_LIST_HEAD(&ctx.cached_list);
2254
2255 if (!ctx.meta_page)
2256 return -ENOMEM;2364 return -ENOMEM;
2257 2365
2258 ret = r5c_recovery_flush_log(log, &ctx);2366 ctx->pos = log->last_checkpoint;
2259 __free_page(ctx.meta_page);2367 ctx->seq = log->last_cp_seq;
2368 INIT_LIST_HEAD(&ctx->cached_list);
2369 ctx->meta_page = alloc_page(GFP_KERNEL);
2260 2370
2261 if (ret)2371 if (!ctx->meta_page) {
2262 return ret;2372 ret = -ENOMEM;
2373 goto meta_page;
2374 }
2375
2376 if (r5l_recovery_allocate_ra_pool(log, ctx) != 0) {
2377 ret = -ENOMEM;
2378 goto ra_pool;
2379 }
2263 2380
2264 pos = ctx.pos;2381 ret = r5c_recovery_flush_log(log, ctx);
2265 ctx.seq += 10000;
2266 2382
2383 if (ret)
2384 goto error;
2267 2385
2268 if ((ctx.data_only_stripes == 0) && (ctx.data_parity_stripes == 0))2386 pos = ctx->pos;
2387 ctx->seq += 10000;
2388
2389 if ((ctx->data_only_stripes == 0) && (ctx->data_parity_stripes == 0))
2269 pr_debug("md/raid:%s: starting from clean shutdown\n",2390 pr_debug("md/raid:%s: starting from clean shutdown\n",
2270 mdname(mddev));2391 mdname(mddev));
2271 else2392 else
2272 pr_debug("md/raid:%s: recovering %d data-only stripes and %d data-parity stripes\n",2393 pr_debug("md/raid:%s: recovering %d data-only stripes and %d data-parity stripes\n",
2273 mdname(mddev), ctx.data_only_stripes,2394 mdname(mddev), ctx->data_only_stripes,
2274 ctx.data_parity_stripes);2395 ctx->data_parity_stripes);
2275 2396
2276 if (ctx.data_only_stripes == 0) {2397 if (ctx->data_only_stripes == 0) {
2277 log->next_checkpoint = ctx.pos;2398 log->next_checkpoint = ctx->pos;
2278 r5l_log_write_empty_meta_block(log, ctx.pos, ctx.seq++);2399 r5l_log_write_empty_meta_block(log, ctx->pos, ctx->seq++);
2279 ctx.pos = r5l_ring_add(log, ctx.pos, BLOCK_SECTORS);2400 ctx->pos = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
2280 } else if (r5c_recovery_rewrite_data_only_stripes(log, &ctx)) {2401 } else if (r5c_recovery_rewrite_data_only_stripes(log, ctx)) {
2281 pr_err("md/raid:%s: failed to rewrite stripes to journal\n",2402 pr_err("md/raid:%s: failed to rewrite stripes to journal\n",
2282 mdname(mddev));2403 mdname(mddev));
2283 return -EIO;2404 ret = -EIO;
2405 goto error;
2284 }2406 }
2285 2407
2286 log->log_start = ctx.pos;2408 log->log_start = ctx->pos;
2287 log->seq = ctx.seq;2409 log->seq = ctx->seq;
2288 log->last_checkpoint = pos;2410 log->last_checkpoint = pos;
2289 r5l_write_super(log, pos);2411 r5l_write_super(log, pos);
2290 2412
2291 r5c_recovery_flush_data_only_stripes(log, &ctx);2413 r5c_recovery_flush_data_only_stripes(log, ctx);
2292 return 0;2414 ret = 0;
2415error:
2416 r5l_recovery_free_ra_pool(log, ctx);
2417ra_pool:
2418 __free_page(ctx->meta_page);
2419meta_page:
2420 kfree(ctx);
2421 return ret;
2293}2422}
2294 2423
2295static void r5l_write_super(struct r5l_log *log, sector_t cp)2424static void r5l_write_super(struct r5l_log *log, sector_t cp)