2005-04-17 02:20:36 +04:00
/*
* gendisk handling
*/
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/genhd.h>
2007-02-21 00:57:48 +03:00
# include <linux/kdev_t.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/blkdev.h>
# include <linux/init.h>
# include <linux/spinlock.h>
2008-10-04 23:53:21 +04:00
# include <linux/proc_fs.h>
2005-04-17 02:20:36 +04:00
# include <linux/seq_file.h>
# include <linux/slab.h>
# include <linux/kmod.h>
# include <linux/kobj_map.h>
2005-05-06 03:15:59 +04:00
# include <linux/buffer_head.h>
2006-02-07 01:12:43 +03:00
# include <linux/mutex.h>
2008-08-25 14:47:22 +04:00
# include <linux/idr.h>
2005-04-17 02:20:36 +04:00
2008-03-04 13:23:45 +03:00
# include "blk.h"
2007-05-22 00:08:01 +04:00
static DEFINE_MUTEX ( block_class_lock ) ;
# ifndef CONFIG_SYSFS_DEPRECATED
struct kobject * block_depr ;
# endif
2005-04-17 02:20:36 +04:00
2008-08-25 14:47:22 +04:00
/* for extended dynamic devt allocation, currently only one major is used */
# define MAX_EXT_DEVT (1 << MINORBITS)
/* For extended devt allocation. ext_devt_mutex prevents look up
* results from going away underneath its user .
*/
static DEFINE_MUTEX ( ext_devt_mutex ) ;
static DEFINE_IDR ( ext_devt_idr ) ;
2008-03-04 13:23:46 +03:00
static struct device_type disk_type ;
2008-09-03 11:03:02 +04:00
/**
* disk_get_part - get partition
* @ disk : disk to look partition from
* @ partno : partition number
*
* Look for partition @ partno from @ disk . If found , increment
* reference count and return it .
*
* CONTEXT :
* Don ' t care .
*
* RETURNS :
* Pointer to the found partition on success , NULL if not found .
*/
struct hd_struct * disk_get_part ( struct gendisk * disk , int partno )
{
2008-08-25 14:56:15 +04:00
struct hd_struct * part = NULL ;
struct disk_part_tbl * ptbl ;
2008-09-03 11:03:02 +04:00
2008-08-25 14:56:15 +04:00
if ( unlikely ( partno < 0 ) )
2008-09-03 11:03:02 +04:00
return NULL ;
2008-08-25 14:56:15 +04:00
2008-09-03 11:03:02 +04:00
rcu_read_lock ( ) ;
2008-08-25 14:56:15 +04:00
ptbl = rcu_dereference ( disk - > part_tbl ) ;
if ( likely ( partno < ptbl - > len ) ) {
part = rcu_dereference ( ptbl - > part [ partno ] ) ;
if ( part )
get_device ( part_to_dev ( part ) ) ;
}
2008-09-03 11:03:02 +04:00
rcu_read_unlock ( ) ;
return part ;
}
EXPORT_SYMBOL_GPL ( disk_get_part ) ;
/**
* disk_part_iter_init - initialize partition iterator
* @ piter : iterator to initialize
* @ disk : disk to iterate over
* @ flags : DISK_PITER_ * flags
*
* Initialize @ piter so that it iterates over partitions of @ disk .
*
* CONTEXT :
* Don ' t care .
*/
void disk_part_iter_init ( struct disk_part_iter * piter , struct gendisk * disk ,
unsigned int flags )
{
2008-08-25 14:56:15 +04:00
struct disk_part_tbl * ptbl ;
rcu_read_lock ( ) ;
ptbl = rcu_dereference ( disk - > part_tbl ) ;
2008-09-03 11:03:02 +04:00
piter - > disk = disk ;
piter - > part = NULL ;
if ( flags & DISK_PITER_REVERSE )
2008-08-25 14:56:15 +04:00
piter - > idx = ptbl - > len - 1 ;
2008-09-03 11:06:42 +04:00
else if ( flags & DISK_PITER_INCL_PART0 )
2008-09-03 11:03:02 +04:00
piter - > idx = 0 ;
2008-09-03 11:06:42 +04:00
else
piter - > idx = 1 ;
2008-09-03 11:03:02 +04:00
piter - > flags = flags ;
2008-08-25 14:56:15 +04:00
rcu_read_unlock ( ) ;
2008-09-03 11:03:02 +04:00
}
EXPORT_SYMBOL_GPL ( disk_part_iter_init ) ;
/**
* disk_part_iter_next - proceed iterator to the next partition and return it
* @ piter : iterator of interest
*
* Proceed @ piter to the next partition and return it .
*
* CONTEXT :
* Don ' t care .
*/
struct hd_struct * disk_part_iter_next ( struct disk_part_iter * piter )
{
2008-08-25 14:56:15 +04:00
struct disk_part_tbl * ptbl ;
2008-09-03 11:03:02 +04:00
int inc , end ;
/* put the last partition */
disk_put_part ( piter - > part ) ;
piter - > part = NULL ;
2008-08-25 14:56:15 +04:00
/* get part_tbl */
2008-09-03 11:03:02 +04:00
rcu_read_lock ( ) ;
2008-08-25 14:56:15 +04:00
ptbl = rcu_dereference ( piter - > disk - > part_tbl ) ;
2008-09-03 11:03:02 +04:00
/* determine iteration parameters */
if ( piter - > flags & DISK_PITER_REVERSE ) {
inc = - 1 ;
2008-09-03 11:06:42 +04:00
if ( piter - > flags & DISK_PITER_INCL_PART0 )
end = - 1 ;
else
end = 0 ;
2008-09-03 11:03:02 +04:00
} else {
inc = 1 ;
2008-08-25 14:56:15 +04:00
end = ptbl - > len ;
2008-09-03 11:03:02 +04:00
}
/* iterate to the next partition */
for ( ; piter - > idx ! = end ; piter - > idx + = inc ) {
struct hd_struct * part ;
2008-08-25 14:56:15 +04:00
part = rcu_dereference ( ptbl - > part [ piter - > idx ] ) ;
2008-09-03 11:03:02 +04:00
if ( ! part )
continue ;
if ( ! ( piter - > flags & DISK_PITER_INCL_EMPTY ) & & ! part - > nr_sects )
continue ;
2008-08-25 14:56:05 +04:00
get_device ( part_to_dev ( part ) ) ;
2008-09-03 11:03:02 +04:00
piter - > part = part ;
piter - > idx + = inc ;
break ;
}
rcu_read_unlock ( ) ;
return piter - > part ;
}
EXPORT_SYMBOL_GPL ( disk_part_iter_next ) ;
/**
* disk_part_iter_exit - finish up partition iteration
* @ piter : iter of interest
*
* Called when iteration is over . Cleans up @ piter .
*
* CONTEXT :
* Don ' t care .
*/
void disk_part_iter_exit ( struct disk_part_iter * piter )
{
disk_put_part ( piter - > part ) ;
piter - > part = NULL ;
}
EXPORT_SYMBOL_GPL ( disk_part_iter_exit ) ;
/**
* disk_map_sector_rcu - map sector to partition
* @ disk : gendisk of interest
* @ sector : sector to map
*
* Find out which partition @ sector maps to on @ disk . This is
* primarily used for stats accounting .
*
* CONTEXT :
* RCU read locked . The returned partition pointer is valid only
* while preemption is disabled .
*
* RETURNS :
2008-08-25 14:56:14 +04:00
* Found partition on success , part0 is returned if no partition matches
2008-09-03 11:03:02 +04:00
*/
struct hd_struct * disk_map_sector_rcu ( struct gendisk * disk , sector_t sector )
{
2008-08-25 14:56:15 +04:00
struct disk_part_tbl * ptbl ;
2008-09-03 11:03:02 +04:00
int i ;
2008-08-25 14:56:15 +04:00
ptbl = rcu_dereference ( disk - > part_tbl ) ;
for ( i = 1 ; i < ptbl - > len ; i + + ) {
struct hd_struct * part = rcu_dereference ( ptbl - > part [ i ] ) ;
2008-09-03 11:03:02 +04:00
if ( part & & part - > start_sect < = sector & &
sector < part - > start_sect + part - > nr_sects )
return part ;
}
2008-08-25 14:56:14 +04:00
return & disk - > part0 ;
2008-09-03 11:03:02 +04:00
}
EXPORT_SYMBOL_GPL ( disk_map_sector_rcu ) ;
2005-04-17 02:20:36 +04:00
/*
* Can be deleted altogether . Later .
*
*/
static struct blk_major_name {
struct blk_major_name * next ;
int major ;
char name [ 16 ] ;
2006-03-31 14:30:32 +04:00
} * major_names [ BLKDEV_MAJOR_HASH_SIZE ] ;
2005-04-17 02:20:36 +04:00
/* index in the above - for now: assume no multimajor ranges */
static inline int major_to_index ( int major )
{
2006-03-31 14:30:32 +04:00
return major % BLKDEV_MAJOR_HASH_SIZE ;
2006-01-15 00:20:38 +03:00
}
2006-03-31 14:30:32 +04:00
# ifdef CONFIG_PROC_FS
2008-09-03 11:01:09 +04:00
void blkdev_show ( struct seq_file * seqf , off_t offset )
2006-01-15 00:20:38 +03:00
{
2006-03-31 14:30:32 +04:00
struct blk_major_name * dp ;
2006-01-15 00:20:38 +03:00
2006-03-31 14:30:32 +04:00
if ( offset < BLKDEV_MAJOR_HASH_SIZE ) {
2007-05-22 00:08:01 +04:00
mutex_lock ( & block_class_lock ) ;
2006-03-31 14:30:32 +04:00
for ( dp = major_names [ offset ] ; dp ; dp = dp - > next )
2008-09-03 11:01:09 +04:00
seq_printf ( seqf , " %3d %s \n " , dp - > major , dp - > name ) ;
2007-05-22 00:08:01 +04:00
mutex_unlock ( & block_class_lock ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-03-31 14:30:32 +04:00
# endif /* CONFIG_PROC_FS */
2005-04-17 02:20:36 +04:00
int register_blkdev ( unsigned int major , const char * name )
{
struct blk_major_name * * n , * p ;
int index , ret = 0 ;
2007-05-22 00:08:01 +04:00
mutex_lock ( & block_class_lock ) ;
2005-04-17 02:20:36 +04:00
/* temporary */
if ( major = = 0 ) {
for ( index = ARRAY_SIZE ( major_names ) - 1 ; index > 0 ; index - - ) {
if ( major_names [ index ] = = NULL )
break ;
}
if ( index = = 0 ) {
printk ( " register_blkdev: failed to get major for %s \n " ,
name ) ;
ret = - EBUSY ;
goto out ;
}
major = index ;
ret = major ;
}
p = kmalloc ( sizeof ( struct blk_major_name ) , GFP_KERNEL ) ;
if ( p = = NULL ) {
ret = - ENOMEM ;
goto out ;
}
p - > major = major ;
strlcpy ( p - > name , name , sizeof ( p - > name ) ) ;
p - > next = NULL ;
index = major_to_index ( major ) ;
for ( n = & major_names [ index ] ; * n ; n = & ( * n ) - > next ) {
if ( ( * n ) - > major = = major )
break ;
}
if ( ! * n )
* n = p ;
else
ret = - EBUSY ;
if ( ret < 0 ) {
printk ( " register_blkdev: cannot get major %d for %s \n " ,
major , name ) ;
kfree ( p ) ;
}
out :
2007-05-22 00:08:01 +04:00
mutex_unlock ( & block_class_lock ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
EXPORT_SYMBOL ( register_blkdev ) ;
2007-07-17 15:03:47 +04:00
void unregister_blkdev ( unsigned int major , const char * name )
2005-04-17 02:20:36 +04:00
{
struct blk_major_name * * n ;
struct blk_major_name * p = NULL ;
int index = major_to_index ( major ) ;
2007-05-22 00:08:01 +04:00
mutex_lock ( & block_class_lock ) ;
2005-04-17 02:20:36 +04:00
for ( n = & major_names [ index ] ; * n ; n = & ( * n ) - > next )
if ( ( * n ) - > major = = major )
break ;
2007-07-17 15:03:45 +04:00
if ( ! * n | | strcmp ( ( * n ) - > name , name ) ) {
WARN_ON ( 1 ) ;
} else {
2005-04-17 02:20:36 +04:00
p = * n ;
* n = p - > next ;
}
2007-05-22 00:08:01 +04:00
mutex_unlock ( & block_class_lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( p ) ;
}
EXPORT_SYMBOL ( unregister_blkdev ) ;
static struct kobj_map * bdev_map ;
2008-08-25 14:47:25 +04:00
/**
* blk_mangle_minor - scatter minor numbers apart
* @ minor : minor number to mangle
*
* Scatter consecutively allocated @ minor number apart if MANGLE_DEVT
* is enabled . Mangling twice gives the original value .
*
* RETURNS :
* Mangled value .
*
* CONTEXT :
* Don ' t care .
*/
static int blk_mangle_minor ( int minor )
{
# ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT
int i ;
for ( i = 0 ; i < MINORBITS / 2 ; i + + ) {
int low = minor & ( 1 < < i ) ;
int high = minor & ( 1 < < ( MINORBITS - 1 - i ) ) ;
int distance = MINORBITS - 1 - 2 * i ;
minor ^ = low | high ; /* clear both bits */
low < < = distance ; /* swap the positions */
high > > = distance ;
minor | = low | high ; /* and set */
}
# endif
return minor ;
}
2008-08-25 14:47:22 +04:00
/**
* blk_alloc_devt - allocate a dev_t for a partition
* @ part : partition to allocate dev_t for
* @ devt : out parameter for resulting dev_t
*
* Allocate a dev_t for block device .
*
* RETURNS :
* 0 on success , allocated dev_t is returned in * @ devt . - errno on
* failure .
*
* CONTEXT :
* Might sleep .
*/
int blk_alloc_devt ( struct hd_struct * part , dev_t * devt )
{
struct gendisk * disk = part_to_disk ( part ) ;
int idx , rc ;
/* in consecutive minor range? */
if ( part - > partno < disk - > minors ) {
* devt = MKDEV ( disk - > major , disk - > first_minor + part - > partno ) ;
return 0 ;
}
/* allocate ext devt */
do {
if ( ! idr_pre_get ( & ext_devt_idr , GFP_KERNEL ) )
return - ENOMEM ;
rc = idr_get_new ( & ext_devt_idr , part , & idx ) ;
} while ( rc = = - EAGAIN ) ;
if ( rc )
return rc ;
if ( idx > MAX_EXT_DEVT ) {
idr_remove ( & ext_devt_idr , idx ) ;
return - EBUSY ;
}
2008-08-25 14:47:25 +04:00
* devt = MKDEV ( BLOCK_EXT_MAJOR , blk_mangle_minor ( idx ) ) ;
2008-08-25 14:47:22 +04:00
return 0 ;
}
/**
* blk_free_devt - free a dev_t
* @ devt : dev_t to free
*
* Free @ devt which was allocated using blk_alloc_devt ( ) .
*
* CONTEXT :
* Might sleep .
*/
void blk_free_devt ( dev_t devt )
{
might_sleep ( ) ;
if ( devt = = MKDEV ( 0 , 0 ) )
return ;
if ( MAJOR ( devt ) = = BLOCK_EXT_MAJOR ) {
mutex_lock ( & ext_devt_mutex ) ;
2008-08-25 14:47:25 +04:00
idr_remove ( & ext_devt_idr , blk_mangle_minor ( MINOR ( devt ) ) ) ;
2008-08-25 14:47:22 +04:00
mutex_unlock ( & ext_devt_mutex ) ;
}
}
2008-08-25 14:47:23 +04:00
static char * bdevt_str ( dev_t devt , char * buf )
{
if ( MAJOR ( devt ) < = 0xff & & MINOR ( devt ) < = 0xff ) {
char tbuf [ BDEVT_SIZE ] ;
snprintf ( tbuf , BDEVT_SIZE , " %02x%02x " , MAJOR ( devt ) , MINOR ( devt ) ) ;
snprintf ( buf , BDEVT_SIZE , " %-9s " , tbuf ) ;
} else
snprintf ( buf , BDEVT_SIZE , " %03x:%05x " , MAJOR ( devt ) , MINOR ( devt ) ) ;
return buf ;
}
2005-04-17 02:20:36 +04:00
/*
* Register device numbers dev . . ( dev + range - 1 )
* range must be nonzero
* The hash chain is sorted on range , so that subranges can override .
*/
2007-05-22 00:08:01 +04:00
void blk_register_region ( dev_t devt , unsigned long range , struct module * module ,
2005-04-17 02:20:36 +04:00
struct kobject * ( * probe ) ( dev_t , int * , void * ) ,
int ( * lock ) ( dev_t , void * ) , void * data )
{
2007-05-22 00:08:01 +04:00
kobj_map ( bdev_map , devt , range , module , probe , lock , data ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( blk_register_region ) ;
2007-05-22 00:08:01 +04:00
void blk_unregister_region ( dev_t devt , unsigned long range )
2005-04-17 02:20:36 +04:00
{
2007-05-22 00:08:01 +04:00
kobj_unmap ( bdev_map , devt , range ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( blk_unregister_region ) ;
2008-09-03 11:01:09 +04:00
static struct kobject * exact_match ( dev_t devt , int * partno , void * data )
2005-04-17 02:20:36 +04:00
{
struct gendisk * p = data ;
2007-05-22 00:08:01 +04:00
2008-08-25 14:56:05 +04:00
return & disk_to_dev ( p ) - > kobj ;
2005-04-17 02:20:36 +04:00
}
2007-05-22 00:08:01 +04:00
static int exact_lock ( dev_t devt , void * data )
2005-04-17 02:20:36 +04:00
{
struct gendisk * p = data ;
if ( ! get_disk ( p ) )
return - 1 ;
return 0 ;
}
/**
* add_disk - add partitioning information to kernel list
* @ disk : per - device partitioning information
*
* This function registers the partitioning information in @ disk
* with the kernel .
2008-08-25 14:56:17 +04:00
*
* FIXME : error handling
2005-04-17 02:20:36 +04:00
*/
void add_disk ( struct gendisk * disk )
{
2008-04-30 11:54:32 +04:00
struct backing_dev_info * bdi ;
2008-08-25 14:56:17 +04:00
dev_t devt ;
2008-05-23 01:21:08 +04:00
int retval ;
2008-04-30 11:54:32 +04:00
2008-08-25 14:56:17 +04:00
/* minors == 0 indicates to use ext devt from part0 and should
* be accompanied with EXT_DEVT flag . Make sure all
* parameters make sense .
*/
WARN_ON ( disk - > minors & & ! ( disk - > major | | disk - > first_minor ) ) ;
WARN_ON ( ! disk - > minors & & ! ( disk - > flags & GENHD_FL_EXT_DEVT ) ) ;
2005-04-17 02:20:36 +04:00
disk - > flags | = GENHD_FL_UP ;
2008-08-25 14:56:17 +04:00
retval = blk_alloc_devt ( & disk - > part0 , & devt ) ;
if ( retval ) {
WARN_ON ( 1 ) ;
return ;
}
disk_to_dev ( disk ) - > devt = devt ;
/* ->major and ->first_minor aren't supposed to be
* dereferenced from here on , but set them just in case .
*/
disk - > major = MAJOR ( devt ) ;
disk - > first_minor = MINOR ( devt ) ;
2008-09-03 11:01:48 +04:00
blk_register_region ( disk_devt ( disk ) , disk - > minors , NULL ,
exact_match , exact_lock , disk ) ;
2005-04-17 02:20:36 +04:00
register_disk ( disk ) ;
blk_register_queue ( disk ) ;
2008-04-30 11:54:32 +04:00
bdi = & disk - > queue - > backing_dev_info ;
2008-09-03 11:01:48 +04:00
bdi_register_dev ( bdi , disk_devt ( disk ) ) ;
2008-08-25 14:56:05 +04:00
retval = sysfs_create_link ( & disk_to_dev ( disk ) - > kobj , & bdi - > dev - > kobj ,
" bdi " ) ;
2008-05-23 01:21:08 +04:00
WARN_ON ( retval ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( add_disk ) ;
EXPORT_SYMBOL ( del_gendisk ) ; /* in partitions/check.c */
void unlink_gendisk ( struct gendisk * disk )
{
2008-08-25 14:56:05 +04:00
sysfs_remove_link ( & disk_to_dev ( disk ) - > kobj , " bdi " ) ;
2008-04-30 11:54:32 +04:00
bdi_unregister ( & disk - > queue - > backing_dev_info ) ;
2005-04-17 02:20:36 +04:00
blk_unregister_queue ( disk ) ;
2008-09-03 11:01:48 +04:00
blk_unregister_region ( disk_devt ( disk ) , disk - > minors ) ;
2005-04-17 02:20:36 +04:00
}
/**
* get_gendisk - get partitioning information for a given device
2008-08-19 22:13:11 +04:00
* @ devt : device to get partitioning information for
2008-10-16 09:46:23 +04:00
* @ partno : returned partition index
2005-04-17 02:20:36 +04:00
*
* This function gets the structure containing partitioning
2008-08-19 22:13:11 +04:00
* information for the given device @ devt .
2005-04-17 02:20:36 +04:00
*/
2008-09-03 11:01:09 +04:00
struct gendisk * get_gendisk ( dev_t devt , int * partno )
2005-04-17 02:20:36 +04:00
{
2008-08-25 14:47:22 +04:00
struct gendisk * disk = NULL ;
if ( MAJOR ( devt ) ! = BLOCK_EXT_MAJOR ) {
struct kobject * kobj ;
kobj = kobj_lookup ( bdev_map , devt , partno ) ;
if ( kobj )
disk = dev_to_disk ( kobj_to_dev ( kobj ) ) ;
} else {
struct hd_struct * part ;
mutex_lock ( & ext_devt_mutex ) ;
2008-08-25 14:47:25 +04:00
part = idr_find ( & ext_devt_idr , blk_mangle_minor ( MINOR ( devt ) ) ) ;
2008-08-25 14:47:22 +04:00
if ( part & & get_disk ( part_to_disk ( part ) ) ) {
* partno = part - > partno ;
disk = part_to_disk ( part ) ;
}
mutex_unlock ( & ext_devt_mutex ) ;
}
2007-05-22 00:08:01 +04:00
2008-08-25 14:47:22 +04:00
return disk ;
2005-04-17 02:20:36 +04:00
}
2008-09-03 11:01:48 +04:00
/**
* bdget_disk - do bdget ( ) by gendisk and partition number
* @ disk : gendisk of interest
* @ partno : partition number
*
* Find partition @ partno from @ disk , do bdget ( ) on it .
*
* CONTEXT :
* Don ' t care .
*
* RETURNS :
* Resulting block_device on success , NULL on failure .
*/
2008-08-28 11:27:42 +04:00
struct block_device * bdget_disk ( struct gendisk * disk , int partno )
2008-09-03 11:01:48 +04:00
{
2008-08-29 11:01:47 +04:00
struct hd_struct * part ;
struct block_device * bdev = NULL ;
2008-09-03 11:01:48 +04:00
2008-08-29 11:01:47 +04:00
part = disk_get_part ( disk , partno ) ;
2008-08-29 13:41:51 +04:00
if ( part )
2008-08-29 11:01:47 +04:00
bdev = bdget ( part_devt ( part ) ) ;
disk_put_part ( part ) ;
2008-09-03 11:01:48 +04:00
2008-08-29 11:01:47 +04:00
return bdev ;
2008-09-03 11:01:48 +04:00
}
EXPORT_SYMBOL ( bdget_disk ) ;
2008-05-23 01:21:08 +04:00
/*
* print a full list of all partitions - intended for places where the root
* filesystem can ' t be mounted and thus to give the victim some idea of what
* went wrong
*/
void __init printk_all_partitions ( void )
{
2008-09-03 10:57:12 +04:00
struct class_dev_iter iter ;
struct device * dev ;
class_dev_iter_init ( & iter , & block_class , NULL , & disk_type ) ;
while ( ( dev = class_dev_iter_next ( & iter ) ) ) {
struct gendisk * disk = dev_to_disk ( dev ) ;
2008-09-03 11:03:02 +04:00
struct disk_part_iter piter ;
struct hd_struct * part ;
2008-08-25 14:47:23 +04:00
char name_buf [ BDEVNAME_SIZE ] ;
char devt_buf [ BDEVT_SIZE ] ;
2008-09-03 10:57:12 +04:00
/*
* Don ' t show empty devices or things that have been
* surpressed
*/
if ( get_capacity ( disk ) = = 0 | |
( disk - > flags & GENHD_FL_SUPPRESS_PARTITION_INFO ) )
continue ;
/*
* Note , unlike / proc / partitions , I am showing the
* numbers in hex - the same format as the root =
* option takes .
*/
2008-08-25 14:56:14 +04:00
disk_part_iter_init ( & piter , disk , DISK_PITER_INCL_PART0 ) ;
while ( ( part = disk_part_iter_next ( & piter ) ) ) {
bool is_part0 = part = = & disk - > part0 ;
2008-09-03 10:57:12 +04:00
2008-08-25 14:56:14 +04:00
printk ( " %s%s %10llu %s " , is_part0 ? " " : " " ,
2008-08-25 14:47:23 +04:00
bdevt_str ( part_devt ( part ) , devt_buf ) ,
2008-09-03 11:01:48 +04:00
( unsigned long long ) part - > nr_sects > > 1 ,
2008-08-25 14:47:23 +04:00
disk_name ( disk , part - > partno , name_buf ) ) ;
2008-08-25 14:56:14 +04:00
if ( is_part0 ) {
if ( disk - > driverfs_dev ! = NULL & &
disk - > driverfs_dev - > driver ! = NULL )
printk ( " driver: %s \n " ,
disk - > driverfs_dev - > driver - > name ) ;
else
printk ( " (driver?) \n " ) ;
} else
printk ( " \n " ) ;
}
2008-09-03 11:03:02 +04:00
disk_part_iter_exit ( & piter ) ;
2008-09-03 10:57:12 +04:00
}
class_dev_iter_exit ( & iter ) ;
2007-05-09 13:33:24 +04:00
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PROC_FS
/* iterator */
2008-09-03 10:57:12 +04:00
static void * disk_seqf_start ( struct seq_file * seqf , loff_t * pos )
2008-05-23 01:21:08 +04:00
{
2008-09-03 10:57:12 +04:00
loff_t skip = * pos ;
struct class_dev_iter * iter ;
struct device * dev ;
2008-05-23 01:21:08 +04:00
2008-08-28 11:27:42 +04:00
iter = kmalloc ( sizeof ( * iter ) , GFP_KERNEL ) ;
2008-09-03 10:57:12 +04:00
if ( ! iter )
return ERR_PTR ( - ENOMEM ) ;
seqf - > private = iter ;
class_dev_iter_init ( iter , & block_class , NULL , & disk_type ) ;
do {
dev = class_dev_iter_next ( iter ) ;
if ( ! dev )
return NULL ;
} while ( skip - - ) ;
return dev_to_disk ( dev ) ;
2008-05-23 01:21:08 +04:00
}
2008-09-03 10:57:12 +04:00
static void * disk_seqf_next ( struct seq_file * seqf , void * v , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
2007-05-22 00:08:01 +04:00
struct device * dev ;
2005-04-17 02:20:36 +04:00
2008-09-03 10:57:12 +04:00
( * pos ) + + ;
dev = class_dev_iter_next ( seqf - > private ) ;
2008-09-03 10:53:37 +04:00
if ( dev )
2008-05-23 01:21:08 +04:00
return dev_to_disk ( dev ) ;
2008-09-03 10:53:37 +04:00
2005-04-17 02:20:36 +04:00
return NULL ;
}
2008-09-03 10:57:12 +04:00
static void disk_seqf_stop ( struct seq_file * seqf , void * v )
2008-05-23 01:21:08 +04:00
{
2008-09-03 10:57:12 +04:00
struct class_dev_iter * iter = seqf - > private ;
2008-05-23 01:21:08 +04:00
2008-09-03 10:57:12 +04:00
/* stop is called even after start failed :-( */
if ( iter ) {
class_dev_iter_exit ( iter ) ;
kfree ( iter ) ;
2008-08-16 16:30:30 +04:00
}
2005-04-17 02:20:36 +04:00
}
2008-09-03 10:57:12 +04:00
static void * show_partition_start ( struct seq_file * seqf , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
2008-09-03 10:57:12 +04:00
static void * p ;
p = disk_seqf_start ( seqf , pos ) ;
2008-09-04 11:17:31 +04:00
if ( ! IS_ERR ( p ) & & p & & ! * pos )
2008-09-03 10:57:12 +04:00
seq_puts ( seqf , " major minor #blocks name \n \n " ) ;
return p ;
2005-04-17 02:20:36 +04:00
}
2008-09-03 11:01:09 +04:00
static int show_partition ( struct seq_file * seqf , void * v )
2005-04-17 02:20:36 +04:00
{
struct gendisk * sgp = v ;
2008-09-03 11:03:02 +04:00
struct disk_part_iter piter ;
struct hd_struct * part ;
2005-04-17 02:20:36 +04:00
char buf [ BDEVNAME_SIZE ] ;
/* Don't show non-partitionable removeable devices or empty devices */
2008-09-03 11:06:42 +04:00
if ( ! get_capacity ( sgp ) | | ( ! disk_partitionable ( sgp ) & &
2008-09-03 11:01:48 +04:00
( sgp - > flags & GENHD_FL_REMOVABLE ) ) )
2005-04-17 02:20:36 +04:00
return 0 ;
if ( sgp - > flags & GENHD_FL_SUPPRESS_PARTITION_INFO )
return 0 ;
/* show the full disk and all non-0 size partitions of it */
2008-08-25 14:56:14 +04:00
disk_part_iter_init ( & piter , sgp , DISK_PITER_INCL_PART0 ) ;
2008-09-03 11:03:02 +04:00
while ( ( part = disk_part_iter_next ( & piter ) ) )
2008-08-25 14:47:23 +04:00
seq_printf ( seqf , " %4d %7d %10llu %s \n " ,
2008-09-03 11:01:48 +04:00
MAJOR ( part_devt ( part ) ) , MINOR ( part_devt ( part ) ) ,
( unsigned long long ) part - > nr_sects > > 1 ,
disk_name ( sgp , part - > partno , buf ) ) ;
2008-09-03 11:03:02 +04:00
disk_part_iter_exit ( & piter ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-10-04 23:53:21 +04:00
static const struct seq_operations partitions_op = {
2008-09-03 10:57:12 +04:00
. start = show_partition_start ,
. next = disk_seqf_next ,
. stop = disk_seqf_stop ,
2007-05-22 00:08:01 +04:00
. show = show_partition
2005-04-17 02:20:36 +04:00
} ;
2008-10-04 23:53:21 +04:00
static int partitions_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & partitions_op ) ;
}
static const struct file_operations proc_partitions_operations = {
. open = partitions_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
2005-04-17 02:20:36 +04:00
# endif
2008-09-03 11:01:09 +04:00
static struct kobject * base_probe ( dev_t devt , int * partno , void * data )
2005-04-17 02:20:36 +04:00
{
2007-05-22 00:08:01 +04:00
if ( request_module ( " block-major-%d-%d " , MAJOR ( devt ) , MINOR ( devt ) ) > 0 )
2005-04-17 02:20:36 +04:00
/* Make old-style 2.4 aliases work */
2007-05-22 00:08:01 +04:00
request_module ( " block-major-%d " , MAJOR ( devt ) ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
static int __init genhd_device_init ( void )
{
2008-04-21 21:51:07 +04:00
int error ;
block_class . dev_kobj = sysfs_dev_block_kobj ;
error = class_register ( & block_class ) ;
2008-03-12 03:13:15 +03:00
if ( unlikely ( error ) )
return error ;
2007-05-22 00:08:01 +04:00
bdev_map = kobj_map_init ( base_probe , & block_class_lock ) ;
2005-04-17 02:20:36 +04:00
blk_dev_init ( ) ;
2007-05-22 00:08:01 +04:00
# ifndef CONFIG_SYSFS_DEPRECATED
/* create top-level block dir */
block_depr = kobject_create_and_add ( " block " , NULL ) ;
# endif
2007-11-06 21:36:58 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
subsys_initcall ( genhd_device_init ) ;
2007-05-22 00:08:01 +04:00
static ssize_t disk_range_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-05-22 00:08:01 +04:00
struct gendisk * disk = dev_to_disk ( dev ) ;
2005-04-17 02:20:36 +04:00
2007-05-22 00:08:01 +04:00
return sprintf ( buf , " %d \n " , disk - > minors ) ;
2005-04-17 02:20:36 +04:00
}
2008-08-25 14:47:23 +04:00
static ssize_t disk_ext_range_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct gendisk * disk = dev_to_disk ( dev ) ;
2008-09-03 11:06:42 +04:00
return sprintf ( buf , " %d \n " , disk_max_parts ( disk ) ) ;
2008-08-25 14:47:23 +04:00
}
2007-05-22 00:08:01 +04:00
static ssize_t disk_removable_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-10-01 16:49:43 +04:00
{
2007-05-22 00:08:01 +04:00
struct gendisk * disk = dev_to_disk ( dev ) ;
2005-10-01 16:49:43 +04:00
2007-05-22 00:08:01 +04:00
return sprintf ( buf , " %d \n " ,
( disk - > flags & GENHD_FL_REMOVABLE ? 1 : 0 ) ) ;
2005-10-01 16:49:43 +04:00
}
2008-06-13 11:41:00 +04:00
static ssize_t disk_ro_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct gendisk * disk = dev_to_disk ( dev ) ;
2008-08-25 14:56:10 +04:00
return sprintf ( buf , " %d \n " , get_disk_ro ( disk ) ? 1 : 0 ) ;
2008-06-13 11:41:00 +04:00
}
2007-05-22 00:08:01 +04:00
static ssize_t disk_capability_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2007-05-24 00:57:38 +04:00
{
2007-05-22 00:08:01 +04:00
struct gendisk * disk = dev_to_disk ( dev ) ;
return sprintf ( buf , " %x \n " , disk - > flags ) ;
2007-05-24 00:57:38 +04:00
}
2007-05-22 00:08:01 +04:00
static DEVICE_ATTR ( range , S_IRUGO , disk_range_show , NULL ) ;
2008-08-25 14:47:23 +04:00
static DEVICE_ATTR ( ext_range , S_IRUGO , disk_ext_range_show , NULL ) ;
2007-05-22 00:08:01 +04:00
static DEVICE_ATTR ( removable , S_IRUGO , disk_removable_show , NULL ) ;
2008-06-13 11:41:00 +04:00
static DEVICE_ATTR ( ro , S_IRUGO , disk_ro_show , NULL ) ;
2008-08-25 14:56:09 +04:00
static DEVICE_ATTR ( size , S_IRUGO , part_size_show , NULL ) ;
2007-05-22 00:08:01 +04:00
static DEVICE_ATTR ( capability , S_IRUGO , disk_capability_show , NULL ) ;
2008-08-25 14:56:14 +04:00
static DEVICE_ATTR ( stat , S_IRUGO , part_stat_show , NULL ) ;
2006-12-08 13:39:46 +03:00
# ifdef CONFIG_FAIL_MAKE_REQUEST
2007-05-22 00:08:01 +04:00
static struct device_attribute dev_attr_fail =
2008-08-25 14:56:13 +04:00
__ATTR ( make - it - fail , S_IRUGO | S_IWUSR , part_fail_show , part_fail_store ) ;
2006-12-08 13:39:46 +03:00
# endif
2008-09-14 16:56:33 +04:00
# ifdef CONFIG_FAIL_IO_TIMEOUT
static struct device_attribute dev_attr_fail_timeout =
__ATTR ( io - timeout - fail , S_IRUGO | S_IWUSR , part_timeout_show ,
part_timeout_store ) ;
# endif
2007-05-22 00:08:01 +04:00
static struct attribute * disk_attrs [ ] = {
& dev_attr_range . attr ,
2008-08-25 14:47:23 +04:00
& dev_attr_ext_range . attr ,
2007-05-22 00:08:01 +04:00
& dev_attr_removable . attr ,
2008-06-13 11:41:00 +04:00
& dev_attr_ro . attr ,
2007-05-22 00:08:01 +04:00
& dev_attr_size . attr ,
& dev_attr_capability . attr ,
& dev_attr_stat . attr ,
# ifdef CONFIG_FAIL_MAKE_REQUEST
& dev_attr_fail . attr ,
2008-09-14 16:56:33 +04:00
# endif
# ifdef CONFIG_FAIL_IO_TIMEOUT
& dev_attr_fail_timeout . attr ,
2007-05-22 00:08:01 +04:00
# endif
NULL
} ;
static struct attribute_group disk_attr_group = {
. attrs = disk_attrs ,
} ;
static struct attribute_group * disk_attr_groups [ ] = {
& disk_attr_group ,
NULL
2005-04-17 02:20:36 +04:00
} ;
2008-08-25 14:56:15 +04:00
static void disk_free_ptbl_rcu_cb ( struct rcu_head * head )
{
struct disk_part_tbl * ptbl =
container_of ( head , struct disk_part_tbl , rcu_head ) ;
kfree ( ptbl ) ;
}
/**
* disk_replace_part_tbl - replace disk - > part_tbl in RCU - safe way
* @ disk : disk to replace part_tbl for
* @ new_ptbl : new part_tbl to install
*
* Replace disk - > part_tbl with @ new_ptbl in RCU - safe way . The
* original ptbl is freed using RCU callback .
*
* LOCKING :
* Matching bd_mutx locked .
*/
static void disk_replace_part_tbl ( struct gendisk * disk ,
struct disk_part_tbl * new_ptbl )
{
struct disk_part_tbl * old_ptbl = disk - > part_tbl ;
rcu_assign_pointer ( disk - > part_tbl , new_ptbl ) ;
if ( old_ptbl )
call_rcu ( & old_ptbl - > rcu_head , disk_free_ptbl_rcu_cb ) ;
}
/**
* disk_expand_part_tbl - expand disk - > part_tbl
* @ disk : disk to expand part_tbl for
* @ partno : expand such that this partno can fit in
*
* Expand disk - > part_tbl such that @ partno can fit in . disk - > part_tbl
* uses RCU to allow unlocked dereferencing for stats and other stuff .
*
* LOCKING :
* Matching bd_mutex locked , might sleep .
*
* RETURNS :
* 0 on success , - errno on failure .
*/
int disk_expand_part_tbl ( struct gendisk * disk , int partno )
{
struct disk_part_tbl * old_ptbl = disk - > part_tbl ;
struct disk_part_tbl * new_ptbl ;
int len = old_ptbl ? old_ptbl - > len : 0 ;
int target = partno + 1 ;
size_t size ;
int i ;
/* disk_max_parts() is zero during initialization, ignore if so */
if ( disk_max_parts ( disk ) & & target > disk_max_parts ( disk ) )
return - EINVAL ;
if ( target < = len )
return 0 ;
size = sizeof ( * new_ptbl ) + target * sizeof ( new_ptbl - > part [ 0 ] ) ;
new_ptbl = kzalloc_node ( size , GFP_KERNEL , disk - > node_id ) ;
if ( ! new_ptbl )
return - ENOMEM ;
INIT_RCU_HEAD ( & new_ptbl - > rcu_head ) ;
new_ptbl - > len = target ;
for ( i = 0 ; i < len ; i + + )
rcu_assign_pointer ( new_ptbl - > part [ i ] , old_ptbl - > part [ i ] ) ;
disk_replace_part_tbl ( disk , new_ptbl ) ;
return 0 ;
}
2007-05-22 00:08:01 +04:00
static void disk_release ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2007-05-22 00:08:01 +04:00
struct gendisk * disk = dev_to_disk ( dev ) ;
2005-04-17 02:20:36 +04:00
kfree ( disk - > random ) ;
2008-08-25 14:56:15 +04:00
disk_replace_part_tbl ( disk , NULL ) ;
2008-08-25 14:56:14 +04:00
free_part_stats ( & disk - > part0 ) ;
2005-04-17 02:20:36 +04:00
kfree ( disk ) ;
}
2007-05-22 00:08:01 +04:00
struct class block_class = {
. name = " block " ,
2005-04-17 02:20:36 +04:00
} ;
2008-03-04 13:23:46 +03:00
static struct device_type disk_type = {
2007-05-22 00:08:01 +04:00
. name = " disk " ,
. groups = disk_attr_groups ,
. release = disk_release ,
2005-04-17 02:20:36 +04:00
} ;
2008-05-23 20:44:11 +04:00
# ifdef CONFIG_PROC_FS
2008-09-03 11:01:09 +04:00
/*
* aggregate disk stat collector . Uses the same stats that the sysfs
* entries do , above , but makes them available through one seq_file .
*
* The output looks suspiciously like / proc / partitions with a bunch of
* extra fields .
*/
static int diskstats_show ( struct seq_file * seqf , void * v )
2005-04-17 02:20:36 +04:00
{
struct gendisk * gp = v ;
2008-09-03 11:03:02 +04:00
struct disk_part_iter piter ;
struct hd_struct * hd ;
2005-04-17 02:20:36 +04:00
char buf [ BDEVNAME_SIZE ] ;
2008-08-25 14:47:21 +04:00
int cpu ;
2005-04-17 02:20:36 +04:00
/*
2008-08-25 14:56:05 +04:00
if ( & disk_to_dev ( gp ) - > kobj . entry = = block_class . devices . next )
2008-09-03 11:01:09 +04:00
seq_puts ( seqf , " major minor name "
2005-04-17 02:20:36 +04:00
" rio rmerge rsect ruse wio wmerge "
" wsect wuse running use aveq "
" \n \n " ) ;
*/
2008-08-25 14:56:14 +04:00
disk_part_iter_init ( & piter , gp , DISK_PITER_INCL_PART0 ) ;
2008-09-03 11:03:02 +04:00
while ( ( hd = disk_part_iter_next ( & piter ) ) ) {
2008-08-25 14:56:14 +04:00
cpu = part_stat_lock ( ) ;
2008-08-25 14:47:21 +04:00
part_round_stats ( cpu , hd ) ;
2008-08-25 14:56:14 +04:00
part_stat_unlock ( ) ;
2008-08-25 14:47:23 +04:00
seq_printf ( seqf , " %4d %7d %s %lu %lu %llu "
2008-02-08 13:04:56 +03:00
" %u %lu %lu %llu %u %u %u %u \n " ,
2008-09-03 11:01:48 +04:00
MAJOR ( part_devt ( hd ) ) , MINOR ( part_devt ( hd ) ) ,
disk_name ( gp , hd - > partno , buf ) ,
2008-02-08 13:04:56 +03:00
part_stat_read ( hd , ios [ 0 ] ) ,
part_stat_read ( hd , merges [ 0 ] ) ,
( unsigned long long ) part_stat_read ( hd , sectors [ 0 ] ) ,
jiffies_to_msecs ( part_stat_read ( hd , ticks [ 0 ] ) ) ,
part_stat_read ( hd , ios [ 1 ] ) ,
part_stat_read ( hd , merges [ 1 ] ) ,
( unsigned long long ) part_stat_read ( hd , sectors [ 1 ] ) ,
jiffies_to_msecs ( part_stat_read ( hd , ticks [ 1 ] ) ) ,
hd - > in_flight ,
jiffies_to_msecs ( part_stat_read ( hd , io_ticks ) ) ,
jiffies_to_msecs ( part_stat_read ( hd , time_in_queue ) )
) ;
2005-04-17 02:20:36 +04:00
}
2008-09-03 11:03:02 +04:00
disk_part_iter_exit ( & piter ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-10-06 12:55:38 +04:00
static const struct seq_operations diskstats_op = {
2008-09-03 10:57:12 +04:00
. start = disk_seqf_start ,
. next = disk_seqf_next ,
. stop = disk_seqf_stop ,
2005-04-17 02:20:36 +04:00
. show = diskstats_show
} ;
2008-10-04 23:53:21 +04:00
2008-10-06 12:55:38 +04:00
static int diskstats_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & diskstats_op ) ;
}
static const struct file_operations proc_diskstats_operations = {
. open = diskstats_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
2008-10-04 23:53:21 +04:00
static int __init proc_genhd_init ( void )
{
2008-10-06 12:55:38 +04:00
proc_create ( " diskstats " , 0 , NULL , & proc_diskstats_operations ) ;
2008-10-04 23:53:21 +04:00
proc_create ( " partitions " , 0 , NULL , & proc_partitions_operations ) ;
return 0 ;
}
module_init ( proc_genhd_init ) ;
2008-05-23 20:44:11 +04:00
# endif /* CONFIG_PROC_FS */
2005-04-17 02:20:36 +04:00
2007-05-24 00:57:38 +04:00
static void media_change_notify_thread ( struct work_struct * work )
{
struct gendisk * gd = container_of ( work , struct gendisk , async_notify ) ;
char event [ ] = " MEDIA_CHANGE=1 " ;
char * envp [ ] = { event , NULL } ;
/*
* set enviroment vars to indicate which event this is for
* so that user space will know to go check the media status .
*/
2008-08-25 14:56:05 +04:00
kobject_uevent_env ( & disk_to_dev ( gd ) - > kobj , KOBJ_CHANGE , envp ) ;
2007-05-24 00:57:38 +04:00
put_device ( gd - > driverfs_dev ) ;
}
2008-03-04 13:23:46 +03:00
#if 0
2007-05-24 00:57:38 +04:00
void genhd_media_change_notify ( struct gendisk * disk )
{
get_device ( disk - > driverfs_dev ) ;
schedule_work ( & disk - > async_notify ) ;
}
EXPORT_SYMBOL_GPL ( genhd_media_change_notify ) ;
2008-03-04 13:23:46 +03:00
# endif /* 0 */
2007-05-24 00:57:38 +04:00
2008-09-03 11:01:09 +04:00
dev_t blk_lookup_devt ( const char * name , int partno )
2008-05-23 01:21:08 +04:00
{
2008-09-03 10:57:12 +04:00
dev_t devt = MKDEV ( 0 , 0 ) ;
struct class_dev_iter iter ;
struct device * dev ;
2008-05-23 01:21:08 +04:00
2008-09-03 10:57:12 +04:00
class_dev_iter_init ( & iter , & block_class , NULL , & disk_type ) ;
while ( ( dev = class_dev_iter_next ( & iter ) ) ) {
2008-05-23 01:21:08 +04:00
struct gendisk * disk = dev_to_disk ( dev ) ;
2008-08-29 11:01:47 +04:00
struct hd_struct * part ;
2008-05-23 01:21:08 +04:00
2008-09-03 11:01:48 +04:00
if ( strcmp ( dev - > bus_id , name ) )
continue ;
2008-08-29 11:01:47 +04:00
part = disk_get_part ( disk , partno ) ;
2008-08-29 13:41:51 +04:00
if ( part ) {
2008-09-03 11:01:48 +04:00
devt = part_devt ( part ) ;
2008-09-03 11:03:02 +04:00
disk_put_part ( part ) ;
2008-08-29 11:01:47 +04:00
break ;
2008-09-03 10:57:12 +04:00
}
2008-08-29 11:01:47 +04:00
disk_put_part ( part ) ;
2008-08-16 16:30:30 +04:00
}
2008-09-03 10:57:12 +04:00
class_dev_iter_exit ( & iter ) ;
2007-05-22 00:08:01 +04:00
return devt ;
}
EXPORT_SYMBOL ( blk_lookup_devt ) ;
2005-04-17 02:20:36 +04:00
struct gendisk * alloc_disk ( int minors )
{
2005-06-23 11:08:19 +04:00
return alloc_disk_node ( minors , - 1 ) ;
}
2008-08-25 14:56:16 +04:00
EXPORT_SYMBOL ( alloc_disk ) ;
2005-06-23 11:08:19 +04:00
struct gendisk * alloc_disk_node ( int minors , int node_id )
{
struct gendisk * disk ;
2007-07-17 15:03:29 +04:00
disk = kmalloc_node ( sizeof ( struct gendisk ) ,
GFP_KERNEL | __GFP_ZERO , node_id ) ;
2005-04-17 02:20:36 +04:00
if ( disk ) {
2008-08-25 14:56:14 +04:00
if ( ! init_part_stats ( & disk - > part0 ) ) {
2005-04-17 02:20:36 +04:00
kfree ( disk ) ;
return NULL ;
}
2008-08-25 14:56:15 +04:00
if ( disk_expand_part_tbl ( disk , 0 ) ) {
free_part_stats ( & disk - > part0 ) ;
2008-09-03 11:06:42 +04:00
kfree ( disk ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
2008-08-25 14:56:15 +04:00
disk - > part_tbl - > part [ 0 ] = & disk - > part0 ;
2008-09-03 11:06:42 +04:00
2005-04-17 02:20:36 +04:00
disk - > minors = minors ;
rand_initialize_disk ( disk ) ;
2008-08-25 14:56:05 +04:00
disk_to_dev ( disk ) - > class = & block_class ;
disk_to_dev ( disk ) - > type = & disk_type ;
device_initialize ( disk_to_dev ( disk ) ) ;
2007-05-24 00:57:38 +04:00
INIT_WORK ( & disk - > async_notify ,
media_change_notify_thread ) ;
2008-08-25 14:56:15 +04:00
disk - > node_id = node_id ;
2005-04-17 02:20:36 +04:00
}
return disk ;
}
2005-06-23 11:08:19 +04:00
EXPORT_SYMBOL ( alloc_disk_node ) ;
2005-04-17 02:20:36 +04:00
struct kobject * get_disk ( struct gendisk * disk )
{
struct module * owner ;
struct kobject * kobj ;
if ( ! disk - > fops )
return NULL ;
owner = disk - > fops - > owner ;
if ( owner & & ! try_module_get ( owner ) )
return NULL ;
2008-08-25 14:56:05 +04:00
kobj = kobject_get ( & disk_to_dev ( disk ) - > kobj ) ;
2005-04-17 02:20:36 +04:00
if ( kobj = = NULL ) {
module_put ( owner ) ;
return NULL ;
}
return kobj ;
}
EXPORT_SYMBOL ( get_disk ) ;
void put_disk ( struct gendisk * disk )
{
if ( disk )
2008-08-25 14:56:05 +04:00
kobject_put ( & disk_to_dev ( disk ) - > kobj ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( put_disk ) ;
void set_device_ro ( struct block_device * bdev , int flag )
{
2008-08-25 14:56:10 +04:00
bdev - > bd_part - > policy = flag ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( set_device_ro ) ;
void set_disk_ro ( struct gendisk * disk , int flag )
{
2008-09-03 11:03:02 +04:00
struct disk_part_iter piter ;
struct hd_struct * part ;
2008-08-25 14:56:10 +04:00
disk_part_iter_init ( & piter , disk ,
DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0 ) ;
2008-09-03 11:03:02 +04:00
while ( ( part = disk_part_iter_next ( & piter ) ) )
part - > policy = flag ;
disk_part_iter_exit ( & piter ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( set_disk_ro ) ;
int bdev_read_only ( struct block_device * bdev )
{
if ( ! bdev )
return 0 ;
2008-08-25 14:56:10 +04:00
return bdev - > bd_part - > policy ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( bdev_read_only ) ;
2008-09-03 11:01:09 +04:00
int invalidate_partition ( struct gendisk * disk , int partno )
2005-04-17 02:20:36 +04:00
{
int res = 0 ;
2008-09-03 11:01:09 +04:00
struct block_device * bdev = bdget_disk ( disk , partno ) ;
2005-04-17 02:20:36 +04:00
if ( bdev ) {
2005-05-06 03:15:59 +04:00
fsync_bdev ( bdev ) ;
res = __invalidate_device ( bdev ) ;
2005-04-17 02:20:36 +04:00
bdput ( bdev ) ;
}
return res ;
}
EXPORT_SYMBOL ( invalidate_partition ) ;