162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 462306a36Sopenharmony_ci * Horst Hummel <Horst.Hummel@de.ibm.com> 562306a36Sopenharmony_ci * Carsten Otte <Cotte@de.ibm.com> 662306a36Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 762306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com> 862306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2001 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * gendisk related functions for the dasd driver. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/major.h> 1862306a36Sopenharmony_ci#include <linux/fs.h> 1962306a36Sopenharmony_ci#include <linux/blkpg.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/uaccess.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* This is ugly... */ 2462306a36Sopenharmony_ci#define PRINTK_HEADER "dasd_gendisk:" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "dasd_int.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic unsigned int queue_depth = 32; 2962306a36Sopenharmony_cistatic unsigned int nr_hw_queues = 4; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cimodule_param(queue_depth, uint, 0444); 3262306a36Sopenharmony_ciMODULE_PARM_DESC(queue_depth, "Default queue depth for new DASD devices"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cimodule_param(nr_hw_queues, uint, 0444); 3562306a36Sopenharmony_ciMODULE_PARM_DESC(nr_hw_queues, "Default number of hardware queues for new DASD devices"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Allocate and register gendisk structure for device. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ciint dasd_gendisk_alloc(struct dasd_block *block) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct gendisk *gdp; 4362306a36Sopenharmony_ci struct dasd_device *base; 4462306a36Sopenharmony_ci int len, rc; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Make sure the minor for this device exists. */ 4762306a36Sopenharmony_ci base = block->base; 4862306a36Sopenharmony_ci if (base->devindex >= DASD_PER_MAJOR) 4962306a36Sopenharmony_ci return -EBUSY; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci block->tag_set.ops = &dasd_mq_ops; 5262306a36Sopenharmony_ci block->tag_set.cmd_size = sizeof(struct dasd_ccw_req); 5362306a36Sopenharmony_ci block->tag_set.nr_hw_queues = nr_hw_queues; 5462306a36Sopenharmony_ci block->tag_set.queue_depth = queue_depth; 5562306a36Sopenharmony_ci block->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; 5662306a36Sopenharmony_ci block->tag_set.numa_node = NUMA_NO_NODE; 5762306a36Sopenharmony_ci rc = blk_mq_alloc_tag_set(&block->tag_set); 5862306a36Sopenharmony_ci if (rc) 5962306a36Sopenharmony_ci return rc; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci gdp = blk_mq_alloc_disk(&block->tag_set, block); 6262306a36Sopenharmony_ci if (IS_ERR(gdp)) { 6362306a36Sopenharmony_ci blk_mq_free_tag_set(&block->tag_set); 6462306a36Sopenharmony_ci return PTR_ERR(gdp); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Initialize gendisk structure. */ 6862306a36Sopenharmony_ci gdp->major = DASD_MAJOR; 6962306a36Sopenharmony_ci gdp->first_minor = base->devindex << DASD_PARTN_BITS; 7062306a36Sopenharmony_ci gdp->minors = 1 << DASD_PARTN_BITS; 7162306a36Sopenharmony_ci gdp->fops = &dasd_device_operations; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * Set device name. 7562306a36Sopenharmony_ci * dasda - dasdz : 26 devices 7662306a36Sopenharmony_ci * dasdaa - dasdzz : 676 devices, added up = 702 7762306a36Sopenharmony_ci * dasdaaa - dasdzzz : 17576 devices, added up = 18278 7862306a36Sopenharmony_ci * dasdaaaa - dasdzzzz : 456976 devices, added up = 475252 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci len = sprintf(gdp->disk_name, "dasd"); 8162306a36Sopenharmony_ci if (base->devindex > 25) { 8262306a36Sopenharmony_ci if (base->devindex > 701) { 8362306a36Sopenharmony_ci if (base->devindex > 18277) 8462306a36Sopenharmony_ci len += sprintf(gdp->disk_name + len, "%c", 8562306a36Sopenharmony_ci 'a'+(((base->devindex-18278) 8662306a36Sopenharmony_ci /17576)%26)); 8762306a36Sopenharmony_ci len += sprintf(gdp->disk_name + len, "%c", 8862306a36Sopenharmony_ci 'a'+(((base->devindex-702)/676)%26)); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci len += sprintf(gdp->disk_name + len, "%c", 9162306a36Sopenharmony_ci 'a'+(((base->devindex-26)/26)%26)); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26)); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (base->features & DASD_FEATURE_READONLY || 9662306a36Sopenharmony_ci test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) 9762306a36Sopenharmony_ci set_disk_ro(gdp, 1); 9862306a36Sopenharmony_ci dasd_add_link_to_gendisk(gdp, base); 9962306a36Sopenharmony_ci block->gdp = gdp; 10062306a36Sopenharmony_ci set_capacity(block->gdp, 0); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci rc = device_add_disk(&base->cdev->dev, block->gdp, NULL); 10362306a36Sopenharmony_ci if (rc) { 10462306a36Sopenharmony_ci dasd_gendisk_free(block); 10562306a36Sopenharmony_ci return rc; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* 11262306a36Sopenharmony_ci * Unregister and free gendisk structure for device. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_civoid dasd_gendisk_free(struct dasd_block *block) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci if (block->gdp) { 11762306a36Sopenharmony_ci del_gendisk(block->gdp); 11862306a36Sopenharmony_ci block->gdp->private_data = NULL; 11962306a36Sopenharmony_ci put_disk(block->gdp); 12062306a36Sopenharmony_ci block->gdp = NULL; 12162306a36Sopenharmony_ci blk_mq_free_tag_set(&block->tag_set); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * Trigger a partition detection. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ciint dasd_scan_partitions(struct dasd_block *block) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct block_device *bdev; 13162306a36Sopenharmony_ci int rc; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci bdev = blkdev_get_by_dev(disk_devt(block->gdp), BLK_OPEN_READ, NULL, 13462306a36Sopenharmony_ci NULL); 13562306a36Sopenharmony_ci if (IS_ERR(bdev)) { 13662306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, block->base, 13762306a36Sopenharmony_ci "scan partitions error, blkdev_get returned %ld", 13862306a36Sopenharmony_ci PTR_ERR(bdev)); 13962306a36Sopenharmony_ci return -ENODEV; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mutex_lock(&block->gdp->open_mutex); 14362306a36Sopenharmony_ci rc = bdev_disk_changed(block->gdp, false); 14462306a36Sopenharmony_ci mutex_unlock(&block->gdp->open_mutex); 14562306a36Sopenharmony_ci if (rc) 14662306a36Sopenharmony_ci DBF_DEV_EVENT(DBF_ERR, block->base, 14762306a36Sopenharmony_ci "scan partitions error, rc %d", rc); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * Since the matching blkdev_put call to the blkdev_get in 15162306a36Sopenharmony_ci * this function is not called before dasd_destroy_partitions 15262306a36Sopenharmony_ci * the offline open_count limit needs to be increased from 15362306a36Sopenharmony_ci * 0 to 1. This is done by setting device->bdev (see 15462306a36Sopenharmony_ci * dasd_generic_set_offline). As long as the partition 15562306a36Sopenharmony_ci * detection is running no offline should be allowed. That 15662306a36Sopenharmony_ci * is why the assignment to device->bdev is done AFTER 15762306a36Sopenharmony_ci * the BLKRRPART ioctl. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci block->bdev = bdev; 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Remove all inodes in the system for a device, delete the 16562306a36Sopenharmony_ci * partitions and make device unusable by setting its size to zero. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_civoid dasd_destroy_partitions(struct dasd_block *block) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct block_device *bdev; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Get the bdev pointer from the device structure and clear 17362306a36Sopenharmony_ci * device->bdev to lower the offline open_count limit again. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci bdev = block->bdev; 17662306a36Sopenharmony_ci block->bdev = NULL; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci mutex_lock(&bdev->bd_disk->open_mutex); 17962306a36Sopenharmony_ci bdev_disk_changed(bdev->bd_disk, true); 18062306a36Sopenharmony_ci mutex_unlock(&bdev->bd_disk->open_mutex); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */ 18362306a36Sopenharmony_ci blkdev_put(bdev, NULL); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciint dasd_gendisk_init(void) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int rc; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Register to static dasd major 94 */ 19162306a36Sopenharmony_ci rc = register_blkdev(DASD_MAJOR, "dasd"); 19262306a36Sopenharmony_ci if (rc != 0) { 19362306a36Sopenharmony_ci pr_warn("Registering the device driver with major number %d failed\n", 19462306a36Sopenharmony_ci DASD_MAJOR); 19562306a36Sopenharmony_ci return rc; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid dasd_gendisk_exit(void) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci unregister_blkdev(DASD_MAJOR, "dasd"); 20362306a36Sopenharmony_ci} 204