diff options
Diffstat (limited to 'drivers/scsi/sd_dif.c')
| -rw-r--r-- | drivers/scsi/sd_dif.c | 435 |
1 files changed, 24 insertions, 411 deletions
diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c index 6174ca4ea275..ff4217fef93b 100644 --- a/drivers/scsi/sd_dif.c +++ b/drivers/scsi/sd_dif.c @@ -1,27 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * sd_dif.c - SCSI Data Integrity Field * * Copyright (C) 2007, 2008 Oracle Corporation * Written by: Martin K. Petersen <martin.petersen@oracle.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, - * USA. - * */ -#include <linux/blkdev.h> -#include <linux/crc-t10dif.h> +#include <linux/blk-integrity.h> +#include <linux/t10-pi.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -33,278 +19,20 @@ #include <scsi/scsi_ioctl.h> #include <scsi/scsicam.h> -#include <net/checksum.h> - #include "sd.h" -typedef __u16 (csum_fn) (void *, unsigned int); - -static __u16 sd_dif_crc_fn(void *data, unsigned int len) -{ - return cpu_to_be16(crc_t10dif(data, len)); -} - -static __u16 sd_dif_ip_fn(void *data, unsigned int len) -{ - return ip_compute_csum(data, len); -} - -/* - * Type 1 and Type 2 protection use the same format: 16 bit guard tag, - * 16 bit app tag, 32 bit reference tag. - */ -static void sd_dif_type1_generate(struct blk_integrity_exchg *bix, csum_fn *fn) -{ - void *buf = bix->data_buf; - struct sd_dif_tuple *sdt = bix->prot_buf; - sector_t sector = bix->sector; - unsigned int i; - - for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { - sdt->guard_tag = fn(buf, bix->sector_size); - sdt->ref_tag = cpu_to_be32(sector & 0xffffffff); - sdt->app_tag = 0; - - buf += bix->sector_size; - sector++; - } -} - -static void sd_dif_type1_generate_crc(struct blk_integrity_exchg *bix) -{ - sd_dif_type1_generate(bix, sd_dif_crc_fn); -} - -static void sd_dif_type1_generate_ip(struct blk_integrity_exchg *bix) -{ - sd_dif_type1_generate(bix, sd_dif_ip_fn); -} - -static int sd_dif_type1_verify(struct blk_integrity_exchg *bix, csum_fn *fn) -{ - void *buf = bix->data_buf; - struct sd_dif_tuple *sdt = bix->prot_buf; - sector_t sector = bix->sector; - unsigned int i; - __u16 csum; - - for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { - /* Unwritten sectors */ - if (sdt->app_tag == 0xffff) - return 0; - - if (be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { - printk(KERN_ERR - "%s: ref tag error on sector %lu (rcvd %u)\n", - bix->disk_name, (unsigned long)sector, - be32_to_cpu(sdt->ref_tag)); - return -EIO; - } - - csum = fn(buf, bix->sector_size); - - if (sdt->guard_tag != csum) { - printk(KERN_ERR "%s: guard tag error on sector %lu " \ - "(rcvd %04x, data %04x)\n", bix->disk_name, - (unsigned long)sector, - be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); - return -EIO; - } - - buf += bix->sector_size; - sector++; - } - - return 0; -} - -static int sd_dif_type1_verify_crc(struct blk_integrity_exchg *bix) -{ - return sd_dif_type1_verify(bix, sd_dif_crc_fn); -} - -static int sd_dif_type1_verify_ip(struct blk_integrity_exchg *bix) -{ - return sd_dif_type1_verify(bix, sd_dif_ip_fn); -} - -/* - * Functions for interleaving and deinterleaving application tags - */ -static void sd_dif_type1_set_tag(void *prot, void *tag_buf, unsigned int sectors) -{ - struct sd_dif_tuple *sdt = prot; - u8 *tag = tag_buf; - unsigned int i, j; - - for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { - sdt->app_tag = tag[j] << 8 | tag[j+1]; - BUG_ON(sdt->app_tag == 0xffff); - } -} - -static void sd_dif_type1_get_tag(void *prot, void *tag_buf, unsigned int sectors) -{ - struct sd_dif_tuple *sdt = prot; - u8 *tag = tag_buf; - unsigned int i, j; - - for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { - tag[j] = (sdt->app_tag & 0xff00) >> 8; - tag[j+1] = sdt->app_tag & 0xff; - } -} - -static struct blk_integrity dif_type1_integrity_crc = { - .name = "T10-DIF-TYPE1-CRC", - .generate_fn = sd_dif_type1_generate_crc, - .verify_fn = sd_dif_type1_verify_crc, - .get_tag_fn = sd_dif_type1_get_tag, - .set_tag_fn = sd_dif_type1_set_tag, - .tuple_size = sizeof(struct sd_dif_tuple), - .tag_size = 0, -}; - -static struct blk_integrity dif_type1_integrity_ip = { - .name = "T10-DIF-TYPE1-IP", - .generate_fn = sd_dif_type1_generate_ip, - .verify_fn = sd_dif_type1_verify_ip, - .get_tag_fn = sd_dif_type1_get_tag, - .set_tag_fn = sd_dif_type1_set_tag, - .tuple_size = sizeof(struct sd_dif_tuple), - .tag_size = 0, -}; - - -/* - * Type 3 protection has a 16-bit guard tag and 16 + 32 bits of opaque - * tag space. - */ -static void sd_dif_type3_generate(struct blk_integrity_exchg *bix, csum_fn *fn) -{ - void *buf = bix->data_buf; - struct sd_dif_tuple *sdt = bix->prot_buf; - unsigned int i; - - for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { - sdt->guard_tag = fn(buf, bix->sector_size); - sdt->ref_tag = 0; - sdt->app_tag = 0; - - buf += bix->sector_size; - } -} - -static void sd_dif_type3_generate_crc(struct blk_integrity_exchg *bix) -{ - sd_dif_type3_generate(bix, sd_dif_crc_fn); -} - -static void sd_dif_type3_generate_ip(struct blk_integrity_exchg *bix) -{ - sd_dif_type3_generate(bix, sd_dif_ip_fn); -} - -static int sd_dif_type3_verify(struct blk_integrity_exchg *bix, csum_fn *fn) -{ - void *buf = bix->data_buf; - struct sd_dif_tuple *sdt = bix->prot_buf; - sector_t sector = bix->sector; - unsigned int i; - __u16 csum; - - for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { - /* Unwritten sectors */ - if (sdt->app_tag == 0xffff && sdt->ref_tag == 0xffffffff) - return 0; - - csum = fn(buf, bix->sector_size); - - if (sdt->guard_tag != csum) { - printk(KERN_ERR "%s: guard tag error on sector %lu " \ - "(rcvd %04x, data %04x)\n", bix->disk_name, - (unsigned long)sector, - be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); - return -EIO; - } - - buf += bix->sector_size; - sector++; - } - - return 0; -} - -static int sd_dif_type3_verify_crc(struct blk_integrity_exchg *bix) -{ - return sd_dif_type3_verify(bix, sd_dif_crc_fn); -} - -static int sd_dif_type3_verify_ip(struct blk_integrity_exchg *bix) -{ - return sd_dif_type3_verify(bix, sd_dif_ip_fn); -} - -static void sd_dif_type3_set_tag(void *prot, void *tag_buf, unsigned int sectors) -{ - struct sd_dif_tuple *sdt = prot; - u8 *tag = tag_buf; - unsigned int i, j; - - for (i = 0, j = 0 ; i < sectors ; i++, j += 6, sdt++) { - sdt->app_tag = tag[j] << 8 | tag[j+1]; - sdt->ref_tag = tag[j+2] << 24 | tag[j+3] << 16 | - tag[j+4] << 8 | tag[j+5]; - } -} - -static void sd_dif_type3_get_tag(void *prot, void *tag_buf, unsigned int sectors) -{ - struct sd_dif_tuple *sdt = prot; - u8 *tag = tag_buf; - unsigned int i, j; - - for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { - tag[j] = (sdt->app_tag & 0xff00) >> 8; - tag[j+1] = sdt->app_tag & 0xff; - tag[j+2] = (sdt->ref_tag & 0xff000000) >> 24; - tag[j+3] = (sdt->ref_tag & 0xff0000) >> 16; - tag[j+4] = (sdt->ref_tag & 0xff00) >> 8; - tag[j+5] = sdt->ref_tag & 0xff; - BUG_ON(sdt->app_tag == 0xffff || sdt->ref_tag == 0xffffffff); - } -} - -static struct blk_integrity dif_type3_integrity_crc = { - .name = "T10-DIF-TYPE3-CRC", - .generate_fn = sd_dif_type3_generate_crc, - .verify_fn = sd_dif_type3_verify_crc, - .get_tag_fn = sd_dif_type3_get_tag, - .set_tag_fn = sd_dif_type3_set_tag, - .tuple_size = sizeof(struct sd_dif_tuple), - .tag_size = 0, -}; - -static struct blk_integrity dif_type3_integrity_ip = { - .name = "T10-DIF-TYPE3-IP", - .generate_fn = sd_dif_type3_generate_ip, - .verify_fn = sd_dif_type3_verify_ip, - .get_tag_fn = sd_dif_type3_get_tag, - .set_tag_fn = sd_dif_type3_set_tag, - .tuple_size = sizeof(struct sd_dif_tuple), - .tag_size = 0, -}; - /* * Configure exchange of protection information between OS and HBA. */ -void sd_dif_config_host(struct scsi_disk *sdkp) +void sd_dif_config_host(struct scsi_disk *sdkp, struct queue_limits *lim) { struct scsi_device *sdp = sdkp->device; - struct gendisk *disk = sdkp->disk; u8 type = sdkp->protection_type; + struct blk_integrity *bi = &lim->integrity; int dif, dix; + memset(bi, 0, sizeof(*bi)); + dif = scsi_host_dif_capable(sdp->host, type); dix = scsi_host_dix_capable(sdp->host, type); @@ -317,144 +45,29 @@ void sd_dif_config_host(struct scsi_disk *sdkp) /* Enable DMA of protection information */ if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) - if (type == SD_DIF_TYPE3_PROTECTION) - blk_integrity_register(disk, &dif_type3_integrity_ip); - else - blk_integrity_register(disk, &dif_type1_integrity_ip); + bi->csum_type = BLK_INTEGRITY_CSUM_IP; else - if (type == SD_DIF_TYPE3_PROTECTION) - blk_integrity_register(disk, &dif_type3_integrity_crc); - else - blk_integrity_register(disk, &dif_type1_integrity_crc); - - sd_printk(KERN_NOTICE, sdkp, - "Enabling DIX %s protection\n", disk->integrity->name); - - /* Signal to block layer that we support sector tagging */ - if (dif && type && sdkp->ATO) { - if (type == SD_DIF_TYPE3_PROTECTION) - disk->integrity->tag_size = sizeof(u16) + sizeof(u32); - else - disk->integrity->tag_size = sizeof(u16); - - sd_printk(KERN_NOTICE, sdkp, "DIF application tag size %u\n", - disk->integrity->tag_size); - } -} - -/* - * The virtual start sector is the one that was originally submitted - * by the block layer. Due to partitioning, MD/DM cloning, etc. the - * actual physical start sector is likely to be different. Remap - * protection information to match the physical LBA. - * - * From a protocol perspective there's a slight difference between - * Type 1 and 2. The latter uses 32-byte CDBs exclusively, and the - * reference tag is seeded in the CDB. This gives us the potential to - * avoid virt->phys remapping during write. However, at read time we - * don't know whether the virt sector is the same as when we wrote it - * (we could be reading from real disk as opposed to MD/DM device. So - * we always remap Type 2 making it identical to Type 1. - * - * Type 3 does not have a reference tag so no remapping is required. - */ -void sd_dif_prepare(struct request *rq, sector_t hw_sector, - unsigned int sector_sz) -{ - const int tuple_sz = sizeof(struct sd_dif_tuple); - struct bio *bio; - struct scsi_disk *sdkp; - struct sd_dif_tuple *sdt; - unsigned int i, j; - u32 phys, virt; - - sdkp = rq->bio->bi_bdev->bd_disk->private_data; - - if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION) - return; - - phys = hw_sector & 0xffffffff; - - __rq_for_each_bio(bio, rq) { - struct bio_vec *iv; - - /* Already remapped? */ - if (bio_flagged(bio, BIO_MAPPED_INTEGRITY)) - break; - - virt = bio->bi_integrity->bip_sector & 0xffffffff; + bi->csum_type = BLK_INTEGRITY_CSUM_CRC; - bip_for_each_vec(iv, bio->bi_integrity, i) { - sdt = kmap_atomic(iv->bv_page) - + iv->bv_offset; + if (type != T10_PI_TYPE3_PROTECTION) + bi->flags |= BLK_INTEGRITY_REF_TAG; - for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) { + bi->metadata_size = sizeof(struct t10_pi_tuple); + bi->pi_tuple_size = bi->metadata_size; - if (be32_to_cpu(sdt->ref_tag) == virt) - sdt->ref_tag = cpu_to_be32(phys); + if (dif && type) { + bi->flags |= BLK_INTEGRITY_DEVICE_CAPABLE; - virt++; - phys++; - } + if (!sdkp->ATO) + return; - kunmap_atomic(sdt); - } - - bio->bi_flags |= (1 << BIO_MAPPED_INTEGRITY); + if (type == T10_PI_TYPE3_PROTECTION) + bi->tag_size = sizeof(u16) + sizeof(u32); + else + bi->tag_size = sizeof(u16); } -} -/* - * Remap physical sector values in the reference tag to the virtual - * values expected by the block layer. - */ -void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes) -{ - const int tuple_sz = sizeof(struct sd_dif_tuple); - struct scsi_disk *sdkp; - struct bio *bio; - struct sd_dif_tuple *sdt; - unsigned int i, j, sectors, sector_sz; - u32 phys, virt; - - sdkp = scsi_disk(scmd->request->rq_disk); - - if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION || good_bytes == 0) - return; - - sector_sz = scmd->device->sector_size; - sectors = good_bytes / sector_sz; - - phys = blk_rq_pos(scmd->request) & 0xffffffff; - if (sector_sz == 4096) - phys >>= 3; - - __rq_for_each_bio(bio, scmd->request) { - struct bio_vec *iv; - - virt = bio->bi_integrity->bip_sector & 0xffffffff; - - bip_for_each_vec(iv, bio->bi_integrity, i) { - sdt = kmap_atomic(iv->bv_page) - + iv->bv_offset; - - for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) { - - if (sectors == 0) { - kunmap_atomic(sdt); - return; - } - - if (be32_to_cpu(sdt->ref_tag) == phys) - sdt->ref_tag = cpu_to_be32(virt); - - virt++; - phys++; - sectors--; - } - - kunmap_atomic(sdt); - } - } + sd_first_printk(KERN_NOTICE, sdkp, + "Enabling DIX %s, application tag size %u bytes\n", + blk_integrity_profile_name(bi), bi->tag_size); } - |
