diff options
author | Akinobu Mita <akinobu.mita@gmail.com> | 2013-09-18 21:27:26 +0900 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-10-25 09:58:12 +0100 |
commit | 65f72f2a2fe89f072d6a88e5cd69a64270b9c436 (patch) | |
tree | 05da71cdc92fcb9ea0888fdd2316c340ac89baf9 | |
parent | bb8c063c6afcd930b8da944927144f2982609638 (diff) |
[SCSI] scsi_debug: avoid partial copying PI from prot_sglist to dif_storep
If data integrity support is enabled, prot_verify_write() is called in
response to WRITE commands and it verifies protection info from
prot_sglist by comparing against data sglist, and copies protection info
to dif_storep.
When multiple blocks are transfered by a WRITE command, it verifies and
copies these blocks one by one. So if it fails to verify protection
info in the middle of blocks, the actual data transfer to fake_storep
isn't proceeded at all although protection info for some blocks are
already copied to dif_storep. Therefore, it breaks the data integrity
between fake_storep and dif_storep.
This fixes it by ensuring that copying protection info to dif_storep is
done after all blocks are successfully verified. Reusing dif_copy_prot()
with supporting the opposite direction simplifies this fix.
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Acked-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r-- | drivers/scsi/scsi_debug.c | 40 |
1 files changed, 17 insertions, 23 deletions
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 99e74d75cf08..43369e9071f7 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1790,7 +1790,7 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data, } static void dif_copy_prot(struct scsi_cmnd *SCpnt, sector_t sector, - unsigned int sectors) + unsigned int sectors, bool read) { unsigned int i, resid; struct scatterlist *psgl; @@ -1809,10 +1809,18 @@ static void dif_copy_prot(struct scsi_cmnd *SCpnt, sector_t sector, rest = start + len - dif_store_end; paddr = kmap_atomic(sg_page(psgl)) + psgl->offset; - memcpy(paddr, start, len - rest); - if (rest) - memcpy(paddr + len - rest, dif_storep, rest); + if (read) + memcpy(paddr, start, len - rest); + else + memcpy(start, paddr, len - rest); + + if (rest) { + if (read) + memcpy(paddr + len - rest, dif_storep, rest); + else + memcpy(dif_storep, paddr + len - rest, rest); + } sector += len / sizeof(*dif_storep); resid -= len; @@ -1845,7 +1853,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, ei_lba++; } - dif_copy_prot(SCpnt, start_sec, sectors); + dif_copy_prot(SCpnt, start_sec, sectors, true); dix_reads++; return 0; @@ -1928,15 +1936,12 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, { int i, j, ret; struct sd_dif_tuple *sdt; - struct scatterlist *dsgl = scsi_sglist(SCpnt); + struct scatterlist *dsgl; struct scatterlist *psgl = scsi_prot_sglist(SCpnt); void *daddr, *paddr; - sector_t tmp_sec = start_sec; - sector_t sector; + sector_t sector = start_sec; int ppage_offset; - sector = do_div(tmp_sec, sdebug_store_sectors); - BUG_ON(scsi_sg_count(SCpnt) == 0); BUG_ON(scsi_prot_sg_count(SCpnt) == 0); @@ -1964,25 +1969,13 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, sdt = paddr + ppage_offset; - ret = dif_verify(sdt, daddr + j, start_sec, ei_lba); + ret = dif_verify(sdt, daddr + j, sector, ei_lba); if (ret) { dump_sector(daddr + j, scsi_debug_sector_size); goto out; } - /* Would be great to copy this in bigger - * chunks. However, for the sake of - * correctness we need to verify each sector - * before writing it to "stable" storage - */ - memcpy(dif_storep + sector, sdt, sizeof(*sdt)); - sector++; - - if (sector == sdebug_store_sectors) - sector = 0; /* Force wrap */ - - start_sec++; ei_lba++; ppage_offset += sizeof(struct sd_dif_tuple); } @@ -1991,6 +1984,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, kunmap_atomic(daddr); } + dif_copy_prot(SCpnt, start_sec, sectors, false); dix_writes++; return 0; |