[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>
This commit is contained in:
committed by
James Bottomley
parent
bb8c063c6a
commit
65f72f2a2f
@@ -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,
|
static void dif_copy_prot(struct scsi_cmnd *SCpnt, sector_t sector,
|
||||||
unsigned int sectors)
|
unsigned int sectors, bool read)
|
||||||
{
|
{
|
||||||
unsigned int i, resid;
|
unsigned int i, resid;
|
||||||
struct scatterlist *psgl;
|
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;
|
rest = start + len - dif_store_end;
|
||||||
|
|
||||||
paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
|
paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
|
||||||
memcpy(paddr, start, len - rest);
|
|
||||||
|
|
||||||
if (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);
|
memcpy(paddr + len - rest, dif_storep, rest);
|
||||||
|
else
|
||||||
|
memcpy(dif_storep, paddr + len - rest, rest);
|
||||||
|
}
|
||||||
|
|
||||||
sector += len / sizeof(*dif_storep);
|
sector += len / sizeof(*dif_storep);
|
||||||
resid -= len;
|
resid -= len;
|
||||||
@@ -1845,7 +1853,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
|
|||||||
ei_lba++;
|
ei_lba++;
|
||||||
}
|
}
|
||||||
|
|
||||||
dif_copy_prot(SCpnt, start_sec, sectors);
|
dif_copy_prot(SCpnt, start_sec, sectors, true);
|
||||||
dix_reads++;
|
dix_reads++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1928,15 +1936,12 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
|
|||||||
{
|
{
|
||||||
int i, j, ret;
|
int i, j, ret;
|
||||||
struct sd_dif_tuple *sdt;
|
struct sd_dif_tuple *sdt;
|
||||||
struct scatterlist *dsgl = scsi_sglist(SCpnt);
|
struct scatterlist *dsgl;
|
||||||
struct scatterlist *psgl = scsi_prot_sglist(SCpnt);
|
struct scatterlist *psgl = scsi_prot_sglist(SCpnt);
|
||||||
void *daddr, *paddr;
|
void *daddr, *paddr;
|
||||||
sector_t tmp_sec = start_sec;
|
sector_t sector = start_sec;
|
||||||
sector_t sector;
|
|
||||||
int ppage_offset;
|
int ppage_offset;
|
||||||
|
|
||||||
sector = do_div(tmp_sec, sdebug_store_sectors);
|
|
||||||
|
|
||||||
BUG_ON(scsi_sg_count(SCpnt) == 0);
|
BUG_ON(scsi_sg_count(SCpnt) == 0);
|
||||||
BUG_ON(scsi_prot_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;
|
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) {
|
if (ret) {
|
||||||
dump_sector(daddr + j, scsi_debug_sector_size);
|
dump_sector(daddr + j, scsi_debug_sector_size);
|
||||||
goto out;
|
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++;
|
sector++;
|
||||||
|
|
||||||
if (sector == sdebug_store_sectors)
|
|
||||||
sector = 0; /* Force wrap */
|
|
||||||
|
|
||||||
start_sec++;
|
|
||||||
ei_lba++;
|
ei_lba++;
|
||||||
ppage_offset += sizeof(struct sd_dif_tuple);
|
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);
|
kunmap_atomic(daddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dif_copy_prot(SCpnt, start_sec, sectors, false);
|
||||||
dix_writes++;
|
dix_writes++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user