2005-04-17 00:20:36 +02: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.
|
|
|
|
*/
|
2009-03-26 15:23:49 +01:00
|
|
|
|
|
|
|
#define KMSG_COMPONENT "dasd"
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/blkpg.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 09:04:11 +01:00
|
|
|
#include <linux/slab.h>
|
2010-01-13 20:44:29 +01:00
|
|
|
#include <asm/compat.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <asm/ccwdev.h>
|
2006-03-24 12:15:21 +01:00
|
|
|
#include <asm/cmb.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
|
|
/* This is ugly... */
|
|
|
|
#define PRINTK_HEADER "dasd_ioctl:"
|
|
|
|
|
|
|
|
#include "dasd_int.h"
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2006-03-24 12:15:19 +01:00
|
|
|
dasd_ioctl_api_version(void __user *argp)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int ver = DASD_API_VERSION;
|
2006-03-24 12:15:32 +01:00
|
|
|
return put_user(ver, (int __user *)argp);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable device.
|
|
|
|
* used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection
|
|
|
|
*/
|
|
|
|
static int
|
2006-03-24 12:15:19 +01:00
|
|
|
dasd_ioctl_enable(struct block_device *bdev)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2011-04-20 10:15:30 +02:00
|
|
|
struct dasd_device *base;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2006-03-24 12:15:19 +01:00
|
|
|
|
2011-04-20 10:15:30 +02:00
|
|
|
base = dasd_device_from_gendisk(bdev->bd_disk);
|
|
|
|
if (!base)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
dasd_enable_device(base);
|
2005-04-17 00:20:36 +02:00
|
|
|
/* Formatting the dasd device can change the capacity. */
|
2006-03-23 12:00:28 +01:00
|
|
|
mutex_lock(&bdev->bd_mutex);
|
2011-04-20 10:15:30 +02:00
|
|
|
i_size_write(bdev->bd_inode,
|
|
|
|
(loff_t)get_capacity(base->block->gdp) << 9);
|
2006-03-23 12:00:28 +01:00
|
|
|
mutex_unlock(&bdev->bd_mutex);
|
2011-04-20 10:15:30 +02:00
|
|
|
dasd_put_device(base);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable device.
|
|
|
|
* Used by dasdfmt. Disable I/O operations but allow ioctls.
|
|
|
|
*/
|
|
|
|
static int
|
2006-03-24 12:15:19 +01:00
|
|
|
dasd_ioctl_disable(struct block_device *bdev)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2011-04-20 10:15:30 +02:00
|
|
|
struct dasd_device *base;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2006-03-24 12:15:19 +01:00
|
|
|
|
2011-04-20 10:15:30 +02:00
|
|
|
base = dasd_device_from_gendisk(bdev->bd_disk);
|
|
|
|
if (!base)
|
|
|
|
return -ENODEV;
|
2005-04-17 00:20:36 +02: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.
|
|
|
|
*/
|
2011-04-20 10:15:30 +02:00
|
|
|
dasd_set_target_state(base, DASD_STATE_BASIC);
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* Set i_size to zero, since read, write, etc. check against this
|
|
|
|
* value.
|
|
|
|
*/
|
2006-03-23 12:00:28 +01:00
|
|
|
mutex_lock(&bdev->bd_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
i_size_write(bdev->bd_inode, 0);
|
2006-03-23 12:00:28 +01:00
|
|
|
mutex_unlock(&bdev->bd_mutex);
|
2011-04-20 10:15:30 +02:00
|
|
|
dasd_put_device(base);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Quiesce device.
|
|
|
|
*/
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_quiesce(struct dasd_block *block)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-01-26 14:11:23 +01:00
|
|
|
struct dasd_device *base;
|
2006-06-29 14:58:12 +02:00
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
base = block->base;
|
2005-04-17 00:20:36 +02:00
|
|
|
if (!capable (CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2006-06-29 14:58:12 +02:00
|
|
|
|
2009-09-11 10:28:30 +02:00
|
|
|
pr_info("%s: The DASD has been put in the quiesce "
|
|
|
|
"state\n", dev_name(&base->cdev->dev));
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
|
2009-12-07 12:51:51 +01:00
|
|
|
dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-03-26 15:23:49 +01:00
|
|
|
* Resume device.
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_resume(struct dasd_block *block)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-01-26 14:11:23 +01:00
|
|
|
struct dasd_device *base;
|
2006-06-29 14:58:12 +02:00
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
base = block->base;
|
2006-06-29 14:58:12 +02:00
|
|
|
if (!capable (CAP_SYS_ADMIN))
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EACCES;
|
|
|
|
|
2009-09-11 10:28:30 +02:00
|
|
|
pr_info("%s: I/O operations have been resumed "
|
|
|
|
"on the DASD\n", dev_name(&base->cdev->dev));
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
|
2009-12-07 12:51:51 +01:00
|
|
|
dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE);
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
dasd_schedule_block_bh(block);
|
2005-04-17 00:20:36 +02: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 14:11:23 +01:00
|
|
|
static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct dasd_ccw_req *cqr;
|
2008-01-26 14:11:23 +01:00
|
|
|
struct dasd_device *base;
|
2005-04-17 00:20:36 +02:00
|
|
|
int rc;
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
base = block->base;
|
|
|
|
if (base->discipline->format_device == NULL)
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EPERM;
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
if (base->state != DASD_STATE_BASIC) {
|
2009-09-11 10:28:30 +02:00
|
|
|
pr_warning("%s: The DASD cannot be formatted while it is "
|
|
|
|
"enabled\n", dev_name(&base->cdev->dev));
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
DBF_DEV_EVENT(DBF_NOTICE, base,
|
2009-03-26 15:23:47 +01:00
|
|
|
"formatting units %u to %u (%u B blocks) flags %u",
|
2005-04-17 00:20:36 +02:00
|
|
|
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 14:11:23 +01:00
|
|
|
struct block_device *bdev = bdget_disk(block->gdp, 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
|
|
|
|
bdput(bdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fdata->start_unit <= fdata->stop_unit) {
|
2008-01-26 14:11:23 +01:00
|
|
|
cqr = base->discipline->format_device(base, fdata);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (IS_ERR(cqr))
|
|
|
|
return PTR_ERR(cqr);
|
|
|
|
rc = dasd_sleep_on_interruptible(cqr);
|
2008-01-26 14:11:23 +01:00
|
|
|
dasd_sfree_request(cqr, cqr->memdev);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (rc) {
|
|
|
|
if (rc != -ERESTARTSYS)
|
2009-09-11 10:28:30 +02:00
|
|
|
pr_err("%s: Formatting unit %d failed with "
|
|
|
|
"rc=%d\n", dev_name(&base->cdev->dev),
|
|
|
|
fdata->start_unit, rc);
|
2005-04-17 00:20:36 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
fdata->start_unit++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Format device.
|
|
|
|
*/
|
|
|
|
static int
|
2006-03-24 12:15:19 +01:00
|
|
|
dasd_ioctl_format(struct block_device *bdev, void __user *argp)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2011-04-20 10:15:30 +02:00
|
|
|
struct dasd_device *base;
|
2005-04-17 00:20:36 +02:00
|
|
|
struct format_data_t fdata;
|
2011-04-20 10:15:30 +02:00
|
|
|
int rc;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2006-03-24 12:15:19 +01:00
|
|
|
if (!argp)
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EINVAL;
|
2011-04-20 10:15:30 +02:00
|
|
|
base = dasd_device_from_gendisk(bdev->bd_disk);
|
|
|
|
if (!base)
|
|
|
|
return -ENODEV;
|
|
|
|
if (base->features & DASD_FEATURE_READONLY ||
|
|
|
|
test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
|
|
|
|
dasd_put_device(base);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EROFS;
|
2011-04-20 10:15:30 +02:00
|
|
|
}
|
|
|
|
if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) {
|
|
|
|
dasd_put_device(base);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EFAULT;
|
2011-04-20 10:15:30 +02:00
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
if (bdev != bdev->bd_contains) {
|
2009-09-11 10:28:30 +02:00
|
|
|
pr_warning("%s: The specified DASD is a partition and cannot "
|
|
|
|
"be formatted\n",
|
2011-04-20 10:15:30 +02:00
|
|
|
dev_name(&base->cdev->dev));
|
|
|
|
dasd_put_device(base);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_format(base->block, &fdata);
|
|
|
|
dasd_put_device(base);
|
|
|
|
return rc;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_DASD_PROFILE
|
|
|
|
/*
|
|
|
|
* Reset device profile information
|
|
|
|
*/
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_reset_profile(struct dasd_block *block)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2011-07-24 10:48:32 +02:00
|
|
|
dasd_profile_reset(&block->profile);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return device profile information
|
|
|
|
*/
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2011-07-24 10:48:32 +02:00
|
|
|
struct dasd_profile_info_t *data;
|
2011-08-24 17:15:10 +02:00
|
|
|
int rc = 0;
|
2011-07-24 10:48:32 +02:00
|
|
|
|
|
|
|
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock_bh(&block->profile.lock);
|
|
|
|
if (block->profile.data) {
|
|
|
|
data->dasd_io_reqs = block->profile.data->dasd_io_reqs;
|
|
|
|
data->dasd_io_sects = block->profile.data->dasd_io_sects;
|
|
|
|
memcpy(data->dasd_io_secs, block->profile.data->dasd_io_secs,
|
|
|
|
sizeof(data->dasd_io_secs));
|
|
|
|
memcpy(data->dasd_io_times, block->profile.data->dasd_io_times,
|
|
|
|
sizeof(data->dasd_io_times));
|
|
|
|
memcpy(data->dasd_io_timps, block->profile.data->dasd_io_timps,
|
|
|
|
sizeof(data->dasd_io_timps));
|
|
|
|
memcpy(data->dasd_io_time1, block->profile.data->dasd_io_time1,
|
|
|
|
sizeof(data->dasd_io_time1));
|
|
|
|
memcpy(data->dasd_io_time2, block->profile.data->dasd_io_time2,
|
|
|
|
sizeof(data->dasd_io_time2));
|
|
|
|
memcpy(data->dasd_io_time2ps,
|
|
|
|
block->profile.data->dasd_io_time2ps,
|
|
|
|
sizeof(data->dasd_io_time2ps));
|
|
|
|
memcpy(data->dasd_io_time3, block->profile.data->dasd_io_time3,
|
|
|
|
sizeof(data->dasd_io_time3));
|
|
|
|
memcpy(data->dasd_io_nr_req,
|
|
|
|
block->profile.data->dasd_io_nr_req,
|
|
|
|
sizeof(data->dasd_io_nr_req));
|
|
|
|
spin_unlock_bh(&block->profile.lock);
|
|
|
|
} else {
|
|
|
|
spin_unlock_bh(&block->profile.lock);
|
2011-08-24 17:15:10 +02:00
|
|
|
rc = -EIO;
|
|
|
|
goto out;
|
2011-07-24 10:48:32 +02:00
|
|
|
}
|
|
|
|
if (copy_to_user(argp, data, sizeof(*data)))
|
2011-08-24 17:15:10 +02:00
|
|
|
rc = -EFAULT;
|
|
|
|
out:
|
|
|
|
kfree(data);
|
|
|
|
return rc;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
#else
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_reset_profile(struct dasd_block *block)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
|
|
|
|
*/
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_information(struct dasd_block *block,
|
|
|
|
unsigned int cmd, void __user *argp)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct dasd_information2_t *dasd_info;
|
|
|
|
unsigned long flags;
|
2005-09-04 00:57:58 +02:00
|
|
|
int rc;
|
2008-01-26 14:11:23 +01:00
|
|
|
struct dasd_device *base;
|
2005-04-17 00:20:36 +02:00
|
|
|
struct ccw_device *cdev;
|
2007-05-10 15:45:42 +02:00
|
|
|
struct ccw_dev_id dev_id;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
base = block->base;
|
2010-01-27 10:12:35 +01:00
|
|
|
if (!base->discipline || !base->discipline->fill_info)
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2006-03-24 12:15:24 +01:00
|
|
|
dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (dasd_info == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
rc = base->discipline->fill_info(base, dasd_info);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (rc) {
|
|
|
|
kfree(dasd_info);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
cdev = base->cdev;
|
2007-05-10 15:45:42 +02:00
|
|
|
ccw_device_get_id(cdev, &dev_id);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-05-10 15:45:42 +02:00
|
|
|
dasd_info->devno = dev_id.devno;
|
2008-01-26 14:11:23 +01:00
|
|
|
dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev);
|
2005-04-17 00:20:36 +02: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 14:11:23 +01:00
|
|
|
dasd_info->status = base->state;
|
2006-02-01 12:06:36 +01: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 14:11:23 +01:00
|
|
|
dasd_info->open_count = atomic_read(&block->open_count);
|
|
|
|
if (!block->bdev)
|
2006-02-01 12:06:36 +01:00
|
|
|
dasd_info->open_count++;
|
2006-06-29 14:58:12 +02:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* check if device is really formatted
|
|
|
|
* LDL / CDL was returned by 'fill_info'
|
|
|
|
*/
|
2008-01-26 14:11:23 +01:00
|
|
|
if ((base->state < DASD_STATE_READY) ||
|
|
|
|
(dasd_check_blocksize(block->bp_block)))
|
2005-04-17 00:20:36 +02:00
|
|
|
dasd_info->format = DASD_FORMAT_NONE;
|
2005-05-01 17:58:59 +02:00
|
|
|
|
2005-09-04 00:57:58 +02:00
|
|
|
dasd_info->features |=
|
2008-01-26 14:11:23 +01:00
|
|
|
((base->features & DASD_FEATURE_READONLY) != 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2010-01-27 10:12:35 +01:00
|
|
|
memcpy(dasd_info->type, base->discipline->name, 4);
|
2006-03-24 12:15:24 +01:00
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
if (block->request_queue->request_fn) {
|
2005-04-17 00:20:36 +02:00
|
|
|
struct list_head *l;
|
|
|
|
#ifdef DASD_EXTENDED_PROFILING
|
|
|
|
{
|
|
|
|
struct list_head *l;
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_lock_irqsave(&block->lock, flags);
|
|
|
|
list_for_each(l, &block->request_queue->queue_head)
|
2005-04-17 00:20:36 +02:00
|
|
|
dasd_info->req_queue_len++;
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_unlock_irqrestore(&block->lock, flags);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
#endif /* DASD_EXTENDED_PROFILING */
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
|
|
|
|
list_for_each(l, &base->ccw_queue)
|
2005-04-17 00:20:36 +02:00
|
|
|
dasd_info->chanq_len++;
|
2008-01-26 14:11:23 +01:00
|
|
|
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
|
2005-04-17 00:20:36 +02:00
|
|
|
flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
2006-03-24 12:15:19 +01:00
|
|
|
if (copy_to_user(argp, dasd_info,
|
|
|
|
((cmd == (unsigned int) BIODASDINFO2) ?
|
2008-01-26 14:11:23 +01:00
|
|
|
sizeof(struct dasd_information2_t) :
|
|
|
|
sizeof(struct dasd_information_t))))
|
2005-04-17 00:20:36 +02:00
|
|
|
rc = -EFAULT;
|
|
|
|
kfree(dasd_info);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set read only
|
|
|
|
*/
|
|
|
|
static int
|
2006-03-24 12:15:19 +01:00
|
|
|
dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2011-04-20 10:15:30 +02:00
|
|
|
struct dasd_device *base;
|
|
|
|
int intval, rc;
|
2005-04-17 00:20:36 +02: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 16:41:55 +02:00
|
|
|
if (get_user(intval, (int __user *)argp))
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EFAULT;
|
2011-04-20 10:15:30 +02:00
|
|
|
base = dasd_device_from_gendisk(bdev->bd_disk);
|
|
|
|
if (!base)
|
|
|
|
return -ENODEV;
|
|
|
|
if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
|
|
|
|
dasd_put_device(base);
|
2010-03-08 12:26:24 +01:00
|
|
|
return -EROFS;
|
2011-04-20 10:15:30 +02:00
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
set_disk_ro(bdev->bd_disk, intval);
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_set_feature(base->cdev, DASD_FEATURE_READONLY, intval);
|
|
|
|
dasd_put_device(base);
|
|
|
|
return rc;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
|
2010-01-13 20:44:29 +01:00
|
|
|
struct cmbdata __user *argp)
|
2006-03-24 12:15:21 +01:00
|
|
|
{
|
|
|
|
size_t size = _IOC_SIZE(cmd);
|
|
|
|
struct cmbdata data;
|
|
|
|
int ret;
|
|
|
|
|
2008-01-26 14:11:23 +01:00
|
|
|
ret = cmf_readall(block->base->cdev, &data);
|
2006-03-24 12:15:21 +01:00
|
|
|
if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp))))
|
|
|
|
return -EFAULT;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-05-31 22:38:40 +02:00
|
|
|
int dasd_ioctl(struct block_device *bdev, fmode_t mode,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2011-04-20 10:15:30 +02:00
|
|
|
struct dasd_block *block;
|
|
|
|
struct dasd_device *base;
|
2010-01-13 20:44:29 +01:00
|
|
|
void __user *argp;
|
2011-04-20 10:15:30 +02:00
|
|
|
int rc;
|
2010-01-13 20:44:29 +01:00
|
|
|
|
|
|
|
if (is_compat_task())
|
|
|
|
argp = compat_ptr(arg);
|
|
|
|
else
|
|
|
|
argp = (void __user *)arg;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2006-03-24 12:15:19 +01:00
|
|
|
if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
|
|
|
|
PRINT_DEBUG("empty data ptr");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2011-04-20 10:15:30 +02:00
|
|
|
base = dasd_device_from_gendisk(bdev->bd_disk);
|
|
|
|
if (!base)
|
|
|
|
return -ENODEV;
|
|
|
|
block = base->block;
|
|
|
|
rc = 0;
|
2006-03-24 12:15:19 +01:00
|
|
|
switch (cmd) {
|
|
|
|
case BIODASDDISABLE:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_disable(bdev);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDENABLE:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_enable(bdev);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDQUIESCE:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_quiesce(block);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDRESUME:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_resume(block);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDFMT:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_format(bdev, argp);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDINFO:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_information(block, cmd, argp);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDINFO2:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_information(block, cmd, argp);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDPRRD:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_read_profile(block, argp);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BIODASDPRRST:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_reset_profile(block);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case BLKROSET:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_set_ro(bdev, argp);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
case DASDAPIVER:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_api_version(argp);
|
|
|
|
break;
|
2006-03-24 12:15:21 +01:00
|
|
|
case BIODASDCMFENABLE:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = enable_cmf(base->cdev);
|
|
|
|
break;
|
2006-03-24 12:15:21 +01:00
|
|
|
case BIODASDCMFDISABLE:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = disable_cmf(base->cdev);
|
|
|
|
break;
|
2006-03-24 12:15:21 +01:00
|
|
|
case BIODASDREADALLCMB:
|
2011-04-20 10:15:30 +02:00
|
|
|
rc = dasd_ioctl_readall_cmb(block, cmd, argp);
|
|
|
|
break;
|
2006-03-24 12:15:19 +01:00
|
|
|
default:
|
2006-03-24 12:15:20 +01:00
|
|
|
/* if the discipline has an ioctl method try it. */
|
2011-04-20 10:15:30 +02:00
|
|
|
if (base->discipline->ioctl) {
|
|
|
|
rc = base->discipline->ioctl(block, cmd, argp);
|
|
|
|
if (rc == -ENOIOCTLCMD)
|
|
|
|
rc = -EINVAL;
|
|
|
|
} else
|
|
|
|
rc = -EINVAL;
|
2006-03-24 12:15:19 +01:00
|
|
|
}
|
2011-04-20 10:15:30 +02:00
|
|
|
dasd_put_device(base);
|
|
|
|
return rc;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|