in raid5-ppl.c [795:968]
static int ppl_recover_entry(struct ppl_log *log, struct ppl_header_entry *e,
sector_t ppl_sector)
{
struct ppl_conf *ppl_conf = log->ppl_conf;
struct mddev *mddev = ppl_conf->mddev;
struct r5conf *conf = mddev->private;
int block_size = ppl_conf->block_size;
struct page *page1;
struct page *page2;
sector_t r_sector_first;
sector_t r_sector_last;
int strip_sectors;
int data_disks;
int i;
int ret = 0;
char b[BDEVNAME_SIZE];
unsigned int pp_size = le32_to_cpu(e->pp_size);
unsigned int data_size = le32_to_cpu(e->data_size);
page1 = alloc_page(GFP_KERNEL);
page2 = alloc_page(GFP_KERNEL);
if (!page1 || !page2) {
ret = -ENOMEM;
goto out;
}
r_sector_first = le64_to_cpu(e->data_sector) * (block_size >> 9);
if ((pp_size >> 9) < conf->chunk_sectors) {
if (pp_size > 0) {
data_disks = data_size / pp_size;
strip_sectors = pp_size >> 9;
} else {
data_disks = conf->raid_disks - conf->max_degraded;
strip_sectors = (data_size >> 9) / data_disks;
}
r_sector_last = r_sector_first +
(data_disks - 1) * conf->chunk_sectors +
strip_sectors;
} else {
data_disks = conf->raid_disks - conf->max_degraded;
strip_sectors = conf->chunk_sectors;
r_sector_last = r_sector_first + (data_size >> 9);
}
pr_debug("%s: array sector first: %llu last: %llu\n", __func__,
(unsigned long long)r_sector_first,
(unsigned long long)r_sector_last);
/* if start and end is 4k aligned, use a 4k block */
if (block_size == 512 &&
(r_sector_first & (RAID5_STRIPE_SECTORS(conf) - 1)) == 0 &&
(r_sector_last & (RAID5_STRIPE_SECTORS(conf) - 1)) == 0)
block_size = RAID5_STRIPE_SIZE(conf);
/* iterate through blocks in strip */
for (i = 0; i < strip_sectors; i += (block_size >> 9)) {
bool update_parity = false;
sector_t parity_sector;
struct md_rdev *parity_rdev;
struct stripe_head sh;
int disk;
int indent = 0;
pr_debug("%s:%*s iter %d start\n", __func__, indent, "", i);
indent += 2;
memset(page_address(page1), 0, PAGE_SIZE);
/* iterate through data member disks */
for (disk = 0; disk < data_disks; disk++) {
int dd_idx;
struct md_rdev *rdev;
sector_t sector;
sector_t r_sector = r_sector_first + i +
(disk * conf->chunk_sectors);
pr_debug("%s:%*s data member disk %d start\n",
__func__, indent, "", disk);
indent += 2;
if (r_sector >= r_sector_last) {
pr_debug("%s:%*s array sector %llu doesn't need parity update\n",
__func__, indent, "",
(unsigned long long)r_sector);
indent -= 2;
continue;
}
update_parity = true;
/* map raid sector to member disk */
sector = raid5_compute_sector(conf, r_sector, 0,
&dd_idx, NULL);
pr_debug("%s:%*s processing array sector %llu => data member disk %d, sector %llu\n",
__func__, indent, "",
(unsigned long long)r_sector, dd_idx,
(unsigned long long)sector);
rdev = conf->disks[dd_idx].rdev;
if (!rdev || (!test_bit(In_sync, &rdev->flags) &&
sector >= rdev->recovery_offset)) {
pr_debug("%s:%*s data member disk %d missing\n",
__func__, indent, "", dd_idx);
update_parity = false;
break;
}
pr_debug("%s:%*s reading data member disk %s sector %llu\n",
__func__, indent, "", bdevname(rdev->bdev, b),
(unsigned long long)sector);
if (!sync_page_io(rdev, sector, block_size, page2,
REQ_OP_READ, 0, false)) {
md_error(mddev, rdev);
pr_debug("%s:%*s read failed!\n", __func__,
indent, "");
ret = -EIO;
goto out;
}
ppl_xor(block_size, page1, page2);
indent -= 2;
}
if (!update_parity)
continue;
if (pp_size > 0) {
pr_debug("%s:%*s reading pp disk sector %llu\n",
__func__, indent, "",
(unsigned long long)(ppl_sector + i));
if (!sync_page_io(log->rdev,
ppl_sector - log->rdev->data_offset + i,
block_size, page2, REQ_OP_READ, 0,
false)) {
pr_debug("%s:%*s read failed!\n", __func__,
indent, "");
md_error(mddev, log->rdev);
ret = -EIO;
goto out;
}
ppl_xor(block_size, page1, page2);
}
/* map raid sector to parity disk */
parity_sector = raid5_compute_sector(conf, r_sector_first + i,
0, &disk, &sh);
BUG_ON(sh.pd_idx != le32_to_cpu(e->parity_disk));
parity_rdev = conf->disks[sh.pd_idx].rdev;
BUG_ON(parity_rdev->bdev->bd_dev != log->rdev->bdev->bd_dev);
pr_debug("%s:%*s write parity at sector %llu, disk %s\n",
__func__, indent, "",
(unsigned long long)parity_sector,
bdevname(parity_rdev->bdev, b));
if (!sync_page_io(parity_rdev, parity_sector, block_size,
page1, REQ_OP_WRITE, 0, false)) {
pr_debug("%s:%*s parity write error!\n", __func__,
indent, "");
md_error(mddev, parity_rdev);
ret = -EIO;
goto out;
}
}
out:
if (page1)
__free_page(page1);
if (page2)
__free_page(page2);
return ret;
}