dasd_ioctl.c 10.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * File...........: linux/drivers/s390/block/dasd_ioctl.c
 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
 *		    Horst Hummel <Horst.Hummel@de.ibm.com>
 *		    Carsten Otte <Cotte@de.ibm.com>
 *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
 * Bugreports.to..: <Linux390@de.ibm.com>
 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
 *
 * i/o controls for the dasd driver.
 */
#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/blkpg.h>

#include <asm/ccwdev.h>
19
#include <asm/cmb.h>
L
Linus Torvalds 已提交
20 21 22 23 24 25 26 27 28
#include <asm/uaccess.h>

/* This is ugly... */
#define PRINTK_HEADER "dasd_ioctl:"

#include "dasd_int.h"


static int
29
dasd_ioctl_api_version(void __user *argp)
L
Linus Torvalds 已提交
30 31
{
	int ver = DASD_API_VERSION;
32
	return put_user(ver, (int *)argp);
L
Linus Torvalds 已提交
33 34 35 36 37 38 39
}

/*
 * Enable device.
 * used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection
 */
static int
40
dasd_ioctl_enable(struct block_device *bdev)
L
Linus Torvalds 已提交
41
{
42
	struct dasd_device *device = bdev->bd_disk->private_data;
L
Linus Torvalds 已提交
43 44 45

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
46

L
Linus Torvalds 已提交
47 48
	dasd_enable_device(device);
	/* Formatting the dasd device can change the capacity. */
49
	mutex_lock(&bdev->bd_mutex);
L
Linus Torvalds 已提交
50
	i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->gdp) << 9);
51
	mutex_unlock(&bdev->bd_mutex);
L
Linus Torvalds 已提交
52 53 54 55 56 57 58 59
	return 0;
}

/*
 * Disable device.
 * Used by dasdfmt. Disable I/O operations but allow ioctls.
 */
static int
60
dasd_ioctl_disable(struct block_device *bdev)
L
Linus Torvalds 已提交
61
{
62
	struct dasd_device *device = bdev->bd_disk->private_data;
L
Linus Torvalds 已提交
63 64 65

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
66

L
Linus Torvalds 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79
	/*
	 * 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.
	 */
	dasd_set_target_state(device, DASD_STATE_BASIC);
	/*
	 * Set i_size to zero, since read, write, etc. check against this
	 * value.
	 */
80
	mutex_lock(&bdev->bd_mutex);
L
Linus Torvalds 已提交
81
	i_size_write(bdev->bd_inode, 0);
82
	mutex_unlock(&bdev->bd_mutex);
L
Linus Torvalds 已提交
83 84 85 86 87 88 89
	return 0;
}

/*
 * Quiesce device.
 */
static int
90
dasd_ioctl_quiesce(struct dasd_device *device)
L
Linus Torvalds 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
{
	unsigned long flags;
	
	if (!capable (CAP_SYS_ADMIN))
		return -EACCES;
	
	DEV_MESSAGE (KERN_DEBUG, device, "%s",
		     "Quiesce IO on device");
	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
	device->stopped |= DASD_STOPPED_QUIESCE;
	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
	return 0;
}


/*
 * Quiesce device.
 */
static int
110
dasd_ioctl_resume(struct dasd_device *device)
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
{
	unsigned long flags;
	
	if (!capable (CAP_SYS_ADMIN)) 
		return -EACCES;

	DEV_MESSAGE (KERN_DEBUG, device, "%s",
		     "resume IO on device");
	
	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
	device->stopped &= ~DASD_STOPPED_QUIESCE;
	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);

	dasd_schedule_bh (device);
	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.
 */
static int
dasd_format(struct dasd_device * device, struct format_data_t * fdata)
{
	struct dasd_ccw_req *cqr;
	int rc;

	if (device->discipline->format_device == NULL)
		return -EPERM;

	if (device->state != DASD_STATE_BASIC) {
		DEV_MESSAGE(KERN_WARNING, device, "%s",
			    "dasd_format: device is not disabled! ");
		return -EBUSY;
	}

	DBF_DEV_EVENT(DBF_NOTICE, device,
		      "formatting units %d to %d (%d B blocks) flags %d",
		      fdata->start_unit,
		      fdata->stop_unit, fdata->blksize, fdata->intensity);

	/* Since dasdfmt keeps the device open after it was disabled,
	 * there still exists an inode for this device.
	 * We must update i_blkbits, otherwise we might get errors when
	 * enabling the device later.
	 */
	if (fdata->start_unit == 0) {
		struct block_device *bdev = bdget_disk(device->gdp, 0);
		bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
		bdput(bdev);
	}

	while (fdata->start_unit <= fdata->stop_unit) {
		cqr = device->discipline->format_device(device, fdata);
		if (IS_ERR(cqr))
			return PTR_ERR(cqr);
		rc = dasd_sleep_on_interruptible(cqr);
		dasd_sfree_request(cqr, cqr->device);
		if (rc) {
			if (rc != -ERESTARTSYS)
				DEV_MESSAGE(KERN_ERR, device,
					    " Formatting of unit %d failed "
					    "with rc = %d",
					    fdata->start_unit, rc);
			return rc;
		}
		fdata->start_unit++;
	}
	return 0;
}

