2008-06-30 22:04:41 +04:00
/*
* blk - integrity . c - Block layer data integrity extensions
*
* 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/mempool.h>
# include <linux/bio.h>
# include <linux/scatterlist.h>
# include "blk.h"
static struct kmem_cache * integrity_cachep ;
/**
* blk_rq_count_integrity_sg - Count number of integrity scatterlist elements
* @ rq : request with integrity metadata attached
*
* Description : Returns the number of elements required in a
* scatterlist corresponding to the integrity metadata in a request .
*/
int blk_rq_count_integrity_sg ( struct request * rq )
{
struct bio_vec * iv , * ivprv ;
struct req_iterator iter ;
unsigned int segments ;
ivprv = NULL ;
segments = 0 ;
rq_for_each_integrity_segment ( iv , rq , iter ) {
if ( ! ivprv | | ! BIOVEC_PHYS_MERGEABLE ( ivprv , iv ) )
segments + + ;
ivprv = iv ;
}
return segments ;
}
EXPORT_SYMBOL ( blk_rq_count_integrity_sg ) ;
/**
* blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist
* @ rq : request with integrity metadata attached
* @ sglist : target scatterlist
*
* Description : Map the integrity vectors in request into a
* scatterlist . The scatterlist must be big enough to hold all
* elements . I . e . sized using blk_rq_count_integrity_sg ( ) .
*/
int blk_rq_map_integrity_sg ( struct request * rq , struct scatterlist * sglist )
{
struct bio_vec * iv , * ivprv ;
struct req_iterator iter ;
struct scatterlist * sg ;
unsigned int segments ;
ivprv = NULL ;
sg = NULL ;
segments = 0 ;
rq_for_each_integrity_segment ( iv , rq , iter ) {
if ( ivprv ) {
if ( ! BIOVEC_PHYS_MERGEABLE ( ivprv , iv ) )
goto new_segment ;
sg - > length + = iv - > bv_len ;
} else {
new_segment :
if ( ! sg )
sg = sglist ;
else {
sg - > page_link & = ~ 0x02 ;
sg = sg_next ( sg ) ;
}
sg_set_page ( sg , iv - > bv_page , iv - > bv_len , iv - > bv_offset ) ;
segments + + ;
}
ivprv = iv ;
}
if ( sg )
sg_mark_end ( sg ) ;
return segments ;
}
EXPORT_SYMBOL ( blk_rq_map_integrity_sg ) ;
/**
2008-10-01 11:38:39 +04:00
* blk_integrity_compare - Compare integrity profile of two disks
* @ gd1 : Disk to compare
* @ gd2 : Disk to compare
2008-06-30 22:04:41 +04:00
*
* Description : Meta - devices like DM and MD need to verify that all
* sub - devices use the same integrity format before advertising to
* upper layers that they can send / receive integrity metadata . This
2008-10-01 11:38:39 +04:00
* function can be used to check whether two gendisk devices have
2008-06-30 22:04:41 +04:00
* compatible integrity formats .
*/
2008-10-01 11:38:39 +04:00
int blk_integrity_compare ( struct gendisk * gd1 , struct gendisk * gd2 )
2008-06-30 22:04:41 +04:00
{
2008-10-01 11:38:39 +04:00
struct blk_integrity * b1 = gd1 - > integrity ;
struct blk_integrity * b2 = gd2 - > integrity ;
2008-06-30 22:04:41 +04:00
2008-10-01 11:38:39 +04:00
if ( ! b1 & & ! b2 )
return 0 ;
2008-06-30 22:04:41 +04:00
if ( ! b1 | | ! b2 )
2008-10-01 11:38:39 +04:00
return - 1 ;
2008-06-30 22:04:41 +04:00
if ( b1 - > sector_size ! = b2 - > sector_size ) {
printk ( KERN_ERR " %s: %s/%s sector sz %u != %u \n " , __func__ ,
2008-10-01 11:38:39 +04:00
gd1 - > disk_name , gd2 - > disk_name ,
2008-06-30 22:04:41 +04:00
b1 - > sector_size , b2 - > sector_size ) ;
return - 1 ;
}
if ( b1 - > tuple_size ! = b2 - > tuple_size ) {
printk ( KERN_ERR " %s: %s/%s tuple sz %u != %u \n " , __func__ ,
2008-10-01 11:38:39 +04:00
gd1 - > disk_name , gd2 - > disk_name ,
2008-06-30 22:04:41 +04:00
b1 - > tuple_size , b2 - > tuple_size ) ;
return - 1 ;
}
if ( b1 - > tag_size & & b2 - > tag_size & & ( b1 - > tag_size ! = b2 - > tag_size ) ) {
printk ( KERN_ERR " %s: %s/%s tag sz %u != %u \n " , __func__ ,
2008-10-01 11:38:39 +04:00
gd1 - > disk_name , gd2 - > disk_name ,
2008-06-30 22:04:41 +04:00
b1 - > tag_size , b2 - > tag_size ) ;
return - 1 ;
}
if ( strcmp ( b1 - > name , b2 - > name ) ) {
printk ( KERN_ERR " %s: %s/%s type %s != %s \n " , __func__ ,
2008-10-01 11:38:39 +04:00
gd1 - > disk_name , gd2 - > disk_name ,
2008-06-30 22:04:41 +04:00
b1 - > name , b2 - > name ) ;
return - 1 ;
}
return 0 ;
}
EXPORT_SYMBOL ( blk_integrity_compare ) ;
struct integrity_sysfs_entry {
struct attribute attr ;
ssize_t ( * show ) ( struct blk_integrity * , char * ) ;
ssize_t ( * store ) ( struct blk_integrity * , const char * , size_t ) ;
} ;
static ssize_t integrity_attr_show ( struct kobject * kobj , struct attribute * attr ,
char * page )
{
struct blk_integrity * bi =
container_of ( kobj , struct blk_integrity , kobj ) ;
struct integrity_sysfs_entry * entry =
container_of ( attr , struct integrity_sysfs_entry , attr ) ;
return entry - > show ( bi , page ) ;
}
2008-06-17 21:05:48 +04:00
static ssize_t integrity_attr_store ( struct kobject * kobj ,
struct attribute * attr , const char * page ,
size_t count )
2008-06-30 22:04:41 +04:00
{
struct blk_integrity * bi =
container_of ( kobj , struct blk_integrity , kobj ) ;
struct integrity_sysfs_entry * entry =
container_of ( attr , struct integrity_sysfs_entry , attr ) ;
ssize_t ret = 0 ;
if ( entry - > store )
ret = entry - > store ( bi , page , count ) ;
return ret ;
}
static ssize_t integrity_format_show ( struct blk_integrity * bi , char * page )
{
if ( bi ! = NULL & & bi - > name ! = NULL )
return sprintf ( page , " %s \n " , bi - > name ) ;
else
return sprintf ( page , " none \n " ) ;
}
static ssize_t integrity_tag_size_show ( struct blk_integrity * bi , char * page )
{
if ( bi ! = NULL )
return sprintf ( page , " %u \n " , bi - > tag_size ) ;
else
return sprintf ( page , " 0 \n " ) ;
}
static ssize_t integrity_read_store ( struct blk_integrity * bi ,
const char * page , size_t count )
{
char * p = ( char * ) page ;
unsigned long val = simple_strtoul ( p , & p , 10 ) ;
if ( val )
2008-06-27 11:12:09 +04:00
bi - > flags | = INTEGRITY_FLAG_READ ;
2008-06-30 22:04:41 +04:00
else
2008-06-27 11:12:09 +04:00
bi - > flags & = ~ INTEGRITY_FLAG_READ ;
2008-06-30 22:04:41 +04:00
return count ;
}
static ssize_t integrity_read_show ( struct blk_integrity * bi , char * page )
{
2008-06-27 11:12:09 +04:00
return sprintf ( page , " %d \n " , ( bi - > flags & INTEGRITY_FLAG_READ ) ! = 0 ) ;
2008-06-30 22:04:41 +04:00
}
static ssize_t integrity_write_store ( struct blk_integrity * bi ,
const char * page , size_t count )
{
char * p = ( char * ) page ;
unsigned long val = simple_strtoul ( p , & p , 10 ) ;
if ( val )
2008-06-27 11:12:09 +04:00
bi - > flags | = INTEGRITY_FLAG_WRITE ;
2008-06-30 22:04:41 +04:00
else
2008-06-27 11:12:09 +04:00
bi - > flags & = ~ INTEGRITY_FLAG_WRITE ;
2008-06-30 22:04:41 +04:00
return count ;
}
static ssize_t integrity_write_show ( struct blk_integrity * bi , char * page )
{
2008-06-27 11:12:09 +04:00
return sprintf ( page , " %d \n " , ( bi - > flags & INTEGRITY_FLAG_WRITE ) ! = 0 ) ;
2008-06-30 22:04:41 +04:00
}
static struct integrity_sysfs_entry integrity_format_entry = {
. attr = { . name = " format " , . mode = S_IRUGO } ,
. show = integrity_format_show ,
} ;
static struct integrity_sysfs_entry integrity_tag_size_entry = {
. attr = { . name = " tag_size " , . mode = S_IRUGO } ,
. show = integrity_tag_size_show ,
} ;
static struct integrity_sysfs_entry integrity_read_entry = {
. attr = { . name = " read_verify " , . mode = S_IRUGO | S_IWUSR } ,
. show = integrity_read_show ,
. store = integrity_read_store ,
} ;
static struct integrity_sysfs_entry integrity_write_entry = {
. attr = { . name = " write_generate " , . mode = S_IRUGO | S_IWUSR } ,
. show = integrity_write_show ,
. store = integrity_write_store ,
} ;
static struct attribute * integrity_attrs [ ] = {
& integrity_format_entry . attr ,
& integrity_tag_size_entry . attr ,
& integrity_read_entry . attr ,
& integrity_write_entry . attr ,
NULL ,
} ;
static struct sysfs_ops integrity_ops = {
. show = & integrity_attr_show ,
. store = & integrity_attr_store ,
} ;
static int __init blk_dev_integrity_init ( void )
{
integrity_cachep = kmem_cache_create ( " blkdev_integrity " ,
sizeof ( struct blk_integrity ) ,
0 , SLAB_PANIC , NULL ) ;
return 0 ;
}
subsys_initcall ( blk_dev_integrity_init ) ;
static void blk_integrity_release ( struct kobject * kobj )
{
struct blk_integrity * bi =
container_of ( kobj , struct blk_integrity , kobj ) ;
kmem_cache_free ( integrity_cachep , bi ) ;
}
static struct kobj_type integrity_ktype = {
. default_attrs = integrity_attrs ,
. sysfs_ops = & integrity_ops ,
. release = blk_integrity_release ,
} ;
/**
* blk_integrity_register - Register a gendisk as being integrity - capable
* @ disk : struct gendisk pointer to make integrity - aware
* @ template : integrity profile
*
* Description : When a device needs to advertise itself as being able
* to send / receive integrity metadata it must use this function to
* register the capability with the block layer . The template is a
* blk_integrity struct with values appropriate for the underlying
* hardware . See Documentation / block / data - integrity . txt .
*/
int blk_integrity_register ( struct gendisk * disk , struct blk_integrity * template )
{
struct blk_integrity * bi ;
BUG_ON ( disk = = NULL ) ;
BUG_ON ( template = = NULL ) ;
if ( disk - > integrity = = NULL ) {
2008-06-17 21:05:48 +04:00
bi = kmem_cache_alloc ( integrity_cachep ,
GFP_KERNEL | __GFP_ZERO ) ;
2008-06-30 22:04:41 +04:00
if ( ! bi )
return - 1 ;
if ( kobject_init_and_add ( & bi - > kobj , & integrity_ktype ,
2008-08-25 14:56:05 +04:00
& disk_to_dev ( disk ) - > kobj ,
" %s " , " integrity " ) ) {
2008-06-30 22:04:41 +04:00
kmem_cache_free ( integrity_cachep , bi ) ;
return - 1 ;
}
kobject_uevent ( & bi - > kobj , KOBJ_ADD ) ;
2008-06-27 11:12:09 +04:00
bi - > flags | = INTEGRITY_FLAG_READ | INTEGRITY_FLAG_WRITE ;
2008-06-30 22:04:41 +04:00
bi - > sector_size = disk - > queue - > hardsect_size ;
disk - > integrity = bi ;
} else
bi = disk - > integrity ;
/* Use the provided profile as template */
bi - > name = template - > name ;
bi - > generate_fn = template - > generate_fn ;
bi - > verify_fn = template - > verify_fn ;
bi - > tuple_size = template - > tuple_size ;
bi - > set_tag_fn = template - > set_tag_fn ;
bi - > get_tag_fn = template - > get_tag_fn ;
bi - > tag_size = template - > tag_size ;
return 0 ;
}
EXPORT_SYMBOL ( blk_integrity_register ) ;
/**
* blk_integrity_unregister - Remove block integrity profile
* @ disk : disk whose integrity profile to deallocate
*
* Description : This function frees all memory used by the block
* integrity profile . To be called at device teardown .
*/
void blk_integrity_unregister ( struct gendisk * disk )
{
struct blk_integrity * bi ;
if ( ! disk | | ! disk - > integrity )
return ;
bi = disk - > integrity ;
kobject_uevent ( & bi - > kobj , KOBJ_REMOVE ) ;
kobject_del ( & bi - > kobj ) ;
kmem_cache_free ( integrity_cachep , bi ) ;
2008-10-01 11:38:38 +04:00
disk - > integrity = NULL ;
2008-06-30 22:04:41 +04:00
}
EXPORT_SYMBOL ( blk_integrity_unregister ) ;