lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu,  3 Jul 2008 17:33:03 +0900
From:	Tejun Heo <tj@...nel.org>
To:	jeff@...zik.org, linux-ide@...r.kernel.org,
	James.Bottomley@...senPartnership.com, bharrosh@...asas.com,
	greg.freemyer@...il.com, linux-scsi@...r.kernel.org,
	brking@...ux.vnet.ibm.com, liml@....ca, jens.axboe@...cle.com,
	viro@....linux.org.uk, linux-kernel@...r.kernel.org,
	device@...ana.org
Cc:	Tejun Heo <tj@...nel.org>
Subject: [PATCH 3/6] block: implement extended minors

Implement extended minors.  A block driver can tell block layer that
it wants to use extended minors.  After the usual minor space is used
up, block layer automatically allocates devt from EXT_BLOCK_MAJOR.

Currently only one major number is allocated for this but as the
allocation is on-demand so ~1mil minor space under it should suffice
for most cases.

For internal implementation simplicity, the first partition can't be
allocated on the extended area.  In other words, genhd->minors should
at least be 1.  Lifting this restriction shouldn't be too difficult.

Signed-off-by: Tejun Heo <tj@...nel.org>
---
 block/genhd.c         |  118 +++++++++++++++++++++++++++++++++++++++++++++++--
 fs/partitions/check.c |   11 +++++
 include/linux/genhd.h |    8 +++-
 include/linux/major.h |    2 +
 4 files changed, 134 insertions(+), 5 deletions(-)

diff --git a/block/genhd.c b/block/genhd.c
index e7310ba..97cc5e4 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -16,6 +16,7 @@
 #include <linux/kobj_map.h>
 #include <linux/buffer_head.h>
 #include <linux/mutex.h>
+#include <linux/idr.h>
 
 #include "blk.h"
 
@@ -24,6 +25,10 @@ static DEFINE_MUTEX(block_class_lock);
 struct kobject *block_depr;
 #endif
 
