static int ppl_recover_entry()

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;
}