2005-04-17 02:20:36 +04:00
/*
* File . . . . . . . . . . . : linux / drivers / s390 / block / dasd_ioctl . c
* Author ( s ) . . . . . . : Holger Smolinski < Holger . Smolinski @ de . ibm . com >
* Horst Hummel < Horst . Hummel @ de . ibm . com >
* Carsten Otte < Cotte @ de . ibm . com >
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
* Bugreports . to . . : < Linux390 @ de . ibm . com >
* ( C ) IBM Corporation , IBM Deutschland Entwicklung GmbH , 1999 - 2001
*
* i / o controls for the dasd driver .
*/
# include <linux/interrupt.h>
# include <linux/major.h>
# include <linux/fs.h>
# include <linux/blkpg.h>
# include <asm/ccwdev.h>
2006-03-24 14:15:21 +03:00
# include <asm/cmb.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
/* This is ugly... */
# define PRINTK_HEADER "dasd_ioctl:"
# include "dasd_int.h"
static int
2006-03-24 14:15:19 +03:00
dasd_ioctl_api_version ( void __user * argp )
2005-04-17 02:20:36 +04:00
{
int ver = DASD_API_VERSION ;
2006-03-24 14:15:32 +03:00
return put_user ( ver , ( int __user * ) argp ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Enable device .
* used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection
*/
static int
2006-03-24 14:15:19 +03:00
dasd_ioctl_enable ( struct block_device * bdev )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
struct dasd_block * block = bdev - > bd_disk - > private_data ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2006-03-24 14:15:19 +03:00
2008-01-26 16:11:23 +03:00
dasd_enable_device ( block - > base ) ;
2005-04-17 02:20:36 +04:00
/* Formatting the dasd device can change the capacity. */
2006-03-23 14:00:28 +03:00
mutex_lock ( & bdev - > bd_mutex ) ;
2008-01-26 16:11:23 +03:00
i_size_write ( bdev - > bd_inode , ( loff_t ) get_capacity ( block - > gdp ) < < 9 ) ;
2006-03-23 14:00:28 +03:00
mutex_unlock ( & bdev - > bd_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Disable device .
* Used by dasdfmt . Disable I / O operations but allow ioctls .
*/
static int
2006-03-24 14:15:19 +03:00
dasd_ioctl_disable ( struct block_device * bdev )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
struct dasd_block * block = bdev - > bd_disk - > private_data ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2006-03-24 14:15:19 +03:00
2005-04-17 02:20:36 +04:00
/*
* Man this is sick . We don ' t do a real disable but only downgrade
* the device to DASD_STATE_BASIC . The reason is that dasdfmt uses
* BIODASDDISABLE to disable accesses to the device via the block
* device layer but it still wants to do i / o on the device by
* using the BIODASDFMT ioctl . Therefore the correct state for the
* device is DASD_STATE_BASIC that allows to do basic i / o .
*/
2008-01-26 16:11:23 +03:00
dasd_set_target_state ( block - > base , DASD_STATE_BASIC ) ;
2005-04-17 02:20:36 +04:00
/*
* Set i_size to zero , since read , write , etc . check against this
* value .
*/
2006-03-23 14:00:28 +03:00
mutex_lock ( & bdev - > bd_mutex ) ;
2005-04-17 02:20:36 +04:00
i_size_write ( bdev - > bd_inode , 0 ) ;
2006-03-23 14:00:28 +03:00
mutex_unlock ( & bdev - > bd_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Quiesce device .
*/
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_quiesce ( struct dasd_block * block )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2008-01-26 16:11:23 +03:00
struct dasd_device * base ;
2006-06-29 16:58:12 +04:00
2008-01-26 16:11:23 +03:00
base = block - > base ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2006-06-29 16:58:12 +04:00
2008-01-26 16:11:23 +03:00
DEV_MESSAGE ( KERN_DEBUG , base , " %s " , " Quiesce IO on device " ) ;
spin_lock_irqsave ( get_ccwdev_lock ( base - > cdev ) , flags ) ;
base - > stopped | = DASD_STOPPED_QUIESCE ;
spin_unlock_irqrestore ( get_ccwdev_lock ( base - > cdev ) , flags ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Quiesce device .
*/
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_resume ( struct dasd_block * block )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2008-01-26 16:11:23 +03:00
struct dasd_device * base ;
2006-06-29 16:58:12 +04:00
2008-01-26 16:11:23 +03:00
base = block - > base ;
2006-06-29 16:58:12 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
2005-04-17 02:20:36 +04:00
return - EACCES ;
2008-01-26 16:11:23 +03:00
DEV_MESSAGE ( KERN_DEBUG , base , " %s " , " resume IO on device " ) ;
spin_lock_irqsave ( get_ccwdev_lock ( base - > cdev ) , flags ) ;
base - > stopped & = ~ DASD_STOPPED_QUIESCE ;
spin_unlock_irqrestore ( get_ccwdev_lock ( base - > cdev ) , flags ) ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
dasd_schedule_block_bh ( block ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* performs formatting of _device_ according to _fdata_
* Note : The discipline ' s format_function is assumed to deliver formatting
* commands to format a single unit of the device . In terms of the ECKD
* devices this means CCWs are generated to format a single track .
*/
2008-01-26 16:11:23 +03:00
static int dasd_format ( struct dasd_block * block , struct format_data_t * fdata )
2005-04-17 02:20:36 +04:00
{
struct dasd_ccw_req * cqr ;
2008-01-26 16:11:23 +03:00
struct dasd_device * base ;
2005-04-17 02:20:36 +04:00
int rc ;
2008-01-26 16:11:23 +03:00
base = block - > base ;
if ( base - > discipline - > format_device = = NULL )
2005-04-17 02:20:36 +04:00
return - EPERM ;
2008-01-26 16:11:23 +03:00
if ( base - > state ! = DASD_STATE_BASIC ) {
DEV_MESSAGE ( KERN_WARNING , base , " %s " ,
2005-04-17 02:20:36 +04:00
" dasd_format: device is not disabled! " ) ;
return - EBUSY ;
}
2008-01-26 16:11:23 +03:00
DBF_DEV_EVENT ( DBF_NOTICE , base ,
2005-04-17 02:20:36 +04:00
" formatting units %d to %d (%d B blocks) flags %d " ,
fdata - > start_unit ,
fdata - > stop_unit , fdata - > blksize , fdata - > intensity ) ;
/* Since dasdfmt keeps the device open after it was disabled,
* there still exists an inode for this device .
* We must update i_blkbits , otherwise we might get errors when
* enabling the device later .
*/
if ( fdata - > start_unit = = 0 ) {
2008-01-26 16:11:23 +03:00
struct block_device * bdev = bdget_disk ( block - > gdp , 0 ) ;
2005-04-17 02:20:36 +04:00
bdev - > bd_inode - > i_blkbits = blksize_bits ( fdata - > blksize ) ;
bdput ( bdev ) ;
}
while ( fdata - > start_unit < = fdata - > stop_unit ) {
2008-01-26 16:11:23 +03:00
cqr = base - > discipline - > format_device ( base , fdata ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( cqr ) )
return PTR_ERR ( cqr ) ;
rc = dasd_sleep_on_interruptible ( cqr ) ;
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
if ( rc ! = - ERESTARTSYS )
2008-01-26 16:11:23 +03:00
DEV_MESSAGE ( KERN_ERR , base ,
2005-04-17 02:20:36 +04:00
" Formatting of unit %d failed "
" with rc = %d " ,
fdata - > start_unit , rc ) ;
return rc ;
}
fdata - > start_unit + + ;
}
return 0 ;
}
/*
* Format device .
*/
static int
2006-03-24 14:15:19 +03:00
dasd_ioctl_format ( struct block_device * bdev , void __user * argp )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
struct dasd_block * block = bdev - > bd_disk - > private_data ;
2005-04-17 02:20:36 +04:00
struct format_data_t fdata ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2006-03-24 14:15:19 +03:00
if ( ! argp )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-05-01 19:58:59 +04:00
2008-01-26 16:11:23 +03:00
if ( block - > base - > features & DASD_FEATURE_READONLY )
2005-04-17 02:20:36 +04:00
return - EROFS ;
2006-03-24 14:15:19 +03:00
if ( copy_from_user ( & fdata , argp , sizeof ( struct format_data_t ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
if ( bdev ! = bdev - > bd_contains ) {
2008-01-26 16:11:23 +03:00
DEV_MESSAGE ( KERN_WARNING , block - > base , " %s " ,
2005-04-17 02:20:36 +04:00
" Cannot low-level format a partition " ) ;
return - EINVAL ;
}
2008-01-26 16:11:23 +03:00
return dasd_format ( block , & fdata ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_DASD_PROFILE
/*
* Reset device profile information
*/
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_reset_profile ( struct dasd_block * block )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
memset ( & block - > profile , 0 , sizeof ( struct dasd_profile_info_t ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Return device profile information
*/
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_read_profile ( struct dasd_block * block , void __user * argp )
2005-04-17 02:20:36 +04:00
{
2006-01-06 11:19:14 +03:00
if ( dasd_profile_level = = DASD_PROFILE_OFF )
return - EIO ;
2008-01-26 16:11:23 +03:00
if ( copy_to_user ( argp , & block - > profile ,
sizeof ( struct dasd_profile_info_t ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
}
# else
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_reset_profile ( struct dasd_block * block )
2005-04-17 02:20:36 +04:00
{
return - ENOSYS ;
}
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_read_profile ( struct dasd_block * block , void __user * argp )
2005-04-17 02:20:36 +04:00
{
return - ENOSYS ;
}
# endif
/*
* Return dasd information . Used for BIODASDINFO and BIODASDINFO2 .
*/
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_information ( struct dasd_block * block ,
unsigned int cmd , void __user * argp )
2005-04-17 02:20:36 +04:00
{
struct dasd_information2_t * dasd_info ;
unsigned long flags ;
2005-09-04 02:57:58 +04:00
int rc ;
2008-01-26 16:11:23 +03:00
struct dasd_device * base ;
2005-04-17 02:20:36 +04:00
struct ccw_device * cdev ;
2007-05-10 17:45:42 +04:00
struct ccw_dev_id dev_id ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
base = block - > base ;
if ( ! base - > discipline - > fill_info )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-03-24 14:15:24 +03:00
dasd_info = kzalloc ( sizeof ( struct dasd_information2_t ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( dasd_info = = NULL )
return - ENOMEM ;
2008-01-26 16:11:23 +03:00
rc = base - > discipline - > fill_info ( base , dasd_info ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
kfree ( dasd_info ) ;
return rc ;
}
2008-01-26 16:11:23 +03:00
cdev = base - > cdev ;
2007-05-10 17:45:42 +04:00
ccw_device_get_id ( cdev , & dev_id ) ;
2005-04-17 02:20:36 +04:00
2007-05-10 17:45:42 +04:00
dasd_info - > devno = dev_id . devno ;
2008-01-26 16:11:23 +03:00
dasd_info - > schid = _ccw_device_get_subchannel_number ( base - > cdev ) ;
2005-04-17 02:20:36 +04:00
dasd_info - > cu_type = cdev - > id . cu_type ;
dasd_info - > cu_model = cdev - > id . cu_model ;
dasd_info - > dev_type = cdev - > id . dev_type ;
dasd_info - > dev_model = cdev - > id . dev_model ;
2008-01-26 16:11:23 +03:00
dasd_info - > status = base - > state ;
2006-02-01 14:06:36 +03:00
/*
* The open_count is increased for every opener , that includes
* the blkdev_get in dasd_scan_partitions .
* This must be hidden from user - space .
*/
2008-01-26 16:11:23 +03:00
dasd_info - > open_count = atomic_read ( & block - > open_count ) ;
if ( ! block - > bdev )
2006-02-01 14:06:36 +03:00
dasd_info - > open_count + + ;
2006-06-29 16:58:12 +04:00
2005-04-17 02:20:36 +04:00
/*
* check if device is really formatted
* LDL / CDL was returned by ' fill_info '
*/
2008-01-26 16:11:23 +03:00
if ( ( base - > state < DASD_STATE_READY ) | |
( dasd_check_blocksize ( block - > bp_block ) ) )
2005-04-17 02:20:36 +04:00
dasd_info - > format = DASD_FORMAT_NONE ;
2005-05-01 19:58:59 +04:00
2005-09-04 02:57:58 +04:00
dasd_info - > features | =
2008-01-26 16:11:23 +03:00
( ( base - > features & DASD_FEATURE_READONLY ) ! = 0 ) ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
if ( base - > discipline )
memcpy ( dasd_info - > type , base - > discipline - > name , 4 ) ;
2005-04-17 02:20:36 +04:00
else
memcpy ( dasd_info - > type , " none " , 4 ) ;
2006-03-24 14:15:24 +03:00
2008-01-26 16:11:23 +03:00
if ( block - > request_queue - > request_fn ) {
2005-04-17 02:20:36 +04:00
struct list_head * l ;
# ifdef DASD_EXTENDED_PROFILING
{
struct list_head * l ;
2008-01-26 16:11:23 +03:00
spin_lock_irqsave ( & block - > lock , flags ) ;
list_for_each ( l , & block - > request_queue - > queue_head )
2005-04-17 02:20:36 +04:00
dasd_info - > req_queue_len + + ;
2008-01-26 16:11:23 +03:00
spin_unlock_irqrestore ( & block - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
# endif /* DASD_EXTENDED_PROFILING */
2008-01-26 16:11:23 +03:00
spin_lock_irqsave ( get_ccwdev_lock ( base - > cdev ) , flags ) ;
list_for_each ( l , & base - > ccw_queue )
2005-04-17 02:20:36 +04:00
dasd_info - > chanq_len + + ;
2008-01-26 16:11:23 +03:00
spin_unlock_irqrestore ( get_ccwdev_lock ( base - > cdev ) ,
2005-04-17 02:20:36 +04:00
flags ) ;
}
rc = 0 ;
2006-03-24 14:15:19 +03:00
if ( copy_to_user ( argp , dasd_info ,
( ( cmd = = ( unsigned int ) BIODASDINFO2 ) ?
2008-01-26 16:11:23 +03:00
sizeof ( struct dasd_information2_t ) :
sizeof ( struct dasd_information_t ) ) ) )
2005-04-17 02:20:36 +04:00
rc = - EFAULT ;
kfree ( dasd_info ) ;
return rc ;
}
/*
* Set read only
*/
static int
2006-03-24 14:15:19 +03:00
dasd_ioctl_set_ro ( struct block_device * bdev , void __user * argp )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
struct dasd_block * block = bdev - > bd_disk - > private_data ;
2006-03-24 14:15:19 +03:00
int intval ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
if ( bdev ! = bdev - > bd_contains )
// ro setting is not allowed for partitions
return - EINVAL ;
2006-07-12 18:41:55 +04:00
if ( get_user ( intval , ( int __user * ) argp ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2005-05-01 19:58:59 +04:00
2005-04-17 02:20:36 +04:00
set_disk_ro ( bdev - > bd_disk , intval ) ;
2008-01-26 16:11:23 +03:00
return dasd_set_feature ( block - > base - > cdev , DASD_FEATURE_READONLY , intval ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:23 +03:00
static int dasd_ioctl_readall_cmb ( struct dasd_block * block , unsigned int cmd ,
2006-03-24 14:15:21 +03:00
unsigned long arg )
{
struct cmbdata __user * argp = ( void __user * ) arg ;
size_t size = _IOC_SIZE ( cmd ) ;
struct cmbdata data ;
int ret ;
2008-01-26 16:11:23 +03:00
ret = cmf_readall ( block - > base - > cdev , & data ) ;
2006-03-24 14:15:21 +03:00
if ( ! ret & & copy_to_user ( argp , & data , min ( size , sizeof ( * argp ) ) ) )
return - EFAULT ;
return ret ;
}
2005-04-17 02:20:36 +04:00
int
2006-03-24 14:15:19 +03:00
dasd_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2006-03-24 14:15:19 +03:00
struct block_device * bdev = inode - > i_bdev ;
2008-01-26 16:11:23 +03:00
struct dasd_block * block = bdev - > bd_disk - > private_data ;
2006-03-24 14:15:19 +03:00
void __user * argp = ( void __user * ) arg ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
if ( ! block )
2006-03-24 14:15:19 +03:00
return - ENODEV ;
if ( ( _IOC_DIR ( cmd ) ! = _IOC_NONE ) & & ! arg ) {
PRINT_DEBUG ( " empty data ptr " ) ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2006-03-24 14:15:19 +03:00
switch ( cmd ) {
case BIODASDDISABLE :
return dasd_ioctl_disable ( bdev ) ;
case BIODASDENABLE :
return dasd_ioctl_enable ( bdev ) ;
case BIODASDQUIESCE :
2008-01-26 16:11:23 +03:00
return dasd_ioctl_quiesce ( block ) ;
2006-03-24 14:15:19 +03:00
case BIODASDRESUME :
2008-01-26 16:11:23 +03:00
return dasd_ioctl_resume ( block ) ;
2006-03-24 14:15:19 +03:00
case BIODASDFMT :
return dasd_ioctl_format ( bdev , argp ) ;
case BIODASDINFO :
2008-01-26 16:11:23 +03:00
return dasd_ioctl_information ( block , cmd , argp ) ;
2006-03-24 14:15:19 +03:00
case BIODASDINFO2 :
2008-01-26 16:11:23 +03:00
return dasd_ioctl_information ( block , cmd , argp ) ;
2006-03-24 14:15:19 +03:00
case BIODASDPRRD :
2008-01-26 16:11:23 +03:00
return dasd_ioctl_read_profile ( block , argp ) ;
2006-03-24 14:15:19 +03:00
case BIODASDPRRST :
2008-01-26 16:11:23 +03:00
return dasd_ioctl_reset_profile ( block ) ;
2006-03-24 14:15:19 +03:00
case BLKROSET :
return dasd_ioctl_set_ro ( bdev , argp ) ;
case DASDAPIVER :
return dasd_ioctl_api_version ( argp ) ;
2006-03-24 14:15:21 +03:00
case BIODASDCMFENABLE :
2008-01-26 16:11:23 +03:00
return enable_cmf ( block - > base - > cdev ) ;
2006-03-24 14:15:21 +03:00
case BIODASDCMFDISABLE :
2008-01-26 16:11:23 +03:00
return disable_cmf ( block - > base - > cdev ) ;
2006-03-24 14:15:21 +03:00
case BIODASDREADALLCMB :
2008-01-26 16:11:23 +03:00
return dasd_ioctl_readall_cmb ( block , cmd , arg ) ;
2006-03-24 14:15:19 +03:00
default :
2006-03-24 14:15:20 +03:00
/* if the discipline has an ioctl method try it. */
2008-01-26 16:11:23 +03:00
if ( block - > base - > discipline - > ioctl ) {
int rval = block - > base - > discipline - > ioctl ( block , cmd , argp ) ;
2006-03-24 14:15:20 +03:00
if ( rval ! = - ENOIOCTLCMD )
return rval ;
}
2006-03-24 14:15:19 +03:00
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
}
2006-03-24 14:15:19 +03:00
long
dasd_compat_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2006-03-24 14:15:19 +03:00
int rval ;
2005-04-17 02:20:36 +04:00
2006-03-24 14:15:19 +03:00
lock_kernel ( ) ;
2006-12-08 13:37:34 +03:00
rval = dasd_ioctl ( filp - > f_path . dentry - > d_inode , filp , cmd , arg ) ;
2006-03-24 14:15:19 +03:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
2006-03-24 14:15:19 +03:00
return ( rval = = - EINVAL ) ? - ENOIOCTLCMD : rval ;
2005-04-17 02:20:36 +04:00
}