+/* for extended dynamic devt allocation, currently only one major is used */
+#define MAX_EXT_DEVT		(1 << MINORBITS)
+static DEFINE_IDR(ext_devt_idr);
+
 static struct device_type disk_type;
 
 /*
@@ -136,6 +141,65 @@ EXPORT_SYMBOL(unregister_blkdev);
 
 static struct kobj_map *bdev_map;
 
+/**
+ * blk_alloc_devt - allocate a dev_t for a partition
+ * @part: partition to allocate dev_t for
+ * @gfp_mask: memory allocation flag
+ * @devt: out parameter for resulting dev_t
+ *
+ * Allocate a dev_t for block device.
+ *
+ * RETURNS:
+ * 0 on success, allocated dev_t is returned in *@...t.  -errno on
+ * failure.
+ *
+ * CONTEXT:
+ * Determined by @gfp_mask.
+ */
+int blk_alloc_devt(struct hd_struct *part, gfp_t gfp_mask, dev_t *devt)
+{
+	int idx, rc;
+
+	if (part->partno < part->disk->minors) {
+		*devt = MKDEV(part->disk->major, part->partno);
+		return 0;
+	}
+
+	while (true) {
+		if (!idr_pre_get(&ext_devt_idr, gfp_mask))
+			return -ENOMEM;
+
+		rc = idr_get_new(&ext_devt_idr, part, &idx);
+		if (rc == 0)
+			break;
+		if (rc && rc != -EAGAIN)
+			return rc;
+	}
+
+	if (idx > MAX_EXT_DEVT) {
+		idr_remove(&ext_devt_idr, idx);
+		return -EBUSY;
+	}
+
+	*devt = MKDEV(EXT_BLOCK_MAJOR, idx);
+	return 0;
+}
+
+/**
+ * blk_free_devt - free a dev_t
+ * @devt: dev_t to free
+ *
+ * Free @devt which was allocated using blk_alloc_devt().
+ *
+ * CONTEXT:
+ * Don't care.
+ */
+void blk_free_devt(dev_t devt)
+{
+	if (MAJOR(devt) == EXT_BLOCK_MAJOR)
+		idr_remove(&ext_devt_idr, MINOR(devt));
+}
+
 /*
  * Register device numbers dev..(dev+range-1)
  * range must be nonzero
@@ -368,12 +432,43 @@ static struct kobject *base_probe(dev_t devt, int *part, void *data)
 	return NULL;
 }
 
+static struct kobject *ext_probe(dev_t devt, int *idx, void *data)
+{
+	struct hd_struct *part;
+
+	part = idr_find(&ext_devt_idr, MINOR(devt));
+	if (unlikely(!part))
+		return NULL;
+
+	*idx = part->partno;
+	return &part->disk->dev.kobj;
+}
+
+static int ext_lock(dev_t devt, void *data)
+{
+	struct hd_struct *part;
+
+	part = idr_find(&ext_devt_idr, MINOR(devt));
+	if (likely(part && get_disk(part->disk)))
+		return 0;
+	return -1;
+}
+
 static int __init genhd_device_init(void)
 {
-	int error = class_register(&block_class);
+	int error;
+
+	error = class_register(&block_class);
 	if (unlikely(error))
 		return error;
+
 	bdev_map = kobj_map_init(base_probe, &block_class_lock);
+	if (!bdev_map)
+		return -ENOMEM;
+
+	blk_register_region(MKDEV(EXT_BLOCK_MAJOR, 0), 1 << MINORBITS, NULL,
+			    ext_probe, ext_lock, NULL);
+
 	blk_dev_init();
 
 #ifndef CONFIG_SYSFS_DEPRECATED
@@ -691,22 +786,34 @@ EXPORT_SYMBOL(blk_lookup_devt);
 
 struct gendisk *alloc_disk(int minors)
 {
-	return alloc_disk_node(minors, -1);
+	return alloc_disk_ext(minors, 0);
 }
 
 struct gendisk *alloc_disk_node(int minors, int node_id)
 {
+	return alloc_disk_ext_node(minors, 0, node_id);
+}
+
+struct gendisk *alloc_disk_ext(int minors, int ext_minors)
+{
+	return alloc_disk_ext_node(minors, ext_minors, -1);
+}
+
+struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id)
+{
 	struct gendisk *disk;
 
 	disk = kmalloc_node(sizeof(struct gendisk),
 				GFP_KERNEL | __GFP_ZERO, node_id);
 	if (disk) {
+		int tot_minors = minors + ext_minors;
+
 		if (!init_disk_stats(disk)) {
 			kfree(disk);
 			return NULL;
 		}
-		if (minors > 1) {
-			int size = (minors - 1) * sizeof(struct hd_struct *);
+		if (tot_minors > 1) {
+			int size = (tot_minors - 1) * sizeof(struct hd_struct *);
 			disk->part = kmalloc_node(size,
 				GFP_KERNEL | __GFP_ZERO, node_id);
 			if (!disk->part) {
@@ -716,6 +823,7 @@ struct gendisk *alloc_disk_node(int minors, int node_id)
 			}
 		}
 		disk->minors = minors;
+		disk->ext_minors = ext_minors;
 		rand_initialize_disk(disk);
 		disk->dev.class = &block_class;
 		disk->dev.type = &disk_type;
@@ -728,6 +836,8 @@ struct gendisk *alloc_disk_node(int minors, int node_id)
 
 EXPORT_SYMBOL(alloc_disk);
 EXPORT_SYMBOL(alloc_disk_node);
+EXPORT_SYMBOL(alloc_disk_ext);
+EXPORT_SYMBOL(alloc_disk_ext_node);
 
 struct kobject *get_disk(struct gendisk *disk)
 {
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 994a621..15d231f 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -331,6 +331,7 @@ void delete_partition(struct gendisk *disk, int part)
 		return;
 	if (!p->nr_sects)
 		return;
+	blk_free_devt(p->dev.devt);
 	disk->part[part-1] = NULL;
 	p->start_sect = 0;
 	p->nr_sects = 0;
@@ -386,6 +387,16 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len,
 	p->dev.class = &block_class;
 	p->dev.type = &part_type;
 	p->dev.parent = &disk->dev;
+
+	err = blk_alloc_devt(p, GFP_KERNEL, &p->dev.devt);
+	if (err) {
+		printk(KERN_WARNING "%s: failed to to allocate MAJOR:MINOR "
+		       "(part=%d, err=%d)\n", name, part, err);
+		free_part_stats(p);
+		kfree(p);
+		return;
+	}
+
 	disk->part[part-1] = p;
 
 	/* delay uevent until 'holders' subdir is created */
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 1db5740..a1843e6 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -117,6 +117,7 @@ struct gendisk {
 	int first_minor;
 	int minors;                     /* maximum number of minors, =1 for
                                          * disks that can't be partitioned. */
+	int ext_minors;			/* number of extended dynamic minors */
 	char disk_name[32];		/* name of major driver */
 	struct hd_struct **part;	/* [indexed by minor - 1] */
 	struct block_device_operations *fops;
@@ -146,7 +147,7 @@ struct gendisk {
 
 static inline int disk_max_parts(struct gendisk *disk)
 {
-	return disk->minors - 1;
+	return disk->minors + disk->ext_minors - 1;
 }
 
 static inline int disk_major(struct gendisk *disk)
@@ -551,6 +552,8 @@ struct unixware_disklabel {
 #define ADDPART_FLAG_RAID	1
 #define ADDPART_FLAG_WHOLEDISK	2
 
+extern int blk_alloc_devt(struct hd_struct *part, gfp_t gfp_mask, dev_t *devt);
+extern void blk_free_devt(dev_t devt);
 extern dev_t blk_lookup_devt(const char *name, int part);
 extern char *disk_name (struct gendisk *hd, int part, char *buf);
 
@@ -561,6 +564,9 @@ extern void printk_all_partitions(void);
 
 extern struct gendisk *alloc_disk_node(int minors, int node_id);
 extern struct gendisk *alloc_disk(int minors);
+extern struct gendisk *alloc_disk_ext_node(int minors, int ext_minrs,
+					   int node_id);
+extern struct gendisk *alloc_disk_ext(int minors, int ext_minors);
 extern struct kobject *get_disk(struct gendisk *disk);
 extern void put_disk(struct gendisk *disk);
 extern void blk_register_region(dev_t devt, unsigned long range,
diff --git a/include/linux/major.h b/include/linux/major.h
index 0cb9805..e7fa573 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -170,4 +170,6 @@
 
 #define VIOTAPE_MAJOR		230
 
+#define EXT_BLOCK_MAJOR		259
+
 #endif
-- 
1.5.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