2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-12-03 17:26:30 +03:00
/*
* Copyright ( C ) 2015 Google , Inc .
*
* Author : Sami Tolvanen < samitolvanen @ google . com >
*/
# include "dm-verity-fec.h"
# include <linux/math64.h>
# define DM_MSG_PREFIX "verity-fec"
/*
* If error correction has been configured , returns true .
*/
bool verity_fec_is_enabled ( struct dm_verity * v )
{
return v - > fec & & v - > fec - > dev ;
}
/*
* Return a pointer to dm_verity_fec_io after dm_verity_io and its variable
* length fields .
*/
static inline struct dm_verity_fec_io * fec_io ( struct dm_verity_io * io )
{
2023-11-28 16:50:23 +03:00
return ( struct dm_verity_fec_io * )
( ( char * ) io + io - > v - > ti - > per_io_data_size - sizeof ( struct dm_verity_fec_io ) ) ;
2015-12-03 17:26:30 +03:00
}
/*
* Return an interleaved offset for a byte in RS block .
*/
static inline u64 fec_interleave ( struct dm_verity * v , u64 offset )
{
u32 mod ;
mod = do_div ( offset , v - > fec - > rsn ) ;
return offset + mod * ( v - > fec - > rounds < < v - > data_dev_block_bits ) ;
}
/*
* Decode an RS block using Reed - Solomon .
*/
static int fec_decode_rs8 ( struct dm_verity * v , struct dm_verity_fec_io * fio ,
u8 * data , u8 * fec , int neras )
{
int i ;
uint16_t par [ DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN ] ;
for ( i = 0 ; i < v - > fec - > roots ; i + + )
par [ i ] = fec [ i ] ;
return decode_rs8 ( fio - > rs , data , par , v - > fec - > rsn , NULL , neras ,
fio - > erasures , 0 , NULL ) ;
}
/*
* Read error - correcting codes for the requested RS block . Returns a pointer
* to the data block . Caller is responsible for releasing buf .
*/
static u8 * fec_read_parity ( struct dm_verity * v , u64 rsb , int index ,
2023-01-25 23:14:58 +03:00
unsigned int * offset , struct dm_buffer * * buf )
2015-12-03 17:26:30 +03:00
{
2021-02-23 23:21:21 +03:00
u64 position , block , rem ;
2015-12-03 17:26:30 +03:00
u8 * res ;
position = ( index + rsb ) * v - > fec - > roots ;
2021-04-14 18:28:28 +03:00
block = div64_u64_rem ( position , v - > fec - > io_size , & rem ) ;
2023-01-25 23:14:58 +03:00
* offset = ( unsigned int ) rem ;
2015-12-03 17:26:30 +03:00
2021-02-23 23:21:21 +03:00
res = dm_bufio_read ( v - > fec - > bufio , block , buf ) ;
2019-02-13 08:46:57 +03:00
if ( IS_ERR ( res ) ) {
2015-12-03 17:26:30 +03:00
DMERR ( " %s: FEC %llu: parity read failed (block %llu): %ld " ,
v - > data_dev - > name , ( unsigned long long ) rsb ,
2021-02-23 23:21:21 +03:00
( unsigned long long ) block , PTR_ERR ( res ) ) ;
2015-12-03 17:26:30 +03:00
* buf = NULL ;
}
return res ;
}
/* Loop over each preallocated buffer slot. */
# define fec_for_each_prealloc_buffer(__i) \
for ( __i = 0 ; __i < DM_VERITY_FEC_BUF_PREALLOC ; __i + + )
/* Loop over each extra buffer slot. */
# define fec_for_each_extra_buffer(io, __i) \
for ( __i = DM_VERITY_FEC_BUF_PREALLOC ; __i < DM_VERITY_FEC_BUF_MAX ; __i + + )
/* Loop over each allocated buffer. */
# define fec_for_each_buffer(io, __i) \
for ( __i = 0 ; __i < ( io ) - > nbufs ; __i + + )
/* Loop over each RS block in each allocated buffer. */
# define fec_for_each_buffer_rs_block(io, __i, __j) \
fec_for_each_buffer ( io , __i ) \
for ( __j = 0 ; __j < 1 < < DM_VERITY_FEC_BUF_RS_BITS ; __j + + )
/*
* Return a pointer to the current RS block when called inside
* fec_for_each_buffer_rs_block .
*/
static inline u8 * fec_buffer_rs_block ( struct dm_verity * v ,
struct dm_verity_fec_io * fio ,
2023-01-25 23:14:58 +03:00
unsigned int i , unsigned int j )
2015-12-03 17:26:30 +03:00
{
return & fio - > bufs [ i ] [ j * v - > fec - > rsn ] ;
}
/*
* Return an index to the current RS block when called inside
* fec_for_each_buffer_rs_block .
*/
2023-01-25 23:14:58 +03:00
static inline unsigned int fec_buffer_rs_index ( unsigned int i , unsigned int j )
2015-12-03 17:26:30 +03:00
{
return ( i < < DM_VERITY_FEC_BUF_RS_BITS ) + j ;
}
/*
* Decode all RS blocks from buffers and copy corrected bytes into fio - > output
* starting from block_offset .
*/
static int fec_decode_bufs ( struct dm_verity * v , struct dm_verity_fec_io * fio ,
2023-01-25 23:14:58 +03:00
u64 rsb , int byte_index , unsigned int block_offset ,
2015-12-03 17:26:30 +03:00
int neras )
{
int r , corrected = 0 , res ;
struct dm_buffer * buf ;
2023-01-25 23:14:58 +03:00
unsigned int n , i , offset ;
2015-12-03 17:26:30 +03:00
u8 * par , * block ;
par = fec_read_parity ( v , rsb , block_offset , & offset , & buf ) ;
if ( IS_ERR ( par ) )
return PTR_ERR ( par ) ;
/*
* Decode the RS blocks we have in bufs . Each RS block results in
* one corrected target byte and consumes fec - > roots parity bytes .
*/
fec_for_each_buffer_rs_block ( fio , n , i ) {
block = fec_buffer_rs_block ( v , fio , n , i ) ;
res = fec_decode_rs8 ( v , fio , block , & par [ offset ] , neras ) ;
if ( res < 0 ) {
r = res ;
goto error ;
}
corrected + = res ;
fio - > output [ block_offset ] = block [ byte_index ] ;
block_offset + + ;
if ( block_offset > = 1 < < v - > data_dev_block_bits )
goto done ;
/* read the next block when we run out of parity bytes */
offset + = v - > fec - > roots ;
2021-04-14 18:28:28 +03:00
if ( offset > = v - > fec - > io_size ) {
2015-12-03 17:26:30 +03:00
dm_bufio_release ( buf ) ;
par = fec_read_parity ( v , rsb , block_offset , & offset , & buf ) ;
2019-02-13 08:46:57 +03:00
if ( IS_ERR ( par ) )
2015-12-03 17:26:30 +03:00
return PTR_ERR ( par ) ;
}
}
done :
r = corrected ;
error :
2017-03-31 22:32:45 +03:00
dm_bufio_release ( buf ) ;
2015-12-03 17:26:30 +03:00
if ( r < 0 & & neras )
DMERR_LIMIT ( " %s: FEC %llu: failed to correct: %d " ,
v - > data_dev - > name , ( unsigned long long ) rsb , r ) ;
else if ( r > 0 )
DMWARN_LIMIT ( " %s: FEC %llu: corrected %d errors " ,
v - > data_dev - > name , ( unsigned long long ) rsb , r ) ;
return r ;
}
/*
* Locate data block erasures using verity hashes .
*/
static int fec_is_erasure ( struct dm_verity * v , struct dm_verity_io * io ,
u8 * want_digest , u8 * data )
{
2017-02-19 15:46:07 +03:00
if ( unlikely ( verity_hash ( v , verity_io_hash_req ( v , io ) ,
2015-12-03 17:26:30 +03:00
data , 1 < < v - > data_dev_block_bits ,
dm-verity: don't use blocking calls from tasklets
The commit 5721d4e5a9cd enhanced dm-verity, so that it can verify blocks
from tasklets rather than from workqueues. This reportedly improves
performance significantly.
However, dm-verity was using the flag CRYPTO_TFM_REQ_MAY_SLEEP from
tasklets which resulted in warnings about sleeping function being called
from non-sleeping context.
BUG: sleeping function called from invalid context at crypto/internal.h:206
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 14, name: ksoftirqd/0
preempt_count: 100, expected: 0
RCU nest depth: 0, expected: 0
CPU: 0 PID: 14 Comm: ksoftirqd/0 Tainted: G W 6.7.0-rc1 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x32/0x50
__might_resched+0x110/0x160
crypto_hash_walk_done+0x54/0xb0
shash_ahash_update+0x51/0x60
verity_hash_update.isra.0+0x4a/0x130 [dm_verity]
verity_verify_io+0x165/0x550 [dm_verity]
? free_unref_page+0xdf/0x170
? psi_group_change+0x113/0x390
verity_tasklet+0xd/0x70 [dm_verity]
tasklet_action_common.isra.0+0xb3/0xc0
__do_softirq+0xaf/0x1ec
? smpboot_thread_fn+0x1d/0x200
? sort_range+0x20/0x20
run_ksoftirqd+0x15/0x30
smpboot_thread_fn+0xed/0x200
kthread+0xdc/0x110
? kthread_complete_and_exit+0x20/0x20
ret_from_fork+0x28/0x40
? kthread_complete_and_exit+0x20/0x20
ret_from_fork_asm+0x11/0x20
</TASK>
This commit fixes dm-verity so that it doesn't use the flags
CRYPTO_TFM_REQ_MAY_SLEEP and CRYPTO_TFM_REQ_MAY_BACKLOG from tasklets. The
crypto API would do GFP_ATOMIC allocation instead, it could return -ENOMEM
and we catch -ENOMEM in verity_tasklet and requeue the request to the
workqueue.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org # v6.0+
Fixes: 5721d4e5a9cd ("dm verity: Add optional "try_verify_in_tasklet" feature")
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
2023-11-17 20:37:25 +03:00
verity_io_real_digest ( v , io ) , true ) ) )
2015-12-03 17:26:30 +03:00
return 0 ;
return memcmp ( verity_io_real_digest ( v , io ) , want_digest ,
v - > digest_size ) ! = 0 ;
}
/*
* Read data blocks that are part of the RS block and deinterleave as much as
* fits into buffers . Check for erasure locations if @ neras is non - NULL .
*/
static int fec_read_bufs ( struct dm_verity * v , struct dm_verity_io * io ,
2023-01-25 23:14:58 +03:00
u64 rsb , u64 target , unsigned int block_offset ,
2015-12-03 17:26:30 +03:00
int * neras )
{
2015-12-03 17:26:31 +03:00
bool is_zero ;
2015-12-03 17:26:30 +03:00
int i , j , target_index = - 1 ;
struct dm_buffer * buf ;
struct dm_bufio_client * bufio ;
struct dm_verity_fec_io * fio = fec_io ( io ) ;
u64 block , ileaved ;
u8 * bbuf , * rs_block ;
2018-08-08 00:18:39 +03:00
u8 want_digest [ HASH_MAX_DIGESTSIZE ] ;
2023-01-25 23:14:58 +03:00
unsigned int n , k ;
2015-12-03 17:26:30 +03:00
if ( neras )
* neras = 0 ;
2018-08-08 00:18:39 +03:00
if ( WARN_ON ( v - > digest_size > sizeof ( want_digest ) ) )
return - EINVAL ;
2015-12-03 17:26:30 +03:00
/*
* read each of the rsn data blocks that are part of the RS block , and
* interleave contents to available bufs
*/
for ( i = 0 ; i < v - > fec - > rsn ; i + + ) {
ileaved = fec_interleave ( v , rsb * v - > fec - > rsn + i ) ;
/*
* target is the data block we want to correct , target_index is
* the index of this block within the rsn RS blocks
*/
if ( ileaved = = target )
target_index = i ;
block = ileaved > > v - > data_dev_block_bits ;
bufio = v - > fec - > data_bufio ;
if ( block > = v - > data_blocks ) {
block - = v - > data_blocks ;
/*
* blocks outside the area were assumed to contain
* zeros when encoding data was generated
*/
if ( unlikely ( block > = v - > fec - > hash_blocks ) )
continue ;
block + = v - > hash_start ;
bufio = v - > bufio ;
}
bbuf = dm_bufio_read ( bufio , block , & buf ) ;
2019-02-13 08:46:57 +03:00
if ( IS_ERR ( bbuf ) ) {
2015-12-03 17:26:30 +03:00
DMWARN_LIMIT ( " %s: FEC %llu: read failed (%llu): %ld " ,
v - > data_dev - > name ,
( unsigned long long ) rsb ,
( unsigned long long ) block , PTR_ERR ( bbuf ) ) ;
/* assume the block is corrupted */
if ( neras & & * neras < = v - > fec - > roots )
fio - > erasures [ ( * neras ) + + ] = i ;
continue ;
}
/* locate erasures if the block is on the data device */
if ( bufio = = v - > fec - > data_bufio & &
2015-12-03 17:26:31 +03:00
verity_hash_for_block ( v , io , block , want_digest ,
& is_zero ) = = 0 ) {
/* skip known zero blocks entirely */
if ( is_zero )
2017-03-31 22:32:45 +03:00
goto done ;
2015-12-03 17:26:31 +03:00
2015-12-03 17:26:30 +03:00
/*
* skip if we have already found the theoretical
* maximum number ( i . e . fec - > roots ) of erasures
*/
if ( neras & & * neras < = v - > fec - > roots & &
fec_is_erasure ( v , io , want_digest , bbuf ) )
fio - > erasures [ ( * neras ) + + ] = i ;
}
/*
* deinterleave and copy the bytes that fit into bufs ,
* starting from block_offset
*/
fec_for_each_buffer_rs_block ( fio , n , j ) {
k = fec_buffer_rs_index ( n , j ) + block_offset ;
if ( k > = 1 < < v - > data_dev_block_bits )
goto done ;
rs_block = fec_buffer_rs_block ( v , fio , n , j ) ;
rs_block [ i ] = bbuf [ k ] ;
}
done :
dm_bufio_release ( buf ) ;
}
return target_index ;
}
/*
* Allocate RS control structure and FEC buffers from preallocated mempools ,
* and attempt to allocate as many extra buffers as available .
*/
static int fec_alloc_bufs ( struct dm_verity * v , struct dm_verity_fec_io * fio )
{
2023-01-25 23:14:58 +03:00
unsigned int n ;
2015-12-03 17:26:30 +03:00
2017-04-10 05:13:00 +03:00
if ( ! fio - > rs )
2018-05-21 01:25:53 +03:00
fio - > rs = mempool_alloc ( & v - > fec - > rs_pool , GFP_NOIO ) ;
2015-12-03 17:26:30 +03:00
fec_for_each_prealloc_buffer ( n ) {
if ( fio - > bufs [ n ] )
continue ;
2018-05-21 01:25:53 +03:00
fio - > bufs [ n ] = mempool_alloc ( & v - > fec - > prealloc_pool , GFP_NOWAIT ) ;
2015-12-03 17:26:30 +03:00
if ( unlikely ( ! fio - > bufs [ n ] ) ) {
DMERR ( " failed to allocate FEC buffer " ) ;
return - ENOMEM ;
}
}
/* try to allocate the maximum number of buffers */
fec_for_each_extra_buffer ( fio , n ) {
if ( fio - > bufs [ n ] )
continue ;
2018-05-21 01:25:53 +03:00
fio - > bufs [ n ] = mempool_alloc ( & v - > fec - > extra_pool , GFP_NOWAIT ) ;
2015-12-03 17:26:30 +03:00
/* we can manage with even one buffer if necessary */
if ( unlikely ( ! fio - > bufs [ n ] ) )
break ;
}
fio - > nbufs = n ;
2017-04-10 05:13:00 +03:00
if ( ! fio - > output )
2018-05-21 01:25:53 +03:00
fio - > output = mempool_alloc ( & v - > fec - > output_pool , GFP_NOIO ) ;
2015-12-03 17:26:30 +03:00
return 0 ;
}
/*
* Initialize buffers and clear erasures . fec_read_bufs ( ) assumes buffers are
* zeroed before deinterleaving .
*/
static void fec_init_bufs ( struct dm_verity * v , struct dm_verity_fec_io * fio )
{
2023-01-25 23:14:58 +03:00
unsigned int n ;
2015-12-03 17:26:30 +03:00
fec_for_each_buffer ( fio , n )
memset ( fio - > bufs [ n ] , 0 , v - > fec - > rsn < < DM_VERITY_FEC_BUF_RS_BITS ) ;
memset ( fio - > erasures , 0 , sizeof ( fio - > erasures ) ) ;
}
/*
* Decode all RS blocks in a single data block and return the target block
* ( indicated by @ offset ) in fio - > output . If @ use_erasures is non - zero , uses
* hashes to locate erasures .
*/
static int fec_decode_rsb ( struct dm_verity * v , struct dm_verity_io * io ,
struct dm_verity_fec_io * fio , u64 rsb , u64 offset ,
bool use_erasures )
{
int r , neras = 0 ;
2023-01-25 23:14:58 +03:00
unsigned int pos ;
2015-12-03 17:26:30 +03:00
r = fec_alloc_bufs ( v , fio ) ;
if ( unlikely ( r < 0 ) )
return r ;
for ( pos = 0 ; pos < 1 < < v - > data_dev_block_bits ; ) {
fec_init_bufs ( v , fio ) ;
r = fec_read_bufs ( v , io , rsb , offset , pos ,
use_erasures ? & neras : NULL ) ;
if ( unlikely ( r < 0 ) )
return r ;
r = fec_decode_bufs ( v , fio , rsb , r , pos , neras ) ;
if ( r < 0 )
return r ;
pos + = fio - > nbufs < < DM_VERITY_FEC_BUF_RS_BITS ;
}
/* Always re-validate the corrected block against the expected hash */
2017-02-19 15:46:07 +03:00
r = verity_hash ( v , verity_io_hash_req ( v , io ) , fio - > output ,
2015-12-03 17:26:30 +03:00
1 < < v - > data_dev_block_bits ,
dm-verity: don't use blocking calls from tasklets
The commit 5721d4e5a9cd enhanced dm-verity, so that it can verify blocks
from tasklets rather than from workqueues. This reportedly improves
performance significantly.
However, dm-verity was using the flag CRYPTO_TFM_REQ_MAY_SLEEP from
tasklets which resulted in warnings about sleeping function being called
from non-sleeping context.
BUG: sleeping function called from invalid context at crypto/internal.h:206
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 14, name: ksoftirqd/0
preempt_count: 100, expected: 0
RCU nest depth: 0, expected: 0
CPU: 0 PID: 14 Comm: ksoftirqd/0 Tainted: G W 6.7.0-rc1 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x32/0x50
__might_resched+0x110/0x160
crypto_hash_walk_done+0x54/0xb0
shash_ahash_update+0x51/0x60
verity_hash_update.isra.0+0x4a/0x130 [dm_verity]
verity_verify_io+0x165/0x550 [dm_verity]
? free_unref_page+0xdf/0x170
? psi_group_change+0x113/0x390
verity_tasklet+0xd/0x70 [dm_verity]
tasklet_action_common.isra.0+0xb3/0xc0
__do_softirq+0xaf/0x1ec
? smpboot_thread_fn+0x1d/0x200
? sort_range+0x20/0x20
run_ksoftirqd+0x15/0x30
smpboot_thread_fn+0xed/0x200
kthread+0xdc/0x110
? kthread_complete_and_exit+0x20/0x20
ret_from_fork+0x28/0x40
? kthread_complete_and_exit+0x20/0x20
ret_from_fork_asm+0x11/0x20
</TASK>
This commit fixes dm-verity so that it doesn't use the flags
CRYPTO_TFM_REQ_MAY_SLEEP and CRYPTO_TFM_REQ_MAY_BACKLOG from tasklets. The
crypto API would do GFP_ATOMIC allocation instead, it could return -ENOMEM
and we catch -ENOMEM in verity_tasklet and requeue the request to the
workqueue.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org # v6.0+
Fixes: 5721d4e5a9cd ("dm verity: Add optional "try_verify_in_tasklet" feature")
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
2023-11-17 20:37:25 +03:00
verity_io_real_digest ( v , io ) , true ) ;
2015-12-03 17:26:30 +03:00
if ( unlikely ( r < 0 ) )
return r ;
if ( memcmp ( verity_io_real_digest ( v , io ) , verity_io_want_digest ( v , io ) ,
v - > digest_size ) ) {
DMERR_LIMIT ( " %s: FEC %llu: failed to correct (%d erasures) " ,
v - > data_dev - > name , ( unsigned long long ) rsb , neras ) ;
return - EILSEQ ;
}
return 0 ;
}
static int fec_bv_copy ( struct dm_verity * v , struct dm_verity_io * io , u8 * data ,
size_t len )
{
struct dm_verity_fec_io * fio = fec_io ( io ) ;
memcpy ( data , & fio - > output [ fio - > output_pos ] , len ) ;
fio - > output_pos + = len ;
return 0 ;
}
/*
* Correct errors in a block . Copies corrected block to dest if non - NULL ,
* otherwise to a bio_vec starting from iter .
*/
int verity_fec_decode ( struct dm_verity * v , struct dm_verity_io * io ,
enum verity_block_type type , sector_t block , u8 * dest ,
struct bvec_iter * iter )
{
int r ;
struct dm_verity_fec_io * fio = fec_io ( io ) ;
u64 offset , res , rsb ;
if ( ! verity_fec_is_enabled ( v ) )
return - EOPNOTSUPP ;
2017-03-16 01:12:23 +03:00
if ( fio - > level > = DM_VERITY_FEC_MAX_RECURSION ) {
DMWARN_LIMIT ( " %s: FEC: recursion too deep " , v - > data_dev - > name ) ;
return - EIO ;
}
fio - > level + + ;
2015-12-03 17:26:30 +03:00
if ( type = = DM_VERITY_BLOCK_TYPE_METADATA )
2020-04-10 06:54:19 +03:00
block = block - v - > hash_start + v - > data_blocks ;
2015-12-03 17:26:30 +03:00
/*
* For RS ( M , N ) , the continuous FEC data is divided into blocks of N
* bytes . Since block size may not be divisible by N , the last block
* is zero padded when decoding .
*
* Each byte of the block is covered by a different RS ( M , N ) code ,
* and each code is interleaved over N blocks to make it less likely
* that bursty corruption will leave us in unrecoverable state .
*/
offset = block < < v - > data_dev_block_bits ;
2016-06-21 21:02:42 +03:00
res = div64_u64 ( offset , v - > fec - > rounds < < v - > data_dev_block_bits ) ;
2015-12-03 17:26:30 +03:00
/*
* The base RS block we can feed to the interleaver to find out all
* blocks required for decoding .
*/
rsb = offset - res * ( v - > fec - > rounds < < v - > data_dev_block_bits ) ;
/*
* Locating erasures is slow , so attempt to recover the block without
* them first . Do a second attempt with erasures if the corruption is
* bad enough .
*/
r = fec_decode_rsb ( v , io , fio , rsb , offset , false ) ;
if ( r < 0 ) {
r = fec_decode_rsb ( v , io , fio , rsb , offset , true ) ;
if ( r < 0 )
2017-03-16 01:12:23 +03:00
goto done ;
2015-12-03 17:26:30 +03:00
}
if ( dest )
memcpy ( dest , fio - > output , 1 < < v - > data_dev_block_bits ) ;
else if ( iter ) {
fio - > output_pos = 0 ;
r = verity_for_bv_block ( v , io , iter , fec_bv_copy ) ;
}
2017-03-16 01:12:23 +03:00
done :
fio - > level - - ;
2015-12-03 17:26:30 +03:00
return r ;
}
/*
* Clean up per - bio data .
*/
void verity_fec_finish_io ( struct dm_verity_io * io )
{
2023-01-25 23:14:58 +03:00
unsigned int n ;
2015-12-03 17:26:30 +03:00
struct dm_verity_fec * f = io - > v - > fec ;
struct dm_verity_fec_io * fio = fec_io ( io ) ;
if ( ! verity_fec_is_enabled ( io - > v ) )
return ;
2018-05-21 01:25:53 +03:00
mempool_free ( fio - > rs , & f - > rs_pool ) ;
2015-12-03 17:26:30 +03:00
fec_for_each_prealloc_buffer ( n )
2018-05-21 01:25:53 +03:00
mempool_free ( fio - > bufs [ n ] , & f - > prealloc_pool ) ;
2015-12-03 17:26:30 +03:00
fec_for_each_extra_buffer ( fio , n )
2018-05-21 01:25:53 +03:00
mempool_free ( fio - > bufs [ n ] , & f - > extra_pool ) ;
2015-12-03 17:26:30 +03:00
2018-05-21 01:25:53 +03:00
mempool_free ( fio - > output , & f - > output_pool ) ;
2015-12-03 17:26:30 +03:00
}
/*
* Initialize per - bio data .
*/
void verity_fec_init_io ( struct dm_verity_io * io )
{
struct dm_verity_fec_io * fio = fec_io ( io ) ;
if ( ! verity_fec_is_enabled ( io - > v ) )
return ;
fio - > rs = NULL ;
memset ( fio - > bufs , 0 , sizeof ( fio - > bufs ) ) ;
fio - > nbufs = 0 ;
fio - > output = NULL ;
2017-03-16 01:12:23 +03:00
fio - > level = 0 ;
2015-12-03 17:26:30 +03:00
}
/*
* Append feature arguments and values to the status table .
*/
2023-01-25 23:14:58 +03:00
unsigned int verity_fec_status_table ( struct dm_verity * v , unsigned int sz ,
char * result , unsigned int maxlen )
2015-12-03 17:26:30 +03:00
{
if ( ! verity_fec_is_enabled ( v ) )
return sz ;
DMEMIT ( " " DM_VERITY_OPT_FEC_DEV " %s "
DM_VERITY_OPT_FEC_BLOCKS " %llu "
DM_VERITY_OPT_FEC_START " %llu "
DM_VERITY_OPT_FEC_ROOTS " %d " ,
v - > fec - > dev - > name ,
( unsigned long long ) v - > fec - > blocks ,
( unsigned long long ) v - > fec - > start ,
v - > fec - > roots ) ;
return sz ;
}
void verity_fec_dtr ( struct dm_verity * v )
{
struct dm_verity_fec * f = v - > fec ;
if ( ! verity_fec_is_enabled ( v ) )
goto out ;
2018-05-21 01:25:53 +03:00
mempool_exit ( & f - > rs_pool ) ;
mempool_exit ( & f - > prealloc_pool ) ;
mempool_exit ( & f - > extra_pool ) ;
2020-03-17 12:15:45 +03:00
mempool_exit ( & f - > output_pool ) ;
2015-12-03 17:26:30 +03:00
kmem_cache_destroy ( f - > cache ) ;
if ( f - > data_bufio )
dm_bufio_client_destroy ( f - > data_bufio ) ;
if ( f - > bufio )
dm_bufio_client_destroy ( f - > bufio ) ;
if ( f - > dev )
dm_put_device ( v - > ti , f - > dev ) ;
out :
kfree ( f ) ;
v - > fec = NULL ;
}
static void * fec_rs_alloc ( gfp_t gfp_mask , void * pool_data )
{
2023-03-17 04:35:54 +03:00
struct dm_verity * v = pool_data ;
2015-12-03 17:26:30 +03:00
2018-04-22 19:23:47 +03:00
return init_rs_gfp ( 8 , 0x11d , 0 , 1 , v - > fec - > roots , gfp_mask ) ;
2015-12-03 17:26:30 +03:00
}
static void fec_rs_free ( void * element , void * pool_data )
{
2023-03-17 04:35:54 +03:00
struct rs_control * rs = element ;
2015-12-03 17:26:30 +03:00
if ( rs )
free_rs ( rs ) ;
}
bool verity_is_fec_opt_arg ( const char * arg_name )
{
return ( ! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_DEV ) | |
! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_BLOCKS ) | |
! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_START ) | |
! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_ROOTS ) ) ;
}
int verity_fec_parse_opt_args ( struct dm_arg_set * as , struct dm_verity * v ,
2023-01-25 23:14:58 +03:00
unsigned int * argc , const char * arg_name )
2015-12-03 17:26:30 +03:00
{
int r ;
struct dm_target * ti = v - > ti ;
const char * arg_value ;
unsigned long long num_ll ;
unsigned char num_c ;
char dummy ;
if ( ! * argc ) {
ti - > error = " FEC feature arguments require a value " ;
return - EINVAL ;
}
arg_value = dm_shift_arg ( as ) ;
( * argc ) - - ;
if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_DEV ) ) {
2023-06-08 14:02:55 +03:00
r = dm_get_device ( ti , arg_value , BLK_OPEN_READ , & v - > fec - > dev ) ;
2015-12-03 17:26:30 +03:00
if ( r ) {
ti - > error = " FEC device lookup failed " ;
return r ;
}
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_BLOCKS ) ) {
if ( sscanf ( arg_value , " %llu%c " , & num_ll , & dummy ) ! = 1 | |
( ( sector_t ) ( num_ll < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) )
> > ( v - > data_dev_block_bits - SECTOR_SHIFT ) ! = num_ll ) ) {
ti - > error = " Invalid " DM_VERITY_OPT_FEC_BLOCKS ;
return - EINVAL ;
}
v - > fec - > blocks = num_ll ;
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_START ) ) {
if ( sscanf ( arg_value , " %llu%c " , & num_ll , & dummy ) ! = 1 | |
( ( sector_t ) ( num_ll < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) ) > >
( v - > data_dev_block_bits - SECTOR_SHIFT ) ! = num_ll ) ) {
ti - > error = " Invalid " DM_VERITY_OPT_FEC_START ;
return - EINVAL ;
}
v - > fec - > start = num_ll ;
} else if ( ! strcasecmp ( arg_name , DM_VERITY_OPT_FEC_ROOTS ) ) {
if ( sscanf ( arg_value , " %hhu%c " , & num_c , & dummy ) ! = 1 | | ! num_c | |
num_c < ( DM_VERITY_FEC_RSM - DM_VERITY_FEC_MAX_RSN ) | |
num_c > ( DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN ) ) {
ti - > error = " Invalid " DM_VERITY_OPT_FEC_ROOTS ;
return - EINVAL ;
}
v - > fec - > roots = num_c ;
} else {
ti - > error = " Unrecognized verity FEC feature request " ;
return - EINVAL ;
}
return 0 ;
}
/*
* Allocate dm_verity_fec for v - > fec . Must be called before verity_fec_ctr .
*/
int verity_fec_ctr_alloc ( struct dm_verity * v )
{
struct dm_verity_fec * f ;
f = kzalloc ( sizeof ( struct dm_verity_fec ) , GFP_KERNEL ) ;
if ( ! f ) {
v - > ti - > error = " Cannot allocate FEC structure " ;
return - ENOMEM ;
}
v - > fec = f ;
return 0 ;
}
/*
* Validate arguments and preallocate memory . Must be called after arguments
* have been parsed using verity_fec_parse_opt_args .
*/
int verity_fec_ctr ( struct dm_verity * v )
{
struct dm_verity_fec * f = v - > fec ;
struct dm_target * ti = v - > ti ;
2021-02-23 23:21:21 +03:00
u64 hash_blocks , fec_blocks ;
2018-05-21 01:25:53 +03:00
int ret ;
2015-12-03 17:26:30 +03:00
if ( ! verity_fec_is_enabled ( v ) ) {
verity_fec_dtr ( v ) ;
return 0 ;
}
/*
* FEC is computed over data blocks , possible metadata , and
* hash blocks . In other words , FEC covers total of fec_blocks
* blocks consisting of the following :
*
* data blocks | hash blocks | metadata ( optional )
*
* We allow metadata after hash blocks to support a use case
* where all data is stored on the same device and FEC covers
* the entire area .
*
* If metadata is included , we require it to be available on the
* hash device after the hash blocks .
*/
hash_blocks = v - > hash_blocks - v - > hash_start ;
/*
* Require matching block sizes for data and hash devices for
* simplicity .
*/
if ( v - > data_dev_block_bits ! = v - > hash_dev_block_bits ) {
ti - > error = " Block sizes must match to use FEC " ;
return - EINVAL ;
}
if ( ! f - > roots ) {
ti - > error = " Missing " DM_VERITY_OPT_FEC_ROOTS ;
return - EINVAL ;
}
f - > rsn = DM_VERITY_FEC_RSM - f - > roots ;
if ( ! f - > blocks ) {
ti - > error = " Missing " DM_VERITY_OPT_FEC_BLOCKS ;
return - EINVAL ;
}
f - > rounds = f - > blocks ;
if ( sector_div ( f - > rounds , f - > rsn ) )
f - > rounds + + ;
/*
* Due to optional metadata , f - > blocks can be larger than
* data_blocks and hash_blocks combined .
*/
if ( f - > blocks < v - > data_blocks + hash_blocks | | ! f - > rounds ) {
ti - > error = " Invalid " DM_VERITY_OPT_FEC_BLOCKS ;
return - EINVAL ;
}
/*
* Metadata is accessed through the hash device , so we require
* it to be large enough .
*/
f - > hash_blocks = f - > blocks - v - > data_blocks ;
if ( dm_bufio_get_device_size ( v - > bufio ) < f - > hash_blocks ) {
ti - > error = " Hash device is too small for "
DM_VERITY_OPT_FEC_BLOCKS ;
return - E2BIG ;
}
2021-04-14 18:28:28 +03:00
if ( ( f - > roots < < SECTOR_SHIFT ) & ( ( 1 < < v - > data_dev_block_bits ) - 1 ) )
f - > io_size = 1 < < v - > data_dev_block_bits ;
else
f - > io_size = v - > fec - > roots < < SECTOR_SHIFT ;
2015-12-03 17:26:30 +03:00
f - > bufio = dm_bufio_client_create ( f - > dev - > bdev ,
2021-04-14 18:28:28 +03:00
f - > io_size ,
2022-07-22 12:38:21 +03:00
1 , 0 , NULL , NULL , 0 ) ;
2015-12-03 17:26:30 +03:00
if ( IS_ERR ( f - > bufio ) ) {
ti - > error = " Cannot initialize FEC bufio client " ;
return PTR_ERR ( f - > bufio ) ;
}
2021-02-23 23:21:21 +03:00
dm_bufio_set_sector_offset ( f - > bufio , f - > start < < ( v - > data_dev_block_bits - SECTOR_SHIFT ) ) ;
fec_blocks = div64_u64 ( f - > rounds * f - > roots , v - > fec - > roots < < SECTOR_SHIFT ) ;
if ( dm_bufio_get_device_size ( f - > bufio ) < fec_blocks ) {
2015-12-03 17:26:30 +03:00
ti - > error = " FEC device is too small " ;
return - E2BIG ;
}
f - > data_bufio = dm_bufio_client_create ( v - > data_dev - > bdev ,
1 < < v - > data_dev_block_bits ,
2022-07-22 12:38:21 +03:00
1 , 0 , NULL , NULL , 0 ) ;
2015-12-03 17:26:30 +03:00
if ( IS_ERR ( f - > data_bufio ) ) {
ti - > error = " Cannot initialize FEC data bufio client " ;
return PTR_ERR ( f - > data_bufio ) ;
}
if ( dm_bufio_get_device_size ( f - > data_bufio ) < v - > data_blocks ) {
ti - > error = " Data device is too small " ;
return - E2BIG ;
}
/* Preallocate an rs_control structure for each worker thread */
2018-05-21 01:25:53 +03:00
ret = mempool_init ( & f - > rs_pool , num_online_cpus ( ) , fec_rs_alloc ,
fec_rs_free , ( void * ) v ) ;
if ( ret ) {
2015-12-03 17:26:30 +03:00
ti - > error = " Cannot allocate RS pool " ;
2018-05-21 01:25:53 +03:00
return ret ;
2015-12-03 17:26:30 +03:00
}
f - > cache = kmem_cache_create ( " dm_verity_fec_buffers " ,
f - > rsn < < DM_VERITY_FEC_BUF_RS_BITS ,
0 , 0 , NULL ) ;
if ( ! f - > cache ) {
ti - > error = " Cannot create FEC buffer cache " ;
return - ENOMEM ;
}
/* Preallocate DM_VERITY_FEC_BUF_PREALLOC buffers for each thread */
2018-05-21 01:25:53 +03:00
ret = mempool_init_slab_pool ( & f - > prealloc_pool , num_online_cpus ( ) *
DM_VERITY_FEC_BUF_PREALLOC ,
f - > cache ) ;
if ( ret ) {
2015-12-03 17:26:30 +03:00
ti - > error = " Cannot allocate FEC buffer prealloc pool " ;
2018-05-21 01:25:53 +03:00
return ret ;
2015-12-03 17:26:30 +03:00
}
2018-05-21 01:25:53 +03:00
ret = mempool_init_slab_pool ( & f - > extra_pool , 0 , f - > cache ) ;
if ( ret ) {
2015-12-03 17:26:30 +03:00
ti - > error = " Cannot allocate FEC buffer extra pool " ;
2018-05-21 01:25:53 +03:00
return ret ;
2015-12-03 17:26:30 +03:00
}
/* Preallocate an output buffer for each thread */
2018-05-21 01:25:53 +03:00
ret = mempool_init_kmalloc_pool ( & f - > output_pool , num_online_cpus ( ) ,
1 < < v - > data_dev_block_bits ) ;
if ( ret ) {
2015-12-03 17:26:30 +03:00
ti - > error = " Cannot allocate FEC output pool " ;
2018-05-21 01:25:53 +03:00
return ret ;
2015-12-03 17:26:30 +03:00
}
/* Reserve space for our per-bio data */
2016-01-31 21:28:26 +03:00
ti - > per_io_data_size + = sizeof ( struct dm_verity_fec_io ) ;
2015-12-03 17:26:30 +03:00
return 0 ;
}