2006-03-27 13:14:37 +04:00
/*
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2000 Jeff Dike ( jdike @ karaya . com )
* Licensed under the GPL
*/
/* 2001-09-28...2002-04-17
* Partition stuff by James_McMechan @ hotmail . com
* old style ubd by setting UBD_SHIFT to 0
* 2002 - 09 - 27. . .2002 - 10 - 18 massive tinkering for 2.5
* partitions have changed in 2.5
* 2003 - 01 - 29 more tinkering for 2.5 .59 - 1
* This should now address the sysfs problems and has
* the symlink for devfs to allow for booting with
* the common / dev / ubd / discX / . . . names rather than
* only / dev / ubdN / discN this version also has lots of
* clean ups preparing for ubd - many .
* James McMechan
*/
# define MAJOR_NR UBD_MAJOR
# define UBD_SHIFT 4
2007-06-09 00:46:54 +04:00
# include "linux/kernel.h"
2005-04-17 02:20:36 +04:00
# include "linux/module.h"
# include "linux/blkdev.h"
# include "linux/hdreg.h"
# include "linux/init.h"
# include "linux/cdrom.h"
# include "linux/proc_fs.h"
# include "linux/ctype.h"
# include "linux/capability.h"
# include "linux/mm.h"
# include "linux/vmalloc.h"
# include "linux/blkpg.h"
# include "linux/genhd.h"
# include "linux/spinlock.h"
2005-10-29 22:07:23 +04:00
# include "linux/platform_device.h"
2007-10-24 15:07:11 +04:00
# include "linux/scatterlist.h"
2005-04-17 02:20:36 +04:00
# include "asm/segment.h"
# include "asm/uaccess.h"
# include "asm/irq.h"
# include "asm/types.h"
# include "asm/tlbflush.h"
# include "mem_user.h"
# include "kern_util.h"
# include "kern.h"
# include "mconsole_kern.h"
# include "init.h"
# include "irq_user.h"
# include "irq_kern.h"
# include "ubd_user.h"
2008-02-05 09:30:46 +03:00
# include "kern_util.h"
2005-04-17 02:20:36 +04:00
# include "os.h"
# include "mem.h"
# include "mem_kern.h"
# include "cow.h"
2005-05-21 00:59:11 +04:00
enum ubd_req { UBD_READ , UBD_WRITE } ;
2005-04-17 02:20:36 +04:00
struct io_thread_req {
2007-02-10 12:44:16 +03:00
struct request * req ;
2005-10-11 07:10:32 +04:00
enum ubd_req op ;
2005-04-17 02:20:36 +04:00
int fds [ 2 ] ;
unsigned long offsets [ 2 ] ;
unsigned long long offset ;
unsigned long length ;
char * buffer ;
int sectorsize ;
2005-10-11 07:10:32 +04:00
unsigned long sector_mask ;
unsigned long long cow_offset ;
unsigned long bitmap_words [ 2 ] ;
2005-04-17 02:20:36 +04:00
int error ;
} ;
2006-03-27 13:14:37 +04:00
extern int open_ubd_file ( char * file , struct openflags * openflags , int shared ,
2005-04-17 02:20:36 +04:00
char * * backing_file_out , int * bitmap_offset_out ,
unsigned long * bitmap_len_out , int * data_offset_out ,
int * create_cow_out ) ;
extern int create_cow_file ( char * cow_file , char * backing_file ,
struct openflags flags , int sectorsize ,
int alignment , int * bitmap_offset_out ,
unsigned long * bitmap_len_out ,
int * data_offset_out ) ;
extern int read_cow_bitmap ( int fd , void * buf , int offset , int len ) ;
2005-10-11 07:10:32 +04:00
extern void do_io ( struct io_thread_req * req ) ;
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
static inline int ubd_test_bit ( __u64 bit , unsigned char * data )
2005-04-17 02:20:36 +04:00
{
__u64 n ;
int bits , off ;
2005-10-11 07:10:32 +04:00
bits = sizeof ( data [ 0 ] ) * 8 ;
2005-04-17 02:20:36 +04:00
n = bit / bits ;
off = bit % bits ;
2007-05-07 01:51:41 +04:00
return ( data [ n ] & ( 1 < < off ) ) ! = 0 ;
2005-04-17 02:20:36 +04:00
}
2005-10-11 07:10:32 +04:00
static inline void ubd_set_bit ( __u64 bit , unsigned char * data )
2005-04-17 02:20:36 +04:00
{
__u64 n ;
int bits , off ;
2005-10-11 07:10:32 +04:00
bits = sizeof ( data [ 0 ] ) * 8 ;
2005-04-17 02:20:36 +04:00
n = bit / bits ;
off = bit % bits ;
2005-10-11 07:10:32 +04:00
data [ n ] | = ( 1 < < off ) ;
2005-04-17 02:20:36 +04:00
}
/*End stuff from ubd_user.h*/
# define DRIVER_NAME "uml-blkdev"
2006-10-31 09:07:07 +03:00
static DEFINE_MUTEX ( ubd_lock ) ;
2005-04-17 02:20:36 +04:00
static int ubd_open ( struct inode * inode , struct file * filp ) ;
static int ubd_release ( struct inode * inode , struct file * file ) ;
static int ubd_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg ) ;
2006-01-08 12:02:50 +03:00
static int ubd_getgeo ( struct block_device * bdev , struct hd_geometry * geo ) ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:03 +03:00
# define MAX_DEV (16)
2005-04-17 02:20:36 +04:00
static struct block_device_operations ubd_blops = {
. owner = THIS_MODULE ,
. open = ubd_open ,
. release = ubd_release ,
. ioctl = ubd_ioctl ,
2006-01-08 12:02:50 +03:00
. getgeo = ubd_getgeo ,
2005-04-17 02:20:36 +04:00
} ;
/* Protected by ubd_lock */
static int fake_major = MAJOR_NR ;
static struct gendisk * ubd_gendisk [ MAX_DEV ] ;
static struct gendisk * fake_gendisk [ MAX_DEV ] ;
2006-03-27 13:14:37 +04:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_BLK_DEV_UBD_SYNC
# define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
. cl = 1 } )
# else
# define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
. cl = 1 } )
# endif
static struct openflags global_openflags = OPEN_FLAGS ;
struct cow {
2006-10-31 09:07:04 +03:00
/* backing file name */
2005-04-17 02:20:36 +04:00
char * file ;
2006-10-31 09:07:04 +03:00
/* backing file fd */
2005-04-17 02:20:36 +04:00
int fd ;
unsigned long * bitmap ;
unsigned long bitmap_len ;
int bitmap_offset ;
2007-05-07 01:51:41 +04:00
int data_offset ;
2005-04-17 02:20:36 +04:00
} ;
2007-05-07 01:51:36 +04:00
# define MAX_SG 64
2005-04-17 02:20:36 +04:00
struct ubd {
2007-05-07 01:51:36 +04:00
struct list_head restart ;
2006-10-31 09:07:04 +03:00
/* name (and fd, below) of the file opened for writing, either the
* backing or the cow file . */
2005-04-17 02:20:36 +04:00
char * file ;
int count ;
int fd ;
__u64 size ;
struct openflags boot_openflags ;
struct openflags openflags ;
2006-10-31 09:07:10 +03:00
unsigned shared : 1 ;
unsigned no_cow : 1 ;
2005-04-17 02:20:36 +04:00
struct cow cow ;
struct platform_device pdev ;
2007-02-10 12:44:16 +03:00
struct request_queue * queue ;
spinlock_t lock ;
2007-05-07 01:51:36 +04:00
struct scatterlist sg [ MAX_SG ] ;
struct request * request ;
int start_sg , end_sg ;
2005-04-17 02:20:36 +04:00
} ;
# define DEFAULT_COW { \
. file = NULL , \
2007-05-07 01:51:41 +04:00
. fd = - 1 , \
. bitmap = NULL , \
2005-04-17 02:20:36 +04:00
. bitmap_offset = 0 , \
2007-05-07 01:51:41 +04:00
. data_offset = 0 , \
2005-04-17 02:20:36 +04:00
}
# define DEFAULT_UBD { \
. file = NULL , \
. count = 0 , \
. fd = - 1 , \
. size = - 1 , \
. boot_openflags = OPEN_FLAGS , \
. openflags = OPEN_FLAGS , \
2007-05-07 01:51:41 +04:00
. no_cow = 0 , \
2006-03-27 13:14:37 +04:00
. shared = 0 , \
2007-05-07 01:51:41 +04:00
. cow = DEFAULT_COW , \
2007-02-10 12:44:16 +03:00
. lock = SPIN_LOCK_UNLOCKED , \
2007-05-07 01:51:36 +04:00
. request = NULL , \
. start_sg = 0 , \
. end_sg = 0 , \
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:44:17 +03:00
/* Protected by ubd_lock */
2006-10-31 09:07:05 +03:00
struct ubd ubd_devs [ MAX_DEV ] = { [ 0 . . . MAX_DEV - 1 ] = DEFAULT_UBD } ;
2005-04-17 02:20:36 +04:00
/* Only changed by fake_ide_setup which is a setup */
static int fake_ide = 0 ;
static struct proc_dir_entry * proc_ide_root = NULL ;
static struct proc_dir_entry * proc_ide = NULL ;
static void make_proc_ide ( void )
{
proc_ide_root = proc_mkdir ( " ide " , NULL ) ;
proc_ide = proc_mkdir ( " ide0 " , proc_ide_root ) ;
}
static int proc_ide_read_media ( char * page , char * * start , off_t off , int count ,
int * eof , void * data )
{
int len ;
strcpy ( page , " disk \n " ) ;
len = strlen ( " disk \n " ) ;
len - = off ;
if ( len < count ) {
* eof = 1 ;
if ( len < = 0 ) return 0 ;
}
else len = count ;
* start = page + off ;
return len ;
}
2008-02-05 09:30:41 +03:00
static void make_ide_entries ( const char * dev_name )
2005-04-17 02:20:36 +04:00
{
struct proc_dir_entry * dir , * ent ;
char name [ 64 ] ;
if ( proc_ide_root = = NULL ) make_proc_ide ( ) ;
dir = proc_mkdir ( dev_name , proc_ide ) ;
if ( ! dir ) return ;
ent = create_proc_entry ( " media " , S_IFREG | S_IRUGO , dir ) ;
if ( ! ent ) return ;
ent - > data = NULL ;
ent - > read_proc = proc_ide_read_media ;
ent - > write_proc = NULL ;
2008-02-05 09:30:41 +03:00
snprintf ( name , sizeof ( name ) , " ide0/%s " , dev_name ) ;
2005-04-17 02:20:36 +04:00
proc_symlink ( dev_name , proc_ide_root , name ) ;
}
static int fake_ide_setup ( char * str )
{
fake_ide = 1 ;
2007-05-07 01:51:41 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
__setup ( " fake_ide " , fake_ide_setup ) ;
__uml_help ( fake_ide_setup ,
" fake_ide \n "
" Create ide0 entries that map onto ubd devices. \n \n "
) ;
static int parse_unit ( char * * ptr )
{
char * str = * ptr , * end ;
int n = - 1 ;
if ( isdigit ( * str ) ) {
n = simple_strtoul ( str , & end , 0 ) ;
if ( end = = str )
2007-05-07 01:51:41 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
* ptr = end ;
}
2006-10-31 09:07:03 +03:00
else if ( ( ' a ' < = * str ) & & ( * str < = ' z ' ) ) {
2005-04-17 02:20:36 +04:00
n = * str - ' a ' ;
str + + ;
* ptr = str ;
}
2007-05-07 01:51:41 +04:00
return n ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 09:07:12 +03:00
/* If *index_out == -1 at exit, the passed option was a general one;
* otherwise , the str pointer is used ( and owned ) inside ubd_devs array , so it
* should not be freed on exit .
*/
2007-02-10 12:43:53 +03:00
static int ubd_setup_common ( char * str , int * index_out , char * * error_out )
2005-04-17 02:20:36 +04:00
{
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev ;
2005-04-17 02:20:36 +04:00
struct openflags flags = global_openflags ;
char * backing_file ;
2007-02-10 12:44:17 +03:00
int n , err = 0 , i ;
2005-04-17 02:20:36 +04:00
if ( index_out ) * index_out = - 1 ;
n = * str ;
if ( n = = ' = ' ) {
char * end ;
int major ;
str + + ;
if ( ! strcmp ( str , " sync " ) ) {
global_openflags = of_sync ( global_openflags ) ;
2007-02-10 12:44:17 +03:00
goto out1 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:44:17 +03:00
err = - EINVAL ;
2005-04-17 02:20:36 +04:00
major = simple_strtoul ( str , & end , 0 ) ;
if ( ( * end ! = ' \0 ' ) | | ( end = = str ) ) {
2007-02-10 12:43:53 +03:00
* error_out = " Didn't parse major number " ;
2007-02-10 12:44:17 +03:00
goto out1 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:43:53 +03:00
mutex_lock ( & ubd_lock ) ;
if ( fake_major ! = MAJOR_NR ) {
* error_out = " Can't assign a fake major twice " ;
goto out1 ;
}
2006-03-27 13:14:37 +04:00
2007-02-10 12:43:53 +03:00
fake_major = major ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " Setting extra ubd major number to %d \n " ,
major ) ;
2007-02-10 12:43:53 +03:00
err = 0 ;
out1 :
mutex_unlock ( & ubd_lock ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
n = parse_unit ( & str ) ;
if ( n < 0 ) {
2007-02-10 12:43:53 +03:00
* error_out = " Couldn't parse device number " ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
if ( n > = MAX_DEV ) {
2007-02-10 12:43:53 +03:00
* error_out = " Device number out of range " ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:43:53 +03:00
err = - EBUSY ;
2006-10-31 09:07:07 +03:00
mutex_lock ( & ubd_lock ) ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
ubd_dev = & ubd_devs [ n ] ;
if ( ubd_dev - > file ! = NULL ) {
2007-02-10 12:43:53 +03:00
* error_out = " Device is already configured " ;
2005-04-17 02:20:36 +04:00
goto out ;
}
if ( index_out )
* index_out = n ;
2007-02-10 12:43:53 +03:00
err = - EINVAL ;
2006-03-27 13:14:37 +04:00
for ( i = 0 ; i < sizeof ( " rscd= " ) ; i + + ) {
2005-04-17 02:20:36 +04:00
switch ( * str ) {
case ' r ' :
flags . w = 0 ;
break ;
case ' s ' :
flags . s = 1 ;
break ;
case ' d ' :
2006-10-31 09:07:05 +03:00
ubd_dev - > no_cow = 1 ;
2005-04-17 02:20:36 +04:00
break ;
2006-03-27 13:14:37 +04:00
case ' c ' :
2006-10-31 09:07:05 +03:00
ubd_dev - > shared = 1 ;
2006-03-27 13:14:37 +04:00
break ;
2005-04-17 02:20:36 +04:00
case ' = ' :
str + + ;
goto break_loop ;
default :
2007-02-10 12:43:53 +03:00
* error_out = " Expected '=' or flag letter "
" (r, s, c, or d) " ;
2005-04-17 02:20:36 +04:00
goto out ;
}
str + + ;
}
2007-02-10 12:43:53 +03:00
if ( * str = = ' = ' )
* error_out = " Too many flags specified " ;
else
* error_out = " Missing '=' " ;
2005-04-17 02:20:36 +04:00
goto out ;
break_loop :
backing_file = strchr ( str , ' , ' ) ;
2007-02-10 12:43:53 +03:00
if ( backing_file = = NULL )
2005-04-17 02:20:36 +04:00
backing_file = strchr ( str , ' : ' ) ;
2007-02-10 12:43:53 +03:00
if ( backing_file ! = NULL ) {
if ( ubd_dev - > no_cow ) {
* error_out = " Can't specify both 'd' and a cow file " ;
goto out ;
}
2005-04-17 02:20:36 +04:00
else {
* backing_file = ' \0 ' ;
backing_file + + ;
}
}
2007-02-10 12:43:53 +03:00
err = 0 ;
2006-10-31 09:07:05 +03:00
ubd_dev - > file = str ;
ubd_dev - > cow . file = backing_file ;
ubd_dev - > boot_openflags = flags ;
2005-04-17 02:20:36 +04:00
out :
2006-10-31 09:07:07 +03:00
mutex_unlock ( & ubd_lock ) ;
2007-02-10 12:43:53 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
static int ubd_setup ( char * str )
{
2007-02-10 12:43:53 +03:00
char * error ;
int err ;
err = ubd_setup_common ( str , NULL , & error ) ;
if ( err )
printk ( KERN_ERR " Failed to initialize device with \" %s \" : "
" %s \n " , str , error ) ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
__setup ( " ubd " , ubd_setup ) ;
__uml_help ( ubd_setup ,
" ubd<n><flags>=<filename>[(:|,)<filename2>] \n "
" This is used to associate a device with a file in the underlying \n "
" filesystem. When specifying two filenames, the first one is the \n "
" COW name and the second is the backing file name. As separator you can \n "
" use either a ':' or a ',': the first one allows writing things like; \n "
" ubd0=~/Uml/root_cow:~/Uml/root_backing_file \n "
" while with a ',' the shell would not expand the 2nd '~'. \n "
2007-02-10 12:43:53 +03:00
" When using only one filename, UML will detect whether to treat it like \n "
2005-04-17 02:20:36 +04:00
" a COW file or a backing file. To override this detection, add the 'd' \n "
" flag: \n "
" ubd0d=BackingFile \n "
" Usually, there is a filesystem in the file, but \n "
" that's not required. Swap devices containing swap files can be \n "
" specified like this. Also, a file which doesn't contain a \n "
" filesystem can have its contents read in the virtual \n "
" machine by running 'dd' on the device. <n> must be in the range \n "
" 0 to 7. Appending an 'r' to the number will cause that device \n "
" to be mounted read-only. For example ubd1r=./ext_fs. Appending \n "
2008-02-05 09:30:37 +03:00
" an 's' will cause data to be written to disk on the host immediately. \n "
" 'c' will cause the device to be treated as being shared between multiple \n "
" UMLs and file locking will be turned off - this is appropriate for a \n "
" cluster filesystem and inappropriate at almost all other times. \n \n "
2005-04-17 02:20:36 +04:00
) ;
2008-02-05 09:30:41 +03:00
static int udb_setup ( const char * str )
2005-04-17 02:20:36 +04:00
{
printk ( " udb%s specified on command line is almost certainly a ubd -> "
" udb TYPO \n " , str ) ;
2007-05-07 01:51:41 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
__setup ( " udb " , udb_setup ) ;
__uml_help ( udb_setup ,
" udb \n "
2005-05-29 02:51:55 +04:00
" This option is here solely to catch ubd -> udb typos, which can be \n "
" to impossible to catch visually unless you specifically look for \n "
" them. The only result of any option starting with 'udb' is an error \n "
2005-04-17 02:20:36 +04:00
" in the boot output. \n \n "
) ;
static int fakehd_set = 0 ;
static int fakehd ( char * str )
{
printk ( KERN_INFO " fakehd : Changing ubd name to \" hd \" . \n " ) ;
fakehd_set = 1 ;
return 1 ;
}
__setup ( " fakehd " , fakehd ) ;
__uml_help ( fakehd ,
" fakehd \n "
" Change the ubd device name to \" hd \" . \n \n "
) ;
2007-07-24 11:28:11 +04:00
static void do_ubd_request ( struct request_queue * q ) ;
2005-10-11 07:10:32 +04:00
/* Only changed by ubd_init, which is an initcall. */
int thread_fd = - 1 ;
2005-04-17 02:20:36 +04:00
2007-12-12 01:42:53 +03:00
static void ubd_end_request ( struct request * req , int bytes , int error )
2005-04-17 02:20:36 +04:00
{
2007-12-12 01:42:53 +03:00
blk_end_request ( req , error , bytes ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 09:07:08 +03:00
/* Callable only from interrupt context - otherwise you need to do
* spin_lock_irq ( ) / spin_lock_irqsave ( ) */
2007-05-07 01:51:36 +04:00
static inline void ubd_finish ( struct request * req , int bytes )
2005-04-17 02:20:36 +04:00
{
2007-05-07 01:51:36 +04:00
if ( bytes < 0 ) {
2007-12-12 01:42:53 +03:00
ubd_end_request ( req , 0 , - EIO ) ;
2007-05-07 01:51:36 +04:00
return ;
}
2007-12-12 01:42:53 +03:00
ubd_end_request ( req , bytes , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:36 +04:00
static LIST_HEAD ( restart ) ;
2006-10-31 09:07:09 +03:00
/* XXX - move this inside ubd_intr. */
2007-02-10 12:44:16 +03:00
/* Called without dev->lock held, and only in interrupt context. */
2005-10-11 07:10:32 +04:00
static void ubd_handler ( void )
2005-04-17 02:20:36 +04:00
{
2007-05-07 01:51:37 +04:00
struct io_thread_req * req ;
2007-02-10 12:44:16 +03:00
struct request * rq ;
2007-05-07 01:51:36 +04:00
struct ubd * ubd ;
struct list_head * list , * next_ele ;
unsigned long flags ;
2005-10-11 07:10:32 +04:00
int n ;
2007-05-07 01:51:36 +04:00
while ( 1 ) {
2007-05-07 01:51:43 +04:00
n = os_read_file ( thread_fd , & req ,
sizeof ( struct io_thread_req * ) ) ;
2007-05-07 01:51:36 +04:00
if ( n ! = sizeof ( req ) ) {
if ( n = = - EAGAIN )
break ;
printk ( KERN_ERR " spurious interrupt in ubd_handler, "
" err = %d \n " , - n ) ;
return ;
}
2007-02-10 12:44:16 +03:00
2007-05-07 01:51:37 +04:00
rq = req - > req ;
rq - > nr_sectors - = req - > length > > 9 ;
2007-05-07 01:51:36 +04:00
if ( rq - > nr_sectors = = 0 )
ubd_finish ( rq , rq - > hard_nr_sectors < < 9 ) ;
2007-05-07 01:51:37 +04:00
kfree ( req ) ;
2007-05-07 01:51:36 +04:00
}
2007-02-10 12:44:16 +03:00
reactivate_fd ( thread_fd , UBD_IRQ ) ;
2007-05-07 01:51:36 +04:00
list_for_each_safe ( list , next_ele , & restart ) {
ubd = container_of ( list , struct ubd , restart ) ;
list_del_init ( & ubd - > restart ) ;
spin_lock_irqsave ( & ubd - > lock , flags ) ;
do_ubd_request ( ubd - > queue ) ;
spin_unlock_irqrestore ( & ubd - > lock , flags ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-10-09 01:49:34 +04:00
static irqreturn_t ubd_intr ( int irq , void * dev )
2005-04-17 02:20:36 +04:00
{
2005-10-11 07:10:32 +04:00
ubd_handler ( ) ;
2007-05-07 01:51:41 +04:00
return IRQ_HANDLED ;
2005-10-11 07:10:32 +04:00
}
2005-09-04 02:57:46 +04:00
2005-10-11 07:10:32 +04:00
/* Only changed by ubd_init, which is an initcall. */
static int io_pid = - 1 ;
2005-09-04 02:57:46 +04:00
2005-10-11 07:10:32 +04:00
void kill_io_thread ( void )
{
2006-03-27 13:14:37 +04:00
if ( io_pid ! = - 1 )
2005-10-11 07:10:32 +04:00
os_kill_process ( io_pid , 1 ) ;
2005-09-04 02:57:46 +04:00
}
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
__uml_exitcall ( kill_io_thread ) ;
2006-10-31 09:07:12 +03:00
static inline int ubd_file_size ( struct ubd * ubd_dev , __u64 * size_out )
2005-04-17 02:20:36 +04:00
{
char * file ;
2006-10-31 09:07:05 +03:00
file = ubd_dev - > cow . file ? ubd_dev - > cow . file : ubd_dev - > file ;
2007-05-07 01:51:41 +04:00
return os_file_size ( file , size_out ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 09:07:06 +03:00
static void ubd_close_dev ( struct ubd * ubd_dev )
2005-04-17 02:20:36 +04:00
{
2006-10-31 09:07:05 +03:00
os_close_file ( ubd_dev - > fd ) ;
if ( ubd_dev - > cow . file = = NULL )
2005-04-17 02:20:36 +04:00
return ;
2006-10-31 09:07:05 +03:00
os_close_file ( ubd_dev - > cow . fd ) ;
vfree ( ubd_dev - > cow . bitmap ) ;
ubd_dev - > cow . bitmap = NULL ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 09:07:05 +03:00
static int ubd_open_dev ( struct ubd * ubd_dev )
2005-04-17 02:20:36 +04:00
{
struct openflags flags ;
char * * back_ptr ;
int err , create_cow , * create_ptr ;
2006-10-31 09:07:11 +03:00
int fd ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
ubd_dev - > openflags = ubd_dev - > boot_openflags ;
2005-04-17 02:20:36 +04:00
create_cow = 0 ;
2006-10-31 09:07:05 +03:00
create_ptr = ( ubd_dev - > cow . file ! = NULL ) ? & create_cow : NULL ;
back_ptr = ubd_dev - > no_cow ? NULL : & ubd_dev - > cow . file ;
2006-10-31 09:07:11 +03:00
fd = open_ubd_file ( ubd_dev - > file , & ubd_dev - > openflags , ubd_dev - > shared ,
2006-10-31 09:07:05 +03:00
back_ptr , & ubd_dev - > cow . bitmap_offset ,
& ubd_dev - > cow . bitmap_len , & ubd_dev - > cow . data_offset ,
2006-03-27 13:14:37 +04:00
create_ptr ) ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:11 +03:00
if ( ( fd = = - ENOENT ) & & create_cow ) {
fd = create_cow_file ( ubd_dev - > file , ubd_dev - > cow . file ,
2006-10-31 09:07:05 +03:00
ubd_dev - > openflags , 1 < < 9 , PAGE_SIZE ,
& ubd_dev - > cow . bitmap_offset ,
& ubd_dev - > cow . bitmap_len ,
& ubd_dev - > cow . data_offset ) ;
2006-10-31 09:07:11 +03:00
if ( fd > = 0 ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " Creating \" %s \" as COW file for "
2006-10-31 09:07:05 +03:00
" \" %s \" \n " , ubd_dev - > file , ubd_dev - > cow . file ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-10-31 09:07:11 +03:00
if ( fd < 0 ) {
2006-10-31 09:07:05 +03:00
printk ( " Failed to open '%s', errno = %d \n " , ubd_dev - > file ,
2006-10-31 09:07:11 +03:00
- fd ) ;
return fd ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 09:07:11 +03:00
ubd_dev - > fd = fd ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > cow . file ! = NULL ) {
2007-08-23 01:01:53 +04:00
blk_queue_max_sectors ( ubd_dev - > queue , 8 * sizeof ( long ) ) ;
2005-04-17 02:20:36 +04:00
err = - ENOMEM ;
2007-10-16 12:27:19 +04:00
ubd_dev - > cow . bitmap = vmalloc ( ubd_dev - > cow . bitmap_len ) ;
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > cow . bitmap = = NULL ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " Failed to vmalloc COW bitmap \n " ) ;
goto error ;
}
flush_tlb_kernel_vm ( ) ;
2006-10-31 09:07:05 +03:00
err = read_cow_bitmap ( ubd_dev - > fd , ubd_dev - > cow . bitmap ,
ubd_dev - > cow . bitmap_offset ,
ubd_dev - > cow . bitmap_len ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
goto error ;
2006-10-31 09:07:05 +03:00
flags = ubd_dev - > openflags ;
2005-04-17 02:20:36 +04:00
flags . w = 0 ;
2006-10-31 09:07:05 +03:00
err = open_ubd_file ( ubd_dev - > cow . file , & flags , ubd_dev - > shared , NULL ,
2006-03-27 13:14:37 +04:00
NULL , NULL , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 ) goto error ;
2006-10-31 09:07:05 +03:00
ubd_dev - > cow . fd = err ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:41 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
error :
2006-10-31 09:07:05 +03:00
os_close_file ( ubd_dev - > fd ) ;
2007-05-07 01:51:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:29 +04:00
static void ubd_device_release ( struct device * dev )
{
struct ubd * ubd_dev = dev - > driver_data ;
blk_cleanup_queue ( ubd_dev - > queue ) ;
* ubd_dev = ( ( struct ubd ) DEFAULT_UBD ) ;
}
2006-10-31 09:07:06 +03:00
static int ubd_disk_register ( int major , u64 size , int unit ,
2007-02-10 12:44:17 +03:00
struct gendisk * * disk_out )
2005-04-17 02:20:36 +04:00
{
struct gendisk * disk ;
disk = alloc_disk ( 1 < < UBD_SHIFT ) ;
if ( disk = = NULL )
2007-05-07 01:51:41 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
disk - > major = major ;
disk - > first_minor = unit < < UBD_SHIFT ;
disk - > fops = & ubd_blops ;
set_capacity ( disk , size / 512 ) ;
2005-06-21 08:15:16 +04:00
if ( major = = MAJOR_NR )
2005-04-17 02:20:36 +04:00
sprintf ( disk - > disk_name , " ubd%c " , ' a ' + unit ) ;
2005-06-21 08:15:16 +04:00
else
2005-04-17 02:20:36 +04:00
sprintf ( disk - > disk_name , " ubd_fake%d " , unit ) ;
/* sysfs register (not for ide fake devices) */
if ( major = = MAJOR_NR ) {
2006-10-31 09:07:05 +03:00
ubd_devs [ unit ] . pdev . id = unit ;
ubd_devs [ unit ] . pdev . name = DRIVER_NAME ;
2007-05-07 01:51:29 +04:00
ubd_devs [ unit ] . pdev . dev . release = ubd_device_release ;
ubd_devs [ unit ] . pdev . dev . driver_data = & ubd_devs [ unit ] ;
2006-10-31 09:07:05 +03:00
platform_device_register ( & ubd_devs [ unit ] . pdev ) ;
disk - > driverfs_dev = & ubd_devs [ unit ] . pdev . dev ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 09:07:05 +03:00
disk - > private_data = & ubd_devs [ unit ] ;
2007-02-10 12:44:16 +03:00
disk - > queue = ubd_devs [ unit ] . queue ;
2005-04-17 02:20:36 +04:00
add_disk ( disk ) ;
* disk_out = disk ;
return 0 ;
}
# define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
2007-02-10 12:43:53 +03:00
static int ubd_add ( int n , char * * error_out )
2005-04-17 02:20:36 +04:00
{
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev = & ubd_devs [ n ] ;
2007-02-10 12:43:53 +03:00
int err = 0 ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > file = = NULL )
2005-09-04 02:57:29 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
err = ubd_file_size ( ubd_dev , & ubd_dev - > size ) ;
2007-02-10 12:43:53 +03:00
if ( err < 0 ) {
* error_out = " Couldn't determine size of device's file " ;
2006-09-29 12:58:51 +04:00
goto out ;
2007-02-10 12:43:53 +03:00
}
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
ubd_dev - > size = ROUND_BLOCK ( ubd_dev - > size ) ;
2005-04-17 02:20:36 +04:00
2007-05-07 01:51:36 +04:00
INIT_LIST_HEAD ( & ubd_dev - > restart ) ;
2007-11-06 01:50:59 +03:00
sg_init_table ( ubd_dev - > sg , MAX_SG ) ;
2007-05-07 01:51:36 +04:00
2007-02-10 12:44:16 +03:00
err = - ENOMEM ;
ubd_dev - > queue = blk_init_queue ( do_ubd_request , & ubd_dev - > lock ) ;
if ( ubd_dev - > queue = = NULL ) {
* error_out = " Failed to initialize device queue " ;
2006-09-29 12:58:51 +04:00
goto out ;
2007-02-10 12:44:16 +03:00
}
ubd_dev - > queue - > queuedata = ubd_dev ;
2007-05-07 01:51:36 +04:00
blk_queue_max_hw_segments ( ubd_dev - > queue , MAX_SG ) ;
2007-02-10 12:44:16 +03:00
err = ubd_disk_register ( MAJOR_NR , ubd_dev - > size , n , & ubd_gendisk [ n ] ) ;
if ( err ) {
* error_out = " Failed to register device " ;
goto out_cleanup ;
}
2006-03-27 13:14:37 +04:00
2005-04-17 02:20:36 +04:00
if ( fake_major ! = MAJOR_NR )
2006-10-31 09:07:06 +03:00
ubd_disk_register ( fake_major , ubd_dev - > size , n ,
2007-02-10 12:44:16 +03:00
& fake_gendisk [ n ] ) ;
2005-04-17 02:20:36 +04:00
/* perhaps this should also be under the "if (fake_major)" above */
/* using the fake_disk->disk_name and also the fakehd_set name */
if ( fake_ide )
make_ide_entries ( ubd_gendisk [ n ] - > disk_name ) ;
2005-09-04 02:57:29 +04:00
err = 0 ;
out :
return err ;
2007-02-10 12:44:16 +03:00
out_cleanup :
blk_cleanup_queue ( ubd_dev - > queue ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:43:53 +03:00
static int ubd_config ( char * str , char * * error_out )
2005-04-17 02:20:36 +04:00
{
2006-10-31 09:07:09 +03:00
int n , ret ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:43:53 +03:00
/* This string is possibly broken up and stored, so it's only
* freed if ubd_setup_common fails , or if only general options
* were set .
*/
2006-01-06 11:18:48 +03:00
str = kstrdup ( str , GFP_KERNEL ) ;
2006-10-31 09:07:09 +03:00
if ( str = = NULL ) {
2007-02-10 12:43:53 +03:00
* error_out = " Failed to allocate memory " ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:43:53 +03:00
ret = ubd_setup_common ( str , & n , error_out ) ;
if ( ret )
2006-10-31 09:07:09 +03:00
goto err_free ;
2007-02-10 12:43:53 +03:00
2006-10-31 09:07:09 +03:00
if ( n = = - 1 ) {
ret = 0 ;
2006-10-31 09:07:12 +03:00
goto err_free ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:41 +04:00
mutex_lock ( & ubd_lock ) ;
2007-02-10 12:43:53 +03:00
ret = ubd_add ( n , error_out ) ;
2006-10-31 09:07:09 +03:00
if ( ret )
2006-10-31 09:07:05 +03:00
ubd_devs [ n ] . file = NULL ;
2007-05-07 01:51:41 +04:00
mutex_unlock ( & ubd_lock ) ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:09 +03:00
out :
2007-05-07 01:51:41 +04:00
return ret ;
2006-10-31 09:07:09 +03:00
err_free :
kfree ( str ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
static int ubd_get_config ( char * name , char * str , int size , char * * error_out )
{
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev ;
2005-04-17 02:20:36 +04:00
int n , len = 0 ;
n = parse_unit ( & name ) ;
if ( ( n > = MAX_DEV ) | | ( n < 0 ) ) {
* error_out = " ubd_get_config : device number out of range " ;
2007-05-07 01:51:41 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 09:07:05 +03:00
ubd_dev = & ubd_devs [ n ] ;
2006-10-31 09:07:07 +03:00
mutex_lock ( & ubd_lock ) ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > file = = NULL ) {
2005-04-17 02:20:36 +04:00
CONFIG_CHUNK ( str , size , len , " " , 1 ) ;
goto out ;
}
2006-10-31 09:07:05 +03:00
CONFIG_CHUNK ( str , size , len , ubd_dev - > file , 0 ) ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > cow . file ! = NULL ) {
2005-04-17 02:20:36 +04:00
CONFIG_CHUNK ( str , size , len , " , " , 0 ) ;
2006-10-31 09:07:05 +03:00
CONFIG_CHUNK ( str , size , len , ubd_dev - > cow . file , 1 ) ;
2005-04-17 02:20:36 +04:00
}
else CONFIG_CHUNK ( str , size , len , " " , 1 ) ;
out :
2006-10-31 09:07:07 +03:00
mutex_unlock ( & ubd_lock ) ;
2007-05-07 01:51:41 +04:00
return len ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:55:25 +04:00
static int ubd_id ( char * * str , int * start_out , int * end_out )
{
2007-05-07 01:51:41 +04:00
int n ;
2005-06-26 01:55:25 +04:00
n = parse_unit ( str ) ;
2007-05-07 01:51:41 +04:00
* start_out = 0 ;
* end_out = MAX_DEV - 1 ;
return n ;
2005-06-26 01:55:25 +04:00
}
2007-02-10 12:43:53 +03:00
static int ubd_remove ( int n , char * * error_out )
2005-04-17 02:20:36 +04:00
{
2007-05-07 01:51:29 +04:00
struct gendisk * disk = ubd_gendisk [ n ] ;
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev ;
2005-06-26 01:55:25 +04:00
int err = - ENODEV ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:07 +03:00
mutex_lock ( & ubd_lock ) ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
ubd_dev = & ubd_devs [ n ] ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > file = = NULL )
2005-06-26 01:55:25 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:55:25 +04:00
/* you cannot remove a open disk */
err = - EBUSY ;
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > count > 0 )
2005-04-17 02:20:36 +04:00
goto out ;
2007-05-07 01:51:41 +04:00
ubd_gendisk [ n ] = NULL ;
2007-05-07 01:51:01 +04:00
if ( disk ! = NULL ) {
del_gendisk ( disk ) ;
put_disk ( disk ) ;
}
2005-04-17 02:20:36 +04:00
if ( fake_gendisk [ n ] ! = NULL ) {
del_gendisk ( fake_gendisk [ n ] ) ;
put_disk ( fake_gendisk [ n ] ) ;
fake_gendisk [ n ] = NULL ;
}
err = 0 ;
2007-05-07 01:51:29 +04:00
platform_device_unregister ( & ubd_dev - > pdev ) ;
2005-06-26 01:55:25 +04:00
out :
2006-10-31 09:07:07 +03:00
mutex_unlock ( & ubd_lock ) ;
2005-06-26 01:55:25 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:43:53 +03:00
/* All these are called by mconsole in process context and without
2007-02-10 12:44:17 +03:00
* ubd - specific locks . The structure itself is const except for . list .
2007-02-10 12:43:53 +03:00
*/
2005-04-17 02:20:36 +04:00
static struct mc_device ubd_mc = {
2007-02-10 12:44:01 +03:00
. list = LIST_HEAD_INIT ( ubd_mc . list ) ,
2005-04-17 02:20:36 +04:00
. name = " ubd " ,
. config = ubd_config ,
2007-05-07 01:51:41 +04:00
. get_config = ubd_get_config ,
2005-06-26 01:55:25 +04:00
. id = ubd_id ,
2005-04-17 02:20:36 +04:00
. remove = ubd_remove ,
} ;
2006-10-31 09:07:12 +03:00
static int __init ubd_mc_init ( void )
2005-04-17 02:20:36 +04:00
{
mconsole_register_dev ( & ubd_mc ) ;
return 0 ;
}
__initcall ( ubd_mc_init ) ;
2006-10-31 09:07:12 +03:00
static int __init ubd0_init ( void )
{
struct ubd * ubd_dev = & ubd_devs [ 0 ] ;
2007-02-10 12:44:17 +03:00
mutex_lock ( & ubd_lock ) ;
2006-10-31 09:07:12 +03:00
if ( ubd_dev - > file = = NULL )
ubd_dev - > file = " root_fs " ;
2007-02-10 12:44:17 +03:00
mutex_unlock ( & ubd_lock ) ;
2007-05-07 01:51:41 +04:00
return 0 ;
2006-10-31 09:07:12 +03:00
}
__initcall ( ubd0_init ) ;
2007-02-10 12:44:17 +03:00
/* Used in ubd_init, which is an initcall */
2005-11-10 01:32:44 +03:00
static struct platform_driver ubd_driver = {
. driver = {
. name = DRIVER_NAME ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2006-10-31 09:07:12 +03:00
static int __init ubd_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:43:53 +03:00
char * error ;
int i , err ;
2005-04-17 02:20:36 +04:00
if ( register_blkdev ( MAJOR_NR , " ubd " ) )
return - 1 ;
if ( fake_major ! = MAJOR_NR ) {
char name [ sizeof ( " ubd_nnn \0 " ) ] ;
snprintf ( name , sizeof ( name ) , " ubd_%d " , fake_major ) ;
if ( register_blkdev ( fake_major , " ubd " ) )
return - 1 ;
}
2005-11-10 01:32:44 +03:00
platform_driver_register ( & ubd_driver ) ;
2007-05-07 01:51:41 +04:00
mutex_lock ( & ubd_lock ) ;
2007-02-10 12:43:53 +03:00
for ( i = 0 ; i < MAX_DEV ; i + + ) {
err = ubd_add ( i , & error ) ;
if ( err )
printk ( KERN_ERR " Failed to initialize ubd device %d : "
" %s \n " , i , error ) ;
}
2007-05-07 01:51:41 +04:00
mutex_unlock ( & ubd_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
late_initcall ( ubd_init ) ;
2006-10-31 09:07:12 +03:00
static int __init ubd_driver_init ( void ) {
2005-10-11 07:10:32 +04:00
unsigned long stack ;
int err ;
/* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
if ( global_openflags . s ) {
printk ( KERN_INFO " ubd: Synchronous mode \n " ) ;
/* Letting ubd=sync be like using ubd#s= instead of ubd#= is
* enough . So use anyway the io thread . */
}
stack = alloc_stack ( 0 , 0 ) ;
2006-03-27 13:14:37 +04:00
io_pid = start_io_thread ( stack + PAGE_SIZE - sizeof ( void * ) ,
2005-10-11 07:10:32 +04:00
& thread_fd ) ;
if ( io_pid < 0 ) {
2006-03-27 13:14:37 +04:00
printk ( KERN_ERR
2005-10-11 07:10:32 +04:00
" ubd : Failed to start I/O thread (errno = %d) - "
" falling back to synchronous I/O \n " , - io_pid ) ;
io_pid = - 1 ;
2007-05-07 01:51:41 +04:00
return 0 ;
2005-10-11 07:10:32 +04:00
}
2006-03-27 13:14:37 +04:00
err = um_request_irq ( UBD_IRQ , thread_fd , IRQ_READ , ubd_intr ,
2006-10-31 09:07:05 +03:00
IRQF_DISABLED , " ubd " , ubd_devs ) ;
2005-10-11 07:10:32 +04:00
if ( err ! = 0 )
printk ( KERN_ERR " um_request_irq failed - errno = %d \n " , - err ) ;
2006-03-31 14:30:10 +04:00
return 0 ;
2005-10-11 07:10:32 +04:00
}
device_initcall ( ubd_driver_init ) ;
2005-04-17 02:20:36 +04:00
static int ubd_open ( struct inode * inode , struct file * filp )
{
struct gendisk * disk = inode - > i_bdev - > bd_disk ;
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev = disk - > private_data ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > count = = 0 ) {
err = ubd_open_dev ( ubd_dev ) ;
2005-04-17 02:20:36 +04:00
if ( err ) {
printk ( KERN_ERR " %s: Can't open \" %s \" : errno = %d \n " ,
2006-10-31 09:07:05 +03:00
disk - > disk_name , ubd_dev - > file , - err ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
}
2006-10-31 09:07:05 +03:00
ubd_dev - > count + + ;
set_disk_ro ( disk , ! ubd_dev - > openflags . w ) ;
2005-05-01 19:58:57 +04:00
/* This should no more be needed. And it didn't work anyway to exclude
* read - write remounting of filesystems . */
2006-10-31 09:07:05 +03:00
/*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
2006-10-31 09:07:06 +03:00
if ( - - ubd_dev - > count = = 0 ) ubd_close_dev ( ubd_dev ) ;
2005-04-17 02:20:36 +04:00
err = - EROFS ;
2005-05-01 19:58:57 +04:00
} */
2005-04-17 02:20:36 +04:00
out :
2007-05-07 01:51:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static int ubd_release ( struct inode * inode , struct file * file )
{
struct gendisk * disk = inode - > i_bdev - > bd_disk ;
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev = disk - > private_data ;
2005-04-17 02:20:36 +04:00
2006-10-31 09:07:05 +03:00
if ( - - ubd_dev - > count = = 0 )
2006-10-31 09:07:06 +03:00
ubd_close_dev ( ubd_dev ) ;
2007-05-07 01:51:41 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-10-11 07:10:32 +04:00
static void cowify_bitmap ( __u64 io_offset , int length , unsigned long * cow_mask ,
__u64 * cow_offset , unsigned long * bitmap ,
__u64 bitmap_offset , unsigned long * bitmap_words ,
__u64 bitmap_len )
2005-04-17 02:20:36 +04:00
{
2005-10-11 07:10:32 +04:00
__u64 sector = io_offset > > 9 ;
int i , update_bitmap = 0 ;
for ( i = 0 ; i < length > > 9 ; i + + ) {
if ( cow_mask ! = NULL )
ubd_set_bit ( i , ( unsigned char * ) cow_mask ) ;
if ( ubd_test_bit ( sector + i , ( unsigned char * ) bitmap ) )
continue ;
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
update_bitmap = 1 ;
ubd_set_bit ( sector + i , ( unsigned char * ) bitmap ) ;
}
if ( ! update_bitmap )
return ;
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
* cow_offset = sector / ( sizeof ( unsigned long ) * 8 ) ;
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
/* This takes care of the case where we're exactly at the end of the
* device , and * cow_offset + 1 is off the end . So , just back it up
* by one word . Thanks to Lynn Kerby for the fix and James McMechan
* for the original diagnosis .
*/
if ( * cow_offset = = ( ( bitmap_len + sizeof ( unsigned long ) - 1 ) /
sizeof ( unsigned long ) - 1 ) )
( * cow_offset ) - - ;
bitmap_words [ 0 ] = bitmap [ * cow_offset ] ;
bitmap_words [ 1 ] = bitmap [ * cow_offset + 1 ] ;
* cow_offset * = sizeof ( unsigned long ) ;
* cow_offset + = bitmap_offset ;
}
static void cowify_req ( struct io_thread_req * req , unsigned long * bitmap ,
__u64 bitmap_offset , __u64 bitmap_len )
{
__u64 sector = req - > offset > > 9 ;
int i ;
if ( req - > length > ( sizeof ( req - > sector_mask ) * 8 ) < < 9 )
panic ( " Operation too long " ) ;
if ( req - > op = = UBD_READ ) {
for ( i = 0 ; i < req - > length > > 9 ; i + + ) {
if ( ubd_test_bit ( sector + i , ( unsigned char * ) bitmap ) )
2006-03-27 13:14:37 +04:00
ubd_set_bit ( i , ( unsigned char * )
2005-10-11 07:10:32 +04:00
& req - > sector_mask ) ;
2007-05-07 01:51:41 +04:00
}
2005-10-11 07:10:32 +04:00
}
else cowify_bitmap ( req - > offset , req - > length , & req - > sector_mask ,
& req - > cow_offset , bitmap , bitmap_offset ,
req - > bitmap_words , bitmap_len ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:44:16 +03:00
/* Called with dev->lock held */
2007-05-07 01:51:36 +04:00
static void prepare_request ( struct request * req , struct io_thread_req * io_req ,
unsigned long long offset , int page_offset ,
int len , struct page * page )
2005-04-17 02:20:36 +04:00
{
struct gendisk * disk = req - > rq_disk ;
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev = disk - > private_data ;
2005-10-11 07:10:32 +04:00
2007-02-10 12:44:16 +03:00
io_req - > req = req ;
2007-05-07 01:51:36 +04:00
io_req - > fds [ 0 ] = ( ubd_dev - > cow . file ! = NULL ) ? ubd_dev - > cow . fd :
ubd_dev - > fd ;
2006-10-31 09:07:05 +03:00
io_req - > fds [ 1 ] = ubd_dev - > fd ;
2005-10-11 07:10:32 +04:00
io_req - > cow_offset = - 1 ;
2005-04-17 02:20:36 +04:00
io_req - > offset = offset ;
io_req - > length = len ;
io_req - > error = 0 ;
2005-10-11 07:10:32 +04:00
io_req - > sector_mask = 0 ;
io_req - > op = ( rq_data_dir ( req ) = = READ ) ? UBD_READ : UBD_WRITE ;
2005-04-17 02:20:36 +04:00
io_req - > offsets [ 0 ] = 0 ;
2006-10-31 09:07:05 +03:00
io_req - > offsets [ 1 ] = ubd_dev - > cow . data_offset ;
2007-05-07 01:51:36 +04:00
io_req - > buffer = page_address ( page ) + page_offset ;
2005-04-17 02:20:36 +04:00
io_req - > sectorsize = 1 < < 9 ;
2006-10-31 09:07:05 +03:00
if ( ubd_dev - > cow . file ! = NULL )
2007-05-07 01:51:36 +04:00
cowify_req ( io_req , ubd_dev - > cow . bitmap ,
ubd_dev - > cow . bitmap_offset , ubd_dev - > cow . bitmap_len ) ;
2005-10-11 07:10:32 +04:00
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:44:16 +03:00
/* Called with dev->lock held */
2007-07-24 11:28:11 +04:00
static void do_ubd_request ( struct request_queue * q )
2005-04-17 02:20:36 +04:00
{
2007-05-07 01:51:37 +04:00
struct io_thread_req * io_req ;
2005-04-17 02:20:36 +04:00
struct request * req ;
2007-07-16 10:38:47 +04:00
int n , last_sectors ;
2007-05-07 01:51:36 +04:00
while ( 1 ) {
2007-03-29 12:20:27 +04:00
struct ubd * dev = q - > queuedata ;
2007-05-07 01:51:36 +04:00
if ( dev - > end_sg = = 0 ) {
struct request * req = elv_next_request ( q ) ;
if ( req = = NULL )
return ;
dev - > request = req ;
blkdev_dequeue_request ( req ) ;
dev - > start_sg = 0 ;
dev - > end_sg = blk_rq_map_sg ( q , req , dev - > sg ) ;
}
req = dev - > request ;
2007-07-16 10:38:47 +04:00
last_sectors = 0 ;
2007-05-07 01:51:36 +04:00
while ( dev - > start_sg < dev - > end_sg ) {
struct scatterlist * sg = & dev - > sg [ dev - > start_sg ] ;
2007-07-16 10:38:47 +04:00
req - > sector + = last_sectors ;
2007-05-07 01:51:37 +04:00
io_req = kmalloc ( sizeof ( struct io_thread_req ) ,
2007-05-07 01:51:38 +04:00
GFP_ATOMIC ) ;
2007-05-07 01:51:37 +04:00
if ( io_req = = NULL ) {
if ( list_empty ( & dev - > restart ) )
list_add ( & dev - > restart , & restart ) ;
return ;
}
prepare_request ( req , io_req ,
2007-05-07 01:51:36 +04:00
( unsigned long long ) req - > sector < < 9 ,
2007-10-22 23:19:53 +04:00
sg - > offset , sg - > length , sg_page ( sg ) ) ;
2007-05-07 01:51:36 +04:00
2007-07-16 10:38:47 +04:00
last_sectors = sg - > length > > 9 ;
2007-05-07 01:51:43 +04:00
n = os_write_file ( thread_fd , & io_req ,
sizeof ( struct io_thread_req * ) ) ;
2007-05-07 01:51:37 +04:00
if ( n ! = sizeof ( struct io_thread_req * ) ) {
2007-05-07 01:51:36 +04:00
if ( n ! = - EAGAIN )
printk ( " write to io thread failed, "
" errno = %d \n " , - n ) ;
else if ( list_empty ( & dev - > restart ) )
list_add ( & dev - > restart , & restart ) ;
2007-11-29 03:21:52 +03:00
kfree ( io_req ) ;
2007-05-07 01:51:36 +04:00
return ;
}
dev - > start_sg + + ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:36 +04:00
dev - > end_sg = 0 ;
dev - > request = NULL ;
2005-04-17 02:20:36 +04:00
}
}
2006-01-08 12:02:50 +03:00
static int ubd_getgeo ( struct block_device * bdev , struct hd_geometry * geo )
{
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev = bdev - > bd_disk - > private_data ;
2006-01-08 12:02:50 +03:00
geo - > heads = 128 ;
geo - > sectors = 32 ;
2006-10-31 09:07:05 +03:00
geo - > cylinders = ubd_dev - > size / ( 128 * 32 * 512 ) ;
2006-01-08 12:02:50 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int ubd_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
2006-10-31 09:07:05 +03:00
struct ubd * ubd_dev = inode - > i_bdev - > bd_disk - > private_data ;
2005-04-17 02:20:36 +04:00
struct hd_driveid ubd_id = {
. cyls = 0 ,
. heads = 128 ,
. sectors = 32 ,
} ;
switch ( cmd ) {
struct cdrom_volctrl volume ;
case HDIO_GET_IDENTITY :
2006-10-31 09:07:05 +03:00
ubd_id . cyls = ubd_dev - > size / ( 128 * 32 * 512 ) ;
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( ( char __user * ) arg , ( char * ) & ubd_id ,
sizeof ( ubd_id ) ) )
2007-05-07 01:51:41 +04:00
return - EFAULT ;
return 0 ;
2007-02-10 12:44:17 +03:00
2005-04-17 02:20:36 +04:00
case CDROMVOLREAD :
if ( copy_from_user ( & volume , ( char __user * ) arg , sizeof ( volume ) ) )
2007-05-07 01:51:41 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
volume . channel0 = 255 ;
volume . channel1 = 255 ;
volume . channel2 = 255 ;
volume . channel3 = 255 ;
if ( copy_to_user ( ( char __user * ) arg , & volume , sizeof ( volume ) ) )
2007-05-07 01:51:41 +04:00
return - EFAULT ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:41 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-01-19 04:43:00 +03:00
static int path_requires_switch ( char * from_cmdline , char * from_cow , char * cow )
2005-04-17 02:20:36 +04:00
{
struct uml_stat buf1 , buf2 ;
int err ;
2006-01-19 04:43:00 +03:00
if ( from_cmdline = = NULL )
return 0 ;
if ( ! strcmp ( from_cmdline , from_cow ) )
return 0 ;
2005-04-17 02:20:36 +04:00
err = os_stat_file ( from_cmdline , & buf1 ) ;
if ( err < 0 ) {
printk ( " Couldn't stat '%s', err = %d \n " , from_cmdline , - err ) ;
2006-01-19 04:43:00 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
err = os_stat_file ( from_cow , & buf2 ) ;
if ( err < 0 ) {
printk ( " Couldn't stat '%s', err = %d \n " , from_cow , - err ) ;
2006-01-19 04:43:00 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
if ( ( buf1 . ust_dev = = buf2 . ust_dev ) & & ( buf1 . ust_ino = = buf2 . ust_ino ) )
2006-01-19 04:43:00 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
printk ( " Backing file mismatch - \" %s \" requested, \n "
" \" %s \" specified in COW header of \" %s \" \n " ,
from_cmdline , from_cow , cow ) ;
2006-01-19 04:43:00 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
static int backing_file_mismatch ( char * file , __u64 size , time_t mtime )
{
unsigned long modtime ;
2006-02-25 00:03:58 +03:00
unsigned long long actual ;
2005-04-17 02:20:36 +04:00
int err ;
err = os_file_modtime ( file , & modtime ) ;
if ( err < 0 ) {
printk ( " Failed to get modification time of backing file "
" \" %s \" , err = %d \n " , file , - err ) ;
2007-05-07 01:51:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
err = os_file_size ( file , & actual ) ;
if ( err < 0 ) {
printk ( " Failed to get size of backing file \" %s \" , "
" err = %d \n " , file , - err ) ;
2007-05-07 01:51:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:41 +04:00
if ( actual ! = size ) {
2005-04-17 02:20:36 +04:00
/*__u64 can be a long on AMD64 and with %lu GCC complains; so
* the typecast . */
printk ( " Size mismatch (%llu vs %llu) of COW header vs backing "
" file \n " , ( unsigned long long ) size , actual ) ;
2007-05-07 01:51:41 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
if ( modtime ! = mtime ) {
printk ( " mtime mismatch (%ld vs %ld) of COW header vs backing "
" file \n " , mtime , modtime ) ;
2007-05-07 01:51:41 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 01:51:41 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
int read_cow_bitmap ( int fd , void * buf , int offset , int len )
{
int err ;
err = os_seek_file ( fd , offset ) ;
if ( err < 0 )
2007-05-07 01:51:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
2007-05-07 01:51:41 +04:00
err = os_read_file ( fd , buf , len ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
2007-05-07 01:51:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
2007-05-07 01:51:41 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-03-27 13:14:37 +04:00
int open_ubd_file ( char * file , struct openflags * openflags , int shared ,
2005-04-17 02:20:36 +04:00
char * * backing_file_out , int * bitmap_offset_out ,
unsigned long * bitmap_len_out , int * data_offset_out ,
int * create_cow_out )
{
time_t mtime ;
unsigned long long size ;
__u32 version , align ;
char * backing_file ;
2006-01-19 04:43:00 +03:00
int fd , err , sectorsize , asked_switch , mode = 0644 ;
2005-04-17 02:20:36 +04:00
fd = os_open_file ( file , * openflags , mode ) ;
2006-01-19 04:43:01 +03:00
if ( fd < 0 ) {
if ( ( fd = = - ENOENT ) & & ( create_cow_out ! = NULL ) )
2005-04-17 02:20:36 +04:00
* create_cow_out = 1 ;
2007-05-07 01:51:41 +04:00
if ( ! openflags - > w | |
( ( fd ! = - EROFS ) & & ( fd ! = - EACCES ) ) )
2006-01-19 04:43:01 +03:00
return fd ;
2005-04-17 02:20:36 +04:00
openflags - > w = 0 ;
fd = os_open_file ( file , * openflags , mode ) ;
2006-01-19 04:43:01 +03:00
if ( fd < 0 )
return fd ;
2007-05-07 01:51:41 +04:00
}
2005-04-17 02:20:36 +04:00
2006-03-27 13:14:37 +04:00
if ( shared )
printk ( " Not locking \" %s \" on the host \n " , file ) ;
else {
err = os_lock_file ( fd , openflags - > w ) ;
if ( err < 0 ) {
printk ( " Failed to lock '%s', err = %d \n " , file , - err ) ;
goto out_close ;
}
2005-04-17 02:20:36 +04:00
}
2006-06-26 20:35:02 +04:00
/* Successful return case! */
2006-01-19 04:43:01 +03:00
if ( backing_file_out = = NULL )
2007-05-07 01:51:41 +04:00
return fd ;
2005-04-17 02:20:36 +04:00
err = read_cow_header ( file_reader , & fd , & version , & backing_file , & mtime ,
& size , & sectorsize , & align , bitmap_offset_out ) ;
if ( err & & ( * backing_file_out ! = NULL ) ) {
printk ( " Failed to read COW header from COW file \" %s \" , "
" errno = %d \n " , file , - err ) ;
goto out_close ;
}
2006-01-19 04:43:01 +03:00
if ( err )
2007-05-07 01:51:41 +04:00
return fd ;
2005-04-17 02:20:36 +04:00
2006-01-19 04:43:00 +03:00
asked_switch = path_requires_switch ( * backing_file_out , backing_file , file ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 04:43:00 +03:00
/* Allow switching only if no mismatch. */
if ( asked_switch & & ! backing_file_mismatch ( * backing_file_out , size , mtime ) ) {
2005-04-17 02:20:36 +04:00
printk ( " Switching backing file to '%s' \n " , * backing_file_out ) ;
err = write_cow_header ( file , fd , * backing_file_out ,
sectorsize , align , & size ) ;
2006-01-19 04:43:01 +03:00
if ( err ) {
2005-04-17 02:20:36 +04:00
printk ( " Switch failed, errno = %d \n " , - err ) ;
2006-01-19 04:43:00 +03:00
goto out_close ;
2005-04-17 02:20:36 +04:00
}
2006-01-19 04:43:01 +03:00
} else {
2005-04-17 02:20:36 +04:00
* backing_file_out = backing_file ;
err = backing_file_mismatch ( * backing_file_out , size , mtime ) ;
2006-01-19 04:43:01 +03:00
if ( err )
goto out_close ;
2005-04-17 02:20:36 +04:00
}
cow_sizes ( version , size , sectorsize , align , * bitmap_offset_out ,
bitmap_len_out , data_offset_out ) ;
2007-05-07 01:51:41 +04:00
return fd ;
2005-04-17 02:20:36 +04:00
out_close :
os_close_file ( fd ) ;
2006-01-19 04:43:01 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
int create_cow_file ( char * cow_file , char * backing_file , struct openflags flags ,
int sectorsize , int alignment , int * bitmap_offset_out ,
unsigned long * bitmap_len_out , int * data_offset_out )
{
int err , fd ;
flags . c = 1 ;
2006-03-27 13:14:37 +04:00
fd = open_ubd_file ( cow_file , & flags , 0 , NULL , NULL , NULL , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( fd < 0 ) {
err = fd ;
printk ( " Open of COW file '%s' failed, errno = %d \n " , cow_file ,
- err ) ;
goto out ;
}
err = init_cow_file ( fd , cow_file , backing_file , sectorsize , alignment ,
bitmap_offset_out , bitmap_len_out ,
data_offset_out ) ;
if ( ! err )
2007-05-07 01:51:41 +04:00
return fd ;
2005-04-17 02:20:36 +04:00
os_close_file ( fd ) ;
out :
2007-05-07 01:51:41 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2005-10-11 07:10:32 +04:00
static int update_bitmap ( struct io_thread_req * req )
2005-04-17 02:20:36 +04:00
{
2005-10-11 07:10:32 +04:00
int n ;
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
if ( req - > cow_offset = = - 1 )
2007-05-07 01:51:41 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
n = os_seek_file ( req - > fds [ 1 ] , req - > cow_offset ) ;
if ( n < 0 ) {
printk ( " do_io - bitmap lseek failed : err = %d \n " , - n ) ;
2007-05-07 01:51:41 +04:00
return 1 ;
2005-10-11 07:10:32 +04:00
}
2005-04-17 02:20:36 +04:00
2007-05-07 01:51:43 +04:00
n = os_write_file ( req - > fds [ 1 ] , & req - > bitmap_words ,
sizeof ( req - > bitmap_words ) ) ;
2005-10-11 07:10:32 +04:00
if ( n ! = sizeof ( req - > bitmap_words ) ) {
printk ( " do_io - bitmap update failed, err = %d fd = %d \n " , - n ,
req - > fds [ 1 ] ) ;
2007-05-07 01:51:41 +04:00
return 1 ;
2005-10-11 07:10:32 +04:00
}
2005-04-17 02:20:36 +04:00
2007-05-07 01:51:41 +04:00
return 0 ;
2005-10-11 07:10:32 +04:00
}
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
void do_io ( struct io_thread_req * req )
{
char * buf ;
unsigned long len ;
int n , nsectors , start , end , bit ;
int err ;
__u64 off ;
nsectors = req - > length / req - > sectorsize ;
start = 0 ;
do {
bit = ubd_test_bit ( start , ( unsigned char * ) & req - > sector_mask ) ;
end = start ;
while ( ( end < nsectors ) & &
( ubd_test_bit ( end , ( unsigned char * )
& req - > sector_mask ) = = bit ) )
end + + ;
off = req - > offset + req - > offsets [ bit ] +
start * req - > sectorsize ;
len = ( end - start ) * req - > sectorsize ;
buf = & req - > buffer [ start * req - > sectorsize ] ;
err = os_seek_file ( req - > fds [ bit ] , off ) ;
if ( err < 0 ) {
printk ( " do_io - lseek failed : err = %d \n " , - err ) ;
req - > error = 1 ;
return ;
}
if ( req - > op = = UBD_READ ) {
n = 0 ;
do {
buf = & buf [ n ] ;
len - = n ;
2007-05-07 01:51:43 +04:00
n = os_read_file ( req - > fds [ bit ] , buf , len ) ;
2005-10-11 07:10:32 +04:00
if ( n < 0 ) {
printk ( " do_io - read failed, err = %d "
" fd = %d \n " , - n , req - > fds [ bit ] ) ;
req - > error = 1 ;
return ;
}
} while ( ( n < len ) & & ( n ! = 0 ) ) ;
if ( n < len ) memset ( & buf [ n ] , 0 , len - n ) ;
} else {
2007-05-07 01:51:43 +04:00
n = os_write_file ( req - > fds [ bit ] , buf , len ) ;
2005-10-11 07:10:32 +04:00
if ( n ! = len ) {
printk ( " do_io - write failed err = %d "
" fd = %d \n " , - n , req - > fds [ bit ] ) ;
req - > error = 1 ;
return ;
}
}
start = end ;
} while ( start < nsectors ) ;
2005-04-17 02:20:36 +04:00
2005-10-11 07:10:32 +04:00
req - > error = update_bitmap ( req ) ;
2005-04-17 02:20:36 +04:00
}
2005-10-11 07:10:32 +04:00
/* Changed in start_io_thread, which is serialized by being called only
* from ubd_init , which is an initcall .
*/
int kernel_fd = - 1 ;
2006-10-31 09:07:12 +03:00
/* Only changed by the io thread. XXX: currently unused. */
static int io_count = 0 ;
2005-10-11 07:10:32 +04:00
int io_thread ( void * arg )
{
2007-05-07 01:51:37 +04:00
struct io_thread_req * req ;
2005-10-11 07:10:32 +04:00
int n ;
ignore_sigwinch_sig ( ) ;
while ( 1 ) {
2007-05-07 01:51:43 +04:00
n = os_read_file ( kernel_fd , & req ,
2007-05-07 01:51:37 +04:00
sizeof ( struct io_thread_req * ) ) ;
if ( n ! = sizeof ( struct io_thread_req * ) ) {
2005-10-11 07:10:32 +04:00
if ( n < 0 )
printk ( " io_thread - read failed, fd = %d, "
" err = %d \n " , kernel_fd , - n ) ;
else {
printk ( " io_thread - short read, fd = %d, "
" length = %d \n " , kernel_fd , n ) ;
}
continue ;
}
io_count + + ;
2007-05-07 01:51:37 +04:00
do_io ( req ) ;
2007-05-07 01:51:43 +04:00
n = os_write_file ( kernel_fd , & req ,
2007-05-07 01:51:37 +04:00
sizeof ( struct io_thread_req * ) ) ;
if ( n ! = sizeof ( struct io_thread_req * ) )
2005-10-11 07:10:32 +04:00
printk ( " io_thread - write failed, fd = %d, err = %d \n " ,
kernel_fd , - n ) ;
}
2006-01-06 11:18:49 +03:00
return 0 ;
}