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>
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 ;
unsigned delay ;
unsigned ops ;
} ;
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 ;
atomic_t may_delay ;
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
}
static void flush_bios ( struct bio * bio )
{
struct bio * n ;
while ( bio ) {
n = bio - > bi_next ;
bio - > bi_next = NULL ;
2022-03-10 19:45:58 +03:00
dm_submit_bio_remap ( bio , NULL ) ;
2007-05-09 13:33:06 +04:00
bio = n ;
}
}
static struct bio * flush_delayed_bios ( struct delay_c * dc , int flush_all )
{
2007-07-12 20:26:32 +04:00
struct dm_delay_info * delayed , * next ;
2007-05-09 13:33:06 +04:00
unsigned long next_expires = 0 ;
2018-04-17 01:33:13 +03:00
unsigned long start_timer = 0 ;
2007-10-20 01:38:55 +04:00
struct bio_list flush_bios = { } ;
2007-05-09 13:33:06 +04:00
mutex_lock ( & delayed_bios_lock ) ;
list_for_each_entry_safe ( delayed , next , & dc - > delayed_bios , list ) {
if ( flush_all | | time_after_eq ( jiffies , delayed - > expires ) ) {
2013-11-16 01:12:51 +04:00
struct bio * bio = dm_bio_from_per_bio_data ( delayed ,
sizeof ( struct dm_delay_info ) ) ;
2007-05-09 13:33:06 +04:00
list_del ( & delayed - > list ) ;
2013-11-16 01:12:51 +04:00
bio_list_add ( & flush_bios , bio ) ;
2018-04-17 01:33:13 +03:00
delayed - > class - > ops - - ;
2007-05-09 13:33:06 +04:00
continue ;
}
if ( ! start_timer ) {
start_timer = 1 ;
next_expires = delayed - > expires ;
} else
next_expires = min ( next_expires , delayed - > expires ) ;
}
mutex_unlock ( & delayed_bios_lock ) ;
if ( start_timer )
queue_timeout ( dc , next_expires ) ;
return bio_list_get ( & flush_bios ) ;
}
static void flush_expired_bios ( struct work_struct * work )
{
struct delay_c * dc ;
dc = container_of ( work , struct delay_c , flush_expired_bios ) ;
flush_bios ( flush_delayed_bios ( dc , 0 ) ) ;
}
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 ) ;
2018-04-17 01:33:13 +03:00
mutex_destroy ( & dc - > timer_lock ) ;
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 ;
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 ;
timer_setup ( & dc - > delay_timer , handle_delayed_timer , 0 ) ;
INIT_WORK ( & dc - > flush_expired_bios , flush_expired_bios ) ;
INIT_LIST_HEAD ( & dc - > delayed_bios ) ;
mutex_init ( & dc - > timer_lock ) ;
atomic_set ( & dc - > may_delay , 1 ) ;
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 ;
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 ;
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 ;
goto out ;
}
ret = delay_class_ctr ( ti , & dc - > flush , argv + 6 ) ;
if ( ret )
goto bad ;
2007-05-09 13:33:06 +04:00
out :
2013-11-16 01:12:20 +04:00
dc - > kdelayd_wq = alloc_workqueue ( " kdelayd " , WQ_MEM_RECLAIM , 0 ) ;
if ( ! dc - > kdelayd_wq ) {
2018-04-17 01:33:13 +03:00
ret = - EINVAL ;
2013-11-16 01:12:20 +04:00
DMERR ( " Couldn't start kdelayd " ) ;
2018-04-17 01:33:13 +03:00
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 ;
2018-04-17 01:33:13 +03:00
if ( ! c - > delay | | ! atomic_read ( & dc - > may_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 ) ;
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 ) ;
queue_timeout ( dc , expires ) ;
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 ;
atomic_set ( & dc - > may_delay , 0 ) ;
del_timer_sync ( & dc - > delay_timer ) ;
flush_bios ( flush_delayed_bios ( dc , 1 ) ) ;
}
static void delay_resume ( struct dm_target * ti )
{
struct delay_c * dc = ti - > private ;
atomic_set ( & dc - > may_delay , 1 ) ;
}
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 ) ;
if ( bio_sectors ( bio ) )
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 ,
unsigned status_flags , char * result , unsigned 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 " ,
2022-02-18 07:40:37 +03:00
. version = { 1 , 3 , 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
} ;
static int __init dm_delay_init ( void )
{
2013-11-16 01:12:51 +04:00
int r ;
2007-05-09 13:33:06 +04:00
r = dm_register_target ( & delay_target ) ;
if ( r < 0 ) {
DMERR ( " register failed %d " , r ) ;
goto bad_register ;
}
return 0 ;
bad_register :
return r ;
}
static void __exit dm_delay_exit ( void )
{
2009-01-06 06:04:58 +03:00
dm_unregister_target ( & delay_target ) ;
2007-05-09 13:33:06 +04:00
}
/* Module hooks */
module_init ( dm_delay_init ) ;
module_exit ( dm_delay_exit ) ;
MODULE_DESCRIPTION ( DM_NAME " delay target " ) ;
MODULE_AUTHOR ( " Heinz Mauelshagen <mauelshagen@redhat.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;