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>
2014-09-27 03:20:07 +04:00
# include <linux/t10-pi.h>
2008-07-17 12:28:35 +04:00
# 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 "sd.h"
/*
* 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 */
2014-09-27 03:20:05 +04:00
if ( scsi_host_get_guard ( sdkp - > device - > host ) & SHOST_DIX_GUARD_IP ) {
2008-07-17 12:28:35 +04:00
if ( type = = SD_DIF_TYPE3_PROTECTION )
2014-09-27 03:20:07 +04:00
blk_integrity_register ( disk , & t10_pi_type3_ip ) ;
2008-07-17 12:28:35 +04:00
else
2014-09-27 03:20:07 +04:00
blk_integrity_register ( disk , & t10_pi_type1_ip ) ;
2014-09-27 03:20:05 +04:00
disk - > integrity - > flags | = BLK_INTEGRITY_IP_CHECKSUM ;
} else
2008-07-17 12:28:35 +04:00
if ( type = = SD_DIF_TYPE3_PROTECTION )
2014-09-27 03:20:07 +04:00
blk_integrity_register ( disk , & t10_pi_type3_crc ) ;
2008-07-17 12:28:35 +04:00
else
2014-09-27 03:20:07 +04:00
blk_integrity_register ( disk , & t10_pi_type1_crc ) ;
2008-07-17 12:28:35 +04:00
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
{
2014-09-27 03:20:07 +04:00
const int tuple_sz = sizeof ( struct t10_pi_tuple ) ;
2008-07-17 12:28:35 +04:00
struct bio * bio ;
struct scsi_disk * sdkp ;
2014-09-27 03:20:07 +04:00
struct t10_pi_tuple * pi ;
2008-07-17 12:28:35 +04:00
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? */
2014-09-27 03:20:04 +04:00
if ( bip - > bip_flags & BIP_MAPPED_INTEGRITY )
2010-10-15 17:49:20 +04:00
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 ) {
2014-09-27 03:20:07 +04:00
pi = kmap_atomic ( iv . bv_page ) + iv . bv_offset ;
2008-07-17 12:28:35 +04:00
2014-09-27 03:20:07 +04:00
for ( j = 0 ; j < iv . bv_len ; j + = tuple_sz , pi + + ) {
2008-07-17 12:28:35 +04:00
2014-09-27 03:20:07 +04:00
if ( be32_to_cpu ( pi - > ref_tag ) = = virt )
pi - > ref_tag = cpu_to_be32 ( phys ) ;
2008-07-17 12:28:35 +04:00
virt + + ;
phys + + ;
}
2014-09-27 03:20:07 +04:00
kunmap_atomic ( pi ) ;
2008-07-17 12:28:35 +04:00
}
2010-10-15 17:49:20 +04:00
2014-09-27 03:20:04 +04:00
bip - > bip_flags | = BIP_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 )
{
2014-09-27 03:20:07 +04:00
const int tuple_sz = sizeof ( struct t10_pi_tuple ) ;
2008-07-17 12:28:35 +04:00
struct scsi_disk * sdkp ;
struct bio * bio ;
2014-09-27 03:20:07 +04:00
struct t10_pi_tuple * pi ;
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 ) {
2014-09-27 03:20:07 +04:00
pi = kmap_atomic ( iv . bv_page ) + iv . bv_offset ;
2008-07-17 12:28:35 +04:00
2014-09-27 03:20:07 +04:00
for ( j = 0 ; j < iv . bv_len ; j + = tuple_sz , pi + + ) {
2008-07-17 12:28:35 +04:00
if ( sectors = = 0 ) {
2014-09-27 03:20:07 +04:00
kunmap_atomic ( pi ) ;
2008-07-17 12:28:35 +04:00
return ;
}
2014-09-27 03:20:07 +04:00
if ( be32_to_cpu ( pi - > ref_tag ) = = phys )
pi - > ref_tag = cpu_to_be32 ( virt ) ;
2008-07-17 12:28:35 +04:00
virt + + ;
phys + + ;
sectors - - ;
}
2014-09-27 03:20:07 +04:00
kunmap_atomic ( pi ) ;
2008-07-17 12:28:35 +04:00
}
}
}