Commit af55ff675a8461da6a632320710b050af4366e0c
Committed by
James Bottomley
1 parent
e0597d7001
Exists in
master
and in
39 other branches
[SCSI] sd: Support for SCSI disk (SBC) Data Integrity Field
Support for controllers and disks that implement DIF protection information: - During command preparation the RDPROTECT/WRPROTECT must be set correctly if the target has DIF enabled. - READ(6) and WRITE(6) are not supported when DIF is on. - The controller must be told how to handle the I/O via the protection operation field in scsi_cmnd. - Refactor the I/O completion code that extracts failed LBA from the returned sense data and handle DIF failures correctly. - sd_dif.c implements the functions required to prepare and complete requests with protection information attached. Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Showing 5 changed files with 647 additions and 40 deletions Side-by-side Diff
drivers/scsi/Kconfig
drivers/scsi/Makefile
... | ... | @@ -151,6 +151,8 @@ |
151 | 151 | scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o |
152 | 152 | |
153 | 153 | sd_mod-objs := sd.o |
154 | +sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o | |
155 | + | |
154 | 156 | sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o |
155 | 157 | ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ |
156 | 158 | := -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \ |
drivers/scsi/sd.c
... | ... | @@ -373,6 +373,7 @@ |
373 | 373 | struct scsi_cmnd *SCpnt; |
374 | 374 | struct scsi_device *sdp = q->queuedata; |
375 | 375 | struct gendisk *disk = rq->rq_disk; |
376 | + struct scsi_disk *sdkp; | |
376 | 377 | sector_t block = rq->sector; |
377 | 378 | unsigned int this_count = rq->nr_sectors; |
378 | 379 | unsigned int timeout = sdp->timeout; |
... | ... | @@ -389,6 +390,7 @@ |
389 | 390 | if (ret != BLKPREP_OK) |
390 | 391 | goto out; |
391 | 392 | SCpnt = rq->special; |
393 | + sdkp = scsi_disk(disk); | |
392 | 394 | |
393 | 395 | /* from here on until we're complete, any goto out |
394 | 396 | * is used for a killable error condition */ |
... | ... | @@ -478,6 +480,11 @@ |
478 | 480 | } |
479 | 481 | SCpnt->cmnd[0] = WRITE_6; |
480 | 482 | SCpnt->sc_data_direction = DMA_TO_DEVICE; |
483 | + | |
484 | + if (blk_integrity_rq(rq) && | |
485 | + sd_dif_prepare(rq, block, sdp->sector_size) == -EIO) | |
486 | + goto out; | |
487 | + | |
481 | 488 | } else if (rq_data_dir(rq) == READ) { |
482 | 489 | SCpnt->cmnd[0] = READ_6; |
483 | 490 | SCpnt->sc_data_direction = DMA_FROM_DEVICE; |
... | ... | @@ -492,8 +499,12 @@ |
492 | 499 | "writing" : "reading", this_count, |
493 | 500 | rq->nr_sectors)); |
494 | 501 | |
495 | - SCpnt->cmnd[1] = 0; | |
496 | - | |
502 | + /* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */ | |
503 | + if (scsi_host_dif_capable(sdp->host, sdkp->protection_type)) | |
504 | + SCpnt->cmnd[1] = 1 << 5; | |
505 | + else | |
506 | + SCpnt->cmnd[1] = 0; | |
507 | + | |
497 | 508 | if (block > 0xffffffff) { |
498 | 509 | SCpnt->cmnd[0] += READ_16 - READ_6; |
499 | 510 | SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0; |
... | ... | @@ -511,6 +522,7 @@ |
511 | 522 | SCpnt->cmnd[13] = (unsigned char) this_count & 0xff; |
512 | 523 | SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0; |
513 | 524 | } else if ((this_count > 0xff) || (block > 0x1fffff) || |
525 | + scsi_device_protection(SCpnt->device) || | |
514 | 526 | SCpnt->device->use_10_for_rw) { |
515 | 527 | if (this_count > 0xffff) |
516 | 528 | this_count = 0xffff; |
... | ... | @@ -545,6 +557,10 @@ |
545 | 557 | } |
546 | 558 | SCpnt->sdb.length = this_count * sdp->sector_size; |
547 | 559 | |
560 | + /* If DIF or DIX is enabled, tell HBA how to handle request */ | |
561 | + if (sdkp->protection_type || scsi_prot_sg_count(SCpnt)) | |
562 | + sd_dif_op(SCpnt, sdkp->protection_type, scsi_prot_sg_count(SCpnt)); | |
563 | + | |
548 | 564 | /* |
549 | 565 | * We shouldn't disconnect in the middle of a sector, so with a dumb |
550 | 566 | * host adapter, it's safe to assume that we can at least transfer |
... | ... | @@ -939,6 +955,48 @@ |
939 | 955 | .revalidate_disk = sd_revalidate_disk, |
940 | 956 | }; |
941 | 957 | |
958 | +static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd) | |
959 | +{ | |
960 | + u64 start_lba = scmd->request->sector; | |
961 | + u64 end_lba = scmd->request->sector + (scsi_bufflen(scmd) / 512); | |
962 | + u64 bad_lba; | |
963 | + int info_valid; | |
964 | + | |
965 | + if (!blk_fs_request(scmd->request)) | |
966 | + return 0; | |
967 | + | |
968 | + info_valid = scsi_get_sense_info_fld(scmd->sense_buffer, | |
969 | + SCSI_SENSE_BUFFERSIZE, | |
970 | + &bad_lba); | |
971 | + if (!info_valid) | |
972 | + return 0; | |
973 | + | |
974 | + if (scsi_bufflen(scmd) <= scmd->device->sector_size) | |
975 | + return 0; | |
976 | + | |
977 | + if (scmd->device->sector_size < 512) { | |
978 | + /* only legitimate sector_size here is 256 */ | |
979 | + start_lba <<= 1; | |
980 | + end_lba <<= 1; | |
981 | + } else { | |
982 | + /* be careful ... don't want any overflows */ | |
983 | + u64 factor = scmd->device->sector_size / 512; | |
984 | + do_div(start_lba, factor); | |
985 | + do_div(end_lba, factor); | |
986 | + } | |
987 | + | |
988 | + /* The bad lba was reported incorrectly, we have no idea where | |
989 | + * the error is. | |
990 | + */ | |
991 | + if (bad_lba < start_lba || bad_lba >= end_lba) | |
992 | + return 0; | |
993 | + | |
994 | + /* This computation should always be done in terms of | |
995 | + * the resolution of the device's medium. | |
996 | + */ | |
997 | + return (bad_lba - start_lba) * scmd->device->sector_size; | |
998 | +} | |
999 | + | |
942 | 1000 | /** |
943 | 1001 | * sd_done - bottom half handler: called when the lower level |
944 | 1002 | * driver has completed (successfully or otherwise) a scsi command. |
945 | 1003 | |
... | ... | @@ -949,15 +1007,10 @@ |
949 | 1007 | static int sd_done(struct scsi_cmnd *SCpnt) |
950 | 1008 | { |
951 | 1009 | int result = SCpnt->result; |
952 | - unsigned int xfer_size = scsi_bufflen(SCpnt); | |
953 | - unsigned int good_bytes = result ? 0 : xfer_size; | |
954 | - u64 start_lba = SCpnt->request->sector; | |
955 | - u64 end_lba = SCpnt->request->sector + (xfer_size / 512); | |
956 | - u64 bad_lba; | |
1010 | + unsigned int good_bytes = result ? 0 : scsi_bufflen(SCpnt); | |
957 | 1011 | struct scsi_sense_hdr sshdr; |
958 | 1012 | int sense_valid = 0; |
959 | 1013 | int sense_deferred = 0; |
960 | - int info_valid; | |
961 | 1014 | |
962 | 1015 | if (result) { |
963 | 1016 | sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr); |
... | ... | @@ -982,36 +1035,7 @@ |
982 | 1035 | switch (sshdr.sense_key) { |
983 | 1036 | case HARDWARE_ERROR: |
984 | 1037 | case MEDIUM_ERROR: |
985 | - if (!blk_fs_request(SCpnt->request)) | |
986 | - goto out; | |
987 | - info_valid = scsi_get_sense_info_fld(SCpnt->sense_buffer, | |
988 | - SCSI_SENSE_BUFFERSIZE, | |
989 | - &bad_lba); | |
990 | - if (!info_valid) | |
991 | - goto out; | |
992 | - if (xfer_size <= SCpnt->device->sector_size) | |
993 | - goto out; | |
994 | - if (SCpnt->device->sector_size < 512) { | |
995 | - /* only legitimate sector_size here is 256 */ | |
996 | - start_lba <<= 1; | |
997 | - end_lba <<= 1; | |
998 | - } else { | |
999 | - /* be careful ... don't want any overflows */ | |
1000 | - u64 factor = SCpnt->device->sector_size / 512; | |
1001 | - do_div(start_lba, factor); | |
1002 | - do_div(end_lba, factor); | |
1003 | - } | |
1004 | - | |
1005 | - if (bad_lba < start_lba || bad_lba >= end_lba) | |
1006 | - /* the bad lba was reported incorrectly, we have | |
1007 | - * no idea where the error is | |
1008 | - */ | |
1009 | - goto out; | |
1010 | - | |
1011 | - /* This computation should always be done in terms of | |
1012 | - * the resolution of the device's medium. | |
1013 | - */ | |
1014 | - good_bytes = (bad_lba - start_lba)*SCpnt->device->sector_size; | |
1038 | + good_bytes = sd_completed_bytes(SCpnt); | |
1015 | 1039 | break; |
1016 | 1040 | case RECOVERED_ERROR: |
1017 | 1041 | case NO_SENSE: |
1018 | 1042 | |
1019 | 1043 | |
... | ... | @@ -1021,10 +1045,23 @@ |
1021 | 1045 | scsi_print_sense("sd", SCpnt); |
1022 | 1046 | SCpnt->result = 0; |
1023 | 1047 | memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); |
1024 | - good_bytes = xfer_size; | |
1048 | + good_bytes = scsi_bufflen(SCpnt); | |
1025 | 1049 | break; |
1050 | + case ABORTED_COMMAND: | |
1051 | + if (sshdr.asc == 0x10) { /* DIF: Disk detected corruption */ | |
1052 | + scsi_print_result(SCpnt); | |
1053 | + scsi_print_sense("sd", SCpnt); | |
1054 | + good_bytes = sd_completed_bytes(SCpnt); | |
1055 | + } | |
1056 | + break; | |
1026 | 1057 | case ILLEGAL_REQUEST: |
1027 | - if (SCpnt->device->use_10_for_rw && | |
1058 | + if (sshdr.asc == 0x10) { /* DIX: HBA detected corruption */ | |
1059 | + scsi_print_result(SCpnt); | |
1060 | + scsi_print_sense("sd", SCpnt); | |
1061 | + good_bytes = sd_completed_bytes(SCpnt); | |
1062 | + } | |
1063 | + if (!scsi_device_protection(SCpnt->device) && | |
1064 | + SCpnt->device->use_10_for_rw && | |
1028 | 1065 | (SCpnt->cmnd[0] == READ_10 || |
1029 | 1066 | SCpnt->cmnd[0] == WRITE_10)) |
1030 | 1067 | SCpnt->device->use_10_for_rw = 0; |
... | ... | @@ -1037,6 +1074,9 @@ |
1037 | 1074 | break; |
1038 | 1075 | } |
1039 | 1076 | out: |
1077 | + if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt)) | |
1078 | + sd_dif_complete(SCpnt, good_bytes); | |
1079 | + | |
1040 | 1080 | return good_bytes; |
1041 | 1081 | } |
1042 | 1082 | |
... | ... | @@ -1826,6 +1866,7 @@ |
1826 | 1866 | |
1827 | 1867 | dev_set_drvdata(dev, sdkp); |
1828 | 1868 | add_disk(gd); |
1869 | + sd_dif_config_host(sdkp); | |
1829 | 1870 | |
1830 | 1871 | sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n", |
1831 | 1872 | sdp->removable ? "removable " : ""); |
drivers/scsi/sd.h
... | ... | @@ -82,5 +82,30 @@ |
82 | 82 | SD_DIF_TYPE3_PROTECTION = 0x3, |
83 | 83 | }; |
84 | 84 | |
85 | +/* | |
86 | + * Data Integrity Field tuple. | |
87 | + */ | |
88 | +struct sd_dif_tuple { | |
89 | + __be16 guard_tag; /* Checksum */ | |
90 | + __be16 app_tag; /* Opaque storage */ | |
91 | + __be32 ref_tag; /* Target LBA or indirect LBA */ | |
92 | +}; | |
93 | + | |
94 | +#if defined(CONFIG_BLK_DEV_INTEGRITY) | |
95 | + | |
96 | +extern void sd_dif_op(struct scsi_cmnd *, unsigned int, unsigned int); | |
97 | +extern void sd_dif_config_host(struct scsi_disk *); | |
98 | +extern int sd_dif_prepare(struct request *rq, sector_t, unsigned int); | |
99 | +extern void sd_dif_complete(struct scsi_cmnd *, unsigned int); | |
100 | + | |
101 | +#else /* CONFIG_BLK_DEV_INTEGRITY */ | |
102 | + | |
103 | +#define sd_dif_op(a, b, c) do { } while (0) | |
104 | +#define sd_dif_config_host(a) do { } while (0) | |
105 | +#define sd_dif_prepare(a, b, c) (0) | |
106 | +#define sd_dif_complete(a, b) (0) | |
107 | + | |
108 | +#endif /* CONFIG_BLK_DEV_INTEGRITY */ | |
109 | + | |
85 | 110 | #endif /* _SCSI_DISK_H */ |
drivers/scsi/sd_dif.c
1 | +/* | |
2 | + * sd_dif.c - SCSI Data Integrity Field | |
3 | + * | |
4 | + * Copyright (C) 2007, 2008 Oracle Corporation | |
5 | + * Written by: Martin K. Petersen <martin.petersen@oracle.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or | |
8 | + * modify it under the terms of the GNU General Public License version | |
9 | + * 2 as published by the Free Software Foundation. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, but | |
12 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; see the file COPYING. If not, write to | |
18 | + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | |
19 | + * USA. | |
20 | + * | |
21 | + */ | |
22 | + | |
23 | +#include <linux/blkdev.h> | |
24 | +#include <linux/crc-t10dif.h> | |
25 | + | |
26 | +#include <scsi/scsi.h> | |
27 | +#include <scsi/scsi_cmnd.h> | |
28 | +#include <scsi/scsi_dbg.h> | |
29 | +#include <scsi/scsi_device.h> | |
30 | +#include <scsi/scsi_driver.h> | |
31 | +#include <scsi/scsi_eh.h> | |
32 | +#include <scsi/scsi_host.h> | |
33 | +#include <scsi/scsi_ioctl.h> | |
34 | +#include <scsi/scsicam.h> | |
35 | + | |
36 | +#include <net/checksum.h> | |
37 | + | |
38 | +#include "sd.h" | |
39 | + | |
40 | +typedef __u16 (csum_fn) (void *, unsigned int); | |
41 | + | |
42 | +static __u16 sd_dif_crc_fn(void *data, unsigned int len) | |
43 | +{ | |
44 | + return cpu_to_be16(crc_t10dif(data, len)); | |
45 | +} | |
46 | + | |
47 | +static __u16 sd_dif_ip_fn(void *data, unsigned int len) | |
48 | +{ | |
49 | + return ip_compute_csum(data, len); | |
50 | +} | |
51 | + | |
52 | +/* | |
53 | + * Type 1 and Type 2 protection use the same format: 16 bit guard tag, | |
54 | + * 16 bit app tag, 32 bit reference tag. | |
55 | + */ | |
56 | +static void sd_dif_type1_generate(struct blk_integrity_exchg *bix, csum_fn *fn) | |
57 | +{ | |
58 | + void *buf = bix->data_buf; | |
59 | + struct sd_dif_tuple *sdt = bix->prot_buf; | |
60 | + sector_t sector = bix->sector; | |
61 | + unsigned int i; | |
62 | + | |
63 | + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { | |
64 | + sdt->guard_tag = fn(buf, bix->sector_size); | |
65 | + sdt->ref_tag = cpu_to_be32(sector & 0xffffffff); | |
66 | + sdt->app_tag = 0; | |
67 | + | |
68 | + buf += bix->sector_size; | |
69 | + sector++; | |
70 | + } | |
71 | +} | |
72 | + | |
73 | +static void sd_dif_type1_generate_crc(struct blk_integrity_exchg *bix) | |
74 | +{ | |
75 | + sd_dif_type1_generate(bix, sd_dif_crc_fn); | |
76 | +} | |
77 | + | |
78 | +static void sd_dif_type1_generate_ip(struct blk_integrity_exchg *bix) | |
79 | +{ | |
80 | + sd_dif_type1_generate(bix, sd_dif_ip_fn); | |
81 | +} | |
82 | + | |
83 | +static int sd_dif_type1_verify(struct blk_integrity_exchg *bix, csum_fn *fn) | |
84 | +{ | |
85 | + void *buf = bix->data_buf; | |
86 | + struct sd_dif_tuple *sdt = bix->prot_buf; | |
87 | + sector_t sector = bix->sector; | |
88 | + unsigned int i; | |
89 | + __u16 csum; | |
90 | + | |
91 | + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { | |
92 | + /* Unwritten sectors */ | |
93 | + if (sdt->app_tag == 0xffff) | |
94 | + return 0; | |
95 | + | |
96 | + /* Bad ref tag received from disk */ | |
97 | + if (sdt->ref_tag == 0xffffffff) { | |
98 | + printk(KERN_ERR | |
99 | + "%s: bad phys ref tag on sector %lu\n", | |
100 | + bix->disk_name, (unsigned long)sector); | |
101 | + return -EIO; | |
102 | + } | |
103 | + | |
104 | + if (be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { | |
105 | + printk(KERN_ERR | |
106 | + "%s: ref tag error on sector %lu (rcvd %u)\n", | |
107 | + bix->disk_name, (unsigned long)sector, | |
108 | + be32_to_cpu(sdt->ref_tag)); | |
109 | + return -EIO; | |
110 | + } | |
111 | + | |
112 | + csum = fn(buf, bix->sector_size); | |
113 | + | |
114 | + if (sdt->guard_tag != csum) { | |
115 | + printk(KERN_ERR "%s: guard tag error on sector %lu " \ | |
116 | + "(rcvd %04x, data %04x)\n", bix->disk_name, | |
117 | + (unsigned long)sector, | |
118 | + be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); | |
119 | + return -EIO; | |
120 | + } | |
121 | + | |
122 | + buf += bix->sector_size; | |
123 | + sector++; | |
124 | + } | |
125 | + | |
126 | + return 0; | |
127 | +} | |
128 | + | |
129 | +static int sd_dif_type1_verify_crc(struct blk_integrity_exchg *bix) | |
130 | +{ | |
131 | + return sd_dif_type1_verify(bix, sd_dif_crc_fn); | |
132 | +} | |
133 | + | |
134 | +static int sd_dif_type1_verify_ip(struct blk_integrity_exchg *bix) | |
135 | +{ | |
136 | + return sd_dif_type1_verify(bix, sd_dif_ip_fn); | |
137 | +} | |
138 | + | |
139 | +/* | |
140 | + * Functions for interleaving and deinterleaving application tags | |
141 | + */ | |
142 | +static void sd_dif_type1_set_tag(void *prot, void *tag_buf, unsigned int sectors) | |
143 | +{ | |
144 | + struct sd_dif_tuple *sdt = prot; | |
145 | + char *tag = tag_buf; | |
146 | + unsigned int i, j; | |
147 | + | |
148 | + for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { | |
149 | + sdt->app_tag = tag[j] << 8 | tag[j+1]; | |
150 | + BUG_ON(sdt->app_tag == 0xffff); | |
151 | + } | |
152 | +} | |
153 | + | |
154 | +static void sd_dif_type1_get_tag(void *prot, void *tag_buf, unsigned int sectors) | |
155 | +{ | |
156 | + struct sd_dif_tuple *sdt = prot; | |
157 | + char *tag = tag_buf; | |
158 | + unsigned int i, j; | |
159 | + | |
160 | + for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { | |
161 | + tag[j] = (sdt->app_tag & 0xff00) >> 8; | |
162 | + tag[j+1] = sdt->app_tag & 0xff; | |
163 | + } | |
164 | +} | |
165 | + | |
166 | +static struct blk_integrity dif_type1_integrity_crc = { | |
167 | + .name = "T10-DIF-TYPE1-CRC", | |
168 | + .generate_fn = sd_dif_type1_generate_crc, | |
169 | + .verify_fn = sd_dif_type1_verify_crc, | |
170 | + .get_tag_fn = sd_dif_type1_get_tag, | |
171 | + .set_tag_fn = sd_dif_type1_set_tag, | |
172 | + .tuple_size = sizeof(struct sd_dif_tuple), | |
173 | + .tag_size = 0, | |
174 | +}; | |
175 | + | |
176 | +static struct blk_integrity dif_type1_integrity_ip = { | |
177 | + .name = "T10-DIF-TYPE1-IP", | |
178 | + .generate_fn = sd_dif_type1_generate_ip, | |
179 | + .verify_fn = sd_dif_type1_verify_ip, | |
180 | + .get_tag_fn = sd_dif_type1_get_tag, | |
181 | + .set_tag_fn = sd_dif_type1_set_tag, | |
182 | + .tuple_size = sizeof(struct sd_dif_tuple), | |
183 | + .tag_size = 0, | |
184 | +}; | |
185 | + | |
186 | + | |
187 | +/* | |
188 | + * Type 3 protection has a 16-bit guard tag and 16 + 32 bits of opaque | |
189 | + * tag space. | |
190 | + */ | |
191 | +static void sd_dif_type3_generate(struct blk_integrity_exchg *bix, csum_fn *fn) | |
192 | +{ | |
193 | + void *buf = bix->data_buf; | |
194 | + struct sd_dif_tuple *sdt = bix->prot_buf; | |
195 | + unsigned int i; | |
196 | + | |
197 | + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { | |
198 | + sdt->guard_tag = fn(buf, bix->sector_size); | |
199 | + sdt->ref_tag = 0; | |
200 | + sdt->app_tag = 0; | |
201 | + | |
202 | + buf += bix->sector_size; | |
203 | + } | |
204 | +} | |
205 | + | |
206 | +static void sd_dif_type3_generate_crc(struct blk_integrity_exchg *bix) | |
207 | +{ | |
208 | + sd_dif_type3_generate(bix, sd_dif_crc_fn); | |
209 | +} | |
210 | + | |
211 | +static void sd_dif_type3_generate_ip(struct blk_integrity_exchg *bix) | |
212 | +{ | |
213 | + sd_dif_type3_generate(bix, sd_dif_ip_fn); | |
214 | +} | |
215 | + | |
216 | +static int sd_dif_type3_verify(struct blk_integrity_exchg *bix, csum_fn *fn) | |
217 | +{ | |
218 | + void *buf = bix->data_buf; | |
219 | + struct sd_dif_tuple *sdt = bix->prot_buf; | |
220 | + sector_t sector = bix->sector; | |
221 | + unsigned int i; | |
222 | + __u16 csum; | |
223 | + | |
224 | + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { | |
225 | + /* Unwritten sectors */ | |
226 | + if (sdt->app_tag == 0xffff && sdt->ref_tag == 0xffffffff) | |
227 | + return 0; | |
228 | + | |
229 | + csum = fn(buf, bix->sector_size); | |
230 | + | |
231 | + if (sdt->guard_tag != csum) { | |
232 | + printk(KERN_ERR "%s: guard tag error on sector %lu " \ | |
233 | + "(rcvd %04x, data %04x)\n", bix->disk_name, | |
234 | + (unsigned long)sector, | |
235 | + be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); | |
236 | + return -EIO; | |
237 | + } | |
238 | + | |
239 | + buf += bix->sector_size; | |
240 | + sector++; | |
241 | + } | |
242 | + | |
243 | + return 0; | |
244 | +} | |
245 | + | |
246 | +static int sd_dif_type3_verify_crc(struct blk_integrity_exchg *bix) | |
247 | +{ | |
248 | + return sd_dif_type3_verify(bix, sd_dif_crc_fn); | |
249 | +} | |
250 | + | |
251 | +static int sd_dif_type3_verify_ip(struct blk_integrity_exchg *bix) | |
252 | +{ | |
253 | + return sd_dif_type3_verify(bix, sd_dif_ip_fn); | |
254 | +} | |
255 | + | |
256 | +static void sd_dif_type3_set_tag(void *prot, void *tag_buf, unsigned int sectors) | |
257 | +{ | |
258 | + struct sd_dif_tuple *sdt = prot; | |
259 | + char *tag = tag_buf; | |
260 | + unsigned int i, j; | |
261 | + | |
262 | + for (i = 0, j = 0 ; i < sectors ; i++, j += 6, sdt++) { | |
263 | + sdt->app_tag = tag[j] << 8 | tag[j+1]; | |
264 | + sdt->ref_tag = tag[j+2] << 24 | tag[j+3] << 16 | | |
265 | + tag[j+4] << 8 | tag[j+5]; | |
266 | + } | |
267 | +} | |
268 | + | |
269 | +static void sd_dif_type3_get_tag(void *prot, void *tag_buf, unsigned int sectors) | |
270 | +{ | |
271 | + struct sd_dif_tuple *sdt = prot; | |
272 | + char *tag = tag_buf; | |
273 | + unsigned int i, j; | |
274 | + | |
275 | + for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { | |
276 | + tag[j] = (sdt->app_tag & 0xff00) >> 8; | |
277 | + tag[j+1] = sdt->app_tag & 0xff; | |
278 | + tag[j+2] = (sdt->ref_tag & 0xff000000) >> 24; | |
279 | + tag[j+3] = (sdt->ref_tag & 0xff0000) >> 16; | |
280 | + tag[j+4] = (sdt->ref_tag & 0xff00) >> 8; | |
281 | + tag[j+5] = sdt->ref_tag & 0xff; | |
282 | + BUG_ON(sdt->app_tag == 0xffff || sdt->ref_tag == 0xffffffff); | |
283 | + } | |
284 | +} | |
285 | + | |
286 | +static struct blk_integrity dif_type3_integrity_crc = { | |
287 | + .name = "T10-DIF-TYPE3-CRC", | |
288 | + .generate_fn = sd_dif_type3_generate_crc, | |
289 | + .verify_fn = sd_dif_type3_verify_crc, | |
290 | + .get_tag_fn = sd_dif_type3_get_tag, | |
291 | + .set_tag_fn = sd_dif_type3_set_tag, | |
292 | + .tuple_size = sizeof(struct sd_dif_tuple), | |
293 | + .tag_size = 0, | |
294 | +}; | |
295 | + | |
296 | +static struct blk_integrity dif_type3_integrity_ip = { | |
297 | + .name = "T10-DIF-TYPE3-IP", | |
298 | + .generate_fn = sd_dif_type3_generate_ip, | |
299 | + .verify_fn = sd_dif_type3_verify_ip, | |
300 | + .get_tag_fn = sd_dif_type3_get_tag, | |
301 | + .set_tag_fn = sd_dif_type3_set_tag, | |
302 | + .tuple_size = sizeof(struct sd_dif_tuple), | |
303 | + .tag_size = 0, | |
304 | +}; | |
305 | + | |
306 | +/* | |
307 | + * Configure exchange of protection information between OS and HBA. | |
308 | + */ | |
309 | +void sd_dif_config_host(struct scsi_disk *sdkp) | |
310 | +{ | |
311 | + struct scsi_device *sdp = sdkp->device; | |
312 | + struct gendisk *disk = sdkp->disk; | |
313 | + u8 type = sdkp->protection_type; | |
314 | + | |
315 | + /* If this HBA doesn't support DIX, resort to normal I/O or DIF */ | |
316 | + if (scsi_host_dix_capable(sdp->host, type) == 0) { | |
317 | + | |
318 | + if (type == SD_DIF_TYPE0_PROTECTION) | |
319 | + return; | |
320 | + | |
321 | + if (scsi_host_dif_capable(sdp->host, type) == 0) { | |
322 | + sd_printk(KERN_INFO, sdkp, "Type %d protection " \ | |
323 | + "unsupported by HBA. Disabling DIF.\n", type); | |
324 | + sdkp->protection_type = 0; | |
325 | + return; | |
326 | + } | |
327 | + | |
328 | + sd_printk(KERN_INFO, sdkp, "Enabling DIF Type %d protection\n", | |
329 | + type); | |
330 | + | |
331 | + return; | |
332 | + } | |
333 | + | |
334 | + /* Enable DMA of protection information */ | |
335 | + if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) | |
336 | + if (type == SD_DIF_TYPE3_PROTECTION) | |
337 | + blk_integrity_register(disk, &dif_type3_integrity_ip); | |
338 | + else | |
339 | + blk_integrity_register(disk, &dif_type1_integrity_ip); | |
340 | + else | |
341 | + if (type == SD_DIF_TYPE3_PROTECTION) | |
342 | + blk_integrity_register(disk, &dif_type3_integrity_crc); | |
343 | + else | |
344 | + blk_integrity_register(disk, &dif_type1_integrity_crc); | |
345 | + | |
346 | + sd_printk(KERN_INFO, sdkp, | |
347 | + "Enabling %s integrity protection\n", disk->integrity->name); | |
348 | + | |
349 | + /* Signal to block layer that we support sector tagging */ | |
350 | + if (type && sdkp->ATO) { | |
351 | + if (type == SD_DIF_TYPE3_PROTECTION) | |
352 | + disk->integrity->tag_size = sizeof(u16) + sizeof(u32); | |
353 | + else | |
354 | + disk->integrity->tag_size = sizeof(u16); | |
355 | + | |
356 | + sd_printk(KERN_INFO, sdkp, "DIF application tag size %u\n", | |
357 | + disk->integrity->tag_size); | |
358 | + } | |
359 | +} | |
360 | + | |
361 | +/* | |
362 | + * DIF DMA operation magic decoder ring. | |
363 | + */ | |
364 | +void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix) | |
365 | +{ | |
366 | + int csum_convert, prot_op; | |
367 | + | |
368 | + prot_op = 0; | |
369 | + | |
370 | + /* Convert checksum? */ | |
371 | + if (scsi_host_get_guard(scmd->device->host) != SHOST_DIX_GUARD_CRC) | |
372 | + csum_convert = 1; | |
373 | + else | |
374 | + csum_convert = 0; | |
375 | + | |
376 | + switch (scmd->cmnd[0]) { | |
377 | + case READ_10: | |
378 | + case READ_12: | |
379 | + case READ_16: | |
380 | + if (dif && dix) | |
381 | + if (csum_convert) | |
382 | + prot_op = SCSI_PROT_READ_CONVERT; | |
383 | + else | |
384 | + prot_op = SCSI_PROT_READ_PASS; | |
385 | + else if (dif && !dix) | |
386 | + prot_op = SCSI_PROT_READ_STRIP; | |
387 | + else if (!dif && dix) | |
388 | + prot_op = SCSI_PROT_READ_INSERT; | |
389 | + | |
390 | + break; | |
391 | + | |
392 | + case WRITE_10: | |
393 | + case WRITE_12: | |
394 | + case WRITE_16: | |
395 | + if (dif && dix) | |
396 | + if (csum_convert) | |
397 | + prot_op = SCSI_PROT_WRITE_CONVERT; | |
398 | + else | |
399 | + prot_op = SCSI_PROT_WRITE_PASS; | |
400 | + else if (dif && !dix) | |
401 | + prot_op = SCSI_PROT_WRITE_INSERT; | |
402 | + else if (!dif && dix) | |
403 | + prot_op = SCSI_PROT_WRITE_STRIP; | |
404 | + | |
405 | + break; | |
406 | + } | |
407 | + | |
408 | + scsi_set_prot_op(scmd, prot_op); | |
409 | + scsi_set_prot_type(scmd, dif); | |
410 | +} | |
411 | + | |
412 | +/* | |
413 | + * The virtual start sector is the one that was originally submitted | |
414 | + * by the block layer. Due to partitioning, MD/DM cloning, etc. the | |
415 | + * actual physical start sector is likely to be different. Remap | |
416 | + * protection information to match the physical LBA. | |
417 | + * | |
418 | + * From a protocol perspective there's a slight difference between | |
419 | + * Type 1 and 2. The latter uses 32-byte CDBs exclusively, and the | |
420 | + * reference tag is seeded in the CDB. This gives us the potential to | |
421 | + * avoid virt->phys remapping during write. However, at read time we | |
422 | + * don't know whether the virt sector is the same as when we wrote it | |
423 | + * (we could be reading from real disk as opposed to MD/DM device. So | |
424 | + * we always remap Type 2 making it identical to Type 1. | |
425 | + * | |
426 | + * Type 3 does not have a reference tag so no remapping is required. | |
427 | + */ | |
428 | +int sd_dif_prepare(struct request *rq, sector_t hw_sector, unsigned int sector_sz) | |
429 | +{ | |
430 | + const int tuple_sz = sizeof(struct sd_dif_tuple); | |
431 | + struct bio *bio; | |
432 | + struct scsi_disk *sdkp; | |
433 | + struct sd_dif_tuple *sdt; | |
434 | + unsigned int i, j; | |
435 | + u32 phys, virt; | |
436 | + | |
437 | + /* Already remapped? */ | |
438 | + if (rq->cmd_flags & REQ_INTEGRITY) | |
439 | + return 0; | |
440 | + | |
441 | + sdkp = rq->bio->bi_bdev->bd_disk->private_data; | |
442 | + | |
443 | + if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION) | |
444 | + return 0; | |
445 | + | |
446 | + rq->cmd_flags |= REQ_INTEGRITY; | |
447 | + phys = hw_sector & 0xffffffff; | |
448 | + | |
449 | + __rq_for_each_bio(bio, rq) { | |
450 | + struct bio_vec *iv; | |
451 | + | |
452 | + virt = bio->bi_integrity->bip_sector & 0xffffffff; | |
453 | + | |
454 | + bip_for_each_vec(iv, bio->bi_integrity, i) { | |
455 | + sdt = kmap_atomic(iv->bv_page, KM_USER0) | |
456 | + + iv->bv_offset; | |
457 | + | |
458 | + for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) { | |
459 | + | |
460 | + if (be32_to_cpu(sdt->ref_tag) != virt) | |
461 | + goto error; | |
462 | + | |
463 | + sdt->ref_tag = cpu_to_be32(phys); | |
464 | + virt++; | |
465 | + phys++; | |
466 | + } | |
467 | + | |
468 | + kunmap_atomic(sdt, KM_USER0); | |
469 | + } | |
470 | + } | |
471 | + | |
472 | + return 0; | |
473 | + | |
474 | +error: | |
475 | + kunmap_atomic(sdt, KM_USER0); | |
476 | + sd_printk(KERN_ERR, sdkp, "%s: virt %u, phys %u, ref %u\n", | |
477 | + __func__, virt, phys, be32_to_cpu(sdt->ref_tag)); | |
478 | + | |
479 | + return -EIO; | |
480 | +} | |
481 | + | |
482 | +/* | |
483 | + * Remap physical sector values in the reference tag to the virtual | |
484 | + * values expected by the block layer. | |
485 | + */ | |
486 | +void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes) | |
487 | +{ | |
488 | + const int tuple_sz = sizeof(struct sd_dif_tuple); | |
489 | + struct scsi_disk *sdkp; | |
490 | + struct bio *bio; | |
491 | + struct sd_dif_tuple *sdt; | |
492 | + unsigned int i, j, sectors, sector_sz; | |
493 | + u32 phys, virt; | |
494 | + | |
495 | + sdkp = scsi_disk(scmd->request->rq_disk); | |
496 | + | |
497 | + if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION || good_bytes == 0) | |
498 | + return; | |
499 | + | |
500 | + sector_sz = scmd->device->sector_size; | |
501 | + sectors = good_bytes / sector_sz; | |
502 | + | |
503 | + phys = scmd->request->sector & 0xffffffff; | |
504 | + if (sector_sz == 4096) | |
505 | + phys >>= 3; | |
506 | + | |
507 | + __rq_for_each_bio(bio, scmd->request) { | |
508 | + struct bio_vec *iv; | |
509 | + | |
510 | + virt = bio->bi_integrity->bip_sector & 0xffffffff; | |
511 | + | |
512 | + bip_for_each_vec(iv, bio->bi_integrity, i) { | |
513 | + sdt = kmap_atomic(iv->bv_page, KM_USER0) | |
514 | + + iv->bv_offset; | |
515 | + | |
516 | + for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) { | |
517 | + | |
518 | + if (sectors == 0) { | |
519 | + kunmap_atomic(sdt, KM_USER0); | |
520 | + return; | |
521 | + } | |
522 | + | |
523 | + if (be32_to_cpu(sdt->ref_tag) != phys && | |
524 | + sdt->app_tag != 0xffff) | |
525 | + sdt->ref_tag = 0xffffffff; /* Bad ref */ | |
526 | + else | |
527 | + sdt->ref_tag = cpu_to_be32(virt); | |
528 | + | |
529 | + virt++; | |
530 | + phys++; | |
531 | + sectors--; | |
532 | + } | |
533 | + | |
534 | + kunmap_atomic(sdt, KM_USER0); | |
535 | + } | |
536 | + } | |
537 | +} |