18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
48c2ecf20Sopenharmony_ci *		    Horst Hummel <Horst.Hummel@de.ibm.com>
58c2ecf20Sopenharmony_ci *		    Carsten Otte <Cotte@de.ibm.com>
68c2ecf20Sopenharmony_ci *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
78c2ecf20Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com>
88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2001
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * gendisk related functions for the dasd driver.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "dasd"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/fs.h>
188c2ecf20Sopenharmony_ci#include <linux/blkpg.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* This is ugly... */
238c2ecf20Sopenharmony_ci#define PRINTK_HEADER "dasd_gendisk:"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "dasd_int.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * Allocate and register gendisk structure for device.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ciint dasd_gendisk_alloc(struct dasd_block *block)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct gendisk *gdp;
338c2ecf20Sopenharmony_ci	struct dasd_device *base;
348c2ecf20Sopenharmony_ci	int len;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	/* Make sure the minor for this device exists. */
378c2ecf20Sopenharmony_ci	base = block->base;
388c2ecf20Sopenharmony_ci	if (base->devindex >= DASD_PER_MAJOR)
398c2ecf20Sopenharmony_ci		return -EBUSY;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	gdp = alloc_disk(1 << DASD_PARTN_BITS);
428c2ecf20Sopenharmony_ci	if (!gdp)
438c2ecf20Sopenharmony_ci		return -ENOMEM;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* Initialize gendisk structure. */
468c2ecf20Sopenharmony_ci	gdp->major = DASD_MAJOR;
478c2ecf20Sopenharmony_ci	gdp->first_minor = base->devindex << DASD_PARTN_BITS;
488c2ecf20Sopenharmony_ci	gdp->fops = &dasd_device_operations;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/*
518c2ecf20Sopenharmony_ci	 * Set device name.
528c2ecf20Sopenharmony_ci	 *   dasda - dasdz : 26 devices
538c2ecf20Sopenharmony_ci	 *   dasdaa - dasdzz : 676 devices, added up = 702
548c2ecf20Sopenharmony_ci	 *   dasdaaa - dasdzzz : 17576 devices, added up = 18278
558c2ecf20Sopenharmony_ci	 *   dasdaaaa - dasdzzzz : 456976 devices, added up = 475252
568c2ecf20Sopenharmony_ci	 */
578c2ecf20Sopenharmony_ci	len = sprintf(gdp->disk_name, "dasd");
588c2ecf20Sopenharmony_ci	if (base->devindex > 25) {
598c2ecf20Sopenharmony_ci		if (base->devindex > 701) {
608c2ecf20Sopenharmony_ci			if (base->devindex > 18277)
618c2ecf20Sopenharmony_ci			        len += sprintf(gdp->disk_name + len, "%c",
628c2ecf20Sopenharmony_ci					       'a'+(((base->devindex-18278)
638c2ecf20Sopenharmony_ci						     /17576)%26));
648c2ecf20Sopenharmony_ci			len += sprintf(gdp->disk_name + len, "%c",
658c2ecf20Sopenharmony_ci				       'a'+(((base->devindex-702)/676)%26));
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci		len += sprintf(gdp->disk_name + len, "%c",
688c2ecf20Sopenharmony_ci			       'a'+(((base->devindex-26)/26)%26));
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26));
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (base->features & DASD_FEATURE_READONLY ||
738c2ecf20Sopenharmony_ci	    test_bit(DASD_FLAG_DEVICE_RO, &base->flags))
748c2ecf20Sopenharmony_ci		set_disk_ro(gdp, 1);
758c2ecf20Sopenharmony_ci	dasd_add_link_to_gendisk(gdp, base);
768c2ecf20Sopenharmony_ci	gdp->queue = block->request_queue;
778c2ecf20Sopenharmony_ci	block->gdp = gdp;
788c2ecf20Sopenharmony_ci	set_capacity(block->gdp, 0);
798c2ecf20Sopenharmony_ci	device_add_disk(&base->cdev->dev, block->gdp, NULL);
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * Unregister and free gendisk structure for device.
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_civoid dasd_gendisk_free(struct dasd_block *block)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	if (block->gdp) {
898c2ecf20Sopenharmony_ci		del_gendisk(block->gdp);
908c2ecf20Sopenharmony_ci		block->gdp->private_data = NULL;
918c2ecf20Sopenharmony_ci		put_disk(block->gdp);
928c2ecf20Sopenharmony_ci		block->gdp = NULL;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/*
978c2ecf20Sopenharmony_ci * Trigger a partition detection.
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_ciint dasd_scan_partitions(struct dasd_block *block)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct block_device *bdev;
1028c2ecf20Sopenharmony_ci	int rc;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	bdev = blkdev_get_by_dev(disk_devt(block->gdp), FMODE_READ, NULL);
1058c2ecf20Sopenharmony_ci	if (IS_ERR(bdev)) {
1068c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, block->base,
1078c2ecf20Sopenharmony_ci			      "scan partitions error, blkdev_get returned %ld",
1088c2ecf20Sopenharmony_ci			      PTR_ERR(bdev));
1098c2ecf20Sopenharmony_ci		return -ENODEV;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	mutex_lock(&bdev->bd_mutex);
1138c2ecf20Sopenharmony_ci	rc = bdev_disk_changed(bdev, false);
1148c2ecf20Sopenharmony_ci	mutex_unlock(&bdev->bd_mutex);
1158c2ecf20Sopenharmony_ci	if (rc)
1168c2ecf20Sopenharmony_ci		DBF_DEV_EVENT(DBF_ERR, block->base,
1178c2ecf20Sopenharmony_ci				"scan partitions error, rc %d", rc);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/*
1208c2ecf20Sopenharmony_ci	 * Since the matching blkdev_put call to the blkdev_get in
1218c2ecf20Sopenharmony_ci	 * this function is not called before dasd_destroy_partitions
1228c2ecf20Sopenharmony_ci	 * the offline open_count limit needs to be increased from
1238c2ecf20Sopenharmony_ci	 * 0 to 1. This is done by setting device->bdev (see
1248c2ecf20Sopenharmony_ci	 * dasd_generic_set_offline). As long as the partition
1258c2ecf20Sopenharmony_ci	 * detection is running no offline should be allowed. That
1268c2ecf20Sopenharmony_ci	 * is why the assignment to device->bdev is done AFTER
1278c2ecf20Sopenharmony_ci	 * the BLKRRPART ioctl.
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	block->bdev = bdev;
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/*
1348c2ecf20Sopenharmony_ci * Remove all inodes in the system for a device, delete the
1358c2ecf20Sopenharmony_ci * partitions and make device unusable by setting its size to zero.
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_civoid dasd_destroy_partitions(struct dasd_block *block)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct block_device *bdev;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/*
1428c2ecf20Sopenharmony_ci	 * Get the bdev pointer from the device structure and clear
1438c2ecf20Sopenharmony_ci	 * device->bdev to lower the offline open_count limit again.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	bdev = block->bdev;
1468c2ecf20Sopenharmony_ci	block->bdev = NULL;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	mutex_lock(&bdev->bd_mutex);
1498c2ecf20Sopenharmony_ci	blk_drop_partitions(bdev);
1508c2ecf20Sopenharmony_ci	mutex_unlock(&bdev->bd_mutex);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
1538c2ecf20Sopenharmony_ci	blkdev_put(bdev, FMODE_READ);
1548c2ecf20Sopenharmony_ci	set_capacity(block->gdp, 0);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciint dasd_gendisk_init(void)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	int rc;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Register to static dasd major 94 */
1628c2ecf20Sopenharmony_ci	rc = register_blkdev(DASD_MAJOR, "dasd");
1638c2ecf20Sopenharmony_ci	if (rc != 0) {
1648c2ecf20Sopenharmony_ci		pr_warn("Registering the device driver with major number %d failed\n",
1658c2ecf20Sopenharmony_ci			DASD_MAJOR);
1668c2ecf20Sopenharmony_ci		return rc;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_civoid dasd_gendisk_exit(void)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	unregister_blkdev(DASD_MAJOR, "dasd");
1748c2ecf20Sopenharmony_ci}
175