On 08/30/2018 12:25 PM, Greg Edwards wrote: > When T10 PI is enabled on a backing device for the iblock backstore, the > PI SGL for the entire command is attached to the first bio only. This > works fine if the command is covered by a single bio, but can result in > ref tag errors in the client for the other bios in a multi-bio command, > e.g. > > [ 47.631236] sda: ref tag error at location 2048 (rcvd 0) > [ 47.637658] sda: ref tag error at location 4096 (rcvd 0) > [ 47.644228] sda: ref tag error at location 6144 (rcvd 0) > > The command will be split into multiple bios if the number of data SG > elements exceeds BIO_MAX_PAGES (see iblock_get_bio()). > > The bios may later be split again in the block layer on the host after > iblock_submit_bios(), depending on the queue limits of the backing > device. The block and SCSI layers will pass through the whole PI SGL > down to the LLDD however that first bio is split up, but the LLDD may > only use the portion that corresponds to the data length (depends on the > LLDD, tested with scsi_debug). > > Split the PI SGL across the bios in the command, so each bio's > bio_integrity_payload contains the protection information for the data > in the bio. Use an sg_mapping_iter to keep track of where we are in PI > SGL, so we know where to start with the next bio. > > Signed-off-by: Greg Edwards <gedwards@xxxxxxx> > --- > Changes from v2: > * add back min(cmd->t_prot_nents, BIO_MAX_PAGES) for bio_integrity_alloc() > from v1 > > Changes from v1: > * expand commit message > * use an sg_mapping_iter to track where we are in the PI SGL > > > drivers/target/target_core_iblock.c | 54 ++++++++++++++++++++--------- > 1 file changed, 37 insertions(+), 17 deletions(-) > > diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c > index ce1321a5cb7b..db78b1c808e8 100644 > --- a/drivers/target/target_core_iblock.c > +++ b/drivers/target/target_core_iblock.c > @@ -635,14 +635,15 @@ static ssize_t iblock_show_configfs_dev_params(struct se_device *dev, char *b) > } > > static int > -iblock_alloc_bip(struct se_cmd *cmd, struct bio *bio) > +iblock_alloc_bip(struct se_cmd *cmd, struct bio *bio, > + struct sg_mapping_iter *miter) > { > struct se_device *dev = cmd->se_dev; > struct blk_integrity *bi; > struct bio_integrity_payload *bip; > struct iblock_dev *ib_dev = IBLOCK_DEV(dev); > - struct scatterlist *sg; > - int i, rc; > + int rc; > + size_t resid, len; > > bi = bdev_get_integrity(ib_dev->ibd_bd); > if (!bi) { > @@ -650,31 +651,39 @@ iblock_alloc_bip(struct se_cmd *cmd, struct bio *bio) > return -ENODEV; > } > > - bip = bio_integrity_alloc(bio, GFP_NOIO, cmd->t_prot_nents); > + bip = bio_integrity_alloc(bio, GFP_NOIO, > + min_t(unsigned int, cmd->t_prot_nents, BIO_MAX_PAGES)); > if (IS_ERR(bip)) { > pr_err("Unable to allocate bio_integrity_payload\n"); > return PTR_ERR(bip); > } > > - bip->bip_iter.bi_size = (cmd->data_length / dev->dev_attrib.block_size) * > - dev->prot_length; > - bip->bip_iter.bi_sector = bio->bi_iter.bi_sector; > + bip->bip_iter.bi_size = bio_integrity_bytes(bi, bio_sectors(bio)); > + bip_set_seed(bip, bio->bi_iter.bi_sector); > > pr_debug("IBLOCK BIP Size: %u Sector: %llu\n", bip->bip_iter.bi_size, > (unsigned long long)bip->bip_iter.bi_sector); > > - for_each_sg(cmd->t_prot_sg, sg, cmd->t_prot_nents, i) { > + resid = bip->bip_iter.bi_size; > + while (resid > 0 && sg_miter_next(miter)) { > > - rc = bio_integrity_add_page(bio, sg_page(sg), sg->length, > - sg->offset); > - if (rc != sg->length) { > + len = min_t(size_t, miter->length, resid); > + rc = bio_integrity_add_page(bio, miter->page, len, > + offset_in_page(miter->addr)); > + if (rc != len) { > pr_err("bio_integrity_add_page() failed; %d\n", rc); > + sg_miter_stop(miter); > return -ENOMEM; > } > > - pr_debug("Added bio integrity page: %p length: %d offset; %d\n", > - sg_page(sg), sg->length, sg->offset); > + pr_debug("Added bio integrity page: %p length: %lu offset: %lu\n", > + miter->page, len, offset_in_page(miter->addr)); > + > + resid -= len; > + if (len < miter->length) > + miter->consumed -= miter->length - len; > } > + sg_miter_stop(miter); > > return 0; > } > @@ -686,12 +695,13 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > struct se_device *dev = cmd->se_dev; > sector_t block_lba = target_to_linux_sector(dev, cmd->t_task_lba); > struct iblock_req *ibr; > - struct bio *bio, *bio_start; > + struct bio *bio; > struct bio_list list; > struct scatterlist *sg; > u32 sg_num = sgl_nents; > unsigned bio_cnt; > - int i, op, op_flags = 0; > + int i, rc, op, op_flags = 0; > + struct sg_mapping_iter prot_miter; > > if (data_direction == DMA_TO_DEVICE) { > struct iblock_dev *ib_dev = IBLOCK_DEV(dev); > @@ -726,13 +736,17 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > if (!bio) > goto fail_free_ibr; > > - bio_start = bio; > bio_list_init(&list); > bio_list_add(&list, bio); > > refcount_set(&ibr->pending, 2); > bio_cnt = 1; > > + if (cmd->prot_type && dev->dev_attrib.pi_prot_type) > + sg_miter_start(&prot_miter, cmd->t_prot_sg, cmd->t_prot_nents, > + op == REQ_OP_READ ? SG_MITER_FROM_SG : > + SG_MITER_TO_SG); > + > for_each_sg(sgl, sg, sgl_nents, i) { > /* > * XXX: if the length the device accepts is shorter than the > @@ -741,6 +755,12 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > */ > while (bio_add_page(bio, sg_page(sg), sg->length, sg->offset) > != sg->length) { > + if (cmd->prot_type && dev->dev_attrib.pi_prot_type) { > + rc = iblock_alloc_bip(cmd, bio, &prot_miter); > + if (rc) > + goto fail_put_bios; > + } > + > if (bio_cnt >= IBLOCK_MAX_BIO_PER_TASK) { > iblock_submit_bios(&list); > bio_cnt = 0; > @@ -762,7 +782,7 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > } > > if (cmd->prot_type && dev->dev_attrib.pi_prot_type) { > - int rc = iblock_alloc_bip(cmd, bio_start); > + rc = iblock_alloc_bip(cmd, bio, &prot_miter); > if (rc) > goto fail_put_bios; > } > Seems ok to me. Reviewed-by: Mike Christie <mchristi@xxxxxxxxxx>