diff options
author | Jan Höppner <hoeppner@linux.ibm.com> | 2018-07-23 05:13:30 -0400 |
---|---|---|
committer | Vasily Gorbik <gor@linux.ibm.com> | 2019-07-11 14:39:54 -0400 |
commit | 91dc4a197569230683ca8bad551e655a4bf14c30 (patch) | |
tree | 4cd4647b811278f165405a7eef4df952810be45c | |
parent | bcf36768ea688e926b04639c88c77d8caea3b736 (diff) |
s390/dasd: Add new ioctl to release space
Userspace tools might have the need to release space for Extent Space
Efficient (ESE) volumes when working with such a device.
Provide the necessarry interface for such a task by implementing a new
ioctl BIODASDRAS. The ioctl uses the format_data_t data structure for
data input:
typedef struct format_data_t {
unsigned int start_unit; /* from track */
unsigned int stop_unit; /* to track */
unsigned int blksize; /* sectorsize */
unsigned int intensity;
} format_data_t;
If the intensity is set to 0x40, start_unit and stop_unit are ignored
and space for the entire volume is released. Otherwise, if intensity is
set to 0, the respective range is released (if possible).
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
-rw-r--r-- | arch/s390/include/uapi/asm/dasd.h | 3 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 264 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.h | 40 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 1 | ||||
-rw-r--r-- | drivers/s390/block/dasd_ioctl.c | 56 |
5 files changed, 364 insertions, 0 deletions
diff --git a/arch/s390/include/uapi/asm/dasd.h b/arch/s390/include/uapi/asm/dasd.h index 48a2f475b90a..9ec86fae9980 100644 --- a/arch/s390/include/uapi/asm/dasd.h +++ b/arch/s390/include/uapi/asm/dasd.h | |||
@@ -194,6 +194,7 @@ typedef struct format_data_t { | |||
194 | #define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ | 194 | #define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ |
195 | #define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ | 195 | #define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ |
196 | #define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */ | 196 | #define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */ |
197 | #define DASD_FMT_INT_ESE_FULL 32 /* release space for entire volume */ | ||
197 | 198 | ||
198 | /* | 199 | /* |
199 | * struct format_check_t | 200 | * struct format_check_t |
@@ -323,6 +324,8 @@ struct dasd_snid_ioctl_data { | |||
323 | #define BIODASDFMT _IOW(DASD_IOCTL_LETTER,1,format_data_t) | 324 | #define BIODASDFMT _IOW(DASD_IOCTL_LETTER,1,format_data_t) |
324 | /* Set Attributes (cache operations) */ | 325 | /* Set Attributes (cache operations) */ |
325 | #define BIODASDSATTR _IOW(DASD_IOCTL_LETTER,2,attrib_data_t) | 326 | #define BIODASDSATTR _IOW(DASD_IOCTL_LETTER,2,attrib_data_t) |
327 | /* Release Allocated Space */ | ||
328 | #define BIODASDRAS _IOW(DASD_IOCTL_LETTER, 3, format_data_t) | ||
326 | 329 | ||
327 | /* Get Sense Path Group ID (SNID) data */ | 330 | /* Get Sense Path Group ID (SNID) data */ |
328 | #define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data) | 331 | #define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data) |
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6109a0e68911..21164a48317d 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -3354,6 +3354,269 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device, | |||
3354 | } | 3354 | } |
3355 | } | 3355 | } |
3356 | 3356 | ||
3357 | static int dasd_eckd_ras_sanity_checks(struct dasd_device *device, | ||
3358 | unsigned int first_trk, | ||
3359 | unsigned int last_trk) | ||
3360 | { | ||
3361 | struct dasd_eckd_private *private = device->private; | ||
3362 | unsigned int trks_per_vol; | ||
3363 | int rc = 0; | ||
3364 | |||
3365 | trks_per_vol = private->real_cyl * private->rdc_data.trk_per_cyl; | ||
3366 | |||
3367 | if (first_trk >= trks_per_vol) { | ||
3368 | dev_warn(&device->cdev->dev, | ||
3369 | "Start track number %u used in the space release command is too big\n", | ||
3370 | first_trk); | ||
3371 | rc = -EINVAL; | ||
3372 | } else if (last_trk >= trks_per_vol) { | ||
3373 | dev_warn(&device->cdev->dev, | ||
3374 | "Stop track number %u used in the space release command is too big\n", | ||
3375 | last_trk); | ||
3376 | rc = -EINVAL; | ||
3377 | } else if (first_trk > last_trk) { | ||
3378 | dev_warn(&device->cdev->dev, | ||
3379 | "Start track %u used in the space release command exceeds the end track\n", | ||
3380 | first_trk); | ||
3381 | rc = -EINVAL; | ||
3382 | } | ||
3383 | return rc; | ||
3384 | } | ||
3385 | |||
3386 | /* | ||
3387 | * Helper function to count the amount of involved extents within a given range | ||
3388 | * with extent alignment in mind. | ||
3389 | */ | ||
3390 | static int count_exts(unsigned int from, unsigned int to, int trks_per_ext) | ||
3391 | { | ||
3392 | int cur_pos = 0; | ||
3393 | int count = 0; | ||
3394 | int tmp; | ||
3395 | |||
3396 | if (from == to) | ||
3397 | return 1; | ||
3398 | |||
3399 | /* Count first partial extent */ | ||
3400 | if (from % trks_per_ext != 0) { | ||
3401 | tmp = from + trks_per_ext - (from % trks_per_ext) - 1; | ||
3402 | if (tmp > to) | ||
3403 | tmp = to; | ||
3404 | cur_pos = tmp - from + 1; | ||
3405 | count++; | ||
3406 | } | ||
3407 | /* Count full extents */ | ||
3408 | if (to - (from + cur_pos) + 1 >= trks_per_ext) { | ||
3409 | tmp = to - ((to - trks_per_ext + 1) % trks_per_ext); | ||
3410 | count += (tmp - (from + cur_pos) + 1) / trks_per_ext; | ||
3411 | cur_pos = tmp; | ||
3412 | } | ||
3413 | /* Count last partial extent */ | ||
3414 | if (cur_pos < to) | ||
3415 | count++; | ||
3416 | |||
3417 | return count; | ||
3418 | } | ||
3419 | |||
3420 | /* | ||
3421 | * Release allocated space for a given range or an entire volume. | ||
3422 | */ | ||
3423 | static struct dasd_ccw_req * | ||
3424 | dasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block, | ||
3425 | struct request *req, unsigned int first_trk, | ||
3426 | unsigned int last_trk, int by_extent) | ||
3427 | { | ||
3428 | struct dasd_eckd_private *private = device->private; | ||
3429 | struct dasd_dso_ras_ext_range *ras_range; | ||
3430 | struct dasd_rssd_features *features; | ||
3431 | struct dasd_dso_ras_data *ras_data; | ||
3432 | u16 heads, beg_head, end_head; | ||
3433 | int cur_to_trk, cur_from_trk; | ||
3434 | struct dasd_ccw_req *cqr; | ||
3435 | u32 beg_cyl, end_cyl; | ||
3436 | struct ccw1 *ccw; | ||
3437 | int trks_per_ext; | ||
3438 | size_t ras_size; | ||
3439 | size_t size; | ||
3440 | int nr_exts; | ||
3441 | void *rq; | ||
3442 | int i; | ||
3443 | |||
3444 | if (dasd_eckd_ras_sanity_checks(device, first_trk, last_trk)) | ||
3445 | return ERR_PTR(-EINVAL); | ||
3446 | |||
3447 | rq = req ? blk_mq_rq_to_pdu(req) : NULL; | ||
3448 | |||
3449 | features = &private->features; | ||
3450 | |||
3451 | trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl; | ||
3452 | nr_exts = 0; | ||
3453 | if (by_extent) | ||
3454 | nr_exts = count_exts(first_trk, last_trk, trks_per_ext); | ||
3455 | ras_size = sizeof(*ras_data); | ||
3456 | size = ras_size + (nr_exts * sizeof(*ras_range)); | ||
3457 | |||
3458 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, size, device, rq); | ||
3459 | if (IS_ERR(cqr)) { | ||
3460 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", | ||
3461 | "Could not allocate RAS request"); | ||
3462 | return cqr; | ||
3463 | } | ||
3464 | |||
3465 | ras_data = cqr->data; | ||
3466 | memset(ras_data, 0, size); | ||
3467 | |||
3468 | ras_data->order = DSO_ORDER_RAS; | ||
3469 | ras_data->flags.vol_type = 0; /* CKD volume */ | ||
3470 | /* Release specified extents or entire volume */ | ||
3471 | ras_data->op_flags.by_extent = by_extent; | ||
3472 | /* | ||
3473 | * This bit guarantees initialisation of tracks within an extent that is | ||
3474 | * not fully specified, but is only supported with a certain feature | ||
3475 | * subset. | ||
3476 | */ | ||
3477 | ras_data->op_flags.guarantee_init = !!(features->feature[56] & 0x01); | ||
3478 | ras_data->lss = private->ned->ID; | ||
3479 | ras_data->dev_addr = private->ned->unit_addr; | ||
3480 | ras_data->nr_exts = nr_exts; | ||
3481 | |||
3482 | if (by_extent) { | ||
3483 | heads = private->rdc_data.trk_per_cyl; | ||
3484 | cur_from_trk = first_trk; | ||
3485 | cur_to_trk = first_trk + trks_per_ext - | ||
3486 | (first_trk % trks_per_ext) - 1; | ||
3487 | if (cur_to_trk > last_trk) | ||
3488 | cur_to_trk = last_trk; | ||
3489 | ras_range = (struct dasd_dso_ras_ext_range *)(cqr->data + ras_size); | ||
3490 | |||
3491 | for (i = 0; i < nr_exts; i++) { | ||
3492 | beg_cyl = cur_from_trk / heads; | ||
3493 | beg_head = cur_from_trk % heads; | ||
3494 | end_cyl = cur_to_trk / heads; | ||
3495 | end_head = cur_to_trk % heads; | ||
3496 | |||
3497 | set_ch_t(&ras_range->beg_ext, beg_cyl, beg_head); | ||
3498 | set_ch_t(&ras_range->end_ext, end_cyl, end_head); | ||
3499 | |||
3500 | cur_from_trk = cur_to_trk + 1; | ||
3501 | cur_to_trk = cur_from_trk + trks_per_ext - 1; | ||
3502 | if (cur_to_trk > last_trk) | ||
3503 | cur_to_trk = last_trk; | ||
3504 | ras_range++; | ||
3505 | } | ||
3506 | } | ||
3507 | |||
3508 | ccw = cqr->cpaddr; | ||
3509 | ccw->cda = (__u32)(addr_t)cqr->data; | ||
3510 | ccw->cmd_code = DASD_ECKD_CCW_DSO; | ||
3511 | ccw->count = size; | ||
3512 | |||
3513 | cqr->startdev = device; | ||
3514 | cqr->memdev = device; | ||
3515 | cqr->block = block; | ||
3516 | cqr->retries = 256; | ||
3517 | cqr->expires = device->default_expires * HZ; | ||
3518 | cqr->buildclk = get_tod_clock(); | ||
3519 | cqr->status = DASD_CQR_FILLED; | ||
3520 | |||
3521 | return cqr; | ||
3522 | } | ||
3523 | |||
3524 | static int dasd_eckd_release_space_full(struct dasd_device *device) | ||
3525 | { | ||
3526 | struct dasd_ccw_req *cqr; | ||
3527 | int rc; | ||
3528 | |||
3529 | cqr = dasd_eckd_dso_ras(device, NULL, NULL, 0, 0, 0); | ||
3530 | if (IS_ERR(cqr)) | ||
3531 | return PTR_ERR(cqr); | ||
3532 | |||
3533 | rc = dasd_sleep_on_interruptible(cqr); | ||
3534 | |||
3535 | dasd_sfree_request(cqr, cqr->memdev); | ||
3536 | |||
3537 | return rc; | ||
3538 | } | ||
3539 | |||
3540 | static int dasd_eckd_release_space_trks(struct dasd_device *device, | ||
3541 | unsigned int from, unsigned int to) | ||
3542 | { | ||
3543 | struct dasd_eckd_private *private = device->private; | ||
3544 | struct dasd_block *block = device->block; | ||
3545 | struct dasd_ccw_req *cqr, *n; | ||
3546 | struct list_head ras_queue; | ||
3547 | unsigned int device_exts; | ||
3548 | int trks_per_ext; | ||
3549 | int stop, step; | ||
3550 | int cur_pos; | ||
3551 | int rc = 0; | ||
3552 | int retry; | ||
3553 | |||
3554 | INIT_LIST_HEAD(&ras_queue); | ||
3555 | |||
3556 | device_exts = private->real_cyl / dasd_eckd_ext_size(device); | ||
3557 | trks_per_ext = dasd_eckd_ext_size(device) * private->rdc_data.trk_per_cyl; | ||
3558 | |||
3559 | /* Make sure device limits are not exceeded */ | ||
3560 | step = trks_per_ext * min(device_exts, DASD_ECKD_RAS_EXTS_MAX); | ||
3561 | cur_pos = from; | ||
3562 | |||
3563 | do { | ||
3564 | retry = 0; | ||
3565 | while (cur_pos < to) { | ||
3566 | stop = cur_pos + step - | ||
3567 | ((cur_pos + step) % trks_per_ext) - 1; | ||
3568 | if (stop > to) | ||
3569 | stop = to; | ||
3570 | |||
3571 | cqr = dasd_eckd_dso_ras(device, NULL, NULL, cur_pos, stop, 1); | ||
3572 | if (IS_ERR(cqr)) { | ||
3573 | rc = PTR_ERR(cqr); | ||
3574 | if (rc == -ENOMEM) { | ||
3575 | if (list_empty(&ras_queue)) | ||
3576 | goto out; | ||
3577 | retry = 1; | ||
3578 | break; | ||
3579 | } | ||
3580 | goto err_out; | ||
3581 | } | ||
3582 | |||
3583 | spin_lock_irq(&block->queue_lock); | ||
3584 | list_add_tail(&cqr->blocklist, &ras_queue); | ||
3585 | spin_unlock_irq(&block->queue_lock); | ||
3586 | cur_pos = stop + 1; | ||
3587 | } | ||
3588 | |||
3589 | rc = dasd_sleep_on_queue_interruptible(&ras_queue); | ||
3590 | |||
3591 | err_out: | ||
3592 | list_for_each_entry_safe(cqr, n, &ras_queue, blocklist) { | ||
3593 | device = cqr->startdev; | ||
3594 | private = device->private; | ||
3595 | |||
3596 | spin_lock_irq(&block->queue_lock); | ||
3597 | list_del_init(&cqr->blocklist); | ||
3598 | spin_unlock_irq(&block->queue_lock); | ||
3599 | dasd_sfree_request(cqr, device); | ||
3600 | private->count--; | ||
3601 | } | ||
3602 | } while (retry); | ||
3603 | |||
3604 | out: | ||
3605 | return rc; | ||
3606 | } | ||
3607 | |||
3608 | static int dasd_eckd_release_space(struct dasd_device *device, | ||
3609 | struct format_data_t *rdata) | ||
3610 | { | ||
3611 | if (rdata->intensity & DASD_FMT_INT_ESE_FULL) | ||
3612 | return dasd_eckd_release_space_full(device); | ||
3613 | else if (rdata->intensity == 0) | ||
3614 | return dasd_eckd_release_space_trks(device, rdata->start_unit, | ||
3615 | rdata->stop_unit); | ||
3616 | else | ||
3617 | return -EINVAL; | ||
3618 | } | ||
3619 | |||
3357 | static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( | 3620 | static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( |
3358 | struct dasd_device *startdev, | 3621 | struct dasd_device *startdev, |
3359 | struct dasd_block *block, | 3622 | struct dasd_block *block, |
@@ -6162,6 +6425,7 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
6162 | .space_allocated = dasd_eckd_space_allocated, | 6425 | .space_allocated = dasd_eckd_space_allocated, |
6163 | .space_configured = dasd_eckd_space_configured, | 6426 | .space_configured = dasd_eckd_space_configured, |
6164 | .logical_capacity = dasd_eckd_logical_capacity, | 6427 | .logical_capacity = dasd_eckd_logical_capacity, |
6428 | .release_space = dasd_eckd_release_space, | ||
6165 | .ext_pool_id = dasd_eckd_ext_pool_id, | 6429 | .ext_pool_id = dasd_eckd_ext_pool_id, |
6166 | .ext_size = dasd_eckd_ext_size, | 6430 | .ext_size = dasd_eckd_ext_size, |
6167 | .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel, | 6431 | .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel, |
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index bc5998068ddf..4226936427ec 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h | |||
@@ -50,6 +50,10 @@ | |||
50 | #define DASD_ECKD_CCW_PFX_READ 0xEA | 50 | #define DASD_ECKD_CCW_PFX_READ 0xEA |
51 | #define DASD_ECKD_CCW_RSCK 0xF9 | 51 | #define DASD_ECKD_CCW_RSCK 0xF9 |
52 | #define DASD_ECKD_CCW_RCD 0xFA | 52 | #define DASD_ECKD_CCW_RCD 0xFA |
53 | #define DASD_ECKD_CCW_DSO 0xF7 | ||
54 | |||
55 | /* Define Subssystem Function / Orders */ | ||
56 | #define DSO_ORDER_RAS 0x81 | ||
53 | 57 | ||
54 | /* | 58 | /* |
55 | * Perform Subsystem Function / Orders | 59 | * Perform Subsystem Function / Orders |
@@ -513,6 +517,42 @@ struct dasd_psf_ssc_data { | |||
513 | unsigned char reserved[59]; | 517 | unsigned char reserved[59]; |
514 | } __attribute__((packed)); | 518 | } __attribute__((packed)); |
515 | 519 | ||
520 | /* Maximum number of extents for a single Release Allocated Space command */ | ||
521 | #define DASD_ECKD_RAS_EXTS_MAX 110U | ||
522 | |||
523 | struct dasd_dso_ras_ext_range { | ||
524 | struct ch_t beg_ext; | ||
525 | struct ch_t end_ext; | ||
526 | } __packed; | ||
527 | |||
528 | /* | ||
529 | * Define Subsytem Operation - Release Allocated Space | ||
530 | */ | ||
531 | struct dasd_dso_ras_data { | ||
532 | __u8 order; | ||
533 | struct { | ||
534 | __u8 message:1; /* Must be zero */ | ||
535 | __u8 reserved1:2; | ||
536 | __u8 vol_type:1; /* 0 - CKD/FBA, 1 - FB */ | ||
537 | __u8 reserved2:4; | ||
538 | } __packed flags; | ||
539 | /* Operation Flags to specify scope */ | ||
540 | struct { | ||
541 | __u8 reserved1:2; | ||
542 | /* Release Space by Extent */ | ||
543 | __u8 by_extent:1; /* 0 - entire volume, 1 - specified extents */ | ||
544 | __u8 guarantee_init:1; | ||
545 | __u8 force_release:1; /* Internal - will be ignored */ | ||
546 | __u16 reserved2:11; | ||
547 | } __packed op_flags; | ||
548 | __u8 lss; | ||
549 | __u8 dev_addr; | ||
550 | __u32 reserved1; | ||
551 | __u8 reserved2[10]; | ||
552 | __u16 nr_exts; /* Defines number of ext_scope - max 110 */ | ||
553 | __u16 reserved3; | ||
554 | } __packed; | ||
555 | |||
516 | 556 | ||
517 | /* | 557 | /* |
518 | * some structures and definitions for alias handling | 558 | * some structures and definitions for alias handling |
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 9a8ef372535b..7fe0c6b9d9ef 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h | |||
@@ -376,6 +376,7 @@ struct dasd_discipline { | |||
376 | int (*space_allocated)(struct dasd_device *); | 376 | int (*space_allocated)(struct dasd_device *); |
377 | int (*space_configured)(struct dasd_device *); | 377 | int (*space_configured)(struct dasd_device *); |
378 | int (*logical_capacity)(struct dasd_device *); | 378 | int (*logical_capacity)(struct dasd_device *); |
379 | int (*release_space)(struct dasd_device *, struct format_data_t *); | ||
379 | /* Extent Pool */ | 380 | /* Extent Pool */ |
380 | int (*ext_pool_id)(struct dasd_device *); | 381 | int (*ext_pool_id)(struct dasd_device *); |
381 | int (*ext_size)(struct dasd_device *); | 382 | int (*ext_size)(struct dasd_device *); |
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 8e26001dc11c..9a5f3add325f 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c | |||
@@ -333,6 +333,59 @@ out_err: | |||
333 | return rc; | 333 | return rc; |
334 | } | 334 | } |
335 | 335 | ||
336 | static int dasd_release_space(struct dasd_device *device, | ||
337 | struct format_data_t *rdata) | ||
338 | { | ||
339 | if (!device->discipline->is_ese && !device->discipline->is_ese(device)) | ||
340 | return -ENOTSUPP; | ||
341 | if (!device->discipline->release_space) | ||
342 | return -ENOTSUPP; | ||
343 | |||
344 | return device->discipline->release_space(device, rdata); | ||
345 | } | ||
346 | |||
347 | /* | ||
348 | * Release allocated space | ||
349 | */ | ||
350 | static int dasd_ioctl_release_space(struct block_device *bdev, void __user *argp) | ||
351 | { | ||
352 | struct format_data_t rdata; | ||
353 | struct dasd_device *base; | ||
354 | int rc = 0; | ||
355 | |||
356 | if (!capable(CAP_SYS_ADMIN)) | ||
357 | return -EACCES; | ||
358 | if (!argp) | ||
359 | return -EINVAL; | ||
360 | |||
361 | base = dasd_device_from_gendisk(bdev->bd_disk); | ||
362 | if (!base) | ||
363 | return -ENODEV; | ||
364 | if (base->features & DASD_FEATURE_READONLY || | ||
365 | test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) { | ||
366 | rc = -EROFS; | ||
367 | goto out_err; | ||
368 | } | ||
369 | if (bdev != bdev->bd_contains) { | ||
370 | pr_warn("%s: The specified DASD is a partition and tracks cannot be released\n", | ||
371 | dev_name(&base->cdev->dev)); | ||
372 | rc = -EINVAL; | ||
373 | goto out_err; | ||
374 | } | ||
375 | |||
376 | if (copy_from_user(&rdata, argp, sizeof(rdata))) { | ||
377 | rc = -EFAULT; | ||
378 | goto out_err; | ||
379 | } | ||
380 | |||
381 | rc = dasd_release_space(base, &rdata); | ||
382 | |||
383 | out_err: | ||
384 | dasd_put_device(base); | ||
385 | |||
386 | return rc; | ||
387 | } | ||
388 | |||
336 | #ifdef CONFIG_DASD_PROFILE | 389 | #ifdef CONFIG_DASD_PROFILE |
337 | /* | 390 | /* |
338 | * Reset device profile information | 391 | * Reset device profile information |
@@ -595,6 +648,9 @@ int dasd_ioctl(struct block_device *bdev, fmode_t mode, | |||
595 | case BIODASDREADALLCMB: | 648 | case BIODASDREADALLCMB: |
596 | rc = dasd_ioctl_readall_cmb(block, cmd, argp); | 649 | rc = dasd_ioctl_readall_cmb(block, cmd, argp); |
597 | break; | 650 | break; |
651 | case BIODASDRAS: | ||
652 | rc = dasd_ioctl_release_space(bdev, argp); | ||
653 | break; | ||
598 | default: | 654 | default: |
599 | /* if the discipline has an ioctl method try it. */ | 655 | /* if the discipline has an ioctl method try it. */ |
600 | rc = -ENOTTY; | 656 | rc = -ENOTTY; |