/*
 * Format device.
 */
static int
188
dasd_ioctl_format(struct block_device *bdev, void __user *argp)
L
Linus Torvalds 已提交
189
{
190
	struct dasd_device *device = bdev->bd_disk->private_data;
L
Linus Torvalds 已提交
191 192 193 194
	struct format_data_t fdata;

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
195
	if (!argp)
L
Linus Torvalds 已提交
196
		return -EINVAL;
197

198
	if (device->features & DASD_FEATURE_READONLY)
L
Linus Torvalds 已提交
199
		return -EROFS;
200
	if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
L
Linus Torvalds 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214
		return -EFAULT;
	if (bdev != bdev->bd_contains) {
		DEV_MESSAGE(KERN_WARNING, device, "%s",
			    "Cannot low-level format a partition");
		return -EINVAL;
	}
	return dasd_format(device, &fdata);
}

#ifdef CONFIG_DASD_PROFILE
/*
 * Reset device profile information
 */
static int
215
dasd_ioctl_reset_profile(struct dasd_device *device)
L
Linus Torvalds 已提交
216 217 218 219 220 221 222 223 224
{
	memset(&device->profile, 0, sizeof (struct dasd_profile_info_t));
	return 0;
}

/*
 * Return device profile information
 */
static int
225
dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
L
Linus Torvalds 已提交
226
{
227 228
	if (dasd_profile_level == DASD_PROFILE_OFF)
		return -EIO;
229
	if (copy_to_user(argp, &device->profile,
L
Linus Torvalds 已提交
230 231 232 233 234 235
			 sizeof (struct dasd_profile_info_t)))
		return -EFAULT;
	return 0;
}
#else
static int
236
dasd_ioctl_reset_profile(struct dasd_device *device)
L
Linus Torvalds 已提交
237 238 239 240 241
{
	return -ENOSYS;
}

static int
242
dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
L
Linus Torvalds 已提交
243 244 245 246 247 248 249 250 251
{
	return -ENOSYS;
}
#endif

/*
 * Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
 */
static int
252 253
dasd_ioctl_information(struct dasd_device *device,
		unsigned int cmd, void __user *argp)
L
Linus Torvalds 已提交
254 255 256
{
	struct dasd_information2_t *dasd_info;
	unsigned long flags;
257
	int rc;
L
Linus Torvalds 已提交
258 259 260 261 262
	struct ccw_device *cdev;

	if (!device->discipline->fill_info)
		return -EINVAL;

263
	dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
L
Linus Torvalds 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
	if (dasd_info == NULL)
		return -ENOMEM;

	rc = device->discipline->fill_info(device, dasd_info);
	if (rc) {
		kfree(dasd_info);
		return rc;
	}

	cdev = device->cdev;

	dasd_info->devno = _ccw_device_get_device_number(device->cdev);
	dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev);
	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;
	dasd_info->status = device->state;
H
Horst Hummel 已提交
282 283 284 285 286 287 288 289
	/*
	 * The open_count is increased for every opener, that includes
	 * the blkdev_get in dasd_scan_partitions.
	 * This must be hidden from user-space.
	 */
	dasd_info->open_count = atomic_read(&device->open_count);
	if (!device->bdev)
		dasd_info->open_count++;
L
Linus Torvalds 已提交
290 291 292 293 294 295 296 297
	
	/*
	 * check if device is really formatted
	 * LDL / CDL was returned by 'fill_info'
	 */
	if ((device->state < DASD_STATE_READY) ||
	    (dasd_check_blocksize(device->bp_block)))
		dasd_info->format = DASD_FORMAT_NONE;
298

299 300
	dasd_info->features |=
		((device->features & DASD_FEATURE_READONLY) != 0);
L
Linus Torvalds 已提交
301 302 303 304 305

	if (device->discipline)
		memcpy(dasd_info->type, device->discipline->name, 4);
	else
		memcpy(dasd_info->type, "none", 4);
306

