diff options
| -rw-r--r-- | drivers/scsi/sd.c | 73 | ||||
| -rw-r--r-- | drivers/scsi/sd.h | 66 | ||||
| -rw-r--r-- | drivers/scsi/sd_dif.c | 23 | ||||
| -rw-r--r-- | include/linux/bio.h | 33 | ||||
| -rw-r--r-- | include/scsi/scsi_cmnd.h | 36 |
5 files changed, 166 insertions, 65 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 2c2041ca4b70..9f7099f4b537 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c | |||
| @@ -610,29 +610,44 @@ static void scsi_disk_put(struct scsi_disk *sdkp) | |||
| 610 | mutex_unlock(&sd_ref_mutex); | 610 | mutex_unlock(&sd_ref_mutex); |
| 611 | } | 611 | } |
| 612 | 612 | ||
| 613 | static void sd_prot_op(struct scsi_cmnd *scmd, unsigned int dif) | 613 | |
| 614 | { | 614 | |
| 615 | unsigned int prot_op = SCSI_PROT_NORMAL; | 615 | static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, |
| 616 | unsigned int dix = scsi_prot_sg_count(scmd); | 616 | unsigned int dix, unsigned int dif) |
| 617 | 617 | { | |
| 618 | if (scmd->sc_data_direction == DMA_FROM_DEVICE) { | 618 | struct bio *bio = scmd->request->bio; |
| 619 | if (dif && dix) | 619 | unsigned int prot_op = sd_prot_op(rq_data_dir(scmd->request), dix, dif); |
| 620 | prot_op = SCSI_PROT_READ_PASS; | 620 | unsigned int protect = 0; |
| 621 | else if (dif && !dix) | 621 | |
| 622 | prot_op = SCSI_PROT_READ_STRIP; | 622 | if (dix) { /* DIX Type 0, 1, 2, 3 */ |
| 623 | else if (!dif && dix) | 623 | if (bio_integrity_flagged(bio, BIP_IP_CHECKSUM)) |
| 624 | prot_op = SCSI_PROT_READ_INSERT; | 624 | scmd->prot_flags |= SCSI_PROT_IP_CHECKSUM; |
| 625 | } else { | 625 | |
| 626 | if (dif && dix) | 626 | if (bio_integrity_flagged(bio, BIP_CTRL_NOCHECK) == false) |
| 627 | prot_op = SCSI_PROT_WRITE_PASS; | 627 | scmd->prot_flags |= SCSI_PROT_GUARD_CHECK; |
| 628 | else if (dif && !dix) | 628 | } |
| 629 | prot_op = SCSI_PROT_WRITE_INSERT; | 629 | |
| 630 | else if (!dif && dix) | 630 | if (dif != SD_DIF_TYPE3_PROTECTION) { /* DIX/DIF Type 0, 1, 2 */ |
| 631 | prot_op = SCSI_PROT_WRITE_STRIP; | 631 | scmd->prot_flags |= SCSI_PROT_REF_INCREMENT; |
| 632 | |||
| 633 | if (bio_integrity_flagged(bio, BIP_CTRL_NOCHECK) == false) | ||
| 634 | scmd->prot_flags |= SCSI_PROT_REF_CHECK; | ||
| 635 | } | ||
| 636 | |||
| 637 | if (dif) { /* DIX/DIF Type 1, 2, 3 */ | ||
| 638 | scmd->prot_flags |= SCSI_PROT_TRANSFER_PI; | ||
| 639 | |||
| 640 | if (bio_integrity_flagged(bio, BIP_DISK_NOCHECK)) | ||
| 641 | protect = 3 << 5; /* Disable target PI checking */ | ||
| 642 | else | ||
| 643 | protect = 1 << 5; /* Enable target PI checking */ | ||
| 632 | } | 644 | } |
| 633 | 645 | ||
| 634 | scsi_set_prot_op(scmd, prot_op); | 646 | scsi_set_prot_op(scmd, prot_op); |
| 635 | scsi_set_prot_type(scmd, dif); | 647 | scsi_set_prot_type(scmd, dif); |
| 648 | scmd->prot_flags &= sd_prot_flag_mask(prot_op); | ||
| 649 | |||
| 650 | return protect; | ||
| 636 | } | 651 | } |
| 637 | 652 | ||
| 638 | static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) | 653 | static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) |
| @@ -893,7 +908,8 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) | |||
| 893 | sector_t block = blk_rq_pos(rq); | 908 | sector_t block = blk_rq_pos(rq); |
| 894 | sector_t threshold; | 909 | sector_t threshold; |
| 895 | unsigned int this_count = blk_rq_sectors(rq); | 910 | unsigned int this_count = blk_rq_sectors(rq); |
| 896 | int ret, host_dif; | 911 | unsigned int dif, dix; |
| 912 | int ret; | ||
| 897 | unsigned char protect; | 913 | unsigned char protect; |
| 898 | 914 | ||
| 899 | ret = scsi_init_io(SCpnt, GFP_ATOMIC); | 915 | ret = scsi_init_io(SCpnt, GFP_ATOMIC); |
| @@ -995,7 +1011,7 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) | |||
| 995 | SCpnt->cmnd[0] = WRITE_6; | 1011 | SCpnt->cmnd[0] = WRITE_6; |
| 996 | 1012 | ||
| 997 | if (blk_integrity_rq(rq)) | 1013 | if (blk_integrity_rq(rq)) |
| 998 | sd_dif_prepare(rq, block, sdp->sector_size); | 1014 | sd_dif_prepare(SCpnt); |
| 999 | 1015 | ||
| 1000 | } else if (rq_data_dir(rq) == READ) { | 1016 | } else if (rq_data_dir(rq) == READ) { |
| 1001 | SCpnt->cmnd[0] = READ_6; | 1017 | SCpnt->cmnd[0] = READ_6; |
| @@ -1010,14 +1026,15 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) | |||
| 1010 | "writing" : "reading", this_count, | 1026 | "writing" : "reading", this_count, |
| 1011 | blk_rq_sectors(rq))); | 1027 | blk_rq_sectors(rq))); |
| 1012 | 1028 | ||
| 1013 | /* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */ | 1029 | dix = scsi_prot_sg_count(SCpnt); |
| 1014 | host_dif = scsi_host_dif_capable(sdp->host, sdkp->protection_type); | 1030 | dif = scsi_host_dif_capable(SCpnt->device->host, sdkp->protection_type); |
| 1015 | if (host_dif) | 1031 | |
| 1016 | protect = 1 << 5; | 1032 | if (dif || dix) |
| 1033 | protect = sd_setup_protect_cmnd(SCpnt, dix, dif); | ||
| 1017 | else | 1034 | else |
| 1018 | protect = 0; | 1035 | protect = 0; |
| 1019 | 1036 | ||
| 1020 | if (host_dif == SD_DIF_TYPE2_PROTECTION) { | 1037 | if (protect && sdkp->protection_type == SD_DIF_TYPE2_PROTECTION) { |
| 1021 | SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); | 1038 | SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); |
| 1022 | 1039 | ||
| 1023 | if (unlikely(SCpnt->cmnd == NULL)) { | 1040 | if (unlikely(SCpnt->cmnd == NULL)) { |
| @@ -1102,10 +1119,6 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) | |||
| 1102 | } | 1119 | } |
| 1103 | SCpnt->sdb.length = this_count * sdp->sector_size; | 1120 | SCpnt->sdb.length = this_count * sdp->sector_size; |
| 1104 | 1121 | ||
| 1105 | /* If DIF or DIX is enabled, tell HBA how to handle request */ | ||
| 1106 | if (host_dif || scsi_prot_sg_count(SCpnt)) | ||
| 1107 | sd_prot_op(SCpnt, host_dif); | ||
| 1108 | |||
| 1109 | /* | 1122 | /* |
| 1110 | * We shouldn't disconnect in the middle of a sector, so with a dumb | 1123 | * We shouldn't disconnect in the middle of a sector, so with a dumb |
| 1111 | * host adapter, it's safe to assume that we can at least transfer | 1124 | * host adapter, it's safe to assume that we can at least transfer |
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 4c3ab8377fd3..467377884b63 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h | |||
| @@ -167,6 +167,68 @@ enum sd_dif_target_protection_types { | |||
| 167 | }; | 167 | }; |
| 168 | 168 | ||
| 169 | /* | 169 | /* |
| 170 | * Look up the DIX operation based on whether the command is read or | ||
| 171 | * write and whether dix and dif are enabled. | ||
| 172 | */ | ||
| 173 | static inline unsigned int sd_prot_op(bool write, bool dix, bool dif) | ||
| 174 | { | ||
| 175 | /* Lookup table: bit 2 (write), bit 1 (dix), bit 0 (dif) */ | ||
| 176 | const unsigned int ops[] = { /* wrt dix dif */ | ||
| 177 | SCSI_PROT_NORMAL, /* 0 0 0 */ | ||
| 178 | SCSI_PROT_READ_STRIP, /* 0 0 1 */ | ||
| 179 | SCSI_PROT_READ_INSERT, /* 0 1 0 */ | ||
| 180 | SCSI_PROT_READ_PASS, /* 0 1 1 */ | ||
| 181 | SCSI_PROT_NORMAL, /* 1 0 0 */ | ||
| 182 | SCSI_PROT_WRITE_INSERT, /* 1 0 1 */ | ||
| 183 | SCSI_PROT_WRITE_STRIP, /* 1 1 0 */ | ||
| 184 | SCSI_PROT_WRITE_PASS, /* 1 1 1 */ | ||
| 185 | }; | ||
| 186 | |||
| 187 | return ops[write << 2 | dix << 1 | dif]; | ||
| 188 | } | ||
| 189 | |||
| 190 | /* | ||
| 191 | * Returns a mask of the protection flags that are valid for a given DIX | ||
| 192 | * operation. | ||
| 193 | */ | ||
| 194 | static inline unsigned int sd_prot_flag_mask(unsigned int prot_op) | ||
| 195 | { | ||
| 196 | const unsigned int flag_mask[] = { | ||
| 197 | [SCSI_PROT_NORMAL] = 0, | ||
| 198 | |||
| 199 | [SCSI_PROT_READ_STRIP] = SCSI_PROT_TRANSFER_PI | | ||
| 200 | SCSI_PROT_GUARD_CHECK | | ||
| 201 | SCSI_PROT_REF_CHECK | | ||
| 202 | SCSI_PROT_REF_INCREMENT, | ||
| 203 | |||
| 204 | [SCSI_PROT_READ_INSERT] = SCSI_PROT_REF_INCREMENT | | ||
| 205 | SCSI_PROT_IP_CHECKSUM, | ||
| 206 | |||
| 207 | [SCSI_PROT_READ_PASS] = SCSI_PROT_TRANSFER_PI | | ||
| 208 | SCSI_PROT_GUARD_CHECK | | ||
| 209 | SCSI_PROT_REF_CHECK | | ||
| 210 | SCSI_PROT_REF_INCREMENT | | ||
| 211 | SCSI_PROT_IP_CHECKSUM, | ||
| 212 | |||
| 213 | [SCSI_PROT_WRITE_INSERT] = SCSI_PROT_TRANSFER_PI | | ||
| 214 | SCSI_PROT_REF_INCREMENT, | ||
| 215 | |||
| 216 | [SCSI_PROT_WRITE_STRIP] = SCSI_PROT_GUARD_CHECK | | ||
| 217 | SCSI_PROT_REF_CHECK | | ||
| 218 | SCSI_PROT_REF_INCREMENT | | ||
| 219 | SCSI_PROT_IP_CHECKSUM, | ||
| 220 | |||
| 221 | [SCSI_PROT_WRITE_PASS] = SCSI_PROT_TRANSFER_PI | | ||
| 222 | SCSI_PROT_GUARD_CHECK | | ||
| 223 | SCSI_PROT_REF_CHECK | | ||
| 224 | SCSI_PROT_REF_INCREMENT | | ||
| 225 | SCSI_PROT_IP_CHECKSUM, | ||
| 226 | }; | ||
| 227 | |||
| 228 | return flag_mask[prot_op]; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* | ||
| 170 | * Data Integrity Field tuple. | 232 | * Data Integrity Field tuple. |
| 171 | */ | 233 | */ |
| 172 | struct sd_dif_tuple { | 234 | struct sd_dif_tuple { |
| @@ -178,7 +240,7 @@ struct sd_dif_tuple { | |||
| 178 | #ifdef CONFIG_BLK_DEV_INTEGRITY | 240 | #ifdef CONFIG_BLK_DEV_INTEGRITY |
| 179 | 241 | ||
| 180 | extern void sd_dif_config_host(struct scsi_disk *); | 242 | extern void sd_dif_config_host(struct scsi_disk *); |
| 181 | extern void sd_dif_prepare(struct request *rq, sector_t, unsigned int); | 243 | extern void sd_dif_prepare(struct scsi_cmnd *scmd); |
| 182 | extern void sd_dif_complete(struct scsi_cmnd *, unsigned int); | 244 | extern void sd_dif_complete(struct scsi_cmnd *, unsigned int); |
| 183 | 245 | ||
| 184 | #else /* CONFIG_BLK_DEV_INTEGRITY */ | 246 | #else /* CONFIG_BLK_DEV_INTEGRITY */ |
| @@ -186,7 +248,7 @@ extern void sd_dif_complete(struct scsi_cmnd *, unsigned int); | |||
| 186 | static inline void sd_dif_config_host(struct scsi_disk *disk) | 248 | static inline void sd_dif_config_host(struct scsi_disk *disk) |
| 187 | { | 249 | { |
| 188 | } | 250 | } |
