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
/*
 * 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/interrupt.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/blkpg.h>

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

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

#include "dasd_int.h"


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

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

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

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

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

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

L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78
	/*
	 * 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.
	 */
79
	mutex_lock(&bdev->bd_mutex);
L
Linus Torvalds 已提交
80
	i_size_write(bdev->bd_inode, 0);
81
	mutex_unlock(&bdev->bd_mutex);
L
Linus Torvalds 已提交
82 83 84 85 86 87 88
	return 0;
}

/*
 * Quiesce device.
 */
static int
89
dasd_ioctl_quiesce(struct dasd_device *device)
L
Linus Torvalds 已提交
90 91
{
	unsigned long flags;
92

L
Linus Torvalds 已提交
93 94
	if (!capable (CAP_SYS_ADMIN))
		return -EACCES;
95

L
Linus Torvalds 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108
	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
109
dasd_ioctl_resume(struct dasd_device *device)
L
Linus Torvalds 已提交
110 111
{
	unsigned long flags;
112 113

	if (!capable (CAP_SYS_ADMIN))
L
Linus Torvalds 已提交
114 115 116 117
		return -EACCES;

	DEV_MESSAGE (KERN_DEBUG, device, "%s",
		     "resume IO on device");
118

L
Linus Torvalds 已提交
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
	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
187
dasd_ioctl_format(struct block_device *bdev, void __user *argp)
L
Linus Torvalds 已提交
188
{
189
	struct dasd_device *device = bdev->bd_disk->private_data;
L
Linus Torvalds 已提交
190 191 192 193
	struct format_data_t fdata;

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

197
	if (device->features & DASD_FEATURE_READONLY)
L
Linus Torvalds 已提交
198
		return -EROFS;
199
	if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
L
Linus Torvalds 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213
		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
214
dasd_ioctl_reset_profile(struct dasd_device *device)
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223
{
	memset(&device->profile, 0, sizeof (struct dasd_profile_info_t));
	return 0;
}

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

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

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

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

262
	dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
L
Linus Torvalds 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
	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 已提交
281 282 283 284 285 286 287 288
	/*
	 * 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++;
289

L
Linus Torvalds 已提交
290 291 292 293 294 295 296
	/*
	 * 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;
297

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

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

L
Linus Torvalds 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
	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;
325 326
	if (copy_to_user(argp, dasd_info,
			 ((cmd == (unsigned int) BIODASDINFO2) ?
L
Linus Torvalds 已提交
327 328 329 330 331 332 333 334 335 336 337
			  sizeof (struct dasd_information2_t) :
			  sizeof (struct dasd_information_t))))
		rc = -EFAULT;
	kfree(dasd_info);
	return rc;
}

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

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

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

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
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 已提交
370
int
371 372
dasd_ioctl(struct inode *inode, struct file *file,
	   unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
373
{
374 375 376
	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 已提交
377

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

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

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
	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);
409 410 411 412 413 414
	case BIODASDCMFENABLE:
		return enable_cmf(device->cdev);
	case BIODASDCMFDISABLE:
		return disable_cmf(device->cdev);
	case BIODASDREADALLCMB:
		return dasd_ioctl_readall_cmb(device, cmd, arg);
415
	default:
416 417 418 419 420 421 422
		/* 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;
		}

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

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

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

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