L
Linus Torvalds 已提交
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
	if (device->request_queue->request_fn) {
		struct list_head *l;
#ifdef DASD_EXTENDED_PROFILING
		{
			struct list_head *l;
			spin_lock_irqsave(&device->lock, flags);
			list_for_each(l, &device->request_queue->queue_head)
				dasd_info->req_queue_len++;
			spin_unlock_irqrestore(&device->lock, flags);
		}
#endif				/* DASD_EXTENDED_PROFILING */
		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
		list_for_each(l, &device->ccw_queue)
			dasd_info->chanq_len++;
		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
				       flags);
	}

	rc = 0;
326 327
	if (copy_to_user(argp, dasd_info,
			 ((cmd == (unsigned int) BIODASDINFO2) ?
L
Linus Torvalds 已提交
328 329 330 331 332 333 334 335 336 337 338
			  sizeof (struct dasd_information2_t) :
			  sizeof (struct dasd_information_t))))
		rc = -EFAULT;
	kfree(dasd_info);
	return rc;
}

/*
 * Set read only
 */
static int
339
dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
L
Linus Torvalds 已提交
340
{
341 342
	struct dasd_device *device =  bdev->bd_disk->private_data;
	int intval;
L
Linus Torvalds 已提交
343 344 345 346 347 348

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
	if (bdev != bdev->bd_contains)
		// ro setting is not allowed for partitions
		return -EINVAL;
349
	if (get_user(intval, (int *)argp))
L
Linus Torvalds 已提交
350
		return -EFAULT;
351

L
Linus Torvalds 已提交
352
	set_disk_ro(bdev->bd_disk, intval);
353
	return dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval);
L
Linus Torvalds 已提交
354 355
}

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
static int
dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
		unsigned long arg)
{
	struct cmbdata __user *argp = (void __user *) arg;
	size_t size = _IOC_SIZE(cmd);
	struct cmbdata data;
	int ret;

	ret = cmf_readall(device->cdev, &data);
	if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp))))
		return -EFAULT;
	return ret;
}

L
Linus Torvalds 已提交
371
int
372 373
dasd_ioctl(struct inode *inode, struct file *file,
	   unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
374
{
375 376 377
	struct block_device *bdev = inode->i_bdev;
	struct dasd_device *device = bdev->bd_disk->private_data;
	void __user *argp = (void __user *)arg;
L
Linus Torvalds 已提交
378

379 380 381 382 383 384 385
	if (!device)
                return -ENODEV;

	if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
		PRINT_DEBUG("empty data ptr");
		return -EINVAL;
	}
L
Linus Torvalds 已提交
386

387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
	switch (cmd) {
	case BIODASDDISABLE:
		return dasd_ioctl_disable(bdev);
	case BIODASDENABLE:
		return dasd_ioctl_enable(bdev);
	case BIODASDQUIESCE:
		return dasd_ioctl_quiesce(device);
	case BIODASDRESUME:
		return dasd_ioctl_resume(device);
	case BIODASDFMT:
		return dasd_ioctl_format(bdev, argp);
	case BIODASDINFO:
		return dasd_ioctl_information(device, cmd, argp);
	case BIODASDINFO2:
		return dasd_ioctl_information(device, cmd, argp);
	case BIODASDPRRD:
		return dasd_ioctl_read_profile(device, argp);
	case BIODASDPRRST:
		return dasd_ioctl_reset_profile(device);
	case BLKROSET:
		return dasd_ioctl_set_ro(bdev, argp);
	case DASDAPIVER:
		return dasd_ioctl_api_version(argp);
410 411 412 413 414 415
	case BIODASDCMFENABLE:
		return enable_cmf(device->cdev);
	case BIODASDCMFDISABLE:
		return disable_cmf(device->cdev);
	case BIODASDREADALLCMB:
		return dasd_ioctl_readall_cmb(device, cmd, arg);
416
	default:
417 418 419 420 421 422 423
		/* if the discipline has an ioctl method try it. */
		if (device->discipline->ioctl) {
			int rval = device->discipline->ioctl(device, cmd, argp);
			if (rval != -ENOIOCTLCMD)
				return rval;
		}

424 425
		return -EINVAL;
	}
L
Linus Torvalds 已提交
426 427
}

428 429
long
dasd_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
430
{
431
	int rval;
L
Linus Torvalds 已提交
432

433 434 435
	lock_kernel();
	rval = dasd_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
	unlock_kernel();
L
Linus Torvalds 已提交
436

437
	return (rval == -EINVAL) ? -ENOIOCTLCMD : rval;
L
Linus Torvalds 已提交
438
}