2008-07-17 12:28:35 +04:00
/*
* 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 0213 9 ,
* USA .
*
*/
# include <linux/blkdev.h>
# include <linux/crc-t10dif.h>
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_dbg.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_driver.h>
# include <scsi/scsi_eh.h>
# include <scsi/scsi_host.h>
# 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 .
*/
2014-09-27 03:20:01 +04:00
static void sd_dif_type1_generate ( struct blk_integrity_iter * iter , csum_fn * fn )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
void * buf = iter - > data_buf ;
struct sd_dif_tuple * sdt = iter - > prot_buf ;
sector_t seed = iter - > seed ;
2008-07-17 12:28:35 +04:00
unsigned int i ;
2014-09-27 03:20:01 +04:00
for ( i = 0 ; i < iter - > data_size ; i + = iter - > interval , sdt + + ) {
sdt - > guard_tag = fn ( buf , iter - > interval ) ;
2014-09-27 03:19:59 +04:00
sdt - > ref_tag = cpu_to_be32 ( seed & 0xffffffff ) ;
2008-07-17 12:28:35 +04:00
sdt - > app_tag = 0 ;
2014-09-27 03:20:01 +04:00
buf + = iter - > interval ;
2014-09-27 03:19:59 +04:00
seed + + ;
2008-07-17 12:28:35 +04:00
}
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type1_generate_crc ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
sd_dif_type1_generate ( iter , sd_dif_crc_fn ) ;
return 0 ;
2008-07-17 12:28:35 +04:00
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type1_generate_ip ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
sd_dif_type1_generate ( iter , sd_dif_ip_fn ) ;
return 0 ;
2008-07-17 12:28:35 +04:00
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type1_verify ( struct blk_integrity_iter * iter , csum_fn * fn )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
void * buf = iter - > data_buf ;
struct sd_dif_tuple * sdt = iter - > prot_buf ;
sector_t seed = iter - > seed ;
2008-07-17 12:28:35 +04:00
unsigned int i ;
__u16 csum ;
2014-09-27 03:20:01 +04:00
for ( i = 0 ; i < iter - > data_size ; i + = iter - > interval , sdt + + ) {
2008-07-17 12:28:35 +04:00
/* Unwritten sectors */
if ( sdt - > app_tag = = 0xffff )
return 0 ;
2014-09-27 03:19:59 +04:00
if ( be32_to_cpu ( sdt - > ref_tag ) ! = ( seed & 0xffffffff ) ) {
2008-07-17 12:28:35 +04:00
printk ( KERN_ERR
" %s: ref tag error on sector %lu (rcvd %u) \n " ,
2014-09-27 03:20:01 +04:00
iter - > disk_name , ( unsigned long ) seed ,
2008-07-17 12:28:35 +04:00
be32_to_cpu ( sdt - > ref_tag ) ) ;
return - EIO ;
}
2014-09-27 03:20:01 +04:00
csum = fn ( buf , iter - > interval ) ;
2008-07-17 12:28:35 +04:00
if ( sdt - > guard_tag ! = csum ) {
printk ( KERN_ERR " %s: guard tag error on sector %lu " \
2014-09-27 03:20:01 +04:00
" (rcvd %04x, data %04x) \n " , iter - > disk_name ,
2014-09-27 03:19:59 +04:00
( unsigned long ) seed ,
2008-07-17 12:28:35 +04:00
be16_to_cpu ( sdt - > guard_tag ) , be16_to_cpu ( csum ) ) ;
return - EIO ;
}
2014-09-27 03:20:01 +04:00
buf + = iter - > interval ;
2014-09-27 03:19:59 +04:00
seed + + ;
2008-07-17 12:28:35 +04:00
}
return 0 ;
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type1_verify_crc ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
return sd_dif_type1_verify ( iter , sd_dif_crc_fn ) ;
2008-07-17 12:28:35 +04:00
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type1_verify_ip ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
return sd_dif_type1_verify ( iter , sd_dif_ip_fn ) ;
2008-07-17 12:28:35 +04:00
}
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 ,
. 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 ,
. 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 .
*/
2014-09-27 03:20:01 +04:00
static void sd_dif_type3_generate ( struct blk_integrity_iter * iter , csum_fn * fn )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
void * buf = iter - > data_buf ;
struct sd_dif_tuple * sdt = iter - > prot_buf ;
2008-07-17 12:28:35 +04:00
unsigned int i ;
2014-09-27 03:20:01 +04:00
for ( i = 0 ; i < iter - > data_size ; i + = iter - > interval , sdt + + ) {
sdt - > guard_tag = fn ( buf , iter - > interval ) ;
2008-07-17 12:28:35 +04:00
sdt - > ref_tag = 0 ;
sdt - > app_tag = 0 ;
2014-09-27 03:20:01 +04:00
buf + = iter - > interval ;
2008-07-17 12:28:35 +04:00
}
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type3_generate_crc ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
sd_dif_type3_generate ( iter , sd_dif_crc_fn ) ;
return 0 ;
2008-07-17 12:28:35 +04:00
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type3_generate_ip ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
sd_dif_type3_generate ( iter , sd_dif_ip_fn ) ;
return 0 ;
2008-07-17 12:28:35 +04:00
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type3_verify ( struct blk_integrity_iter * iter , csum_fn * fn )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
void * buf = iter - > data_buf ;
struct sd_dif_tuple * sdt = iter - > prot_buf ;
sector_t seed = iter - > seed ;
2008-07-17 12:28:35 +04:00
unsigned int i ;
__u16 csum ;
2014-09-27 03:20:01 +04:00
for ( i = 0 ; i < iter - > data_size ; i + = iter - > interval , sdt + + ) {
2008-07-17 12:28:35 +04:00
/* Unwritten sectors */
if ( sdt - > app_tag = = 0xffff & & sdt - > ref_tag = = 0xffffffff )
return 0 ;
2014-09-27 03:20:01 +04:00
csum = fn ( buf , iter - > interval ) ;
2008-07-17 12:28:35 +04:00
if ( sdt - > guard_tag ! = csum ) {
printk ( KERN_ERR " %s: guard tag error on sector %lu " \
2014-09-27 03:20:01 +04:00
" (rcvd %04x, data %04x) \n " , iter - > disk_name ,
2014-09-27 03:19:59 +04:00
( unsigned long ) seed ,
2008-07-17 12:28:35 +04:00
be16_to_cpu ( sdt - > guard_tag ) , be16_to_cpu ( csum ) ) ;
return - EIO ;
}
2014-09-27 03:20:01 +04:00
buf + = iter - > interval ;
2014-09-27 03:19:59 +04:00
seed + + ;
2008-07-17 12:28:35 +04:00
}
return 0 ;
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type3_verify_crc ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
return sd_dif_type3_verify ( iter , sd_dif_crc_fn ) ;
2008-07-17 12:28:35 +04:00
}
2014-09-27 03:20:01 +04:00
static int sd_dif_type3_verify_ip ( struct blk_integrity_iter * iter )
2008-07-17 12:28:35 +04:00
{
2014-09-27 03:20:01 +04:00
return sd_dif_type3_verify ( iter , sd_dif_ip_fn ) ;
2008-07-17 12:28:35 +04:00
}
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 ,
. 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 ,
. 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 )
{
struct scsi_device * sdp = sdkp - > device ;
struct gendisk * disk = sdkp - > disk ;
u8 type = sdkp - > protection_type ;
2008-09-20 02:47:21 +04:00
int dif , dix ;
2008-07-17 12:28:35 +04:00
2008-09-20 02:47:21 +04:00
dif = scsi_host_dif_capable ( sdp - > host , type ) ;
dix = scsi_host_dix_capable ( sdp - > host , type ) ;
2008-07-17 12:28:35 +04:00
2008-09-20 02:47:21 +04:00
if ( ! dix & & scsi_host_dix_capable ( sdp - > host , 0 ) ) {
dif = 0 ; dix = 1 ;
}
2008-07-17 12:28:35 +04:00
2008-09-20 02:47:21 +04:00
if ( ! dix )
2008-07-17 12:28:35 +04:00
return ;
/* 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 ) ;
else
if ( type = = SD_DIF_TYPE3_PROTECTION )
blk_integrity_register ( disk , & dif_type3_integrity_crc ) ;
else
blk_integrity_register ( disk , & dif_type1_integrity_crc ) ;
2008-10-01 09:37:21 +04:00
sd_printk ( KERN_NOTICE , sdkp ,
2008-09-20 02:47:21 +04:00
" Enabling DIX %s protection \n " , disk - > integrity - > name ) ;
2008-07-17 12:28:35 +04:00
/* Signal to block layer that we support sector tagging */
2014-09-27 03:20:03 +04:00
if ( dif & & type ) {
disk - > integrity - > flags | = BLK_INTEGRITY_DEVICE_CAPABLE ;
if ( ! sdkp )
return ;
2008-07-17 12:28:35 +04:00
if ( type = = SD_DIF_TYPE3_PROTECTION )
disk - > integrity - > tag_size = sizeof ( u16 ) + sizeof ( u32 ) ;
else
disk - > integrity - > tag_size = sizeof ( u16 ) ;
2008-10-01 09:37:21 +04:00
sd_printk ( KERN_NOTICE , sdkp , " DIF application tag size %u \n " ,
2008-07-17 12:28:35 +04:00
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 .
*/
2012-08-28 22:29:33 +04:00
void sd_dif_prepare ( struct request * rq , sector_t hw_sector ,
unsigned int sector_sz )
2008-07-17 12:28:35 +04:00
{
const int tuple_sz = sizeof ( struct sd_dif_tuple ) ;
struct bio * bio ;
struct scsi_disk * sdkp ;
struct sd_dif_tuple * sdt ;
u32 phys , virt ;
sdkp = rq - > bio - > bi_bdev - > bd_disk - > private_data ;
if ( sdkp - > protection_type = = SD_DIF_TYPE3_PROTECTION )
2012-08-28 22:29:33 +04:00
return ;
2008-07-17 12:28:35 +04:00
phys = hw_sector & 0xffffffff ;
__rq_for_each_bio ( bio , rq ) {
2014-09-27 03:20:01 +04:00
struct bio_integrity_payload * bip = bio_integrity ( bio ) ;
2013-11-24 05:20:16 +04:00
struct bio_vec iv ;
struct bvec_iter iter ;
unsigned int j ;
2008-07-17 12:28:35 +04:00
2010-10-15 17:49:20 +04:00
/* Already remapped? */
if ( bio_flagged ( bio , BIO_MAPPED_INTEGRITY ) )
break ;
2014-09-27 03:20:01 +04:00
virt = bip_get_seed ( bip ) & 0xffffffff ;
2008-07-17 12:28:35 +04:00
2014-09-27 03:20:01 +04:00
bip_for_each_vec ( iv , bip , iter ) {
2013-11-24 05:20:16 +04:00
sdt = kmap_atomic ( iv . bv_page )
+ iv . bv_offset ;
2008-07-17 12:28:35 +04:00
2013-11-24 05:20:16 +04:00
for ( j = 0 ; j < iv . bv_len ; j + = tuple_sz , sdt + + ) {
2008-07-17 12:28:35 +04:00
2012-08-28 22:29:33 +04:00
if ( be32_to_cpu ( sdt - > ref_tag ) = = virt )
sdt - > ref_tag = cpu_to_be32 ( phys ) ;
2008-07-17 12:28:35 +04:00
virt + + ;
phys + + ;
}
2011-11-25 19:14:23 +04:00
kunmap_atomic ( sdt ) ;
2008-07-17 12:28:35 +04:00
}
2010-10-15 17:49:20 +04:00
2012-03-06 02:59:16 +04:00
bio - > bi_flags | = ( 1 < < BIO_MAPPED_INTEGRITY ) ;
2008-07-17 12:28:35 +04:00
}
}
/*
* 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 ;
2013-11-24 05:20:16 +04:00
unsigned int j , sectors , sector_sz ;
2008-07-17 12:28:35 +04:00
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 ;
2009-05-07 17:24:39 +04:00
phys = blk_rq_pos ( scmd - > request ) & 0xffffffff ;
2008-07-17 12:28:35 +04:00
if ( sector_sz = = 4096 )
phys > > = 3 ;
__rq_for_each_bio ( bio , scmd - > request ) {
2014-09-27 03:20:01 +04:00
struct bio_integrity_payload * bip = bio_integrity ( bio ) ;
2013-11-24 05:20:16 +04:00
struct bio_vec iv ;
struct bvec_iter iter ;
2008-07-17 12:28:35 +04:00
2014-09-27 03:20:01 +04:00
virt = bip_get_seed ( bip ) & 0xffffffff ;
2008-07-17 12:28:35 +04:00
2014-09-27 03:20:01 +04:00
bip_for_each_vec ( iv , bip , iter ) {
2013-11-24 05:20:16 +04:00
sdt = kmap_atomic ( iv . bv_page )
+ iv . bv_offset ;
2008-07-17 12:28:35 +04:00
2013-11-24 05:20:16 +04:00
for ( j = 0 ; j < iv . bv_len ; j + = tuple_sz , sdt + + ) {
2008-07-17 12:28:35 +04:00
if ( sectors = = 0 ) {
2011-11-25 19:14:23 +04:00
kunmap_atomic ( sdt ) ;
2008-07-17 12:28:35 +04:00
return ;
}
2012-08-28 22:29:33 +04:00
if ( be32_to_cpu ( sdt - > ref_tag ) = = phys )
2008-07-17 12:28:35 +04:00
sdt - > ref_tag = cpu_to_be32 ( virt ) ;
virt + + ;
phys + + ;
sectors - - ;
}
2011-11-25 19:14:23 +04:00
kunmap_atomic ( sdt ) ;
2008-07-17 12:28:35 +04:00
}
}
}