dasd_ioctl.c 12.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6
/*
 * 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>
7
 * Copyright IBM Corp. 1999, 2001
L
Linus Torvalds 已提交
8 9 10
 *
 * i/o controls for the dasd driver.
 */
S
Stefan Haberland 已提交
11 12 13

#define KMSG_COMPONENT "dasd"

L
Linus Torvalds 已提交
14
#include <linux/interrupt.h>
15
#include <linux/compat.h>
L
Linus Torvalds 已提交
16 17 18
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/blkpg.h>
19
#include <linux/slab.h>
20
#include <asm/compat.h>
L
Linus Torvalds 已提交
21
#include <asm/ccwdev.h>
22
#include <asm/cmb.h>
L
Linus Torvalds 已提交
23 24 25 26 27 28 29 30 31
#include <asm/uaccess.h>

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

#include "dasd_int.h"


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

/*
 * Enable device.
 * used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection
 */
static int
43
dasd_ioctl_enable(struct block_device *bdev)
L
Linus Torvalds 已提交
44
{
45
	struct dasd_device *base;
L
Linus Torvalds 已提交
46 47 48

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

50 51 52 53 54
	base = dasd_device_from_gendisk(bdev->bd_disk);
	if (!base)
		return -ENODEV;

	dasd_enable_device(base);
L
Linus Torvalds 已提交
55
	/* Formatting the dasd device can change the capacity. */
56
	mutex_lock(&bdev->bd_mutex);
57 58
	i_size_write(bdev->bd_inode,
		     (loff_t)get_capacity(base->block->gdp) << 9);
59
	mutex_unlock(&bdev->bd_mutex);
60
	dasd_put_device(base);
L
Linus Torvalds 已提交
61 62 63 64 65 66 67 68
	return 0;
}

/*
 * Disable device.
 * Used by dasdfmt. Disable I/O operations but allow ioctls.
 */
static int
69
dasd_ioctl_disable(struct block_device *bdev)
L
Linus Torvalds 已提交
70
{
71
	struct dasd_device *base;
L
Linus Torvalds 已提交
72 73 74

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

76 77 78
	base = dasd_device_from_gendisk(bdev->bd_disk);
	if (!base)
		return -ENODEV;
L
Linus Torvalds 已提交
79 80 81 82 83 84 85 86
	/*
	 * 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.
	 */
87
	dasd_set_target_state(base, DASD_STATE_BASIC);
L
Linus Torvalds 已提交
88 89 90 91
	/*
	 * Set i_size to zero, since read, write, etc. check against this
	 * value.
	 */
92
	mutex_lock(&bdev->bd_mutex);
L
Linus Torvalds 已提交
93
	i_size_write(bdev->bd_inode, 0);
94
	mutex_unlock(&bdev->bd_mutex);
95
	dasd_put_device(base);
L
Linus Torvalds 已提交
96 97 98 99 100 101
	return 0;
}

/*
 * Quiesce device.
 */
102
static int dasd_ioctl_quiesce(struct dasd_block *block)
L
Linus Torvalds 已提交
103 104
{
	unsigned long flags;
105
	struct dasd_device *base;
106

107
	base = block->base;
L
Linus Torvalds 已提交
108 109
	if (!capable (CAP_SYS_ADMIN))
		return -EACCES;
110

111 112
	pr_info("%s: The DASD has been put in the quiesce "
		"state\n", dev_name(&base->cdev->dev));
113
	spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
114
	dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
115
	spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
L
Linus Torvalds 已提交
116 117 118 119 120
	return 0;
}


/*
S
Stefan Haberland 已提交
121
 * Resume device.
L
Linus Torvalds 已提交
122
 */
123
static int dasd_ioctl_resume(struct dasd_block *block)
L
Linus Torvalds 已提交
124 125
{
	unsigned long flags;
126
	struct dasd_device *base;
127

128
	base = block->base;
129
	if (!capable (CAP_SYS_ADMIN))
L
Linus Torvalds 已提交
130 131
		return -EACCES;

132 133
	pr_info("%s: I/O operations have been resumed "
		"on the DASD\n", dev_name(&base->cdev->dev));
134
	spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
135
	dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE);
