diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -443,6 +443,26 @@ raid5_end_read_request(struct bio *bi, int error); static void raid5_end_write_request(struct bio *bi, int error); +static inline void r5dev_switch_page(struct r5dev *dev, struct page *page) +{ + BUG_ON(dev->page_save != NULL); + BUG_ON(dev->page != bio_iovec_idx(&dev->req, 0)->bv_page); + /* The pointer must be restored whenever the LOCKED gets cleared. */ + dev->page_save = dev->page; + dev->page = bio_iovec_idx(&dev->req, 0)->bv_page = page; + kmap(dev->page); /* for sync_xor on 32-bit systems */ +} + +static inline void r5dev_restore_page(struct r5dev *dev) +{ + BUG_ON(dev->page_save == NULL); + BUG_ON(dev->page != bio_iovec_idx(&dev->req, 0)->bv_page); + BUG_ON(dev->page == dev->page_save); + kunmap(dev->page_save); + dev->page = bio_iovec_idx(&dev->req, 0)->bv_page = dev->page_save; + dev->page_save = NULL; +} + static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s) { raid5_conf_t *conf = sh->raid_conf; @@ -509,12 +529,8 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s) pr_debug("skip op %ld on disc %d for sector %llu\n", bi->bi_rw, i, (unsigned long long)sh->sector); - if (test_bit(R5_Direct, &sh->dev[i].flags)) { - struct page *page = bio_iovec_idx(&sh->dev[i].req, 0)->bv_page; - BUG_ON(page == sh->dev[i].page); - bio_iovec_idx(&sh->dev[i].req, 0)->bv_page = sh->dev[i].page; - kunmap(page); - } + if (test_bit(R5_Direct, &sh->dev[i].flags)) + r5dev_restore_page(&sh->dev[i]); clear_bit(R5_LOCKED, &sh->dev[i].flags); set_bit(STRIPE_HANDLE, &sh->state); @@ -978,11 +994,8 @@ static int try_reuse_data_page(struct r5dev *dev) test_bit(R5_Insync, &dev->flags)) { page = zero_copy_data(wbi, sector); if (page) { - /* The pointer must be restored whenever the LOCKED - * gets cleared. */ - kmap(page); /* for sync_xor on 32-bit systems */ - bio_iovec_idx(&dev->req, 0)->bv_page = page; set_bit(R5_Direct, &dev->flags); + r5dev_switch_page(dev, page); clear_bit(R5_UPTODATE, &dev->flags); clear_bit(R5_OVERWRITE, &dev->flags); return 1; @@ -1653,14 +1664,8 @@ static void raid5_end_write_request(struct bio *bi, int error) rdev_dec_pending(conf->disks[i].rdev, conf->mddev); - if (test_bit(R5_Direct, &sh->dev[i].flags)) { - struct page *page = bio_iovec_idx(&sh->dev[i].req, 0)->bv_page; - BUG_ON(page == sh->dev[i].page); - bio_iovec_idx(&sh->dev[i].req, 0)->bv_page = sh->dev[i].page; - kunmap(page); - } else { - BUG_ON(bio_iovec_idx(&sh->dev[i].req, 0)->bv_page != sh->dev[i].page); - } + if (test_bit(R5_Direct, &sh->dev[i].flags)) + r5dev_restore_page(&sh->dev[i]); clear_bit(R5_LOCKED, &sh->dev[i].flags); set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -232,7 +232,7 @@ struct stripe_head { struct r5dev { struct bio req; struct bio_vec vec; - struct page *page; + struct page *page, *page_save; struct bio *toread, *read, *towrite, *written; sector_t sector; /* sector of this page */ unsigned long flags; -- 1.7.1