Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jan Höppner and committed by
Vasily Gorbik
91dc4a19 bcf36768

+364
+3
arch/s390/include/uapi/asm/dasd.h
··· 194 194 #define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ 195 195 #define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ 196 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 200 * struct format_check_t ··· 324 323 #define BIODASDFMT _IOW(DASD_IOCTL_LETTER,1,format_data_t) 325 324 /* Set Attributes (cache operations) */ 326 325 #define BIODASDSATTR _IOW(DASD_IOCTL_LETTER,2,attrib_data_t) 326 + /* Release Allocated Space */ 327 + #define BIODASDRAS _IOW(DASD_IOCTL_LETTER, 3, format_data_t) 327 328 328 329 /* Get Sense Path Group ID (SNID) data */ 329 330 #define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data)
+264
drivers/s390/block/dasd_eckd.c
··· 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 3620 static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( 3358 3621 struct dasd_device *startdev, 3359 3622 struct dasd_block *block, ··· 6425 6162 .space_allocated = dasd_eckd_space_allocated, 6426 6163 .space_configured = dasd_eckd_space_configured, 6427 6164 .logical_capacity = dasd_eckd_logical_capacity, 6165 + .release_space = dasd_eckd_release_space, 6428 6166 .ext_pool_id = dasd_eckd_ext_pool_id, 6429 6167 .ext_size = dasd_eckd_ext_size, 6430 6168 .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel,
+40
drivers/s390/block/dasd_eckd.h
··· 50 50 #define DASD_ECKD_CCW_PFX_READ 0xEA 51 51 #define DASD_ECKD_CCW_RSCK 0xF9 52 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 59 * Perform Subsystem Function / Orders ··· 516 512 unsigned char suborder; 517 513 unsigned char reserved[59]; 518 514 } __attribute__((packed)); 515 + 516 + /* Maximum number of extents for a single Release Allocated Space command */ 517 + #define DASD_ECKD_RAS_EXTS_MAX 110U 518 + 519 + struct dasd_dso_ras_ext_range { 520 + struct ch_t beg_ext; 521 + struct ch_t end_ext; 522 + } __packed; 523 + 524 + /* 525 + * Define Subsytem Operation - Release Allocated Space 526 + */ 527 + struct dasd_dso_ras_data { 528 + __u8 order; 529 + struct { 530 + __u8 message:1; /* Must be zero */ 531 + __u8 reserved1:2; 532 + __u8 vol_type:1; /* 0 - CKD/FBA, 1 - FB */ 533 + __u8 reserved2:4; 534 + } __packed flags; 535 + /* Operation Flags to specify scope */ 536 + struct { 537 + __u8 reserved1:2; 538 + /* Release Space by Extent */ 539 + __u8 by_extent:1; /* 0 - entire volume, 1 - specified extents */ 540 + __u8 guarantee_init:1; 541 + __u8 force_release:1; /* Internal - will be ignored */ 542 + __u16 reserved2:11; 543 + } __packed op_flags; 544 + __u8 lss; 545 + __u8 dev_addr; 546 + __u32 reserved1; 547 + __u8 reserved2[10]; 548 + __u16 nr_exts; /* Defines number of ext_scope - max 110 */ 549 + __u16 reserved3; 550 + } __packed; 519 551 520 552 521 553 /*
+1
drivers/s390/block/dasd_int.h
··· 376 376 int (*space_allocated)(struct dasd_device *); 377 377 int (*space_configured)(struct dasd_device *); 378 378 int (*logical_capacity)(struct dasd_device *); 379 + int (*release_space)(struct dasd_device *, struct format_data_t *); 379 380 /* Extent Pool */ 380 381 int (*ext_pool_id)(struct dasd_device *); 381 382 int (*ext_size)(struct dasd_device *);
+56
drivers/s390/block/dasd_ioctl.c
··· 333 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 389 #ifdef CONFIG_DASD_PROFILE 337 390 /* 338 391 * Reset device profile information ··· 647 594 break; 648 595 case BIODASDREADALLCMB: 649 596 rc = dasd_ioctl_readall_cmb(block, cmd, argp); 597 + break; 598 + case BIODASDRAS: 599 + rc = dasd_ioctl_release_space(bdev, argp); 650 600 break; 651 601 default: 652 602 /* if the discipline has an ioctl method try it. */