| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * File...........: linux/drivers/s390/block/dasd_ioctl.c | 
 | 3 |  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> | 
 | 4 |  *		    Horst Hummel <Horst.Hummel@de.ibm.com> | 
 | 5 |  *		    Carsten Otte <Cotte@de.ibm.com> | 
 | 6 |  *		    Martin Schwidefsky <schwidefsky@de.ibm.com> | 
 | 7 |  * Bugreports.to..: <Linux390@de.ibm.com> | 
 | 8 |  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 | 
 | 9 |  * | 
 | 10 |  * i/o controls for the dasd driver. | 
 | 11 |  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 | #include <linux/interrupt.h> | 
 | 13 | #include <linux/major.h> | 
 | 14 | #include <linux/fs.h> | 
 | 15 | #include <linux/blkpg.h> | 
 | 16 |  | 
 | 17 | #include <asm/ccwdev.h> | 
| Christoph Hellwig | 8b2eb66 | 2006-03-24 03:15:21 -0800 | [diff] [blame] | 18 | #include <asm/cmb.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 19 | #include <asm/uaccess.h> | 
 | 20 |  | 
 | 21 | /* This is ugly... */ | 
 | 22 | #define PRINTK_HEADER "dasd_ioctl:" | 
 | 23 |  | 
 | 24 | #include "dasd_int.h" | 
 | 25 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 26 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 27 | static int | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 28 | dasd_ioctl_api_version(void __user *argp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | { | 
 | 30 | 	int ver = DASD_API_VERSION; | 
| Bastian Blank | b502962 | 2006-03-24 03:15:32 -0800 | [diff] [blame] | 31 | 	return put_user(ver, (int __user *)argp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 | } | 
 | 33 |  | 
 | 34 | /* | 
 | 35 |  * Enable device. | 
 | 36 |  * used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection | 
 | 37 |  */ | 
 | 38 | static int | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 39 | dasd_ioctl_enable(struct block_device *bdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 41 | 	struct dasd_block *block = bdev->bd_disk->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 |  | 
 | 43 | 	if (!capable(CAP_SYS_ADMIN)) | 
 | 44 | 		return -EACCES; | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 45 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 46 | 	dasd_enable_device(block->base); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 | 	/* Formatting the dasd device can change the capacity. */ | 
| Arjan van de Ven | c039e31 | 2006-03-23 03:00:28 -0800 | [diff] [blame] | 48 | 	mutex_lock(&bdev->bd_mutex); | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 49 | 	i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9); | 
| Arjan van de Ven | c039e31 | 2006-03-23 03:00:28 -0800 | [diff] [blame] | 50 | 	mutex_unlock(&bdev->bd_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | 	return 0; | 
 | 52 | } | 
 | 53 |  | 
 | 54 | /* | 
 | 55 |  * Disable device. | 
 | 56 |  * Used by dasdfmt. Disable I/O operations but allow ioctls. | 
 | 57 |  */ | 
 | 58 | static int | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 59 | dasd_ioctl_disable(struct block_device *bdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 61 | 	struct dasd_block *block = bdev->bd_disk->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 |  | 
 | 63 | 	if (!capable(CAP_SYS_ADMIN)) | 
 | 64 | 		return -EACCES; | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 65 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | 	/* | 
 | 67 | 	 * Man this is sick. We don't do a real disable but only downgrade | 
 | 68 | 	 * the device to DASD_STATE_BASIC. The reason is that dasdfmt uses | 
 | 69 | 	 * BIODASDDISABLE to disable accesses to the device via the block | 
 | 70 | 	 * device layer but it still wants to do i/o on the device by | 
 | 71 | 	 * using the BIODASDFMT ioctl. Therefore the correct state for the | 
 | 72 | 	 * device is DASD_STATE_BASIC that allows to do basic i/o. | 
 | 73 | 	 */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 74 | 	dasd_set_target_state(block->base, DASD_STATE_BASIC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 75 | 	/* | 
 | 76 | 	 * Set i_size to zero, since read, write, etc. check against this | 
 | 77 | 	 * value. | 
 | 78 | 	 */ | 
| Arjan van de Ven | c039e31 | 2006-03-23 03:00:28 -0800 | [diff] [blame] | 79 | 	mutex_lock(&bdev->bd_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 | 	i_size_write(bdev->bd_inode, 0); | 
| Arjan van de Ven | c039e31 | 2006-03-23 03:00:28 -0800 | [diff] [blame] | 81 | 	mutex_unlock(&bdev->bd_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 82 | 	return 0; | 
 | 83 | } | 
 | 84 |  | 
 | 85 | /* | 
 | 86 |  * Quiesce device. | 
 | 87 |  */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 88 | static int dasd_ioctl_quiesce(struct dasd_block *block) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | 	unsigned long flags; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 91 | 	struct dasd_device *base; | 
| Horst Hummel | 138c014 | 2006-06-29 14:58:12 +0200 | [diff] [blame] | 92 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 93 | 	base = block->base; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 | 	if (!capable (CAP_SYS_ADMIN)) | 
 | 95 | 		return -EACCES; | 
| Horst Hummel | 138c014 | 2006-06-29 14:58:12 +0200 | [diff] [blame] | 96 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 97 | 	DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device"); | 
 | 98 | 	spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); | 
 | 99 | 	base->stopped |= DASD_STOPPED_QUIESCE; | 
 | 100 | 	spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | 	return 0; | 
 | 102 | } | 
 | 103 |  | 
 | 104 |  | 
 | 105 | /* | 
 | 106 |  * Quiesce device. | 
 | 107 |  */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 108 | static int dasd_ioctl_resume(struct dasd_block *block) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 110 | 	unsigned long flags; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 111 | 	struct dasd_device *base; | 
| Horst Hummel | 138c014 | 2006-06-29 14:58:12 +0200 | [diff] [blame] | 112 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 113 | 	base = block->base; | 
| Horst Hummel | 138c014 | 2006-06-29 14:58:12 +0200 | [diff] [blame] | 114 | 	if (!capable (CAP_SYS_ADMIN)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | 		return -EACCES; | 
 | 116 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 117 | 	DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device"); | 
 | 118 | 	spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); | 
 | 119 | 	base->stopped &= ~DASD_STOPPED_QUIESCE; | 
 | 120 | 	spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); | 
| Horst Hummel | 138c014 | 2006-06-29 14:58:12 +0200 | [diff] [blame] | 121 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 122 | 	dasd_schedule_block_bh(block); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 | 	return 0; | 
 | 124 | } | 
 | 125 |  | 
 | 126 | /* | 
 | 127 |  * performs formatting of _device_ according to _fdata_ | 
 | 128 |  * Note: The discipline's format_function is assumed to deliver formatting | 
 | 129 |  * commands to format a single unit of the device. In terms of the ECKD | 
 | 130 |  * devices this means CCWs are generated to format a single track. | 
 | 131 |  */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 132 | static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 | { | 
 | 134 | 	struct dasd_ccw_req *cqr; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 135 | 	struct dasd_device *base; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 136 | 	int rc; | 
 | 137 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 138 | 	base = block->base; | 
 | 139 | 	if (base->discipline->format_device == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 140 | 		return -EPERM; | 
 | 141 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 142 | 	if (base->state != DASD_STATE_BASIC) { | 
 | 143 | 		DEV_MESSAGE(KERN_WARNING, base, "%s", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 144 | 			    "dasd_format: device is not disabled! "); | 
 | 145 | 		return -EBUSY; | 
 | 146 | 	} | 
 | 147 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 148 | 	DBF_DEV_EVENT(DBF_NOTICE, base, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 149 | 		      "formatting units %d to %d (%d B blocks) flags %d", | 
 | 150 | 		      fdata->start_unit, | 
 | 151 | 		      fdata->stop_unit, fdata->blksize, fdata->intensity); | 
 | 152 |  | 
 | 153 | 	/* Since dasdfmt keeps the device open after it was disabled, | 
 | 154 | 	 * there still exists an inode for this device. | 
 | 155 | 	 * We must update i_blkbits, otherwise we might get errors when | 
 | 156 | 	 * enabling the device later. | 
 | 157 | 	 */ | 
 | 158 | 	if (fdata->start_unit == 0) { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 159 | 		struct block_device *bdev = bdget_disk(block->gdp, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 | 		bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize); | 
 | 161 | 		bdput(bdev); | 
 | 162 | 	} | 
 | 163 |  | 
 | 164 | 	while (fdata->start_unit <= fdata->stop_unit) { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 165 | 		cqr = base->discipline->format_device(base, fdata); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 166 | 		if (IS_ERR(cqr)) | 
 | 167 | 			return PTR_ERR(cqr); | 
 | 168 | 		rc = dasd_sleep_on_interruptible(cqr); | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 169 | 		dasd_sfree_request(cqr, cqr->memdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 170 | 		if (rc) { | 
 | 171 | 			if (rc != -ERESTARTSYS) | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 172 | 				DEV_MESSAGE(KERN_ERR, base, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 | 					    " Formatting of unit %d failed " | 
 | 174 | 					    "with rc = %d", | 
 | 175 | 					    fdata->start_unit, rc); | 
 | 176 | 			return rc; | 
 | 177 | 		} | 
 | 178 | 		fdata->start_unit++; | 
 | 179 | 	} | 
 | 180 | 	return 0; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | /* | 
 | 184 |  * Format device. | 
 | 185 |  */ | 
 | 186 | static int | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 187 | dasd_ioctl_format(struct block_device *bdev, void __user *argp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 188 | { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 189 | 	struct dasd_block *block = bdev->bd_disk->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 190 | 	struct format_data_t fdata; | 
 | 191 |  | 
 | 192 | 	if (!capable(CAP_SYS_ADMIN)) | 
 | 193 | 		return -EACCES; | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 194 | 	if (!argp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 195 | 		return -EINVAL; | 
| Horst Hummel | f24acd4 | 2005-05-01 08:58:59 -0700 | [diff] [blame] | 196 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 197 | 	if (block->base->features & DASD_FEATURE_READONLY) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | 		return -EROFS; | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 199 | 	if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 200 | 		return -EFAULT; | 
 | 201 | 	if (bdev != bdev->bd_contains) { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 202 | 		DEV_MESSAGE(KERN_WARNING, block->base, "%s", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 203 | 			    "Cannot low-level format a partition"); | 
 | 204 | 		return -EINVAL; | 
 | 205 | 	} | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 206 | 	return dasd_format(block, &fdata); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | } | 
 | 208 |  | 
 | 209 | #ifdef CONFIG_DASD_PROFILE | 
 | 210 | /* | 
 | 211 |  * Reset device profile information | 
 | 212 |  */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 213 | static int dasd_ioctl_reset_profile(struct dasd_block *block) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 215 | 	memset(&block->profile, 0, sizeof(struct dasd_profile_info_t)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 | 	return 0; | 
 | 217 | } | 
 | 218 |  | 
 | 219 | /* | 
 | 220 |  * Return device profile information | 
 | 221 |  */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 222 | static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 223 | { | 
| Horst Hummel | 9a7af28 | 2006-01-06 00:19:14 -0800 | [diff] [blame] | 224 | 	if (dasd_profile_level == DASD_PROFILE_OFF) | 
 | 225 | 		return -EIO; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 226 | 	if (copy_to_user(argp, &block->profile, | 
 | 227 | 			 sizeof(struct dasd_profile_info_t))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 | 		return -EFAULT; | 
 | 229 | 	return 0; | 
 | 230 | } | 
 | 231 | #else | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 232 | static int dasd_ioctl_reset_profile(struct dasd_block *block) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 233 | { | 
 | 234 | 	return -ENOSYS; | 
 | 235 | } | 
 | 236 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 237 | static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 238 | { | 
 | 239 | 	return -ENOSYS; | 
 | 240 | } | 
 | 241 | #endif | 
 | 242 |  | 
 | 243 | /* | 
 | 244 |  * Return dasd information. Used for BIODASDINFO and BIODASDINFO2. | 
 | 245 |  */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 246 | static int dasd_ioctl_information(struct dasd_block *block, | 
 | 247 | 				  unsigned int cmd, void __user *argp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 248 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 249 | 	struct dasd_information2_t *dasd_info; | 
 | 250 | 	unsigned long flags; | 
| Horst Hummel | c6eb7b7 | 2005-09-03 15:57:58 -0700 | [diff] [blame] | 251 | 	int rc; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 252 | 	struct dasd_device *base; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | 	struct ccw_device *cdev; | 
| Cornelia Huck | 9a92fe4 | 2007-05-10 15:45:42 +0200 | [diff] [blame] | 254 | 	struct ccw_dev_id dev_id; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 255 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 256 | 	base = block->base; | 
 | 257 | 	if (!base->discipline->fill_info) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 258 | 		return -EINVAL; | 
 | 259 |  | 
| Horst Hummel | 554a826 | 2006-03-24 03:15:24 -0800 | [diff] [blame] | 260 | 	dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | 	if (dasd_info == NULL) | 
 | 262 | 		return -ENOMEM; | 
 | 263 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 264 | 	rc = base->discipline->fill_info(base, dasd_info); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 265 | 	if (rc) { | 
 | 266 | 		kfree(dasd_info); | 
 | 267 | 		return rc; | 
 | 268 | 	} | 
 | 269 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 270 | 	cdev = base->cdev; | 
| Cornelia Huck | 9a92fe4 | 2007-05-10 15:45:42 +0200 | [diff] [blame] | 271 | 	ccw_device_get_id(cdev, &dev_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 272 |  | 
| Cornelia Huck | 9a92fe4 | 2007-05-10 15:45:42 +0200 | [diff] [blame] | 273 | 	dasd_info->devno = dev_id.devno; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 274 | 	dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 275 | 	dasd_info->cu_type = cdev->id.cu_type; | 
 | 276 | 	dasd_info->cu_model = cdev->id.cu_model; | 
 | 277 | 	dasd_info->dev_type = cdev->id.dev_type; | 
 | 278 | 	dasd_info->dev_model = cdev->id.dev_model; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 279 | 	dasd_info->status = base->state; | 
| Horst Hummel | 5746719 | 2006-02-01 03:06:36 -0800 | [diff] [blame] | 280 | 	/* | 
 | 281 | 	 * The open_count is increased for every opener, that includes | 
 | 282 | 	 * the blkdev_get in dasd_scan_partitions. | 
 | 283 | 	 * This must be hidden from user-space. | 
 | 284 | 	 */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 285 | 	dasd_info->open_count = atomic_read(&block->open_count); | 
 | 286 | 	if (!block->bdev) | 
| Horst Hummel | 5746719 | 2006-02-01 03:06:36 -0800 | [diff] [blame] | 287 | 		dasd_info->open_count++; | 
| Horst Hummel | 138c014 | 2006-06-29 14:58:12 +0200 | [diff] [blame] | 288 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | 	/* | 
 | 290 | 	 * check if device is really formatted | 
 | 291 | 	 * LDL / CDL was returned by 'fill_info' | 
 | 292 | 	 */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 293 | 	if ((base->state < DASD_STATE_READY) || | 
 | 294 | 	    (dasd_check_blocksize(block->bp_block))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 295 | 		dasd_info->format = DASD_FORMAT_NONE; | 
| Horst Hummel | f24acd4 | 2005-05-01 08:58:59 -0700 | [diff] [blame] | 296 |  | 
| Horst Hummel | c6eb7b7 | 2005-09-03 15:57:58 -0700 | [diff] [blame] | 297 | 	dasd_info->features |= | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 298 | 		((base->features & DASD_FEATURE_READONLY) != 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 300 | 	if (base->discipline) | 
 | 301 | 		memcpy(dasd_info->type, base->discipline->name, 4); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | 	else | 
 | 303 | 		memcpy(dasd_info->type, "none", 4); | 
| Horst Hummel | 554a826 | 2006-03-24 03:15:24 -0800 | [diff] [blame] | 304 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 305 | 	if (block->request_queue->request_fn) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 306 | 		struct list_head *l; | 
 | 307 | #ifdef DASD_EXTENDED_PROFILING | 
 | 308 | 		{ | 
 | 309 | 			struct list_head *l; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 310 | 			spin_lock_irqsave(&block->lock, flags); | 
 | 311 | 			list_for_each(l, &block->request_queue->queue_head) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 312 | 				dasd_info->req_queue_len++; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 313 | 			spin_unlock_irqrestore(&block->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 314 | 		} | 
 | 315 | #endif				/* DASD_EXTENDED_PROFILING */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 316 | 		spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); | 
 | 317 | 		list_for_each(l, &base->ccw_queue) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 318 | 			dasd_info->chanq_len++; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 319 | 		spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | 				       flags); | 
 | 321 | 	} | 
 | 322 |  | 
 | 323 | 	rc = 0; | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 324 | 	if (copy_to_user(argp, dasd_info, | 
 | 325 | 			 ((cmd == (unsigned int) BIODASDINFO2) ? | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 326 | 			  sizeof(struct dasd_information2_t) : | 
 | 327 | 			  sizeof(struct dasd_information_t)))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 | 		rc = -EFAULT; | 
 | 329 | 	kfree(dasd_info); | 
 | 330 | 	return rc; | 
 | 331 | } | 
 | 332 |  | 
 | 333 | /* | 
 | 334 |  * Set read only | 
 | 335 |  */ | 
 | 336 | static int | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 337 | dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 | { | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 339 | 	struct dasd_block *block =  bdev->bd_disk->private_data; | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 340 | 	int intval; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 341 |  | 
 | 342 | 	if (!capable(CAP_SYS_ADMIN)) | 
 | 343 | 		return -EACCES; | 
 | 344 | 	if (bdev != bdev->bd_contains) | 
 | 345 | 		// ro setting is not allowed for partitions | 
 | 346 | 		return -EINVAL; | 
| Heiko Carstens | d2c993d | 2006-07-12 16:41:55 +0200 | [diff] [blame] | 347 | 	if (get_user(intval, (int __user *)argp)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 | 		return -EFAULT; | 
| Horst Hummel | f24acd4 | 2005-05-01 08:58:59 -0700 | [diff] [blame] | 349 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 350 | 	set_disk_ro(bdev->bd_disk, intval); | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 351 | 	return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 352 | } | 
 | 353 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 354 | static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, | 
| Christoph Hellwig | 8b2eb66 | 2006-03-24 03:15:21 -0800 | [diff] [blame] | 355 | 		unsigned long arg) | 
 | 356 | { | 
 | 357 | 	struct cmbdata __user *argp = (void __user *) arg; | 
 | 358 | 	size_t size = _IOC_SIZE(cmd); | 
 | 359 | 	struct cmbdata data; | 
 | 360 | 	int ret; | 
 | 361 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 362 | 	ret = cmf_readall(block->base->cdev, &data); | 
| Christoph Hellwig | 8b2eb66 | 2006-03-24 03:15:21 -0800 | [diff] [blame] | 363 | 	if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp)))) | 
 | 364 | 		return -EFAULT; | 
 | 365 | 	return ret; | 
 | 366 | } | 
 | 367 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 368 | int | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 369 | dasd_ioctl(struct inode *inode, struct file *file, | 
 | 370 | 	   unsigned int cmd, unsigned long arg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 371 | { | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 372 | 	struct block_device *bdev = inode->i_bdev; | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 373 | 	struct dasd_block *block = bdev->bd_disk->private_data; | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 374 | 	void __user *argp = (void __user *)arg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 375 |  | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 376 | 	if (!block) | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 377 |                 return -ENODEV; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 378 |  | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 379 | 	if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) { | 
 | 380 | 		PRINT_DEBUG("empty data ptr"); | 
 | 381 | 		return -EINVAL; | 
 | 382 | 	} | 
 | 383 |  | 
 | 384 | 	switch (cmd) { | 
 | 385 | 	case BIODASDDISABLE: | 
 | 386 | 		return dasd_ioctl_disable(bdev); | 
 | 387 | 	case BIODASDENABLE: | 
 | 388 | 		return dasd_ioctl_enable(bdev); | 
 | 389 | 	case BIODASDQUIESCE: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 390 | 		return dasd_ioctl_quiesce(block); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 391 | 	case BIODASDRESUME: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 392 | 		return dasd_ioctl_resume(block); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 393 | 	case BIODASDFMT: | 
 | 394 | 		return dasd_ioctl_format(bdev, argp); | 
 | 395 | 	case BIODASDINFO: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 396 | 		return dasd_ioctl_information(block, cmd, argp); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 397 | 	case BIODASDINFO2: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 398 | 		return dasd_ioctl_information(block, cmd, argp); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 399 | 	case BIODASDPRRD: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 400 | 		return dasd_ioctl_read_profile(block, argp); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 401 | 	case BIODASDPRRST: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 402 | 		return dasd_ioctl_reset_profile(block); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 403 | 	case BLKROSET: | 
 | 404 | 		return dasd_ioctl_set_ro(bdev, argp); | 
 | 405 | 	case DASDAPIVER: | 
 | 406 | 		return dasd_ioctl_api_version(argp); | 
| Christoph Hellwig | 8b2eb66 | 2006-03-24 03:15:21 -0800 | [diff] [blame] | 407 | 	case BIODASDCMFENABLE: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 408 | 		return enable_cmf(block->base->cdev); | 
| Christoph Hellwig | 8b2eb66 | 2006-03-24 03:15:21 -0800 | [diff] [blame] | 409 | 	case BIODASDCMFDISABLE: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 410 | 		return disable_cmf(block->base->cdev); | 
| Christoph Hellwig | 8b2eb66 | 2006-03-24 03:15:21 -0800 | [diff] [blame] | 411 | 	case BIODASDREADALLCMB: | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 412 | 		return dasd_ioctl_readall_cmb(block, cmd, arg); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 413 | 	default: | 
| Christoph Hellwig | 1107ccf | 2006-03-24 03:15:20 -0800 | [diff] [blame] | 414 | 		/* if the discipline has an ioctl method try it. */ | 
| Stefan Weinhuber | 8e09f21 | 2008-01-26 14:11:23 +0100 | [diff] [blame] | 415 | 		if (block->base->discipline->ioctl) { | 
 | 416 | 			int rval = block->base->discipline->ioctl(block, cmd, argp); | 
| Christoph Hellwig | 1107ccf | 2006-03-24 03:15:20 -0800 | [diff] [blame] | 417 | 			if (rval != -ENOIOCTLCMD) | 
 | 418 | 				return rval; | 
 | 419 | 		} | 
 | 420 |  | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 421 | 		return -EINVAL; | 
 | 422 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 423 | } | 
 | 424 |  | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 425 | long | 
 | 426 | dasd_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 | { | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 428 | 	int rval; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 429 |  | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 430 | 	lock_kernel(); | 
| Josef Sipek | 49522c9 | 2006-12-08 02:37:34 -0800 | [diff] [blame] | 431 | 	rval = dasd_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg); | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 432 | 	unlock_kernel(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 433 |  | 
| Christoph Hellwig | 13c6204 | 2006-03-24 03:15:19 -0800 | [diff] [blame] | 434 | 	return (rval == -EINVAL) ? -ENOIOCTLCMD : rval; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 435 | } |