136
	spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
L
Linus Torvalds 已提交
137

138
	dasd_schedule_block_bh(block);
L
Linus Torvalds 已提交
139 140 141 142 143 144 145 146 147
	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.
 */
148
static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
L
Linus Torvalds 已提交
149 150
{
	struct dasd_ccw_req *cqr;
151
	struct dasd_device *base;
L
Linus Torvalds 已提交
152 153
	int rc;

154 155
	base = block->base;
	if (base->discipline->format_device == NULL)
L
Linus Torvalds 已提交
156 157
		return -EPERM;

158
	if (base->state != DASD_STATE_BASIC) {
159 160
		pr_warning("%s: The DASD cannot be formatted while it is "
			   "enabled\n",  dev_name(&base->cdev->dev));
L
Linus Torvalds 已提交
161 162 163
		return -EBUSY;
	}

164
	DBF_DEV_EVENT(DBF_NOTICE, base,
165
		      "formatting units %u to %u (%u B blocks) flags %u",
L
Linus Torvalds 已提交
166 167 168 169 170 171 172 173 174
		      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) {
175
		struct block_device *bdev = bdget_disk(block->gdp, 0);
L
Linus Torvalds 已提交
176 177 178 179 180
		bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
		bdput(bdev);
	}

	while (fdata->start_unit <= fdata->stop_unit) {
181
		cqr = base->discipline->format_device(base, fdata);
L
Linus Torvalds 已提交
182 183 184
		if (IS_ERR(cqr))
			return PTR_ERR(cqr);
		rc = dasd_sleep_on_interruptible(cqr);
185
		dasd_sfree_request(cqr, cqr->memdev);
L
Linus Torvalds 已提交
186 187
		if (rc) {
			if (rc != -ERESTARTSYS)
188 189 190
				pr_err("%s: Formatting unit %d failed with "
				       "rc=%d\n", dev_name(&base->cdev->dev),
				       fdata->start_unit, rc);
L
Linus Torvalds 已提交
191 192 193 194 195 196 197 198 199 200 201
			return rc;
		}
		fdata->start_unit++;
	}
	return 0;
}

/*
 * Format device.
 */
static int
202
dasd_ioctl_format(struct block_device *bdev, void __user *argp)
L
Linus Torvalds 已提交
203
{
204
	struct dasd_device *base;
L
Linus Torvalds 已提交
205
	struct format_data_t fdata;
206
	int rc;
L
Linus Torvalds 已提交
207 208 209

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
210
	if (!argp)
L
Linus Torvalds 已提交
211
		return -EINVAL;
212 213 214 215 216 217
	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);
L
Linus Torvalds 已提交
218
		return -EROFS;
219 220 221
	}
	if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) {
		dasd_put_device(base);
L
Linus Torvalds 已提交
222
		return -EFAULT;
223
	}
L
Linus Torvalds 已提交
224
	if (bdev != bdev->bd_contains) {
225 226
		pr_warning("%s: The specified DASD is a partition and cannot "
			   "be formatted\n",
227 228
			   dev_name(&base->cdev->dev));
		dasd_put_device(base);
L
Linus Torvalds 已提交
229 230
		return -EINVAL;
	}
231 232 233
	rc = dasd_format(base->block, &fdata);
	dasd_put_device(base);
	return rc;
L
Linus Torvalds 已提交
234 235 236 237 238 239
}

#ifdef CONFIG_DASD_PROFILE
/*
 * Reset device profile information
 */
240
static int dasd_ioctl_reset_profile(struct dasd_block *block)
L
Linus Torvalds 已提交
241
{
242
	dasd_profile_reset(&block->profile);
L
Linus Torvalds 已提交
243 244 245 246 247 248
	return 0;
}

/*
 * Return device profile information
 */
249
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
L
Linus Torvalds 已提交
250
{
251
	struct dasd_profile_info_t *data;
252
	int rc = 0;
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

	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);
