Commit f7be6772 authored by Martin K. Petersen's avatar Martin K. Petersen

scsi: scsi_debug: Improve RDPROTECT/WRPROTECT handling

It is useful for testing purposes to be able to inject errors by writing
bad protection information to media with checking disabled and then
attempting to read it back. Extend scsi_debug's PI verification logic to
give the driver feature parity with commercially available drives. Almost
all devices with PI capability support RDPROTECT and WRPROTECT values of 0,
1, and 3.

Link: https://lore.kernel.org/r/20210609033929.3815-10-martin.petersen@oracle.comReviewed-by: default avatarDouglas Gilbert <dgilbert@interlog.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Message-Id: <20210609033929.3815-10-martin.petersen@oracle.com>
parent c78be80d
...@@ -3076,6 +3076,7 @@ static void dif_copy_prot(struct scsi_cmnd *scp, sector_t sector, ...@@ -3076,6 +3076,7 @@ static void dif_copy_prot(struct scsi_cmnd *scp, sector_t sector,
static int prot_verify_read(struct scsi_cmnd *scp, sector_t start_sec, static int prot_verify_read(struct scsi_cmnd *scp, sector_t start_sec,
unsigned int sectors, u32 ei_lba) unsigned int sectors, u32 ei_lba)
{ {
int ret = 0;
unsigned int i; unsigned int i;
sector_t sector; sector_t sector;
struct sdeb_store_info *sip = devip2sip((struct sdebug_dev_info *) struct sdeb_store_info *sip = devip2sip((struct sdebug_dev_info *)
...@@ -3083,26 +3084,33 @@ static int prot_verify_read(struct scsi_cmnd *scp, sector_t start_sec, ...@@ -3083,26 +3084,33 @@ static int prot_verify_read(struct scsi_cmnd *scp, sector_t start_sec,
struct t10_pi_tuple *sdt; struct t10_pi_tuple *sdt;
for (i = 0; i < sectors; i++, ei_lba++) { for (i = 0; i < sectors; i++, ei_lba++) {
int ret;
sector = start_sec + i; sector = start_sec + i;
sdt = dif_store(sip, sector); sdt = dif_store(sip, sector);
if (sdt->app_tag == cpu_to_be16(0xffff)) if (sdt->app_tag == cpu_to_be16(0xffff))
continue; continue;
ret = dif_verify(sdt, lba2fake_store(sip, sector), sector, /*
ei_lba); * Because scsi_debug acts as both initiator and
if (ret) { * target we proceed to verify the PI even if
dif_errors++; * RDPROTECT=3. This is done so the "initiator" knows
return ret; * which type of error to return. Otherwise we would
* have to iterate over the PI twice.
*/
if (scp->cmnd[1] >> 5) { /* RDPROTECT */
ret = dif_verify(sdt, lba2fake_store(sip, sector),
sector, ei_lba);
if (ret) {
dif_errors++;
break;
}
} }
} }
dif_copy_prot(scp, start_sec, sectors, true); dif_copy_prot(scp, start_sec, sectors, true);
dix_reads++; dix_reads++;
return 0; return ret;
} }
static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
...@@ -3196,12 +3204,29 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) ...@@ -3196,12 +3204,29 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
/* DIX + T10 DIF */ /* DIX + T10 DIF */
if (unlikely(sdebug_dix && scsi_prot_sg_count(scp))) { if (unlikely(sdebug_dix && scsi_prot_sg_count(scp))) {
int prot_ret = prot_verify_read(scp, lba, num, ei_lba); switch (prot_verify_read(scp, lba, num, ei_lba)) {
case 1: /* Guard tag error */
if (prot_ret) { if (cmd[1] >> 5 != 3) { /* RDPROTECT != 3 */
read_unlock(macc_lckp); read_unlock(macc_lckp);
mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, prot_ret); mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1);
return illegal_condition_result; return check_condition_result;
} else if (scp->prot_flags & SCSI_PROT_GUARD_CHECK) {
read_unlock(macc_lckp);
mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1);
return illegal_condition_result;
}
break;
case 3: /* Reference tag error */
if (cmd[1] >> 5 != 3) { /* RDPROTECT != 3 */
read_unlock(macc_lckp);
mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 3);
return check_condition_result;
} else if (scp->prot_flags & SCSI_PROT_REF_CHECK) {
read_unlock(macc_lckp);
mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 3);
return illegal_condition_result;
}
break;
} }
} }
...@@ -3277,9 +3302,11 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, ...@@ -3277,9 +3302,11 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
sdt = piter.addr + ppage_offset; sdt = piter.addr + ppage_offset;
daddr = diter.addr + dpage_offset; daddr = diter.addr + dpage_offset;
ret = dif_verify(sdt, daddr, sector, ei_lba); if (SCpnt->cmnd[1] >> 5 != 3) { /* WRPROTECT */
if (ret) ret = dif_verify(sdt, daddr, sector, ei_lba);
goto out; if (ret)
goto out;
}
sector++; sector++;
ei_lba++; ei_lba++;
...@@ -3456,12 +3483,29 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) ...@@ -3456,12 +3483,29 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
/* DIX + T10 DIF */ /* DIX + T10 DIF */
if (unlikely(sdebug_dix && scsi_prot_sg_count(scp))) { if (unlikely(sdebug_dix && scsi_prot_sg_count(scp))) {
int prot_ret = prot_verify_write(scp, lba, num, ei_lba); switch (prot_verify_write(scp, lba, num, ei_lba)) {
case 1: /* Guard tag error */
if (prot_ret) { if (scp->prot_flags & SCSI_PROT_GUARD_CHECK) {
write_unlock(macc_lckp); write_unlock(macc_lckp);
mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, prot_ret); mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1);
return illegal_condition_result; return illegal_condition_result;
} else if (scp->cmnd[1] >> 5 != 3) { /* WRPROTECT != 3 */
write_unlock(macc_lckp);
mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1);
return check_condition_result;
}
break;
case 3: /* Reference tag error */
if (scp->prot_flags & SCSI_PROT_REF_CHECK) {
write_unlock(macc_lckp);
mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 3);
return illegal_condition_result;
} else if (scp->cmnd[1] >> 5 != 3) { /* WRPROTECT != 3 */
write_unlock(macc_lckp);
mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 3);
return check_condition_result;
}
break;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment