2005-04-17 02:20:36 +04:00
/*
* Deadline i / o scheduler .
*
2006-09-04 17:41:16 +04:00
* Copyright ( C ) 2002 Jens Axboe < axboe @ kernel . dk >
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/blkdev.h>
# include <linux/elevator.h>
# include <linux/bio.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/compiler.h>
# include <linux/rbtree.h>
/*
* See Documentation / block / deadline - iosched . txt
*/
2006-01-06 11:46:02 +03:00
static const int read_expire = HZ / 2 ; /* max time before a read is submitted. */
static const int write_expire = 5 * HZ ; /* ditto for writes, these limits are SOFT! */
static const int writes_starved = 2 ; /* max times reads can starve a write */
static const int fifo_batch = 16 ; /* # of sequential requests treated as one
2005-04-17 02:20:36 +04:00
by the above parameters . For throughput . */
struct deadline_data {
/*
* run time data
*/
/*
* requests ( deadline_rq s ) are present on both sort_list and fifo_list
*/
struct rb_root sort_list [ 2 ] ;
struct list_head fifo_list [ 2 ] ;
/*
* next in sort order . read , write or both are NULL
*/
2006-07-13 14:36:41 +04:00
struct request * next_rq [ 2 ] ;
2005-04-17 02:20:36 +04:00
unsigned int batching ; /* number of sequential requests made */
sector_t last_sector ; /* head position */
unsigned int starved ; /* times reads have starved writes */
/*
* settings that change how the i / o scheduler behaves
*/
int fifo_expire [ 2 ] ;
int fifo_batch ;
int writes_starved ;
int front_merges ;
} ;
2006-07-13 14:36:41 +04:00
static void deadline_move_request ( struct deadline_data * , struct request * ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:34:24 +04:00
# define RQ_RB_ROOT(dd, rq) (&(dd)->sort_list[rq_data_dir((rq))])
2005-04-17 02:20:36 +04:00
static void
2006-07-13 14:36:41 +04:00
deadline_add_rq_rb ( struct deadline_data * dd , struct request * rq )
2005-04-17 02:20:36 +04:00
{
2006-07-13 14:34:24 +04:00
struct rb_root * root = RQ_RB_ROOT ( dd , rq ) ;
struct request * __alias ;
2005-04-17 02:20:36 +04:00
retry :
2006-07-13 14:34:24 +04:00
__alias = elv_rb_add ( root , rq ) ;
if ( unlikely ( __alias ) ) {
2006-07-13 14:36:41 +04:00
deadline_move_request ( dd , __alias ) ;
2006-07-13 14:34:24 +04:00
goto retry ;
2005-04-17 02:20:36 +04:00
}
}
static inline void
2006-07-13 14:36:41 +04:00
deadline_del_rq_rb ( struct deadline_data * dd , struct request * rq )
2005-04-17 02:20:36 +04:00
{
2006-07-13 14:34:24 +04:00
const int data_dir = rq_data_dir ( rq ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
if ( dd - > next_rq [ data_dir ] = = rq ) {
2006-07-13 14:34:24 +04:00
struct rb_node * rbnext = rb_next ( & rq - > rb_node ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
dd - > next_rq [ data_dir ] = NULL ;
2005-04-17 02:20:36 +04:00
if ( rbnext )
2006-07-13 14:36:41 +04:00
dd - > next_rq [ data_dir ] = rb_entry_rq ( rbnext ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-13 14:34:24 +04:00
elv_rb_del ( RQ_RB_ROOT ( dd , rq ) , rq ) ;
2005-04-17 02:20:36 +04:00
}
/*
2006-07-13 14:36:41 +04:00
* add rq to rbtree and fifo
2005-04-17 02:20:36 +04:00
*/
2005-10-20 18:42:29 +04:00
static void
2005-04-17 02:20:36 +04:00
deadline_add_request ( struct request_queue * q , struct request * rq )
{
struct deadline_data * dd = q - > elevator - > elevator_data ;
2006-07-13 14:36:41 +04:00
const int data_dir = rq_data_dir ( rq ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
deadline_add_rq_rb ( dd , rq ) ;
2006-07-28 11:23:08 +04:00
2005-04-17 02:20:36 +04:00
/*
* set expire time ( only used for reads ) and add to fifo list
*/
2006-07-13 14:36:41 +04:00
rq_set_fifo_time ( rq , jiffies + dd - > fifo_expire [ data_dir ] ) ;
list_add_tail ( & rq - > queuelist , & dd - > fifo_list [ data_dir ] ) ;
2005-04-17 02:20:36 +04:00
}
/*
2006-07-28 11:23:08 +04:00
* remove rq from rbtree and fifo .
2005-04-17 02:20:36 +04:00
*/
static void deadline_remove_request ( request_queue_t * q , struct request * rq )
{
2005-10-20 18:42:29 +04:00
struct deadline_data * dd = q - > elevator - > elevator_data ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
rq_fifo_clear ( rq ) ;
deadline_del_rq_rb ( dd , rq ) ;
2005-04-17 02:20:36 +04:00
}
static int
deadline_merge ( request_queue_t * q , struct request * * req , struct bio * bio )
{
struct deadline_data * dd = q - > elevator - > elevator_data ;
struct request * __rq ;
int ret ;
/*
* check for front merge
*/
if ( dd - > front_merges ) {
2006-07-13 14:34:24 +04:00
sector_t sector = bio - > bi_sector + bio_sectors ( bio ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:34:24 +04:00
__rq = elv_rb_find ( & dd - > sort_list [ bio_data_dir ( bio ) ] , sector ) ;
2005-04-17 02:20:36 +04:00
if ( __rq ) {
2006-07-13 14:34:24 +04:00
BUG_ON ( sector ! = __rq - > sector ) ;
2005-04-17 02:20:36 +04:00
if ( elv_rq_merge_ok ( __rq , bio ) ) {
ret = ELEVATOR_FRONT_MERGE ;
goto out ;
}
}
}
return ELEVATOR_NO_MERGE ;
out :
* req = __rq ;
return ret ;
}
2006-07-13 14:34:24 +04:00
static void deadline_merged_request ( request_queue_t * q , struct request * req ,
int type )
2005-04-17 02:20:36 +04:00
{
struct deadline_data * dd = q - > elevator - > elevator_data ;
/*
* if the merge was a front merge , we need to reposition request
*/
2006-07-13 14:34:24 +04:00
if ( type = = ELEVATOR_FRONT_MERGE ) {
elv_rb_del ( RQ_RB_ROOT ( dd , req ) , req ) ;
2006-07-13 14:36:41 +04:00
deadline_add_rq_rb ( dd , req ) ;
2005-04-17 02:20:36 +04:00
}
}
static void
deadline_merged_requests ( request_queue_t * q , struct request * req ,
struct request * next )
{
/*
2006-07-13 14:36:41 +04:00
* if next expires before rq , assign its expire time to rq
* and move into next position ( next will be deleted ) in fifo
2005-04-17 02:20:36 +04:00
*/
2006-07-13 14:36:41 +04:00
if ( ! list_empty ( & req - > queuelist ) & & ! list_empty ( & next - > queuelist ) ) {
if ( time_before ( rq_fifo_time ( next ) , rq_fifo_time ( req ) ) ) {
list_move ( & req - > queuelist , & next - > queuelist ) ;
rq_set_fifo_time ( req , rq_fifo_time ( next ) ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* kill knowledge of next , this one is a goner
*/
deadline_remove_request ( q , next ) ;
}
/*
* move request from sort list to dispatch queue .
*/
static inline void
2006-07-13 14:36:41 +04:00
deadline_move_to_dispatch ( struct deadline_data * dd , struct request * rq )
2005-04-17 02:20:36 +04:00
{
2006-07-13 14:36:41 +04:00
request_queue_t * q = rq - > q ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
deadline_remove_request ( q , rq ) ;
elv_dispatch_add_tail ( q , rq ) ;
2005-04-17 02:20:36 +04:00
}
/*
* move an entry to dispatch queue
*/
static void
2006-07-13 14:36:41 +04:00
deadline_move_request ( struct deadline_data * dd , struct request * rq )
2005-04-17 02:20:36 +04:00
{
2006-07-13 14:34:24 +04:00
const int data_dir = rq_data_dir ( rq ) ;
struct rb_node * rbnext = rb_next ( & rq - > rb_node ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
dd - > next_rq [ READ ] = NULL ;
dd - > next_rq [ WRITE ] = NULL ;
2005-04-17 02:20:36 +04:00
if ( rbnext )
2006-07-13 14:36:41 +04:00
dd - > next_rq [ data_dir ] = rb_entry_rq ( rbnext ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
dd - > last_sector = rq - > sector + rq - > nr_sectors ;
2005-04-17 02:20:36 +04:00
/*
* take it off the sort and fifo list , move
* to dispatch queue
*/
2006-07-13 14:36:41 +04:00
deadline_move_to_dispatch ( dd , rq ) ;
2005-04-17 02:20:36 +04:00
}
/*
* deadline_check_fifo returns 0 if there are no expired reads on the fifo ,
* 1 otherwise . Requires ! list_empty ( & dd - > fifo_list [ data_dir ] )
*/
static inline int deadline_check_fifo ( struct deadline_data * dd , int ddir )
{
2006-07-13 14:36:41 +04:00
struct request * rq = rq_entry_fifo ( dd - > fifo_list [ ddir ] . next ) ;
2005-04-17 02:20:36 +04:00
/*
2006-07-13 14:36:41 +04:00
* rq is expired !
2005-04-17 02:20:36 +04:00
*/
2006-07-13 14:36:41 +04:00
if ( time_after ( jiffies , rq_fifo_time ( rq ) ) )
2005-04-17 02:20:36 +04:00
return 1 ;
return 0 ;
}
/*
* deadline_dispatch_requests selects the best request according to
* read / write expire , fifo_batch , etc
*/
2005-10-20 18:42:29 +04:00
static int deadline_dispatch_requests ( request_queue_t * q , int force )
2005-04-17 02:20:36 +04:00
{
2005-10-20 18:42:29 +04:00
struct deadline_data * dd = q - > elevator - > elevator_data ;
2005-04-17 02:20:36 +04:00
const int reads = ! list_empty ( & dd - > fifo_list [ READ ] ) ;
const int writes = ! list_empty ( & dd - > fifo_list [ WRITE ] ) ;
2006-07-13 14:36:41 +04:00
struct request * rq ;
2005-09-07 02:17:20 +04:00
int data_dir ;
2005-04-17 02:20:36 +04:00
/*
* batches are currently reads XOR writes
*/
2006-07-13 14:36:41 +04:00
if ( dd - > next_rq [ WRITE ] )
rq = dd - > next_rq [ WRITE ] ;
2005-09-10 00:02:12 +04:00
else
2006-07-13 14:36:41 +04:00
rq = dd - > next_rq [ READ ] ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
if ( rq ) {
2005-04-17 02:20:36 +04:00
/* we have a "next request" */
2006-07-13 14:36:41 +04:00
if ( dd - > last_sector ! = rq - > sector )
2005-04-17 02:20:36 +04:00
/* end the batch on a non sequential request */
dd - > batching + = dd - > fifo_batch ;
if ( dd - > batching < dd - > fifo_batch )
/* we are still entitled to batch */
goto dispatch_request ;
}
/*
* at this point we are not running a batch . select the appropriate
* data direction ( read / write )
*/
if ( reads ) {
2006-06-21 11:36:18 +04:00
BUG_ON ( RB_EMPTY_ROOT ( & dd - > sort_list [ READ ] ) ) ;
2005-04-17 02:20:36 +04:00
if ( writes & & ( dd - > starved + + > = dd - > writes_starved ) )
goto dispatch_writes ;
data_dir = READ ;
goto dispatch_find_request ;
}
/*
* there are either no reads or writes have been starved
*/
if ( writes ) {
dispatch_writes :
2006-06-21 11:36:18 +04:00
BUG_ON ( RB_EMPTY_ROOT ( & dd - > sort_list [ WRITE ] ) ) ;
2005-04-17 02:20:36 +04:00
dd - > starved = 0 ;
data_dir = WRITE ;
goto dispatch_find_request ;
}
return 0 ;
dispatch_find_request :
/*
* we are not running a batch , find best request for selected data_dir
*/
if ( deadline_check_fifo ( dd , data_dir ) ) {
/* An expired request exists - satisfy it */
dd - > batching = 0 ;
2006-07-13 14:36:41 +04:00
rq = rq_entry_fifo ( dd - > fifo_list [ data_dir ] . next ) ;
2005-04-17 02:20:36 +04:00
2006-07-13 14:36:41 +04:00
} else if ( dd - > next_rq [ data_dir ] ) {
2005-04-17 02:20:36 +04:00
/*
* The last req was the same dir and we have a next request in
* sort order . No expired requests so continue on from here .
*/
2006-07-13 14:36:41 +04:00
rq = dd - > next_rq [ data_dir ] ;
2005-04-17 02:20:36 +04:00
} else {
2006-07-13 14:36:41 +04:00
struct rb_node * node ;
2005-04-17 02:20:36 +04:00
/*
* The last req was the other direction or we have run out of
* higher - sectored requests . Go back to the lowest sectored
* request ( 1 way elevator ) and start a new batch .
*/
dd - > batching = 0 ;
2006-07-13 14:36:41 +04:00
node = rb_first ( & dd - > sort_list [ data_dir ] ) ;
if ( node )
rq = rb_entry_rq ( node ) ;
2005-04-17 02:20:36 +04:00
}
dispatch_request :
/*
2006-07-13 14:36:41 +04:00
* rq is the selected appropriate request .
2005-04-17 02:20:36 +04:00
*/
dd - > batching + + ;
2006-07-13 14:36:41 +04:00
deadline_move_request ( dd , rq ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
static int deadline_queue_empty ( request_queue_t * q )
{
struct deadline_data * dd = q - > elevator - > elevator_data ;
2005-10-20 18:42:29 +04:00
return list_empty ( & dd - > fifo_list [ WRITE ] )
& & list_empty ( & dd - > fifo_list [ READ ] ) ;
2005-04-17 02:20:36 +04:00
}
static void deadline_exit_queue ( elevator_t * e )
{
struct deadline_data * dd = e - > elevator_data ;
BUG_ON ( ! list_empty ( & dd - > fifo_list [ READ ] ) ) ;
BUG_ON ( ! list_empty ( & dd - > fifo_list [ WRITE ] ) ) ;
kfree ( dd ) ;
}
/*
2006-07-13 14:36:41 +04:00
* initialize elevator private data ( deadline_data ) .
2005-04-17 02:20:36 +04:00
*/
2006-12-01 12:42:33 +03:00
static void * deadline_init_queue ( request_queue_t * q )
2005-04-17 02:20:36 +04:00
{
struct deadline_data * dd ;
2005-06-23 11:08:19 +04:00
dd = kmalloc_node ( sizeof ( * dd ) , GFP_KERNEL , q - > node ) ;
2005-04-17 02:20:36 +04:00
if ( ! dd )
2006-06-08 10:49:06 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
memset ( dd , 0 , sizeof ( * dd ) ) ;
INIT_LIST_HEAD ( & dd - > fifo_list [ READ ] ) ;
INIT_LIST_HEAD ( & dd - > fifo_list [ WRITE ] ) ;
dd - > sort_list [ READ ] = RB_ROOT ;
dd - > sort_list [ WRITE ] = RB_ROOT ;
dd - > fifo_expire [ READ ] = read_expire ;
dd - > fifo_expire [ WRITE ] = write_expire ;
dd - > writes_starved = writes_starved ;
dd - > front_merges = 1 ;
dd - > fifo_batch = fifo_batch ;
2006-06-08 10:49:06 +04:00
return dd ;
2005-04-17 02:20:36 +04:00
}
/*
* sysfs parts below
*/
static ssize_t
deadline_var_show ( int var , char * page )
{
return sprintf ( page , " %d \n " , var ) ;
}
static ssize_t
deadline_var_store ( int * var , const char * page , size_t count )
{
char * p = ( char * ) page ;
* var = simple_strtol ( p , & p , 10 ) ;
return count ;
}
# define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
2006-03-19 02:35:43 +03:00
static ssize_t __FUNC ( elevator_t * e , char * page ) \
2005-04-17 02:20:36 +04:00
{ \
2006-03-19 02:35:43 +03:00
struct deadline_data * dd = e - > elevator_data ; \
int __data = __VAR ; \
2005-04-17 02:20:36 +04:00
if ( __CONV ) \
__data = jiffies_to_msecs ( __data ) ; \
return deadline_var_show ( __data , ( page ) ) ; \
}
2006-03-19 06:27:18 +03:00
SHOW_FUNCTION ( deadline_read_expire_show , dd - > fifo_expire [ READ ] , 1 ) ;
SHOW_FUNCTION ( deadline_write_expire_show , dd - > fifo_expire [ WRITE ] , 1 ) ;
SHOW_FUNCTION ( deadline_writes_starved_show , dd - > writes_starved , 0 ) ;
SHOW_FUNCTION ( deadline_front_merges_show , dd - > front_merges , 0 ) ;
SHOW_FUNCTION ( deadline_fifo_batch_show , dd - > fifo_batch , 0 ) ;
2005-04-17 02:20:36 +04:00
# undef SHOW_FUNCTION
# define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \
2006-03-19 02:35:43 +03:00
static ssize_t __FUNC ( elevator_t * e , const char * page , size_t count ) \
2005-04-17 02:20:36 +04:00
{ \
2006-03-19 02:35:43 +03:00
struct deadline_data * dd = e - > elevator_data ; \
2005-04-17 02:20:36 +04:00
int __data ; \
int ret = deadline_var_store ( & __data , ( page ) , count ) ; \
if ( __data < ( MIN ) ) \
__data = ( MIN ) ; \
else if ( __data > ( MAX ) ) \
__data = ( MAX ) ; \
if ( __CONV ) \
* ( __PTR ) = msecs_to_jiffies ( __data ) ; \
else \
* ( __PTR ) = __data ; \
return ret ; \
}
2006-03-19 06:27:18 +03:00
STORE_FUNCTION ( deadline_read_expire_store , & dd - > fifo_expire [ READ ] , 0 , INT_MAX , 1 ) ;
STORE_FUNCTION ( deadline_write_expire_store , & dd - > fifo_expire [ WRITE ] , 0 , INT_MAX , 1 ) ;
STORE_FUNCTION ( deadline_writes_starved_store , & dd - > writes_starved , INT_MIN , INT_MAX , 0 ) ;
STORE_FUNCTION ( deadline_front_merges_store , & dd - > front_merges , 0 , 1 , 0 ) ;
STORE_FUNCTION ( deadline_fifo_batch_store , & dd - > fifo_batch , 0 , INT_MAX , 0 ) ;
2005-04-17 02:20:36 +04:00
# undef STORE_FUNCTION
2006-03-19 06:27:18 +03:00
# define DD_ATTR(name) \
__ATTR ( name , S_IRUGO | S_IWUSR , deadline_ # # name # # _show , \
deadline_ # # name # # _store )
static struct elv_fs_entry deadline_attrs [ ] = {
DD_ATTR ( read_expire ) ,
DD_ATTR ( write_expire ) ,
DD_ATTR ( writes_starved ) ,
DD_ATTR ( front_merges ) ,
DD_ATTR ( fifo_batch ) ,
__ATTR_NULL
2005-04-17 02:20:36 +04:00
} ;
static struct elevator_type iosched_deadline = {
. ops = {
. elevator_merge_fn = deadline_merge ,
. elevator_merged_fn = deadline_merged_request ,
. elevator_merge_req_fn = deadline_merged_requests ,
2005-10-20 18:42:29 +04:00
. elevator_dispatch_fn = deadline_dispatch_requests ,
. elevator_add_req_fn = deadline_add_request ,
2005-04-17 02:20:36 +04:00
. elevator_queue_empty_fn = deadline_queue_empty ,
2006-07-13 14:34:24 +04:00
. elevator_former_req_fn = elv_rb_former_request ,
. elevator_latter_req_fn = elv_rb_latter_request ,
2005-04-17 02:20:36 +04:00
. elevator_init_fn = deadline_init_queue ,
. elevator_exit_fn = deadline_exit_queue ,
} ,
2006-03-19 02:35:43 +03:00
. elevator_attrs = deadline_attrs ,
2005-04-17 02:20:36 +04:00
. elevator_name = " deadline " ,
. elevator_owner = THIS_MODULE ,
} ;
static int __init deadline_init ( void )
{
2006-07-13 14:36:41 +04:00
return elv_register ( & iosched_deadline ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit deadline_exit ( void )
{
elv_unregister ( & iosched_deadline ) ;
}
module_init ( deadline_init ) ;
module_exit ( deadline_exit ) ;
MODULE_AUTHOR ( " Jens Axboe " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " deadline IO scheduler " ) ;