283 284
		rc = -EIO;
		goto out;
285 286
	}
	if (copy_to_user(argp, data, sizeof(*data)))
287 288 289 290
		rc = -EFAULT;
out:
	kfree(data);
	return rc;
L
Linus Torvalds 已提交
291 292
}
#else
293
static int dasd_ioctl_reset_profile(struct dasd_block *block)
L
Linus Torvalds 已提交
294
{
295
	return -ENOTTY;
L
Linus Torvalds 已提交
296 297
}

298
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
L
Linus Torvalds 已提交
299
{
300
	return -ENOTTY;
L
Linus Torvalds 已提交
301 302 303 304 305 306
}
#endif

/*
 * Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
 */
307 308
static int dasd_ioctl_information(struct dasd_block *block,
				  unsigned int cmd, void __user *argp)
L
Linus Torvalds 已提交
309 310 311
{
	struct dasd_information2_t *dasd_info;
	unsigned long flags;
312
	int rc;
313
	struct dasd_device *base;
L
Linus Torvalds 已提交
314
	struct ccw_device *cdev;
315
	struct ccw_dev_id dev_id;
L
Linus Torvalds 已提交
316

317
	base = block->base;
318
	if (!base->discipline || !base->discipline->fill_info)
L
Linus Torvalds 已提交
319 320
		return -EINVAL;

321
	dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
L
Linus Torvalds 已提交
322 323 324
	if (dasd_info == NULL)
		return -ENOMEM;

325
	rc = base->discipline->fill_info(base, dasd_info);
L
Linus Torvalds 已提交
326 327 328 329 330
	if (rc) {
		kfree(dasd_info);
		return rc;
	}

331
	cdev = base->cdev;
332
	ccw_device_get_id(cdev, &dev_id);
L
Linus Torvalds 已提交
333

334
	dasd_info->devno = dev_id.devno;
335
	dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev);
L
Linus Torvalds 已提交
336 337 338 339
	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;
340
	dasd_info->status = base->state;
H
Horst Hummel 已提交
341 342 343 344 345
	/*
	 * The open_count is increased for every opener, that includes
	 * the blkdev_get in dasd_scan_partitions.
	 * This must be hidden from user-space.
	 */
346 347
	dasd_info->open_count = atomic_read(&block->open_count);
	if (!block->bdev)
H
Horst Hummel 已提交
348
		dasd_info->open_count++;
349

L
Linus Torvalds 已提交
350 351 352 353
	/*
	 * check if device is really formatted
	 * LDL / CDL was returned by 'fill_info'
	 */
354 355
	if ((base->state < DASD_STATE_READY) ||
	    (dasd_check_blocksize(block->bp_block)))
L
Linus Torvalds 已提交
356
		dasd_info->format = DASD_FORMAT_NONE;
357

358
	dasd_info->features |=
359
		((base->features & DASD_FEATURE_READONLY) != 0);
L
Linus Torvalds 已提交
360

361
	memcpy(dasd_info->type, base->discipline->name, 4);
362

363
	if (block->request_queue->request_fn) {
L
Linus Torvalds 已提交
364 365 366 367
		struct list_head *l;
#ifdef DASD_EXTENDED_PROFILING
		{
			struct list_head *l;
368 369
			spin_lock_irqsave(&block->lock, flags);
			list_for_each(l, &block->request_queue->queue_head)
L
Linus Torvalds 已提交
370
				dasd_info->req_queue_len++;
371
			spin_unlock_irqrestore(&block->lock, flags);
L
Linus Torvalds 已提交
372 373
		}
#endif				/* DASD_EXTENDED_PROFILING */
374 375
		spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
		list_for_each(l, &base->ccw_queue)
L
Linus Torvalds 已提交
376
			dasd_info->chanq_len++;
377
		spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
L
Linus Torvalds 已提交
378 379 380 381
				       flags);
	}

	rc = 0;
382 383
	if (copy_to_user(argp, dasd_info,
			 ((cmd == (unsigned int) BIODASDINFO2) ?
384 385
			  sizeof(struct dasd_information2_t) :
			  sizeof(struct dasd_information_t))))
