162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 462306a36Sopenharmony_ci * Horst Hummel <Horst.Hummel@de.ibm.com> 562306a36Sopenharmony_ci * Carsten Otte <Cotte@de.ibm.com> 662306a36Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 762306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 862306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2001 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * i/o controls for the dasd driver. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/compat.h> 1762306a36Sopenharmony_ci#include <linux/major.h> 1862306a36Sopenharmony_ci#include <linux/fs.h> 1962306a36Sopenharmony_ci#include <linux/blkpg.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <asm/ccwdev.h> 2262306a36Sopenharmony_ci#include <asm/schid.h> 2362306a36Sopenharmony_ci#include <asm/cmb.h> 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/dasd_mod.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* This is ugly... */ 2862306a36Sopenharmony_ci#define PRINTK_HEADER "dasd_ioctl:" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "dasd_int.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int 3462306a36Sopenharmony_cidasd_ioctl_api_version(void __user *argp) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci int ver = DASD_API_VERSION; 3762306a36Sopenharmony_ci return put_user(ver, (int __user *)argp); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Enable device. 4262306a36Sopenharmony_ci * used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistatic int 4562306a36Sopenharmony_cidasd_ioctl_enable(struct block_device *bdev) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct dasd_device *base; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 5062306a36Sopenharmony_ci return -EACCES; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci base = dasd_device_from_gendisk(bdev->bd_disk); 5362306a36Sopenharmony_ci if (!base) 5462306a36Sopenharmony_ci return -ENODEV; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci dasd_enable_device(base); 5762306a36Sopenharmony_ci dasd_put_device(base); 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * Disable device. 6362306a36Sopenharmony_ci * Used by dasdfmt. Disable I/O operations but allow ioctls. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistatic int 6662306a36Sopenharmony_cidasd_ioctl_disable(struct block_device *bdev) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct dasd_device *base; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 7162306a36Sopenharmony_ci return -EACCES; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci base = dasd_device_from_gendisk(bdev->bd_disk); 7462306a36Sopenharmony_ci if (!base) 7562306a36Sopenharmony_ci return -ENODEV; 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * Man this is sick. We don't do a real disable but only downgrade 7862306a36Sopenharmony_ci * the device to DASD_STATE_BASIC. The reason is that dasdfmt uses 7962306a36Sopenharmony_ci * BIODASDDISABLE to disable accesses to the device via the block 8062306a36Sopenharmony_ci * device layer but it still wants to do i/o on the device by 8162306a36Sopenharmony_ci * using the BIODASDFMT ioctl. Therefore the correct state for the 8262306a36Sopenharmony_ci * device is DASD_STATE_BASIC that allows to do basic i/o. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci dasd_set_target_state(base, DASD_STATE_BASIC); 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * Set i_size to zero, since read, write, etc. check against this 8762306a36Sopenharmony_ci * value. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci set_capacity(bdev->bd_disk, 0); 9062306a36Sopenharmony_ci dasd_put_device(base); 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Quiesce device. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic int dasd_ioctl_quiesce(struct dasd_block *block) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned long flags; 10062306a36Sopenharmony_ci struct dasd_device *base; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci base = block->base; 10362306a36Sopenharmony_ci if (!capable (CAP_SYS_ADMIN)) 10462306a36Sopenharmony_ci return -EACCES; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci pr_info("%s: The DASD has been put in the quiesce " 10762306a36Sopenharmony_ci "state\n", dev_name(&base->cdev->dev)); 10862306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); 10962306a36Sopenharmony_ci dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE); 11062306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * Resume device. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic int dasd_ioctl_resume(struct dasd_block *block) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned long flags; 12162306a36Sopenharmony_ci struct dasd_device *base; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci base = block->base; 12462306a36Sopenharmony_ci if (!capable (CAP_SYS_ADMIN)) 12562306a36Sopenharmony_ci return -EACCES; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci pr_info("%s: I/O operations have been resumed " 12862306a36Sopenharmony_ci "on the DASD\n", dev_name(&base->cdev->dev)); 12962306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); 13062306a36Sopenharmony_ci dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE); 13162306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci dasd_schedule_block_bh(block); 13462306a36Sopenharmony_ci dasd_schedule_device_bh(base); 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* 13962306a36Sopenharmony_ci * Abort all failfast I/O on a device. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic int dasd_ioctl_abortio(struct dasd_block *block) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci unsigned long flags; 14462306a36Sopenharmony_ci struct dasd_device *base; 14562306a36Sopenharmony_ci struct dasd_ccw_req *cqr, *n; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci base = block->base; 14862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 14962306a36Sopenharmony_ci return -EACCES; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (test_and_set_bit(DASD_FLAG_ABORTALL, &base->flags)) 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag set"); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci spin_lock_irqsave(&block->request_queue_lock, flags); 15662306a36Sopenharmony_ci spin_lock(&block->queue_lock); 15762306a36Sopenharmony_ci list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) { 15862306a36Sopenharmony_ci if (test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && 15962306a36Sopenharmony_ci cqr->callback_data && 16062306a36Sopenharmony_ci cqr->callback_data != DASD_SLEEPON_START_TAG && 16162306a36Sopenharmony_ci cqr->callback_data != DASD_SLEEPON_END_TAG) { 16262306a36Sopenharmony_ci spin_unlock(&block->queue_lock); 16362306a36Sopenharmony_ci blk_abort_request(cqr->callback_data); 16462306a36Sopenharmony_ci spin_lock(&block->queue_lock); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci spin_unlock(&block->queue_lock); 16862306a36Sopenharmony_ci spin_unlock_irqrestore(&block->request_queue_lock, flags); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci dasd_schedule_block_bh(block); 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * Allow I/O on a device 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_cistatic int dasd_ioctl_allowio(struct dasd_block *block) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct dasd_device *base; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci base = block->base; 18262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 18362306a36Sopenharmony_ci return -EACCES; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (test_and_clear_bit(DASD_FLAG_ABORTALL, &base->flags)) 18662306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag unset"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* 19262306a36Sopenharmony_ci * performs formatting of _device_ according to _fdata_ 19362306a36Sopenharmony_ci * Note: The discipline's format_function is assumed to deliver formatting 19462306a36Sopenharmony_ci * commands to format multiple units of the device. In terms of the ECKD 19562306a36Sopenharmony_ci * devices this means CCWs are generated to format multiple tracks. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic int 19862306a36Sopenharmony_cidasd_format(struct dasd_block *block, struct format_data_t *fdata) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct dasd_device *base; 20162306a36Sopenharmony_ci int rc; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci base = block->base; 20462306a36Sopenharmony_ci if (base->discipline->format_device == NULL) 20562306a36Sopenharmony_ci return -EPERM; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (base->state != DASD_STATE_BASIC) { 20862306a36Sopenharmony_ci pr_warn("%s: The DASD cannot be formatted while it is enabled\n", 20962306a36Sopenharmony_ci dev_name(&base->cdev->dev)); 21062306a36Sopenharmony_ci return -EBUSY; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_NOTICE, base, 21462306a36Sopenharmony_ci "formatting units %u to %u (%u B blocks) flags %u", 21562306a36Sopenharmony_ci fdata->start_unit, 21662306a36Sopenharmony_ci fdata->stop_unit, fdata->blksize, fdata->intensity); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Since dasdfmt keeps the device open after it was disabled, 21962306a36Sopenharmony_ci * there still exists an inode for this device. 22062306a36Sopenharmony_ci * We must update i_blkbits, otherwise we might get errors when 22162306a36Sopenharmony_ci * enabling the device later. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci if (fdata->start_unit == 0) { 22462306a36Sopenharmony_ci block->gdp->part0->bd_inode->i_blkbits = 22562306a36Sopenharmony_ci blksize_bits(fdata->blksize); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci rc = base->discipline->format_device(base, fdata, 1); 22962306a36Sopenharmony_ci if (rc == -EAGAIN) 23062306a36Sopenharmony_ci rc = base->discipline->format_device(base, fdata, 0); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return rc; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int dasd_check_format(struct dasd_block *block, 23662306a36Sopenharmony_ci struct format_check_t *cdata) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct dasd_device *base; 23962306a36Sopenharmony_ci int rc; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci base = block->base; 24262306a36Sopenharmony_ci if (!base->discipline->check_device_format) 24362306a36Sopenharmony_ci return -ENOTTY; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci rc = base->discipline->check_device_format(base, cdata, 1); 24662306a36Sopenharmony_ci if (rc == -EAGAIN) 24762306a36Sopenharmony_ci rc = base->discipline->check_device_format(base, cdata, 0); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return rc; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Format device. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_cistatic int 25662306a36Sopenharmony_cidasd_ioctl_format(struct block_device *bdev, void __user *argp) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct dasd_device *base; 25962306a36Sopenharmony_ci struct format_data_t fdata; 26062306a36Sopenharmony_ci int rc; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 26362306a36Sopenharmony_ci return -EACCES; 26462306a36Sopenharmony_ci if (!argp) 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci base = dasd_device_from_gendisk(bdev->bd_disk); 26762306a36Sopenharmony_ci if (!base) 26862306a36Sopenharmony_ci return -ENODEV; 26962306a36Sopenharmony_ci if (base->features & DASD_FEATURE_READONLY || 27062306a36Sopenharmony_ci test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) { 27162306a36Sopenharmony_ci dasd_put_device(base); 27262306a36Sopenharmony_ci return -EROFS; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) { 27562306a36Sopenharmony_ci dasd_put_device(base); 27662306a36Sopenharmony_ci return -EFAULT; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (bdev_is_partition(bdev)) { 27962306a36Sopenharmony_ci pr_warn("%s: The specified DASD is a partition and cannot be formatted\n", 28062306a36Sopenharmony_ci dev_name(&base->cdev->dev)); 28162306a36Sopenharmony_ci dasd_put_device(base); 28262306a36Sopenharmony_ci return -EINVAL; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci rc = dasd_format(base->block, &fdata); 28562306a36Sopenharmony_ci dasd_put_device(base); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return rc; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* 29162306a36Sopenharmony_ci * Check device format 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_cistatic int dasd_ioctl_check_format(struct block_device *bdev, void __user *argp) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct format_check_t cdata; 29662306a36Sopenharmony_ci struct dasd_device *base; 29762306a36Sopenharmony_ci int rc = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!argp) 30062306a36Sopenharmony_ci return -EINVAL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci base = dasd_device_from_gendisk(bdev->bd_disk); 30362306a36Sopenharmony_ci if (!base) 30462306a36Sopenharmony_ci return -ENODEV; 30562306a36Sopenharmony_ci if (bdev_is_partition(bdev)) { 30662306a36Sopenharmony_ci pr_warn("%s: The specified DASD is a partition and cannot be checked\n", 30762306a36Sopenharmony_ci dev_name(&base->cdev->dev)); 30862306a36Sopenharmony_ci rc = -EINVAL; 30962306a36Sopenharmony_ci goto out_err; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (copy_from_user(&cdata, argp, sizeof(cdata))) { 31362306a36Sopenharmony_ci rc = -EFAULT; 31462306a36Sopenharmony_ci goto out_err; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci rc = dasd_check_format(base->block, &cdata); 31862306a36Sopenharmony_ci if (rc) 31962306a36Sopenharmony_ci goto out_err; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (copy_to_user(argp, &cdata, sizeof(cdata))) 32262306a36Sopenharmony_ci rc = -EFAULT; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciout_err: 32562306a36Sopenharmony_ci dasd_put_device(base); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return rc; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int dasd_release_space(struct dasd_device *device, 33162306a36Sopenharmony_ci struct format_data_t *rdata) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci if (!device->discipline->is_ese && !device->discipline->is_ese(device)) 33462306a36Sopenharmony_ci return -ENOTSUPP; 33562306a36Sopenharmony_ci if (!device->discipline->release_space) 33662306a36Sopenharmony_ci return -ENOTSUPP; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return device->discipline->release_space(device, rdata); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* 34262306a36Sopenharmony_ci * Release allocated space 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_cistatic int dasd_ioctl_release_space(struct block_device *bdev, void __user *argp) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct format_data_t rdata; 34762306a36Sopenharmony_ci struct dasd_device *base; 34862306a36Sopenharmony_ci int rc = 0; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 35162306a36Sopenharmony_ci return -EACCES; 35262306a36Sopenharmony_ci if (!argp) 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci base = dasd_device_from_gendisk(bdev->bd_disk); 35662306a36Sopenharmony_ci if (!base) 35762306a36Sopenharmony_ci return -ENODEV; 35862306a36Sopenharmony_ci if (base->features & DASD_FEATURE_READONLY || 35962306a36Sopenharmony_ci test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) { 36062306a36Sopenharmony_ci rc = -EROFS; 36162306a36Sopenharmony_ci goto out_err; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci if (bdev_is_partition(bdev)) { 36462306a36Sopenharmony_ci pr_warn("%s: The specified DASD is a partition and tracks cannot be released\n", 36562306a36Sopenharmony_ci dev_name(&base->cdev->dev)); 36662306a36Sopenharmony_ci rc = -EINVAL; 36762306a36Sopenharmony_ci goto out_err; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (copy_from_user(&rdata, argp, sizeof(rdata))) { 37162306a36Sopenharmony_ci rc = -EFAULT; 37262306a36Sopenharmony_ci goto out_err; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci rc = dasd_release_space(base, &rdata); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ciout_err: 37862306a36Sopenharmony_ci dasd_put_device(base); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return rc; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * Swap driver iternal copy relation. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_cistatic int 38762306a36Sopenharmony_cidasd_ioctl_copy_pair_swap(struct block_device *bdev, void __user *argp) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct dasd_copypair_swap_data_t data; 39062306a36Sopenharmony_ci struct dasd_device *device; 39162306a36Sopenharmony_ci int rc; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 39462306a36Sopenharmony_ci return -EACCES; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci device = dasd_device_from_gendisk(bdev->bd_disk); 39762306a36Sopenharmony_ci if (!device) 39862306a36Sopenharmony_ci return -ENODEV; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (copy_from_user(&data, argp, sizeof(struct dasd_copypair_swap_data_t))) { 40162306a36Sopenharmony_ci dasd_put_device(device); 40262306a36Sopenharmony_ci return -EFAULT; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci if (memchr_inv(data.reserved, 0, sizeof(data.reserved))) { 40562306a36Sopenharmony_ci pr_warn("%s: Invalid swap data specified\n", 40662306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 40762306a36Sopenharmony_ci dasd_put_device(device); 40862306a36Sopenharmony_ci return DASD_COPYPAIRSWAP_INVALID; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci if (bdev_is_partition(bdev)) { 41162306a36Sopenharmony_ci pr_warn("%s: The specified DASD is a partition and cannot be swapped\n", 41262306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 41362306a36Sopenharmony_ci dasd_put_device(device); 41462306a36Sopenharmony_ci return DASD_COPYPAIRSWAP_INVALID; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci if (!device->copy) { 41762306a36Sopenharmony_ci pr_warn("%s: The specified DASD has no copy pair set up\n", 41862306a36Sopenharmony_ci dev_name(&device->cdev->dev)); 41962306a36Sopenharmony_ci dasd_put_device(device); 42062306a36Sopenharmony_ci return -ENODEV; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci if (!device->discipline->copy_pair_swap) { 42362306a36Sopenharmony_ci dasd_put_device(device); 42462306a36Sopenharmony_ci return -EOPNOTSUPP; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci rc = device->discipline->copy_pair_swap(device, data.primary, 42762306a36Sopenharmony_ci data.secondary); 42862306a36Sopenharmony_ci dasd_put_device(device); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return rc; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci#ifdef CONFIG_DASD_PROFILE 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * Reset device profile information 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_cistatic int dasd_ioctl_reset_profile(struct dasd_block *block) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci dasd_profile_reset(&block->profile); 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/* 44462306a36Sopenharmony_ci * Return device profile information 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_cistatic int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct dasd_profile_info_t *data; 44962306a36Sopenharmony_ci int rc = 0; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci data = kmalloc(sizeof(*data), GFP_KERNEL); 45262306a36Sopenharmony_ci if (!data) 45362306a36Sopenharmony_ci return -ENOMEM; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci spin_lock_bh(&block->profile.lock); 45662306a36Sopenharmony_ci if (block->profile.data) { 45762306a36Sopenharmony_ci data->dasd_io_reqs = block->profile.data->dasd_io_reqs; 45862306a36Sopenharmony_ci data->dasd_io_sects = block->profile.data->dasd_io_sects; 45962306a36Sopenharmony_ci memcpy(data->dasd_io_secs, block->profile.data->dasd_io_secs, 46062306a36Sopenharmony_ci sizeof(data->dasd_io_secs)); 46162306a36Sopenharmony_ci memcpy(data->dasd_io_times, block->profile.data->dasd_io_times, 46262306a36Sopenharmony_ci sizeof(data->dasd_io_times)); 46362306a36Sopenharmony_ci memcpy(data->dasd_io_timps, block->profile.data->dasd_io_timps, 46462306a36Sopenharmony_ci sizeof(data->dasd_io_timps)); 46562306a36Sopenharmony_ci memcpy(data->dasd_io_time1, block->profile.data->dasd_io_time1, 46662306a36Sopenharmony_ci sizeof(data->dasd_io_time1)); 46762306a36Sopenharmony_ci memcpy(data->dasd_io_time2, block->profile.data->dasd_io_time2, 46862306a36Sopenharmony_ci sizeof(data->dasd_io_time2)); 46962306a36Sopenharmony_ci memcpy(data->dasd_io_time2ps, 47062306a36Sopenharmony_ci block->profile.data->dasd_io_time2ps, 47162306a36Sopenharmony_ci sizeof(data->dasd_io_time2ps)); 47262306a36Sopenharmony_ci memcpy(data->dasd_io_time3, block->profile.data->dasd_io_time3, 47362306a36Sopenharmony_ci sizeof(data->dasd_io_time3)); 47462306a36Sopenharmony_ci memcpy(data->dasd_io_nr_req, 47562306a36Sopenharmony_ci block->profile.data->dasd_io_nr_req, 47662306a36Sopenharmony_ci sizeof(data->dasd_io_nr_req)); 47762306a36Sopenharmony_ci spin_unlock_bh(&block->profile.lock); 47862306a36Sopenharmony_ci } else { 47962306a36Sopenharmony_ci spin_unlock_bh(&block->profile.lock); 48062306a36Sopenharmony_ci rc = -EIO; 48162306a36Sopenharmony_ci goto out; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci if (copy_to_user(argp, data, sizeof(*data))) 48462306a36Sopenharmony_ci rc = -EFAULT; 48562306a36Sopenharmony_ciout: 48662306a36Sopenharmony_ci kfree(data); 48762306a36Sopenharmony_ci return rc; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci#else 49062306a36Sopenharmony_cistatic int dasd_ioctl_reset_profile(struct dasd_block *block) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci return -ENOTTY; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci return -ENOTTY; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci#endif 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci/* 50262306a36Sopenharmony_ci * Return dasd information. Used for BIODASDINFO and BIODASDINFO2. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_cistatic int __dasd_ioctl_information(struct dasd_block *block, 50562306a36Sopenharmony_ci struct dasd_information2_t *dasd_info) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct subchannel_id sch_id; 50862306a36Sopenharmony_ci struct ccw_dev_id dev_id; 50962306a36Sopenharmony_ci struct dasd_device *base; 51062306a36Sopenharmony_ci struct ccw_device *cdev; 51162306a36Sopenharmony_ci struct list_head *l; 51262306a36Sopenharmony_ci unsigned long flags; 51362306a36Sopenharmony_ci int rc; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci base = block->base; 51662306a36Sopenharmony_ci if (!base->discipline || !base->discipline->fill_info) 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci rc = base->discipline->fill_info(base, dasd_info); 52062306a36Sopenharmony_ci if (rc) 52162306a36Sopenharmony_ci return rc; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci cdev = base->cdev; 52462306a36Sopenharmony_ci ccw_device_get_id(cdev, &dev_id); 52562306a36Sopenharmony_ci ccw_device_get_schid(cdev, &sch_id); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci dasd_info->devno = dev_id.devno; 52862306a36Sopenharmony_ci dasd_info->schid = sch_id.sch_no; 52962306a36Sopenharmony_ci dasd_info->cu_type = cdev->id.cu_type; 53062306a36Sopenharmony_ci dasd_info->cu_model = cdev->id.cu_model; 53162306a36Sopenharmony_ci dasd_info->dev_type = cdev->id.dev_type; 53262306a36Sopenharmony_ci dasd_info->dev_model = cdev->id.dev_model; 53362306a36Sopenharmony_ci dasd_info->status = base->state; 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * The open_count is increased for every opener, that includes 53662306a36Sopenharmony_ci * the blkdev_get in dasd_scan_partitions. 53762306a36Sopenharmony_ci * This must be hidden from user-space. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci dasd_info->open_count = atomic_read(&block->open_count); 54062306a36Sopenharmony_ci if (!block->bdev) 54162306a36Sopenharmony_ci dasd_info->open_count++; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * check if device is really formatted 54562306a36Sopenharmony_ci * LDL / CDL was returned by 'fill_info' 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci if ((base->state < DASD_STATE_READY) || 54862306a36Sopenharmony_ci (dasd_check_blocksize(block->bp_block))) 54962306a36Sopenharmony_ci dasd_info->format = DASD_FORMAT_NONE; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci dasd_info->features |= 55262306a36Sopenharmony_ci ((base->features & DASD_FEATURE_READONLY) != 0); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci memcpy(dasd_info->type, base->discipline->name, 4); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); 55762306a36Sopenharmony_ci list_for_each(l, &base->ccw_queue) 55862306a36Sopenharmony_ci dasd_info->chanq_len++; 55962306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int dasd_ioctl_information(struct dasd_block *block, void __user *argp, 56462306a36Sopenharmony_ci size_t copy_size) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct dasd_information2_t *dasd_info; 56762306a36Sopenharmony_ci int error; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci dasd_info = kzalloc(sizeof(*dasd_info), GFP_KERNEL); 57062306a36Sopenharmony_ci if (!dasd_info) 57162306a36Sopenharmony_ci return -ENOMEM; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci error = __dasd_ioctl_information(block, dasd_info); 57462306a36Sopenharmony_ci if (!error && copy_to_user(argp, dasd_info, copy_size)) 57562306a36Sopenharmony_ci error = -EFAULT; 57662306a36Sopenharmony_ci kfree(dasd_info); 57762306a36Sopenharmony_ci return error; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/* 58162306a36Sopenharmony_ci * Set read only 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ciint dasd_set_read_only(struct block_device *bdev, bool ro) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct dasd_device *base; 58662306a36Sopenharmony_ci int rc; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* do not manipulate hardware state for partitions */ 58962306a36Sopenharmony_ci if (bdev_is_partition(bdev)) 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci base = dasd_device_from_gendisk(bdev->bd_disk); 59362306a36Sopenharmony_ci if (!base) 59462306a36Sopenharmony_ci return -ENODEV; 59562306a36Sopenharmony_ci if (!ro && test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) 59662306a36Sopenharmony_ci rc = -EROFS; 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci rc = dasd_set_feature(base->cdev, DASD_FEATURE_READONLY, ro); 59962306a36Sopenharmony_ci dasd_put_device(base); 60062306a36Sopenharmony_ci return rc; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, 60462306a36Sopenharmony_ci struct cmbdata __user *argp) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci size_t size = _IOC_SIZE(cmd); 60762306a36Sopenharmony_ci struct cmbdata data; 60862306a36Sopenharmony_ci int ret; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ret = cmf_readall(block->base->cdev, &data); 61162306a36Sopenharmony_ci if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp)))) 61262306a36Sopenharmony_ci return -EFAULT; 61362306a36Sopenharmony_ci return ret; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciint dasd_ioctl(struct block_device *bdev, blk_mode_t mode, 61762306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct dasd_block *block; 62062306a36Sopenharmony_ci struct dasd_device *base; 62162306a36Sopenharmony_ci void __user *argp; 62262306a36Sopenharmony_ci int rc; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (is_compat_task()) 62562306a36Sopenharmony_ci argp = compat_ptr(arg); 62662306a36Sopenharmony_ci else 62762306a36Sopenharmony_ci argp = (void __user *)arg; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) 63062306a36Sopenharmony_ci return -EINVAL; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci base = dasd_device_from_gendisk(bdev->bd_disk); 63362306a36Sopenharmony_ci if (!base) 63462306a36Sopenharmony_ci return -ENODEV; 63562306a36Sopenharmony_ci block = base->block; 63662306a36Sopenharmony_ci rc = 0; 63762306a36Sopenharmony_ci switch (cmd) { 63862306a36Sopenharmony_ci case BIODASDDISABLE: 63962306a36Sopenharmony_ci rc = dasd_ioctl_disable(bdev); 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case BIODASDENABLE: 64262306a36Sopenharmony_ci rc = dasd_ioctl_enable(bdev); 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case BIODASDQUIESCE: 64562306a36Sopenharmony_ci rc = dasd_ioctl_quiesce(block); 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci case BIODASDRESUME: 64862306a36Sopenharmony_ci rc = dasd_ioctl_resume(block); 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci case BIODASDABORTIO: 65162306a36Sopenharmony_ci rc = dasd_ioctl_abortio(block); 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case BIODASDALLOWIO: 65462306a36Sopenharmony_ci rc = dasd_ioctl_allowio(block); 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci case BIODASDFMT: 65762306a36Sopenharmony_ci rc = dasd_ioctl_format(bdev, argp); 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci case BIODASDCHECKFMT: 66062306a36Sopenharmony_ci rc = dasd_ioctl_check_format(bdev, argp); 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci case BIODASDINFO: 66362306a36Sopenharmony_ci rc = dasd_ioctl_information(block, argp, 66462306a36Sopenharmony_ci sizeof(struct dasd_information_t)); 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case BIODASDINFO2: 66762306a36Sopenharmony_ci rc = dasd_ioctl_information(block, argp, 66862306a36Sopenharmony_ci sizeof(struct dasd_information2_t)); 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case BIODASDPRRD: 67162306a36Sopenharmony_ci rc = dasd_ioctl_read_profile(block, argp); 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case BIODASDPRRST: 67462306a36Sopenharmony_ci rc = dasd_ioctl_reset_profile(block); 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci case DASDAPIVER: 67762306a36Sopenharmony_ci rc = dasd_ioctl_api_version(argp); 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case BIODASDCMFENABLE: 68062306a36Sopenharmony_ci rc = enable_cmf(base->cdev); 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci case BIODASDCMFDISABLE: 68362306a36Sopenharmony_ci rc = disable_cmf(base->cdev); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci case BIODASDREADALLCMB: 68662306a36Sopenharmony_ci rc = dasd_ioctl_readall_cmb(block, cmd, argp); 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci case BIODASDRAS: 68962306a36Sopenharmony_ci rc = dasd_ioctl_release_space(bdev, argp); 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci case BIODASDCOPYPAIRSWAP: 69262306a36Sopenharmony_ci rc = dasd_ioctl_copy_pair_swap(bdev, argp); 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci default: 69562306a36Sopenharmony_ci /* if the discipline has an ioctl method try it. */ 69662306a36Sopenharmony_ci rc = -ENOTTY; 69762306a36Sopenharmony_ci if (base->discipline->ioctl) 69862306a36Sopenharmony_ci rc = base->discipline->ioctl(block, cmd, argp); 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci dasd_put_device(base); 70162306a36Sopenharmony_ci return rc; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci/** 70662306a36Sopenharmony_ci * dasd_biodasdinfo() - fill out the dasd information structure 70762306a36Sopenharmony_ci * @disk: [in] pointer to gendisk structure that references a DASD 70862306a36Sopenharmony_ci * @info: [out] pointer to the dasd_information2_t structure 70962306a36Sopenharmony_ci * 71062306a36Sopenharmony_ci * Provide access to DASD specific information. 71162306a36Sopenharmony_ci * The gendisk structure is checked if it belongs to the DASD driver by 71262306a36Sopenharmony_ci * comparing the gendisk->fops pointer. 71362306a36Sopenharmony_ci * If it does not belong to the DASD driver -EINVAL is returned. 71462306a36Sopenharmony_ci * Otherwise the provided dasd_information2_t structure is filled out. 71562306a36Sopenharmony_ci * 71662306a36Sopenharmony_ci * Returns: 71762306a36Sopenharmony_ci * %0 on success and a negative error value on failure. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ciint dasd_biodasdinfo(struct gendisk *disk, struct dasd_information2_t *info) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct dasd_device *base; 72262306a36Sopenharmony_ci int error; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (disk->fops != &dasd_device_operations) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci base = dasd_device_from_gendisk(disk); 72862306a36Sopenharmony_ci if (!base) 72962306a36Sopenharmony_ci return -ENODEV; 73062306a36Sopenharmony_ci error = __dasd_ioctl_information(base->block, info); 73162306a36Sopenharmony_ci dasd_put_device(base); 73262306a36Sopenharmony_ci return error; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci/* export that symbol_get in partition detection is possible */ 73562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dasd_biodasdinfo); 736