summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Höppner <hoeppner@linux.ibm.com>2018-07-23 05:13:30 -0400
committerVasily Gorbik <gor@linux.ibm.com>2019-07-11 14:39:54 -0400
commit91dc4a197569230683ca8bad551e655a4bf14c30 (patch)
tree4cd4647b811278f165405a7eef4df952810be45c
parentbcf36768ea688e926b04639c88c77d8caea3b736 (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.h3
-rw-r--r--drivers/s390/block/dasd_eckd.c264
-rw-r--r--drivers/s390/block/dasd_eckd.h40
-rw-r--r--drivers/s390/block/dasd_int.h1
-rw-r--r--drivers/s390/block/dasd_ioctl.c56
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
3357static 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 */
3390static 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 */
3423static struct dasd_ccw_req *
3424dasd_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
3524static 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
3540static 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
3591err_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
3604out:
3605 return rc;
3606}
3607
3608static 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
3357static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( 3620static 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
523struct 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 */
531struct 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
336static 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 */
350static 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
383out_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;