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