2012-03-28 21:43:38 +04:00
/*
* Copyright ( C ) 2012 Red Hat , Inc .
*
* Author : Mikulas Patocka < mpatocka @ redhat . com >
*
* Based on Chromium dm - verity driver ( C ) 2011 The Chromium OS Authors
*
* This file is released under the GPLv2 .
*
* In the file " /sys/module/dm_verity/parameters/prefetch_cluster " you can set
* default prefetch value . Data are read in " prefetch_cluster " chunks from the
* hash device . Setting this greatly improves performance when data and hash
* are on the same disk on different partitions on devices with poor random
* access behavior .
*/
2015-12-04 00:01:51 +03:00
# include "dm-verity.h"
2015-12-03 17:26:30 +03:00
# include "dm-verity-fec.h"
2012-03-28 21:43:38 +04:00
# include <linux/module.h>
2015-03-18 18:52:14 +03:00
# include <linux/reboot.h>
2012-03-28 21:43:38 +04:00
# define DM_MSG_PREFIX "verity"
2015-03-18 18:52:14 +03:00
# define DM_VERITY_ENV_LENGTH 42
# define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR"
2012-03-28 21:43:38 +04:00
# define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
2015-03-18 18:52:14 +03:00
# define DM_VERITY_MAX_CORRUPTED_ERRS 100
# define DM_VERITY_OPT_LOGGING "ignore_corruption"
# define DM_VERITY_OPT_RESTART "restart_on_corruption"
2015-12-03 17:26:31 +03:00
# define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
2018-03-23 04:18:04 +03:00
# define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
2012-03-28 21:43:38 +04:00
2015-12-03 17:26:31 +03:00
# define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
2015-11-05 05:02:32 +03:00
2012-03-28 21:43:38 +04:00
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE ;
module_param_named ( prefetch_cluster , dm_verity_prefetch_cluster , uint , S_IRUGO | S_IWUSR ) ;
2013-03-20 21:21:25 +04:00
struct dm_verity_prefetch_work {
struct work_struct work ;
struct dm_verity * v ;
sector_t block ;
unsigned n_blocks ;
} ;
2012-03-28 21:43:38 +04:00
/*
* Auxiliary structure appended to each dm - bufio buffer . If the value
* hash_verified is nonzero , hash of the block has been verified .
*
* The variable hash_verified is set to 0 when allocating the buffer , then
* it can be changed to 1 and it is never reset to 0 again .
*
* There is no lock around this value , a race condition can at worst cause
* that multiple processes verify the hash of the same buffer simultaneously
* and write 1 to hash_verified simultaneously .
* This condition is harmless , so we don ' t need locking .
*/
struct buffer_aux {
int hash_verified ;
} ;
/*
* Initialize struct buffer_aux for a freshly created buffer .
*/
static void dm_bufio_alloc_callback ( struct dm_buffer * buf )
{
struct buffer_aux * aux = dm_bufio_get_aux_data ( buf ) ;
aux - > hash_verified = 0 ;
}
/*
* Translate input sector number to the sector number on the target device .
*/
static sector_t verity_map_sector ( struct dm_verity * v , sector_t bi_sector )
{
return v - > data_start + dm_target_offset ( v - > ti , bi_sector ) ;
}
/*
* Return hash position of a specified block at a specified tree level
* ( 0 is the lowest level ) .
* The lowest " hash_per_block_bits " - bits of the result denote hash position
* inside a hash block . The remaining bits denote location of the hash block .
*/
static sector_t verity_position_at_level ( struct dm_verity * v , sector_t block ,
int level )
{
return block > > ( level * v - > hash_per_block_bits ) ;
}
2017-02-19 15:46:07 +03:00
static int verity_hash_update ( struct dm_verity * v , struct ahash_request * req ,
const u8 * data , size_t len ,
2017-10-18 10:00:45 +03:00
struct crypto_wait * wait )
2015-11-05 05:02:31 +03:00
{
2017-02-19 15:46:07 +03:00
struct scatterlist sg ;
2015-11-05 05:02:31 +03:00
2017-02-19 15:46:07 +03:00
sg_init_one ( & sg , data , len ) ;
ahash_request_set_crypt ( req , & sg , NULL , len ) ;
2017-10-18 10:00:45 +03:00
return crypto_wait_req ( crypto_ahash_update ( req ) , wait ) ;
2017-02-19 15:46:07 +03:00
}
/*
* Wrapper for crypto_ahash_init , which handles verity salting .
*/
static int verity_hash_init ( struct dm_verity * v , struct ahash_request * req ,
2017-10-18 10:00:45 +03:00
struct crypto_wait * wait )
2017-02-19 15:46:07 +03:00
{
int r ;
ahash_request_set_tfm ( req , v - > tfm ) ;
ahash_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG ,
2017-10-18 10:00:45 +03:00
crypto_req_done , ( void * ) wait ) ;
crypto_init_wait ( wait ) ;
2017-02-19 15:46:07 +03:00
2017-10-18 10:00:45 +03:00
r = crypto_wait_req ( crypto_ahash_init ( req ) , wait ) ;
2017-02-19 15:46:07 +03:00
if ( unlikely ( r < 0 ) ) {
DMERR ( " crypto_ahash_init failed: %d " , r ) ;
return r ;
}
2017-05-18 13:47:25 +03:00
if ( likely ( v - > salt_size & & ( v - > version > = 1 ) ) )
2017-10-18 10:00:45 +03:00
r = verity_hash_update ( v , req , v - > salt , v - > salt_size , wait ) ;
2015-11-05 05:02:31 +03:00
return r ;
}
2017-02-19 15:46:07 +03:00
static int verity_hash_final ( struct dm_verity * v , struct ahash_request * req ,
2017-10-18 10:00:45 +03:00
u8 * digest , struct crypto_wait * wait )
2015-11-05 05:02:31 +03:00
{
int r ;
2017-05-18 13:47:25 +03:00
if ( unlikely ( v - > salt_size & & ( ! v - > version ) ) ) {
2017-10-18 10:00:45 +03:00
r = verity_hash_update ( v , req , v - > salt , v - > salt_size , wait ) ;
2015-11-05 05:02:31 +03:00
if ( r < 0 ) {
2017-02-19 15:46:07 +03:00
DMERR ( " verity_hash_final failed updating salt: %d " , r ) ;
goto out ;
2015-11-05 05:02:31 +03:00
}
}
2017-02-19 15:46:07 +03:00
ahash_request_set_crypt ( req , NULL , digest , 0 ) ;
2017-10-18 10:00:45 +03:00
r = crypto_wait_req ( crypto_ahash_final ( req ) , wait ) ;
2017-02-19 15:46:07 +03:00
out :
2015-11-05 05:02:31 +03:00
return r ;
}
2017-02-19 15:46:07 +03:00
int verity_hash ( struct dm_verity * v , struct ahash_request * req ,
2015-12-04 00:01:51 +03:00
const u8 * data , size_t len , u8 * digest )
2015-11-05 05:02:31 +03:00
{
int r ;
2017-10-18 10:00:45 +03:00
struct crypto_wait wait ;
2015-11-05 05:02:31 +03:00
2017-10-18 10:00:45 +03:00
r = verity_hash_init ( v , req , & wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2017-02-19 15:46:07 +03:00
goto out ;
2015-11-05 05:02:31 +03:00
2017-10-18 10:00:45 +03:00
r = verity_hash_update ( v , req , data , len , & wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2017-02-19 15:46:07 +03:00
goto out ;
2017-10-18 10:00:45 +03:00
r = verity_hash_final ( v , req , digest , & wait ) ;
2015-11-05 05:02:31 +03:00
2017-02-19 15:46:07 +03:00
out :
return r ;
2015-11-05 05:02:31 +03:00
}
2012-03-28 21:43:38 +04:00
static void verity_hash_at_level ( struct dm_verity * v , sector_t block , int level ,
sector_t * hash_block , unsigned * offset )
{
sector_t position = verity_position_at_level ( v , block , level ) ;
unsigned idx ;
* hash_block = v - > hash_level_block [ level ] + ( position > > v - > hash_per_block_bits ) ;
if ( ! offset )
return ;
idx = position & ( ( 1 < < v - > hash_per_block_bits ) - 1 ) ;
if ( ! v - > version )
* offset = idx * v - > digest_size ;
else
* offset = idx < < ( v - > hash_dev_block_bits - v - > hash_per_block_bits ) ;
}
2015-03-18 18:52:14 +03:00
/*
* Handle verification errors .
*/
static int verity_handle_err ( struct dm_verity * v , enum verity_block_type type ,
unsigned long long block )
{
char verity_env [ DM_VERITY_ENV_LENGTH ] ;
char * envp [ ] = { verity_env , NULL } ;
const char * type_str = " " ;
struct mapped_device * md = dm_table_get_md ( v - > ti - > table ) ;
/* Corruption should be visible in device status in all modes */
v - > hash_failed = 1 ;
if ( v - > corrupted_errs > = DM_VERITY_MAX_CORRUPTED_ERRS )
goto out ;
v - > corrupted_errs + + ;
switch ( type ) {
case DM_VERITY_BLOCK_TYPE_DATA :
type_str = " data " ;
break ;
case DM_VERITY_BLOCK_TYPE_METADATA :
type_str = " metadata " ;
break ;
default :
BUG ( ) ;
}
DMERR ( " %s: %s block %llu is corrupted " , v - > data_dev - > name , type_str ,
block ) ;
if ( v - > corrupted_errs = = DM_VERITY_MAX_CORRUPTED_ERRS )
DMERR ( " %s: reached maximum errors " , v - > data_dev - > name ) ;
snprintf ( verity_env , DM_VERITY_ENV_LENGTH , " %s=%d,%llu " ,
DM_VERITY_ENV_VAR_NAME , type , block ) ;
kobject_uevent_env ( & disk_to_dev ( dm_disk ( md ) ) - > kobj , KOBJ_CHANGE , envp ) ;
out :
if ( v - > mode = = DM_VERITY_MODE_LOGGING )
return 0 ;
if ( v - > mode = = DM_VERITY_MODE_RESTART )
kernel_restart ( " dm-verity device corrupted " ) ;
return 1 ;
}
2012-03-28 21:43:38 +04:00
/*
* Verify hash of a metadata block pertaining to the specified data block
* ( " block " argument ) at a specified level ( " level " argument ) .
*
2015-12-04 00:01:51 +03:00
* On successful return , verity_io_want_digest ( v , io ) contains the hash value
* for a lower tree level or for the data block ( if we ' re at the lowest level ) .
2012-03-28 21:43:38 +04:00
*
* If " skip_unverified " is true , unverified buffer is skipped and 1 is returned .
* If " skip_unverified " is false , unverified buffer is hashed and verified
2015-12-04 00:01:51 +03:00
* against current value of verity_io_want_digest ( v , io ) .
2012-03-28 21:43:38 +04:00
*/
2015-11-05 05:02:31 +03:00
static int verity_verify_level ( struct dm_verity * v , struct dm_verity_io * io ,
sector_t block , int level , bool skip_unverified ,
u8 * want_digest )
2012-03-28 21:43:38 +04:00
{
struct dm_buffer * buf ;
struct buffer_aux * aux ;
u8 * data ;
int r ;
sector_t hash_block ;
unsigned offset ;
verity_hash_at_level ( v , block , level , & hash_block , & offset ) ;
data = dm_bufio_read ( v - > bufio , hash_block , & buf ) ;
2015-08-10 09:12:26 +03:00
if ( IS_ERR ( data ) )
2012-03-28 21:43:38 +04:00
return PTR_ERR ( data ) ;
aux = dm_bufio_get_aux_data ( buf ) ;
if ( ! aux - > hash_verified ) {
if ( skip_unverified ) {
r = 1 ;
goto release_ret_r ;
}
2017-02-19 15:46:07 +03:00
r = verity_hash ( v , verity_io_hash_req ( v , io ) ,
2015-11-05 05:02:31 +03:00
data , 1 < < v - > hash_dev_block_bits ,
2015-12-04 00:01:51 +03:00
verity_io_real_digest ( v , io ) ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2012-03-28 21:43:38 +04:00
goto release_ret_r ;
2015-12-04 00:01:51 +03:00
if ( likely ( memcmp ( verity_io_real_digest ( v , io ) , want_digest ,
2015-11-05 05:02:31 +03:00
v - > digest_size ) = = 0 ) )
aux - > hash_verified = 1 ;
2015-12-03 17:26:30 +03:00
else if ( verity_fec_decode ( v , io ,
DM_VERITY_BLOCK_TYPE_METADATA ,
hash_block , data , NULL ) = = 0 )
aux - > hash_verified = 1 ;
2015-11-05 05:02:31 +03:00
else if ( verity_handle_err ( v ,
DM_VERITY_BLOCK_TYPE_METADATA ,
hash_block ) ) {
r = - EIO ;
2012-03-28 21:43:38 +04:00
goto release_ret_r ;
}
}
data + = offset ;
2015-11-05 05:02:31 +03:00
memcpy ( want_digest , data , v - > digest_size ) ;
r = 0 ;
2012-03-28 21:43:38 +04:00
release_ret_r :
dm_bufio_release ( buf ) ;
return r ;
}
2015-11-05 05:02:31 +03:00
/*
* Find a hash for a given block , write it to digest and verify the integrity
* of the hash tree if necessary .
*/
2015-12-04 00:01:51 +03:00
int verity_hash_for_block ( struct dm_verity * v , struct dm_verity_io * io ,
2015-12-03 17:26:31 +03:00
sector_t block , u8 * digest , bool * is_zero )
2015-11-05 05:02:31 +03:00
{
2015-12-03 17:26:31 +03:00
int r = 0 , i ;
2015-11-05 05:02:31 +03:00
if ( likely ( v - > levels ) ) {
/*
* First , we try to get the requested hash for
* the current block . If the hash block itself is
* verified , zero is returned . If it isn ' t , this
* function returns 1 and we fall back to whole
* chain verification .
*/
r = verity_verify_level ( v , io , block , 0 , true , digest ) ;
if ( likely ( r < = 0 ) )
2015-12-03 17:26:31 +03:00
goto out ;
2015-11-05 05:02:31 +03:00
}
memcpy ( digest , v - > root_digest , v - > digest_size ) ;
for ( i = v - > levels - 1 ; i > = 0 ; i - - ) {
r = verity_verify_level ( v , io , block , i , false , digest ) ;
if ( unlikely ( r ) )
2015-12-03 17:26:31 +03:00
goto out ;
2015-11-05 05:02:31 +03:00
}
2015-12-03 17:26:31 +03:00
out :
if ( ! r & & v - > zero_digest )
* is_zero = ! memcmp ( v - > zero_digest , digest , v - > digest_size ) ;
else
* is_zero = false ;
2015-11-05 05:02:31 +03:00
2015-12-03 17:26:31 +03:00
return r ;
2015-11-05 05:02:31 +03:00
}
2017-02-19 15:46:07 +03:00
/*
* Calculates the digest for the given bio
*/
2018-03-28 14:11:58 +03:00
static int verity_for_io_block ( struct dm_verity * v , struct dm_verity_io * io ,
struct bvec_iter * iter , struct crypto_wait * wait )
2017-02-19 15:46:07 +03:00
{
unsigned int todo = 1 < < v - > data_dev_block_bits ;
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
struct scatterlist sg ;
struct ahash_request * req = verity_io_hash_req ( v , io ) ;
do {
int r ;
unsigned int len ;
struct bio_vec bv = bio_iter_iovec ( bio , * iter ) ;
sg_init_table ( & sg , 1 ) ;
len = bv . bv_len ;
if ( likely ( len > = todo ) )
len = todo ;
/*
* Operating on a single page at a time looks suboptimal
* until you consider the typical block size is 4 , 096 B .
* Going through this loops twice should be very rare .
*/
sg_set_page ( & sg , bv . bv_page , len , bv . bv_offset ) ;
ahash_request_set_crypt ( req , & sg , NULL , len ) ;
2017-10-18 10:00:45 +03:00
r = crypto_wait_req ( crypto_ahash_update ( req ) , wait ) ;
2017-02-19 15:46:07 +03:00
if ( unlikely ( r < 0 ) ) {
DMERR ( " verity_for_io_block crypto op failed: %d " , r ) ;
return r ;
}
bio_advance_iter ( bio , iter , len ) ;
todo - = len ;
} while ( todo ) ;
return 0 ;
}
2015-12-04 00:30:36 +03:00
/*
* Calls function process for 1 < < v - > data_dev_block_bits bytes in the bio_vec
* starting from iter .
*/
int verity_for_bv_block ( struct dm_verity * v , struct dm_verity_io * io ,
struct bvec_iter * iter ,
int ( * process ) ( struct dm_verity * v ,
struct dm_verity_io * io , u8 * data ,
size_t len ) )
{
unsigned todo = 1 < < v - > data_dev_block_bits ;
2016-01-31 21:28:26 +03:00
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
2015-12-04 00:30:36 +03:00
do {
int r ;
u8 * page ;
unsigned len ;
struct bio_vec bv = bio_iter_iovec ( bio , * iter ) ;
page = kmap_atomic ( bv . bv_page ) ;
len = bv . bv_len ;
if ( likely ( len > = todo ) )
len = todo ;
r = process ( v , io , page + bv . bv_offset , len ) ;
kunmap_atomic ( page ) ;
if ( r < 0 )
return r ;
bio_advance_iter ( bio , iter , len ) ;
todo - = len ;
} while ( todo ) ;
return 0 ;
}
2015-12-03 17:26:31 +03:00
static int verity_bv_zero ( struct dm_verity * v , struct dm_verity_io * io ,
u8 * data , size_t len )
{
memset ( data , 0 , len ) ;
return 0 ;
}
2018-03-23 04:18:04 +03:00
/*
* Moves the bio iter one data block forward .
*/
static inline void verity_bv_skip_block ( struct dm_verity * v ,
struct dm_verity_io * io ,
struct bvec_iter * iter )
{
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
bio_advance_iter ( bio , iter , 1 < < v - > data_dev_block_bits ) ;
}
2012-03-28 21:43:38 +04:00
/*
* Verify one " dm_verity_io " structure .
*/
static int verity_verify_io ( struct dm_verity_io * io )
{
2015-12-03 17:26:31 +03:00
bool is_zero ;
2012-03-28 21:43:38 +04:00
struct dm_verity * v = io - > v ;
2015-12-04 00:30:36 +03:00
struct bvec_iter start ;
2012-03-28 21:43:38 +04:00
unsigned b ;
2017-10-18 10:00:45 +03:00
struct crypto_wait wait ;
2012-03-28 21:43:38 +04:00
for ( b = 0 ; b < io - > n_blocks ; b + + ) {
int r ;
2018-03-23 04:18:04 +03:00
sector_t cur_block = io - > block + b ;
2017-02-19 15:46:07 +03:00
struct ahash_request * req = verity_io_hash_req ( v , io ) ;
2012-03-28 21:43:38 +04:00
2018-03-23 04:18:04 +03:00
if ( v - > validated_blocks & &
likely ( test_bit ( cur_block , v - > validated_blocks ) ) ) {
verity_bv_skip_block ( v , io , & io - > iter ) ;
continue ;
}
r = verity_hash_for_block ( v , io , cur_block ,
2015-12-03 17:26:31 +03:00
verity_io_want_digest ( v , io ) ,
& is_zero ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
return r ;
2012-03-28 21:43:38 +04:00
2015-12-03 17:26:31 +03:00
if ( is_zero ) {
/*
* If we expect a zero block , don ' t validate , just
* return zeros .
*/
r = verity_for_bv_block ( v , io , & io - > iter ,
verity_bv_zero ) ;
if ( unlikely ( r < 0 ) )
return r ;
continue ;
}
2017-10-18 10:00:45 +03:00
r = verity_hash_init ( v , req , & wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2012-03-28 21:43:38 +04:00
return r ;
2015-12-04 00:30:36 +03:00
start = io - > iter ;
2017-10-18 10:00:45 +03:00
r = verity_for_io_block ( v , io , & io - > iter , & wait ) ;
2015-12-04 00:30:36 +03:00
if ( unlikely ( r < 0 ) )
return r ;
2012-03-28 21:43:38 +04:00
2017-02-19 15:46:07 +03:00
r = verity_hash_final ( v , req , verity_io_real_digest ( v , io ) ,
2017-10-18 10:00:45 +03:00
& wait ) ;
2015-11-05 05:02:31 +03:00
if ( unlikely ( r < 0 ) )
2012-03-28 21:43:38 +04:00
return r ;
2015-11-05 05:02:31 +03:00
2015-12-04 00:01:51 +03:00
if ( likely ( memcmp ( verity_io_real_digest ( v , io ) ,
2018-03-23 04:18:04 +03:00
verity_io_want_digest ( v , io ) , v - > digest_size ) = = 0 ) ) {
if ( v - > validated_blocks )
set_bit ( cur_block , v - > validated_blocks ) ;
2015-11-05 05:02:31 +03:00
continue ;
2018-03-23 04:18:04 +03:00
}
2015-12-03 17:26:30 +03:00
else if ( verity_fec_decode ( v , io , DM_VERITY_BLOCK_TYPE_DATA ,
2018-03-23 04:18:04 +03:00
cur_block , NULL , & start ) = = 0 )
2015-12-03 17:26:30 +03:00
continue ;
2015-11-05 05:02:31 +03:00
else if ( verity_handle_err ( v , DM_VERITY_BLOCK_TYPE_DATA ,
2018-03-23 04:18:04 +03:00
cur_block ) )
2015-11-05 05:02:31 +03:00
return - EIO ;
2012-03-28 21:43:38 +04:00
}
return 0 ;
}
/*
* End one " io " structure with a given error .
*/
2017-06-03 10:38:06 +03:00
static void verity_finish_io ( struct dm_verity_io * io , blk_status_t status )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = io - > v ;
2016-01-31 21:28:26 +03:00
struct bio * bio = dm_bio_from_per_bio_data ( io , v - > ti - > per_io_data_size ) ;
2012-03-28 21:43:38 +04:00
bio - > bi_end_io = io - > orig_bi_end_io ;
2017-06-03 10:38:06 +03:00
bio - > bi_status = status ;
2012-03-28 21:43:38 +04:00
2015-12-03 17:26:30 +03:00
verity_fec_finish_io ( io ) ;
2015-07-20 16:29:37 +03:00
bio_endio ( bio ) ;
2012-03-28 21:43:38 +04:00
}
static void verity_work ( struct work_struct * w )
{
struct dm_verity_io * io = container_of ( w , struct dm_verity_io , work ) ;
2017-06-03 10:38:06 +03:00
verity_finish_io ( io , errno_to_blk_status ( verity_verify_io ( io ) ) ) ;
2012-03-28 21:43:38 +04:00
}
2015-07-20 16:29:37 +03:00
static void verity_end_io ( struct bio * bio )
2012-03-28 21:43:38 +04:00
{
struct dm_verity_io * io = bio - > bi_private ;
2017-06-03 10:38:06 +03:00
if ( bio - > bi_status & & ! verity_fec_is_enabled ( io - > v ) ) {
verity_finish_io ( io , bio - > bi_status ) ;
2012-03-28 21:43:38 +04:00
return ;
}
INIT_WORK ( & io - > work , verity_work ) ;
queue_work ( io - > v - > verify_wq , & io - > work ) ;
}
/*
* Prefetch buffers for the specified io .
* The root buffer is not prefetched , it is assumed that it will be cached
* all the time .
*/
2013-03-20 21:21:25 +04:00
static void verity_prefetch_io ( struct work_struct * work )
2012-03-28 21:43:38 +04:00
{
2013-03-20 21:21:25 +04:00
struct dm_verity_prefetch_work * pw =
container_of ( work , struct dm_verity_prefetch_work , work ) ;
struct dm_verity * v = pw - > v ;
2012-03-28 21:43:38 +04:00
int i ;
for ( i = v - > levels - 2 ; i > = 0 ; i - - ) {
sector_t hash_block_start ;
sector_t hash_block_end ;
2013-03-20 21:21:25 +04:00
verity_hash_at_level ( v , pw - > block , i , & hash_block_start , NULL ) ;
verity_hash_at_level ( v , pw - > block + pw - > n_blocks - 1 , i , & hash_block_end , NULL ) ;
2012-03-28 21:43:38 +04:00
if ( ! i ) {
locking/atomics: COCCINELLE/treewide: Convert trivial ACCESS_ONCE() patterns to READ_ONCE()/WRITE_ONCE()
Please do not apply this to mainline directly, instead please re-run the
coccinelle script shown below and apply its output.
For several reasons, it is desirable to use {READ,WRITE}_ONCE() in
preference to ACCESS_ONCE(), and new code is expected to use one of the
former. So far, there's been no reason to change most existing uses of
ACCESS_ONCE(), as these aren't harmful, and changing them results in
churn.
However, for some features, the read/write distinction is critical to
correct operation. To distinguish these cases, separate read/write
accessors must be used. This patch migrates (most) remaining
ACCESS_ONCE() instances to {READ,WRITE}_ONCE(), using the following
coccinelle script:
----
// Convert trivial ACCESS_ONCE() uses to equivalent READ_ONCE() and
// WRITE_ONCE()
// $ make coccicheck COCCI=/home/mark/once.cocci SPFLAGS="--include-headers" MODE=patch
virtual patch
@ depends on patch @
expression E1, E2;
@@
- ACCESS_ONCE(E1) = E2
+ WRITE_ONCE(E1, E2)
@ depends on patch @
expression E;
@@
- ACCESS_ONCE(E)
+ READ_ONCE(E)
----
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: davem@davemloft.net
Cc: linux-arch@vger.kernel.org
Cc: mpe@ellerman.id.au
Cc: shuah@kernel.org
Cc: snitzer@redhat.com
Cc: thor.thayer@linux.intel.com
Cc: tj@kernel.org
Cc: viro@zeniv.linux.org.uk
Cc: will.deacon@arm.com
Link: http://lkml.kernel.org/r/1508792849-3115-19-git-send-email-paulmck@linux.vnet.ibm.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-10-24 00:07:29 +03:00
unsigned cluster = READ_ONCE ( dm_verity_prefetch_cluster ) ;
2012-03-28 21:43:38 +04:00
cluster > > = v - > data_dev_block_bits ;
if ( unlikely ( ! cluster ) )
goto no_prefetch_cluster ;
if ( unlikely ( cluster & ( cluster - 1 ) ) )
2013-07-11 02:41:17 +04:00
cluster = 1 < < __fls ( cluster ) ;
2012-03-28 21:43:38 +04:00
hash_block_start & = ~ ( sector_t ) ( cluster - 1 ) ;
hash_block_end | = cluster - 1 ;
if ( unlikely ( hash_block_end > = v - > hash_blocks ) )
hash_block_end = v - > hash_blocks - 1 ;
}
no_prefetch_cluster :
dm_bufio_prefetch ( v - > bufio , hash_block_start ,
hash_block_end - hash_block_start + 1 ) ;
}
2013-03-20 21:21:25 +04:00
kfree ( pw ) ;
}
static void verity_submit_prefetch ( struct dm_verity * v , struct dm_verity_io * io )
{
struct dm_verity_prefetch_work * pw ;
pw = kmalloc ( sizeof ( struct dm_verity_prefetch_work ) ,
GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN ) ;
if ( ! pw )
return ;
INIT_WORK ( & pw - > work , verity_prefetch_io ) ;
pw - > v = v ;
pw - > block = io - > block ;
pw - > n_blocks = io - > n_blocks ;
queue_work ( v - > verify_wq , & pw - > work ) ;
2012-03-28 21:43:38 +04:00
}
/*
* Bio map function . It allocates dm_verity_io structure and bio vector and
* fills them . Then it issues prefetches and the I / O .
*/
2012-12-22 00:23:41 +04:00
static int verity_map ( struct dm_target * ti , struct bio * bio )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = ti - > private ;
struct dm_verity_io * io ;
2017-08-23 20:10:32 +03:00
bio_set_dev ( bio , v - > data_dev - > bdev ) ;
2013-10-12 02:44:27 +04:00
bio - > bi_iter . bi_sector = verity_map_sector ( v , bio - > bi_iter . bi_sector ) ;
2012-03-28 21:43:38 +04:00
2013-10-12 02:44:27 +04:00
if ( ( ( unsigned ) bio - > bi_iter . bi_sector | bio_sectors ( bio ) ) &
2012-03-28 21:43:38 +04:00
( ( 1 < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) ) - 1 ) ) {
DMERR_LIMIT ( " unaligned io " ) ;
2017-06-03 10:38:02 +03:00
return DM_MAPIO_KILL ;
2012-03-28 21:43:38 +04:00
}
2012-09-26 02:05:12 +04:00
if ( bio_end_sector ( bio ) > >
2012-03-28 21:43:38 +04:00
( v - > data_dev_block_bits - SECTOR_SHIFT ) > v - > data_blocks ) {
DMERR_LIMIT ( " io out of range " ) ;
2017-06-03 10:38:02 +03:00
return DM_MAPIO_KILL ;
2012-03-28 21:43:38 +04:00
}
if ( bio_data_dir ( bio ) = = WRITE )
2017-06-03 10:38:02 +03:00
return DM_MAPIO_KILL ;
2012-03-28 21:43:38 +04:00
2016-01-31 21:28:26 +03:00
io = dm_per_bio_data ( bio , ti - > per_io_data_size ) ;
2012-03-28 21:43:38 +04:00
io - > v = v ;
io - > orig_bi_end_io = bio - > bi_end_io ;
2013-10-12 02:44:27 +04:00
io - > block = bio - > bi_iter . bi_sector > > ( v - > data_dev_block_bits - SECTOR_SHIFT ) ;
io - > n_blocks = bio - > bi_iter . bi_size > > v - > data_dev_block_bits ;
2012-03-28 21:43:38 +04:00
bio - > bi_end_io = verity_end_io ;
bio - > bi_private = io ;
2013-10-12 02:45:43 +04:00
io - > iter = bio - > bi_iter ;
2012-03-28 21:43:38 +04:00
2015-12-03 17:26:30 +03:00
verity_fec_init_io ( io ) ;
2013-03-20 21:21:25 +04:00
verity_submit_prefetch ( v , io ) ;
2012-03-28 21:43:38 +04:00
generic_make_request ( bio ) ;
return DM_MAPIO_SUBMITTED ;
}
/*
* Status : V ( valid ) or C ( corruption found )
*/
2013-03-02 02:45:44 +04:00
static void verity_status ( struct dm_target * ti , status_type_t type ,
unsigned status_flags , char * result , unsigned maxlen )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = ti - > private ;
2015-12-03 17:26:30 +03:00
unsigned args = 0 ;
2012-03-28 21:43:38 +04:00
unsigned sz = 0 ;
unsigned x ;
switch ( type ) {
case STATUSTYPE_INFO :
DMEMIT ( " %c " , v - > hash_failed ? ' C ' : ' V ' ) ;
break ;
case STATUSTYPE_TABLE :
DMEMIT ( " %u %s %s %u %u %llu %llu %s " ,
v - > version ,
v - > data_dev - > name ,
v - > hash_dev - > name ,
1 < < v - > data_dev_block_bits ,
1 < < v - > hash_dev_block_bits ,
( unsigned long long ) v - > data_blocks ,
( unsigned long long ) v - > hash_start ,
v - > alg_name
) ;
for ( x = 0 ; x < v - > digest_size ; x + + )
DMEMIT ( " %02x " , v - > root_digest [ x ] ) ;
DMEMIT ( " " ) ;
if ( ! v - > salt_size )
DMEMIT ( " - " ) ;
else
for ( x = 0 ; x < v - > salt_size ; x + + )
DMEMIT ( " %02x " , v - > salt [ x ] ) ;
2015-12-03 17:26:30 +03:00
if ( v - > mode ! = DM_VERITY_MODE_EIO )
args + + ;
if ( verity_fec_is_enabled ( v ) )
args + = DM_VERITY_OPTS_FEC ;
2015-12-03 17:26:31 +03:00
if ( v - > zero_digest )
args + + ;
2018-03-23 04:18:04 +03:00
if ( v - > validated_blocks )
args + + ;
2015-12-03 17:26:30 +03:00
if ( ! args )
return ;
DMEMIT ( " %u " , args ) ;
2015-03-18 18:52:14 +03:00
if ( v - > mode ! = DM_VERITY_MODE_EIO ) {
2015-12-03 17:26:30 +03:00
DMEMIT ( " " ) ;
2015-03-18 18:52:14 +03:00
switch ( v - > mode ) {
case DM_VERITY_MODE_LOGGING :
DMEMIT ( DM_VERITY_OPT_LOGGING ) ;
break ;
case DM_VERITY_MODE_RESTART :
DMEMIT ( DM_VERITY_OPT_RESTART ) ;
break ;
default :
BUG ( ) ;
}
}
2015-12-03 17:26:31 +03:00
if ( v - > zero_digest )
DMEMIT ( " " DM_VERITY_OPT_IGN_ZEROES ) ;
2018-03-23 04:18:04 +03:00
if ( v - > validated_blocks )
DMEMIT ( " " DM_VERITY_OPT_AT_MOST_ONCE ) ;
2015-12-03 17:26:30 +03:00
sz = verity_fec_status_table ( v , sz , result , maxlen ) ;
2012-03-28 21:43:38 +04:00
break ;
}
}
2018-04-03 23:54:10 +03:00
static int verity_prepare_ioctl ( struct dm_target * ti , struct block_device * * bdev )
2012-03-28 21:43:38 +04:00
{
struct dm_verity * v = ti - > private ;
2015-10-15 15:10:50 +03:00
* bdev = v - > data_dev - > bdev ;
2012-03-28 21:43:38 +04:00
if ( v - > data_start | |
ti - > len ! = i_size_read ( v - > data_dev - > bdev - > bd_inode ) > > SECTOR_SHIFT )
2015-10-15 15:10:50 +03:00
return 1 ;
return 0 ;
2012-03-28 21:43:38 +04:00
}
static int verity_iterate_devices ( struct dm_target * ti ,
iterate_devices_callout_fn fn , void * data )
{
struct dm_verity * v = ti - > private ;
return fn ( ti , v - > data_dev , v - > data_start , ti - > len , data ) ;
}
static void verity_io_hints ( struct dm_target * ti , struct queue_limits * limits )
{
struct dm_verity * v = ti - > private ;
if ( limits - > logical_block_size < 1 < < v - > data_dev_block_bits )
limits - > logical_block_size = 1 < < v - > data_dev_block_bits ;
if ( limits - > physical_block_size < 1 < < v - > data_dev_block_bits )
limits - > physical_block_size = 1 < < v - > data_dev_block_bits ;
blk_limits_io_min ( limits , limits - > logical_block_size ) ;
}
static void verity_dtr ( struct dm_target * ti )
{
struct dm_verity * v = ti - > private ;
if ( v - > verify_wq )
destroy_workqueue ( v - > verify_wq ) ;
if ( v - > bufio )
dm_bufio_client_destroy ( v - > bufio ) ;
2018-03-23 04:18:04 +03:00
kvfree ( v - > validated_blocks ) ;
2012-03-28 21:43:38 +04:00
kfree ( v - > salt ) ;
kfree ( v - > root_digest ) ;
2015-12-03 17:26:31 +03:00
kfree ( v - > zero_digest ) ;
2012-03-28 21:43:38 +04:00
if ( v - > tfm )
2017-02-19 15:46:07 +03:00
crypto_free_ahash ( v - > tfm ) ;
2012-03-28 21:43:38 +04:00
kfree ( v - > alg_name ) ;
if ( v - > hash_dev )
dm_put_device ( ti , v - > hash_dev ) ;
if ( v - > data_dev )
dm_put_device ( ti , v - > data_dev ) ;
2015-12-03 17:26:30 +03:00
verity_fec_dtr ( v ) ;
2012-03-28 21:43:38 +04:00
kfree ( v ) ;
}
2018-03-23 04:18:04 +03:00
static int verity_alloc_most_once ( struct dm_verity * v )
{
struct dm_target * ti = v - > ti ;
/* the bitset can only handle INT_MAX blocks */
if ( v - > data_blocks > INT_MAX ) {
ti - > error = " device too large to use check_at_most_once " ;
return - E2BIG ;
}
v - > validated_blocks = kvzalloc ( BITS_TO_LONGS ( v - > data_blocks ) *
sizeof ( unsigned long ) , GFP_KERNEL ) ;
if ( ! v - > validated_blocks ) {
ti - > error = " failed to allocate bitset for check_at_most_once " ;
return - ENOMEM ;
}
return 0 ;
}
2015-12-03 17:26:31 +03:00
static int verity_alloc_zero_digest ( struct dm_verity * v )
{
int r = - ENOMEM ;
2017-02-19 15:46:07 +03:00
struct ahash_request * req ;
2015-12-03 17:26:31 +03:00
u8 * zero_data ;
v - > zero_digest = kmalloc ( v - > digest_size , GFP_KERNEL ) ;
if ( ! v - > zero_digest )
return r ;
2017-02-19 15:46:07 +03:00
req = kmalloc ( v - > ahash_reqsize , GFP_KERNEL ) ;
2015-12-03 17:26:31 +03:00
2017-02-19 15:46:07 +03:00
if ( ! req )
2015-12-03 17:26:31 +03:00
return r ; /* verity_dtr will free zero_digest */
zero_data = kzalloc ( 1 < < v - > data_dev_block_bits , GFP_KERNEL ) ;
if ( ! zero_data )
goto out ;
2017-02-19 15:46:07 +03:00
r = verity_hash ( v , req , zero_data , 1 < < v - > data_dev_block_bits ,
2015-12-03 17:26:31 +03:00
v - > zero_digest ) ;
out :
2017-02-19 15:46:07 +03:00
kfree ( req ) ;
2015-12-03 17:26:31 +03:00
kfree ( zero_data ) ;
return r ;
}
2015-11-05 05:02:32 +03:00
static int verity_parse_opt_args ( struct dm_arg_set * as , struct dm_verity * v )
{
int r ;
unsigned argc ;
struct dm_target * ti = v - > ti ;
const char * arg_name ;
2017-06-22 21:32:45 +03:00
static const struct dm_arg _args [ ] = {
2015-11-05 05:02:32 +03:00
{ 0 , DM_VERITY_OPTS_MAX , " Invalid number of feature args " } ,
} ;
r = dm_read_arg_group ( _args , as , & argc , & ti - > error ) ;
if ( r )
return - EINVAL ;
if ( ! argc )
return 0 ;
do {
arg_name = dm_shift_arg ( as ) ;
argc - - ;
if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_LOGGING ) ) {
v - > mode = DM_VERITY_MODE_LOGGING ;
continue ;
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_RESTART ) ) {
v - > mode = DM_VERITY_MODE_RESTART ;
continue ;
2015-12-03 17:26:30 +03:00
2015-12-03 17:26:31 +03:00
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_IGN_ZEROES ) ) {
r = verity_alloc_zero_digest ( v ) ;
if ( r ) {
ti - > error = " Cannot allocate zero digest " ;
return r ;
}
continue ;
2018-03-23 04:18:04 +03:00
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_AT_MOST_ONCE ) ) {
r = verity_alloc_most_once ( v ) ;
if ( r )
return r ;
continue ;
2015-12-03 17:26:30 +03:00
} else if ( verity_is_fec_opt_arg ( arg_name ) ) {
r = verity_fec_parse_opt_args ( as , v , & argc , arg_name ) ;
if ( r )
return r ;
continue ;
2015-11-05 05:02:32 +03:00
}
ti - > error = " Unrecognized verity feature request " ;
return - EINVAL ;
} while ( argc & & ! r ) ;
return r ;
}
2012-03-28 21:43:38 +04:00
/*
* Target parameters :
* < version > The current format is version 1.
* Vsn 0 is compatible with original Chromium OS releases .
* < data device >
* < hash device >
* < data block size >
* < hash block size >
* < the number of data blocks >
* < hash start block >
* < algorithm >
* < digest >
* < salt > Hex string or " - " if no salt .
*/
static int verity_ctr ( struct dm_target * ti , unsigned argc , char * * argv )
{
struct dm_verity * v ;
2015-03-18 18:52:14 +03:00
struct dm_arg_set as ;
2015-11-05 05:02:32 +03:00
unsigned int num ;
2012-03-28 21:43:38 +04:00
unsigned long long num_ll ;
int r ;
int i ;
sector_t hash_position ;
char dummy ;
v = kzalloc ( sizeof ( struct dm_verity ) , GFP_KERNEL ) ;
if ( ! v ) {
ti - > error = " Cannot allocate verity structure " ;
return - ENOMEM ;
}
ti - > private = v ;
v - > ti = ti ;
2015-12-03 17:26:30 +03:00
r = verity_fec_ctr_alloc ( v ) ;
if ( r )
goto bad ;
2012-03-28 21:43:38 +04:00
if ( ( dm_table_get_mode ( ti - > table ) & ~ FMODE_READ ) ) {
ti - > error = " Device must be readonly " ;
r = - EINVAL ;
goto bad ;
}
2015-03-18 18:52:14 +03:00
if ( argc < 10 ) {
ti - > error = " Not enough arguments " ;
2012-03-28 21:43:38 +04:00
r = - EINVAL ;
goto bad ;
}
2013-07-11 02:41:17 +04:00
if ( sscanf ( argv [ 0 ] , " %u%c " , & num , & dummy ) ! = 1 | |
num > 1 ) {
2012-03-28 21:43:38 +04:00
ti - > error = " Invalid version " ;
r = - EINVAL ;
goto bad ;
}
v - > version = num ;
r = dm_get_device ( ti , argv [ 1 ] , FMODE_READ , & v - > data_dev ) ;
if ( r ) {
ti - > error = " Data device lookup failed " ;
goto bad ;
}
r = dm_get_device ( ti , argv [ 2 ] , FMODE_READ , & v - > hash_dev ) ;
if ( r ) {
2016-09-08 22:21:28 +03:00
ti - > error = " Hash device lookup failed " ;
2012-03-28 21:43:38 +04:00
goto bad ;
}
if ( sscanf ( argv [ 3 ] , " %u%c " , & num , & dummy ) ! = 1 | |
! num | | ( num & ( num - 1 ) ) | |
num < bdev_logical_block_size ( v - > data_dev - > bdev ) | |
num > PAGE_SIZE ) {
ti - > error = " Invalid data device block size " ;
r = - EINVAL ;
goto bad ;
}
2013-07-11 02:41:17 +04:00
v - > data_dev_block_bits = __ffs ( num ) ;
2012-03-28 21:43:38 +04:00
if ( sscanf ( argv [ 4 ] , " %u%c " , & num , & dummy ) ! = 1 | |
! num | | ( num & ( num - 1 ) ) | |
num < bdev_logical_block_size ( v - > hash_dev - > bdev ) | |
num > INT_MAX ) {
ti - > error = " Invalid hash device block size " ;
r = - EINVAL ;
goto bad ;
}
2013-07-11 02:41:17 +04:00
v - > hash_dev_block_bits = __ffs ( num ) ;
2012-03-28 21:43:38 +04:00
if ( sscanf ( argv [ 5 ] , " %llu%c " , & num_ll , & dummy ) ! = 1 | |
2012-09-27 02:45:48 +04:00
( sector_t ) ( num_ll < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) )
> > ( v - > data_dev_block_bits - SECTOR_SHIFT ) ! = num_ll ) {
2012-03-28 21:43:38 +04:00
ti - > error = " Invalid data blocks " ;
r = - EINVAL ;
goto bad ;
}
v - > data_blocks = num_ll ;
if ( ti - > len > ( v - > data_blocks < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) ) ) {
ti - > error = " Data device is too small " ;
r = - EINVAL ;
goto bad ;
}
if ( sscanf ( argv [ 6 ] , " %llu%c " , & num_ll , & dummy ) ! = 1 | |
2012-09-27 02:45:48 +04:00
( sector_t ) ( num_ll < < ( v - > hash_dev_block_bits - SECTOR_SHIFT ) )
> > ( v - > hash_dev_block_bits - SECTOR_SHIFT ) ! = num_ll ) {
2012-03-28 21:43:38 +04:00
ti - > error = " Invalid hash start " ;
r = - EINVAL ;
goto bad ;
}
v - > hash_start = num_ll ;
v - > alg_name = kstrdup ( argv [ 7 ] , GFP_KERNEL ) ;
if ( ! v - > alg_name ) {
ti - > error = " Cannot allocate algorithm name " ;
r = - ENOMEM ;
goto bad ;
}
2017-02-19 15:46:07 +03:00
v - > tfm = crypto_alloc_ahash ( v - > alg_name , 0 , 0 ) ;
2012-03-28 21:43:38 +04:00
if ( IS_ERR ( v - > tfm ) ) {
ti - > error = " Cannot initialize hash function " ;
r = PTR_ERR ( v - > tfm ) ;
v - > tfm = NULL ;
goto bad ;
}
2017-02-19 15:46:07 +03:00
v - > digest_size = crypto_ahash_digestsize ( v - > tfm ) ;
2012-03-28 21:43:38 +04:00
if ( ( 1 < < v - > hash_dev_block_bits ) < v - > digest_size * 2 ) {
ti - > error = " Digest size too big " ;
r = - EINVAL ;
goto bad ;
}
2017-02-19 15:46:07 +03:00
v - > ahash_reqsize = sizeof ( struct ahash_request ) +
crypto_ahash_reqsize ( v - > tfm ) ;
2012-03-28 21:43:38 +04:00
v - > root_digest = kmalloc ( v - > digest_size , GFP_KERNEL ) ;
if ( ! v - > root_digest ) {
ti - > error = " Cannot allocate root digest " ;
r = - ENOMEM ;
goto bad ;
}
if ( strlen ( argv [ 8 ] ) ! = v - > digest_size * 2 | |
hex2bin ( v - > root_digest , argv [ 8 ] , v - > digest_size ) ) {
ti - > error = " Invalid root digest " ;
r = - EINVAL ;
goto bad ;
}
if ( strcmp ( argv [ 9 ] , " - " ) ) {
v - > salt_size = strlen ( argv [ 9 ] ) / 2 ;
v - > salt = kmalloc ( v - > salt_size , GFP_KERNEL ) ;
if ( ! v - > salt ) {
ti - > error = " Cannot allocate salt " ;
r = - ENOMEM ;
goto bad ;
}
if ( strlen ( argv [ 9 ] ) ! = v - > salt_size * 2 | |
hex2bin ( v - > salt , argv [ 9 ] , v - > salt_size ) ) {
ti - > error = " Invalid salt " ;
r = - EINVAL ;
goto bad ;
}
}
2015-03-18 18:52:14 +03:00
argv + = 10 ;
argc - = 10 ;
/* Optional parameters */
if ( argc ) {
as . argc = argc ;
as . argv = argv ;
2015-11-05 05:02:32 +03:00
r = verity_parse_opt_args ( & as , v ) ;
if ( r < 0 )
2015-03-18 18:52:14 +03:00
goto bad ;
}
2012-03-28 21:43:38 +04:00
v - > hash_per_block_bits =
2013-07-11 02:41:17 +04:00
__fls ( ( 1 < < v - > hash_dev_block_bits ) / v - > digest_size ) ;
2012-03-28 21:43:38 +04:00
v - > levels = 0 ;
if ( v - > data_blocks )
while ( v - > hash_per_block_bits * v - > levels < 64 & &
( unsigned long long ) ( v - > data_blocks - 1 ) > >
( v - > hash_per_block_bits * v - > levels ) )
v - > levels + + ;
if ( v - > levels > DM_VERITY_MAX_LEVELS ) {
ti - > error = " Too many tree levels " ;
r = - E2BIG ;
goto bad ;
}
hash_position = v - > hash_start ;
for ( i = v - > levels - 1 ; i > = 0 ; i - - ) {
sector_t s ;
v - > hash_level_block [ i ] = hash_position ;
2013-07-11 02:41:16 +04:00
s = ( v - > data_blocks + ( ( sector_t ) 1 < < ( ( i + 1 ) * v - > hash_per_block_bits ) ) - 1 )
> > ( ( i + 1 ) * v - > hash_per_block_bits ) ;
2012-03-28 21:43:38 +04:00
if ( hash_position + s < hash_position ) {
ti - > error = " Hash device offset overflow " ;
r = - E2BIG ;
goto bad ;
}
hash_position + = s ;
}
v - > hash_blocks = hash_position ;
v - > bufio = dm_bufio_client_create ( v - > hash_dev - > bdev ,
1 < < v - > hash_dev_block_bits , 1 , sizeof ( struct buffer_aux ) ,
dm_bufio_alloc_callback , NULL ) ;
if ( IS_ERR ( v - > bufio ) ) {
ti - > error = " Cannot initialize dm-bufio " ;
r = PTR_ERR ( v - > bufio ) ;
v - > bufio = NULL ;
goto bad ;
}
if ( dm_bufio_get_device_size ( v - > bufio ) < v - > hash_blocks ) {
ti - > error = " Hash device is too small " ;
r = - E2BIG ;
goto bad ;
}
/* WQ_UNBOUND greatly improves performance when running on ramdisk */
v - > verify_wq = alloc_workqueue ( " kverityd " , WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND , num_online_cpus ( ) ) ;
if ( ! v - > verify_wq ) {
ti - > error = " Cannot allocate workqueue " ;
r = - ENOMEM ;
goto bad ;
}
2016-01-31 21:28:26 +03:00
ti - > per_io_data_size = sizeof ( struct dm_verity_io ) +
2017-02-19 15:46:07 +03:00
v - > ahash_reqsize + v - > digest_size * 2 ;
2015-12-03 17:26:30 +03:00
r = verity_fec_ctr ( v ) ;
if ( r )
goto bad ;
2016-01-31 21:28:26 +03:00
ti - > per_io_data_size = roundup ( ti - > per_io_data_size ,
__alignof__ ( struct dm_verity_io ) ) ;
2015-12-03 17:26:30 +03:00
2012-03-28 21:43:38 +04:00
return 0 ;
bad :
verity_dtr ( ti ) ;
return r ;
}
static struct target_type verity_target = {
. name = " verity " ,
2018-03-23 04:18:04 +03:00
. version = { 1 , 4 , 0 } ,
2012-03-28 21:43:38 +04:00
. module = THIS_MODULE ,
. ctr = verity_ctr ,
. dtr = verity_dtr ,
. map = verity_map ,
. status = verity_status ,
2015-10-15 15:10:50 +03:00
. prepare_ioctl = verity_prepare_ioctl ,
2012-03-28 21:43:38 +04:00
. iterate_devices = verity_iterate_devices ,
. io_hints = verity_io_hints ,
} ;
static int __init dm_verity_init ( void )
{
int r ;
r = dm_register_target ( & verity_target ) ;
if ( r < 0 )
DMERR ( " register failed %d " , r ) ;
return r ;
}
static void __exit dm_verity_exit ( void )
{
dm_unregister_target ( & verity_target ) ;
}
module_init ( dm_verity_init ) ;
module_exit ( dm_verity_exit ) ;
MODULE_AUTHOR ( " Mikulas Patocka <mpatocka@redhat.com> " ) ;
MODULE_AUTHOR ( " Mandeep Baines <msb@chromium.org> " ) ;
MODULE_AUTHOR ( " Will Drewry <wad@chromium.org> " ) ;
MODULE_DESCRIPTION ( DM_NAME " target for transparent disk integrity checking " ) ;
MODULE_LICENSE ( " GPL " ) ;