2023-01-25 23:00:44 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2007-05-09 13:33:06 +04:00
/*
* Copyright ( C ) 2005 - 2007 Red Hat GmbH
*
* A target that delays reads and / or writes and can send
* them to different devices .
*
* This file is released under the GPL .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/bio.h>
# include <linux/slab.h>
2023-10-20 14:46:05 +03:00
# include <linux/kthread.h>
2007-05-09 13:33:06 +04:00
2008-10-21 20:44:59 +04:00
# include <linux/device-mapper.h>
2007-05-09 13:33:06 +04:00
# define DM_MSG_PREFIX "delay"
2018-04-17 01:33:13 +03:00
struct delay_class {
struct dm_dev * dev ;
sector_t start ;
2023-01-25 23:14:58 +03:00
unsigned int delay ;
unsigned int ops ;
2018-04-17 01:33:13 +03:00
} ;
2007-05-09 13:33:06 +04:00
struct delay_c {
struct timer_list delay_timer ;
2007-07-12 20:26:47 +04:00
struct mutex timer_lock ;
2013-11-16 01:12:20 +04:00
struct workqueue_struct * kdelayd_wq ;
2007-05-09 13:33:06 +04:00
struct work_struct flush_expired_bios ;
struct list_head delayed_bios ;
2023-10-20 14:46:05 +03:00
struct task_struct * worker ;
2023-11-17 20:21:14 +03:00
bool may_delay ;
2007-05-09 13:33:06 +04:00
2018-04-17 01:33:13 +03:00
struct delay_class read ;
struct delay_class write ;
2018-04-17 01:33:14 +03:00
struct delay_class flush ;
2007-05-09 13:33:06 +04:00
2018-04-17 01:33:13 +03:00
int argc ;
2007-05-09 13:33:06 +04:00
} ;
2007-07-12 20:26:32 +04:00
struct dm_delay_info {
2007-05-09 13:33:06 +04:00
struct delay_c * context ;
2018-04-17 01:33:13 +03:00
struct delay_class * class ;
2007-05-09 13:33:06 +04:00
struct list_head list ;
unsigned long expires ;
} ;
static DEFINE_MUTEX ( delayed_bios_lock ) ;
2017-10-17 03:01:48 +03:00
static void handle_delayed_timer ( struct timer_list * t )
2007-05-09 13:33:06 +04:00
{
2017-10-17 03:01:48 +03:00
struct delay_c * dc = from_timer ( dc , t , delay_timer ) ;
2007-05-09 13:33:06 +04:00
2013-11-16 01:12:20 +04:00
queue_work ( dc - > kdelayd_wq , & dc - > flush_expired_bios ) ;
2007-05-09 13:33:06 +04:00
}
static void queue_timeout ( struct delay_c * dc , unsigned long expires )
{
2007-07-12 20:26:47 +04:00
mutex_lock ( & dc - > timer_lock ) ;
2007-05-09 13:33:06 +04:00
if ( ! timer_pending ( & dc - > delay_timer ) | | expires < dc - > delay_timer . expires )
mod_timer ( & dc - > delay_timer , expires ) ;
2007-07-12 20:26:47 +04:00
mutex_unlock ( & dc - > timer_lock ) ;
2007-05-09 13:33:06 +04:00
}
2023-10-20 14:46:05 +03:00
static inline bool delay_is_fast ( struct delay_c * dc )
{
return ! ! dc - > worker ;
}
2023-11-17 20:22:47 +03:00
static void flush_bios ( struct bio * bio )
{
struct bio * n ;
while ( bio ) {
n = bio - > bi_next ;
bio - > bi_next = NULL ;
dm_submit_bio_remap ( bio , NULL ) ;
bio = n ;
}
}
2023-11-17 20:24:04 +03:00
static void flush_delayed_bios ( struct delay_c * dc , bool flush_all )
2023-10-20 14:46:05 +03:00
{
struct dm_delay_info * delayed , * next ;
2023-11-17 20:22:47 +03:00
struct bio_list flush_bio_list ;
2023-11-17 20:24:04 +03:00
unsigned long next_expires = 0 ;
bool start_timer = false ;
2023-11-17 20:22:47 +03:00
bio_list_init ( & flush_bio_list ) ;
2023-10-20 14:46:05 +03:00
mutex_lock ( & delayed_bios_lock ) ;
list_for_each_entry_safe ( delayed , next , & dc - > delayed_bios , list ) {
2023-11-17 20:24:04 +03:00
cond_resched ( ) ;
2023-10-20 14:46:05 +03:00
if ( flush_all | | time_after_eq ( jiffies , delayed - > expires ) ) {
struct bio * bio = dm_bio_from_per_bio_data ( delayed ,
sizeof ( struct dm_delay_info ) ) ;
list_del ( & delayed - > list ) ;
2023-11-17 20:22:47 +03:00
bio_list_add ( & flush_bio_list , bio ) ;
2023-10-20 14:46:05 +03:00
delayed - > class - > ops - - ;
2023-11-17 20:24:04 +03:00
continue ;
}
if ( ! delay_is_fast ( dc ) ) {
if ( ! start_timer ) {
start_timer = true ;
next_expires = delayed - > expires ;
} else {
next_expires = min ( next_expires , delayed - > expires ) ;
}
2023-10-20 14:46:05 +03:00
}
}
mutex_unlock ( & delayed_bios_lock ) ;
2023-11-17 20:22:47 +03:00
2023-11-17 20:24:04 +03:00
if ( start_timer )
queue_timeout ( dc , next_expires ) ;
2023-11-17 20:22:47 +03:00
flush_bios ( bio_list_get ( & flush_bio_list ) ) ;
2023-10-20 14:46:05 +03:00
}
static int flush_worker_fn ( void * data )
{
struct delay_c * dc = data ;
2023-11-17 20:22:47 +03:00
while ( ! kthread_should_stop ( ) ) {
2023-11-17 20:24:04 +03:00
flush_delayed_bios ( dc , false ) ;
2023-11-17 20:22:47 +03:00
mutex_lock ( & delayed_bios_lock ) ;
2023-10-20 14:46:05 +03:00
if ( unlikely ( list_empty ( & dc - > delayed_bios ) ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
2023-11-17 20:22:47 +03:00
mutex_unlock ( & delayed_bios_lock ) ;
2023-10-20 14:46:05 +03:00
schedule ( ) ;
2023-11-17 20:22:47 +03:00
} else {
mutex_unlock ( & delayed_bios_lock ) ;
2023-10-20 14:46:05 +03:00
cond_resched ( ) ;
2023-11-17 20:22:47 +03:00
}
2023-10-20 14:46:05 +03:00
}
return 0 ;
}
2007-05-09 13:33:06 +04:00
static void flush_expired_bios ( struct work_struct * work )
{
struct delay_c * dc ;
dc = container_of ( work , struct delay_c , flush_expired_bios ) ;
2023-11-17 20:24:04 +03:00
flush_delayed_bios ( dc , false ) ;
2007-05-09 13:33:06 +04:00
}
2018-04-17 01:33:13 +03:00
static void delay_dtr ( struct dm_target * ti )
{
struct delay_c * dc = ti - > private ;
2019-04-25 19:07:54 +03:00
if ( dc - > kdelayd_wq )
destroy_workqueue ( dc - > kdelayd_wq ) ;
2018-04-17 01:33:13 +03:00
if ( dc - > read . dev )
dm_put_device ( ti , dc - > read . dev ) ;
if ( dc - > write . dev )
dm_put_device ( ti , dc - > write . dev ) ;
2018-04-17 01:33:14 +03:00
if ( dc - > flush . dev )
dm_put_device ( ti , dc - > flush . dev ) ;
2023-10-20 14:46:05 +03:00
if ( dc - > worker )
kthread_stop ( dc - > worker ) ;
2018-04-17 01:33:13 +03:00
2023-11-17 20:22:47 +03:00
mutex_destroy ( & dc - > timer_lock ) ;
2018-04-17 01:33:13 +03:00
kfree ( dc ) ;
}
static int delay_class_ctr ( struct dm_target * ti , struct delay_class * c , char * * argv )
{
int ret ;
unsigned long long tmpll ;
char dummy ;
2018-11-08 00:24:55 +03:00
if ( sscanf ( argv [ 1 ] , " %llu%c " , & tmpll , & dummy ) ! = 1 | | tmpll ! = ( sector_t ) tmpll ) {
2018-04-17 01:33:13 +03:00
ti - > error = " Invalid device sector " ;
return - EINVAL ;
}
c - > start = tmpll ;
if ( sscanf ( argv [ 2 ] , " %u%c " , & c - > delay , & dummy ) ! = 1 ) {
ti - > error = " Invalid delay " ;
return - EINVAL ;
}
ret = dm_get_device ( ti , argv [ 0 ] , dm_table_get_mode ( ti - > table ) , & c - > dev ) ;
if ( ret ) {
ti - > error = " Device lookup failed " ;
return ret ;
}
return 0 ;
}
2007-05-09 13:33:06 +04:00
/*
* Mapping parameters :
* < device > < offset > < delay > [ < write_device > < write_offset > < write_delay > ]
*
* With separate write parameters , the first set is only used for reads .
2015-10-27 22:38:56 +03:00
* Offsets are specified in sectors .
2007-05-09 13:33:06 +04:00
* Delays are specified in milliseconds .
*/
static int delay_ctr ( struct dm_target * ti , unsigned int argc , char * * argv )
{
struct delay_c * dc ;
2015-07-31 16:20:36 +03:00
int ret ;
2023-10-20 14:46:05 +03:00
unsigned int max_delay ;
2007-05-09 13:33:06 +04:00
2018-04-17 01:33:14 +03:00
if ( argc ! = 3 & & argc ! = 6 & & argc ! = 9 ) {
ti - > error = " Requires exactly 3, 6 or 9 arguments " ;
2007-05-09 13:33:06 +04:00
return - EINVAL ;
}
2018-04-17 01:33:13 +03:00
dc = kzalloc ( sizeof ( * dc ) , GFP_KERNEL ) ;
2007-05-09 13:33:06 +04:00
if ( ! dc ) {
ti - > error = " Cannot allocate context " ;
return - ENOMEM ;
}
2018-04-17 01:33:13 +03:00
ti - > private = dc ;
INIT_LIST_HEAD ( & dc - > delayed_bios ) ;
2023-11-17 20:22:47 +03:00
mutex_init ( & dc - > timer_lock ) ;
2023-11-17 20:21:14 +03:00
dc - > may_delay = true ;
2018-04-17 01:33:13 +03:00
dc - > argc = argc ;
2007-05-09 13:33:06 +04:00
2018-04-17 01:33:13 +03:00
ret = delay_class_ctr ( ti , & dc - > read , argv ) ;
if ( ret )
2007-05-09 13:33:06 +04:00
goto bad ;
2023-10-20 14:46:05 +03:00
max_delay = dc - > read . delay ;
2007-05-09 13:33:06 +04:00
2018-04-17 01:33:13 +03:00
if ( argc = = 3 ) {
ret = delay_class_ctr ( ti , & dc - > write , argv ) ;
2018-04-17 01:33:14 +03:00
if ( ret )
goto bad ;
ret = delay_class_ctr ( ti , & dc - > flush , argv ) ;
2018-04-17 01:33:13 +03:00
if ( ret )
goto bad ;
2023-10-20 14:46:05 +03:00
max_delay = max ( max_delay , dc - > write . delay ) ;
max_delay = max ( max_delay , dc - > flush . delay ) ;
2007-05-09 13:33:06 +04:00
goto out ;
}
2018-04-17 01:33:13 +03:00
ret = delay_class_ctr ( ti , & dc - > write , argv + 3 ) ;
if ( ret )
goto bad ;
2018-04-17 01:33:14 +03:00
if ( argc = = 6 ) {
ret = delay_class_ctr ( ti , & dc - > flush , argv + 3 ) ;
if ( ret )
goto bad ;
2023-10-20 14:46:05 +03:00
max_delay = max ( max_delay , dc - > flush . delay ) ;
2018-04-17 01:33:14 +03:00
goto out ;
}
ret = delay_class_ctr ( ti , & dc - > flush , argv + 6 ) ;
if ( ret )
goto bad ;
2023-10-20 14:46:05 +03:00
max_delay = max ( max_delay , dc - > flush . delay ) ;
2007-05-09 13:33:06 +04:00
out :
2023-10-20 14:46:05 +03:00
if ( max_delay < 50 ) {
/*
* In case of small requested delays , use kthread instead of
* timers and workqueue to achieve better latency .
*/
dc - > worker = kthread_create ( & flush_worker_fn , dc ,
" dm-delay-flush-worker " ) ;
if ( IS_ERR ( dc - > worker ) ) {
ret = PTR_ERR ( dc - > worker ) ;
2023-11-17 20:22:47 +03:00
dc - > worker = NULL ;
2023-10-20 14:46:05 +03:00
goto bad ;
}
} else {
timer_setup ( & dc - > delay_timer , handle_delayed_timer , 0 ) ;
INIT_WORK ( & dc - > flush_expired_bios , flush_expired_bios ) ;
dc - > kdelayd_wq = alloc_workqueue ( " kdelayd " , WQ_MEM_RECLAIM , 0 ) ;
if ( ! dc - > kdelayd_wq ) {
ret = - EINVAL ;
DMERR ( " Couldn't start kdelayd " ) ;
goto bad ;
}
2013-11-16 01:12:20 +04:00
}
2013-03-02 02:45:47 +04:00
ti - > num_flush_bios = 1 ;
ti - > num_discard_bios = 1 ;
2022-02-18 07:40:37 +03:00
ti - > accounts_remapped_io = true ;
2016-01-31 21:28:26 +03:00
ti - > per_io_data_size = sizeof ( struct dm_delay_info ) ;
2007-05-09 13:33:06 +04:00
return 0 ;
bad :
2018-04-17 01:33:13 +03:00
delay_dtr ( ti ) ;
2015-07-31 16:20:36 +03:00
return ret ;
2007-05-09 13:33:06 +04:00
}
2018-04-17 01:33:13 +03:00
static int delay_bio ( struct delay_c * dc , struct delay_class * c , struct bio * bio )
2007-05-09 13:33:06 +04:00
{
2007-07-12 20:26:32 +04:00
struct dm_delay_info * delayed ;
2007-05-09 13:33:06 +04:00
unsigned long expires = 0 ;
2023-11-17 20:21:14 +03:00
if ( ! c - > delay )
2015-10-27 22:38:55 +03:00
return DM_MAPIO_REMAPPED ;
2007-05-09 13:33:06 +04:00
2013-11-16 01:12:51 +04:00
delayed = dm_per_bio_data ( bio , sizeof ( struct dm_delay_info ) ) ;
2007-05-09 13:33:06 +04:00
delayed - > context = dc ;
2018-04-17 01:33:13 +03:00
delayed - > expires = expires = jiffies + msecs_to_jiffies ( c - > delay ) ;
2007-05-09 13:33:06 +04:00
mutex_lock ( & delayed_bios_lock ) ;
2023-11-17 20:21:14 +03:00
if ( unlikely ( ! dc - > may_delay ) ) {
mutex_unlock ( & delayed_bios_lock ) ;
return DM_MAPIO_REMAPPED ;
}
2018-04-17 01:33:13 +03:00
c - > ops + + ;
2007-05-09 13:33:06 +04:00
list_add_tail ( & delayed - > list , & dc - > delayed_bios ) ;
mutex_unlock ( & delayed_bios_lock ) ;
2023-10-20 14:46:05 +03:00
if ( delay_is_fast ( dc ) )
wake_up_process ( dc - > worker ) ;
else
queue_timeout ( dc , expires ) ;
2007-05-09 13:33:06 +04:00
2015-10-27 22:38:55 +03:00
return DM_MAPIO_SUBMITTED ;
2007-05-09 13:33:06 +04:00
}
static void delay_presuspend ( struct dm_target * ti )
{
struct delay_c * dc = ti - > private ;
2023-11-17 20:21:14 +03:00
mutex_lock ( & delayed_bios_lock ) ;
dc - > may_delay = false ;
mutex_unlock ( & delayed_bios_lock ) ;
2023-10-20 14:46:05 +03:00
2023-11-17 20:24:04 +03:00
if ( ! delay_is_fast ( dc ) )
2023-10-20 14:46:05 +03:00
del_timer_sync ( & dc - > delay_timer ) ;
2023-11-17 20:24:04 +03:00
flush_delayed_bios ( dc , true ) ;
2007-05-09 13:33:06 +04:00
}
static void delay_resume ( struct dm_target * ti )
{
struct delay_c * dc = ti - > private ;
2023-11-17 20:21:14 +03:00
dc - > may_delay = true ;
2007-05-09 13:33:06 +04:00
}
2012-12-22 00:23:41 +04:00
static int delay_map ( struct dm_target * ti , struct bio * bio )
2007-05-09 13:33:06 +04:00
{
struct delay_c * dc = ti - > private ;
2018-04-17 01:33:13 +03:00
struct delay_class * c ;
struct dm_delay_info * delayed = dm_per_bio_data ( bio , sizeof ( struct dm_delay_info ) ) ;
2007-05-09 13:33:06 +04:00
2018-04-17 01:33:13 +03:00
if ( bio_data_dir ( bio ) = = WRITE ) {
2018-04-17 01:33:14 +03:00
if ( unlikely ( bio - > bi_opf & REQ_PREFLUSH ) )
c = & dc - > flush ;
else
c = & dc - > write ;
2018-04-17 01:33:13 +03:00
} else {
c = & dc - > read ;
2007-05-09 13:33:06 +04:00
}
2018-04-17 01:33:13 +03:00
delayed - > class = c ;
bio_set_dev ( bio , c - > dev - > bdev ) ;
2022-03-30 20:52:10 +03:00
bio - > bi_iter . bi_sector = c - > start + dm_target_offset ( ti , bio - > bi_iter . bi_sector ) ;
2007-05-09 13:33:06 +04:00
2018-04-17 01:33:13 +03:00
return delay_bio ( dc , c , bio ) ;
2007-05-09 13:33:06 +04:00
}
2018-04-17 01:33:13 +03:00
# define DMEMIT_DELAY_CLASS(c) \
DMEMIT ( " %s %llu %u " , ( c ) - > dev - > name , ( unsigned long long ) ( c ) - > start , ( c ) - > delay )
2013-03-02 02:45:44 +04:00
static void delay_status ( struct dm_target * ti , status_type_t type ,
2023-01-25 23:14:58 +03:00
unsigned int status_flags , char * result , unsigned int maxlen )
2007-05-09 13:33:06 +04:00
{
struct delay_c * dc = ti - > private ;
int sz = 0 ;
switch ( type ) {
case STATUSTYPE_INFO :
2018-04-17 01:33:14 +03:00
DMEMIT ( " %u %u %u " , dc - > read . ops , dc - > write . ops , dc - > flush . ops ) ;
2007-05-09 13:33:06 +04:00
break ;
case STATUSTYPE_TABLE :
2018-04-17 01:33:13 +03:00
DMEMIT_DELAY_CLASS ( & dc - > read ) ;
if ( dc - > argc > = 6 ) {
DMEMIT ( " " ) ;
DMEMIT_DELAY_CLASS ( & dc - > write ) ;
}
2018-04-17 01:33:14 +03:00
if ( dc - > argc > = 9 ) {
DMEMIT ( " " ) ;
DMEMIT_DELAY_CLASS ( & dc - > flush ) ;
}
2007-05-09 13:33:06 +04:00
break ;
2021-07-13 03:49:03 +03:00
case STATUSTYPE_IMA :
* result = ' \0 ' ;
break ;
2007-05-09 13:33:06 +04:00
}
}
2009-06-22 13:12:33 +04:00
static int delay_iterate_devices ( struct dm_target * ti ,
iterate_devices_callout_fn fn , void * data )
{
struct delay_c * dc = ti - > private ;
int ret = 0 ;
2018-04-17 01:33:13 +03:00
ret = fn ( ti , dc - > read . dev , dc - > read . start , ti - > len , data ) ;
if ( ret )
goto out ;
ret = fn ( ti , dc - > write . dev , dc - > write . start , ti - > len , data ) ;
2009-06-22 13:12:33 +04:00
if ( ret )
goto out ;
2018-04-17 01:33:14 +03:00
ret = fn ( ti , dc - > flush . dev , dc - > flush . start , ti - > len , data ) ;
if ( ret )
goto out ;
2009-06-22 13:12:33 +04:00
out :
return ret ;
}
2007-05-09 13:33:06 +04:00
static struct target_type delay_target = {
. name = " delay " ,
2023-10-20 14:46:05 +03:00
. version = { 1 , 4 , 0 } ,
2017-04-18 23:51:48 +03:00
. features = DM_TARGET_PASSES_INTEGRITY ,
2007-05-09 13:33:06 +04:00
. module = THIS_MODULE ,
. ctr = delay_ctr ,
. dtr = delay_dtr ,
. map = delay_map ,
. presuspend = delay_presuspend ,
. resume = delay_resume ,
. status = delay_status ,
2009-06-22 13:12:33 +04:00
. iterate_devices = delay_iterate_devices ,
2007-05-09 13:33:06 +04:00
} ;
2023-04-09 19:43:37 +03:00
module_dm ( delay ) ;
2007-05-09 13:33:06 +04:00
MODULE_DESCRIPTION ( DM_NAME " delay target " ) ;
MODULE_AUTHOR ( " Heinz Mauelshagen <mauelshagen@redhat.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;