L
Linus Torvalds 已提交
386 387 388 389 390 391 392 393 394
		rc = -EFAULT;
	kfree(dasd_info);
	return rc;
}

/*
 * Set read only
 */
static int
395
dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
L
Linus Torvalds 已提交
396
{
397 398
	struct dasd_device *base;
	int intval, rc;
L
Linus Torvalds 已提交
399 400 401 402 403 404

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
	if (bdev != bdev->bd_contains)
		// ro setting is not allowed for partitions
		return -EINVAL;
H
Heiko Carstens 已提交
405
	if (get_user(intval, (int __user *)argp))
L
Linus Torvalds 已提交
406
		return -EFAULT;
407 408 409 410 411
	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);
412
		return -EROFS;
413
	}
L
Linus Torvalds 已提交
414
	set_disk_ro(bdev->bd_disk, intval);
415 416 417
	rc = dasd_set_feature(base->cdev, DASD_FEATURE_READONLY, intval);
	dasd_put_device(base);
	return rc;
L
Linus Torvalds 已提交
418 419
}

420
static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
421
				  struct cmbdata __user *argp)
422 423 424 425 426
{
	size_t size = _IOC_SIZE(cmd);
	struct cmbdata data;
	int ret;

427
	ret = cmf_readall(block->base->cdev, &data);
428 429 430 431 432
	if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp))))
		return -EFAULT;
	return ret;
}

433 434
int dasd_ioctl(struct block_device *bdev, fmode_t mode,
	       unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
435
{
436 437
	struct dasd_block *block;
	struct dasd_device *base;
438
	void __user *argp;
439
	int rc;
440 441 442 443 444

	if (is_compat_task())
		argp = compat_ptr(arg);
	else
		argp = (void __user *)arg;
L
Linus Torvalds 已提交
445

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

451 452 453 454 455
	base = dasd_device_from_gendisk(bdev->bd_disk);
	if (!base)
		return -ENODEV;
	block = base->block;
	rc = 0;
456 457
	switch (cmd) {
	case BIODASDDISABLE:
458 459
		rc = dasd_ioctl_disable(bdev);
		break;
460
	case BIODASDENABLE:
461 462
		rc = dasd_ioctl_enable(bdev);
		break;
463
	case BIODASDQUIESCE:
464 465
		rc = dasd_ioctl_quiesce(block);
		break;
466
	case BIODASDRESUME:
467 468
		rc = dasd_ioctl_resume(block);
		break;
469
	case BIODASDFMT:
470 471
		rc = dasd_ioctl_format(bdev, argp);
		break;
472
	case BIODASDINFO:
473 474
		rc = dasd_ioctl_information(block, cmd, argp);
		break;
475
	case BIODASDINFO2:
476 477
		rc = dasd_ioctl_information(block, cmd, argp);
		break;
478
	case BIODASDPRRD:
479 480
		rc = dasd_ioctl_read_profile(block, argp);
		break;
481
	case BIODASDPRRST:
482 483
		rc = dasd_ioctl_reset_profile(block);
		break;
484
	case BLKROSET:
485 486
		rc = dasd_ioctl_set_ro(bdev, argp);
		break;
487
	case DASDAPIVER:
488 489
		rc = dasd_ioctl_api_version(argp);
		break;
490
	case BIODASDCMFENABLE:
491 492
		rc = enable_cmf(base->cdev);
		break;
493
	case BIODASDCMFDISABLE:
494 495
		rc = disable_cmf(base->cdev);
		break;
496
	case BIODASDREADALLCMB:
497 498
		rc = dasd_ioctl_readall_cmb(block, cmd, argp);
		break;
499
	default:
500
		/* if the discipline has an ioctl method try it. */
501 502
		rc = -ENOTTY;
		if (base->discipline->ioctl)
503
			rc = base->discipline->ioctl(block, cmd, argp);
504
	}
505 506
	dasd_put_device(base);
	return rc;
L
Linus Torvalds 已提交
507
}