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

block: implement extended dev numbers

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

Currently only one major number is allocated for this but as the
allocation is strictly on-demand, ~1mil minor space under it should
suffice unless the system actually has more than ~1mil partitions and
if that ever happens adding more majors to the extended devt area is
easy.

Due to internal implementation issues, the first partition can't be
allocated on the extended area. In other words, genhd->minors should
at least be 1. This limitation will be lifted by later changes.

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>

authored by

Tejun Heo and committed by
Jens Axboe
bcce3de1 c9959059

+135 -9
+115 -5
block/genhd.c
··· 16 16 #include <linux/kobj_map.h> 17 17 #include <linux/buffer_head.h> 18 18 #include <linux/mutex.h> 19 + #include <linux/idr.h> 19 20 20 21 #include "blk.h" 21 22 ··· 24 23 #ifndef CONFIG_SYSFS_DEPRECATED 25 24 struct kobject *block_depr; 26 25 #endif 26 + 27 + /* for extended dynamic devt allocation, currently only one major is used */ 28 + #define MAX_EXT_DEVT (1 << MINORBITS) 29 + 30 + /* For extended devt allocation. ext_devt_mutex prevents look up 31 + * results from going away underneath its user. 32 + */ 33 + static DEFINE_MUTEX(ext_devt_mutex); 34 + static DEFINE_IDR(ext_devt_idr); 27 35 28 36 static struct device_type disk_type; 29 37 ··· 298 288 299 289 static struct kobj_map *bdev_map; 300 290 291 + /** 292 + * blk_alloc_devt - allocate a dev_t for a partition 293 + * @part: partition to allocate dev_t for 294 + * @gfp_mask: memory allocation flag 295 + * @devt: out parameter for resulting dev_t 296 + * 297 + * Allocate a dev_t for block device. 298 + * 299 + * RETURNS: 300 + * 0 on success, allocated dev_t is returned in *@devt. -errno on 301 + * failure. 302 + * 303 + * CONTEXT: 304 + * Might sleep. 305 + */ 306 + int blk_alloc_devt(struct hd_struct *part, dev_t *devt) 307 + { 308 + struct gendisk *disk = part_to_disk(part); 309 + int idx, rc; 310 + 311 + /* in consecutive minor range? */ 312 + if (part->partno < disk->minors) { 313 + *devt = MKDEV(disk->major, disk->first_minor + part->partno); 314 + return 0; 315 + } 316 + 317 + /* allocate ext devt */ 318 + do { 319 + if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL)) 320 + return -ENOMEM; 321 + rc = idr_get_new(&ext_devt_idr, part, &idx); 322 + } while (rc == -EAGAIN); 323 + 324 + if (rc) 325 + return rc; 326 + 327 + if (idx > MAX_EXT_DEVT) { 328 + idr_remove(&ext_devt_idr, idx); 329 + return -EBUSY; 330 + } 331 + 332 + *devt = MKDEV(BLOCK_EXT_MAJOR, idx); 333 + return 0; 334 + } 335 + 336 + /** 337 + * blk_free_devt - free a dev_t 338 + * @devt: dev_t to free 339 + * 340 + * Free @devt which was allocated using blk_alloc_devt(). 341 + * 342 + * CONTEXT: 343 + * Might sleep. 344 + */ 345 + void blk_free_devt(dev_t devt) 346 + { 347 + might_sleep(); 348 + 349 + if (devt == MKDEV(0, 0)) 350 + return; 351 + 352 + if (MAJOR(devt) == BLOCK_EXT_MAJOR) { 353 + mutex_lock(&ext_devt_mutex); 354 + idr_remove(&ext_devt_idr, MINOR(devt)); 355 + mutex_unlock(&ext_devt_mutex); 356 + } 357 + } 358 + 301 359 /* 302 360 * Register device numbers dev..(dev+range-1) 303 361 * range must be nonzero ··· 449 371 */ 450 372 struct gendisk *get_gendisk(dev_t devt, int *partno) 451 373 { 452 - struct kobject *kobj = kobj_lookup(bdev_map, devt, partno); 453 - struct device *dev = kobj_to_dev(kobj); 374 + struct gendisk *disk = NULL; 454 375 455 - return kobj ? dev_to_disk(dev) : NULL; 376 + if (MAJOR(devt) != BLOCK_EXT_MAJOR) { 377 + struct kobject *kobj; 378 + 379 + kobj = kobj_lookup(bdev_map, devt, partno); 380 + if (kobj) 381 + disk = dev_to_disk(kobj_to_dev(kobj)); 382 + } else { 383 + struct hd_struct *part; 384 + 385 + mutex_lock(&ext_devt_mutex); 386 + part = idr_find(&ext_devt_idr, MINOR(devt)); 387 + if (part && get_disk(part_to_disk(part))) { 388 + *partno = part->partno; 389 + disk = part_to_disk(part); 390 + } 391 + mutex_unlock(&ext_devt_mutex); 392 + } 393 + 394 + return disk; 456 395 } 457 396 458 397 /** ··· 973 878 974 879 struct gendisk *alloc_disk_node(int minors, int node_id) 975 880 { 881 + return alloc_disk_ext_node(minors, 0, node_id); 882 + } 883 + 884 + struct gendisk *alloc_disk_ext(int minors, int ext_minors) 885 + { 886 + return alloc_disk_ext_node(minors, ext_minors, -1); 887 + } 888 + 889 + struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) 890 + { 976 891 struct gendisk *disk; 977 892 978 893 disk = kmalloc_node(sizeof(struct gendisk), 979 894 GFP_KERNEL | __GFP_ZERO, node_id); 980 895 if (disk) { 896 + int tot_minors = minors + ext_minors; 897 + 981 898 if (!init_disk_stats(disk)) { 982 899 kfree(disk); 983 900 return NULL; 984 901 } 985 - if (minors > 1) { 986 - int size = (minors - 1) * sizeof(struct hd_struct *); 902 + if (tot_minors > 1) { 903 + int size = (tot_minors - 1) * sizeof(struct hd_struct *); 987 904 disk->__part = kmalloc_node(size, 988 905 GFP_KERNEL | __GFP_ZERO, node_id); 989 906 if (!disk->__part) { ··· 1005 898 } 1006 899 } 1007 900 disk->minors = minors; 901 + disk->ext_minors = ext_minors; 1008 902 rand_initialize_disk(disk); 1009 903 disk->dev.class = &block_class; 1010 904 disk->dev.type = &disk_type; ··· 1018 910 1019 911 EXPORT_SYMBOL(alloc_disk); 1020 912 EXPORT_SYMBOL(alloc_disk_node); 913 + EXPORT_SYMBOL(alloc_disk_ext); 914 + EXPORT_SYMBOL(alloc_disk_ext_node); 1021 915 1022 916 struct kobject *get_disk(struct gendisk *disk) 1023 917 {
+8 -1
fs/partitions/check.c
··· 333 333 if (!part) 334 334 return; 335 335 336 + blk_free_devt(part_devt(part)); 336 337 rcu_assign_pointer(disk->__part[partno-1], NULL); 337 338 kobject_put(part->holder_dir); 338 339 device_del(&part->dev); ··· 353 352 sector_t start, sector_t len, int flags) 354 353 { 355 354 struct hd_struct *p; 355 + dev_t devt = MKDEV(0, 0); 356 356 int err; 357 357 358 358 if (disk->__part[partno - 1]) ··· 380 378 "%s%d", disk->dev.bus_id, partno); 381 379 382 380 device_initialize(&p->dev); 383 - p->dev.devt = MKDEV(disk->major, disk->first_minor + partno); 384 381 p->dev.class = &block_class; 385 382 p->dev.type = &part_type; 386 383 p->dev.parent = &disk->dev; 384 + 385 + err = blk_alloc_devt(p, &devt); 386 + if (err) 387 + goto out_put; 388 + p->dev.devt = devt; 387 389 388 390 /* delay uevent until 'holders' subdir is created */ 389 391 p->dev.uevent_suppress = 1; ··· 425 419 device_del(&p->dev); 426 420 out_put: 427 421 put_device(&p->dev); 422 + blk_free_devt(devt); 428 423 return err; 429 424 } 430 425
+10 -3
include/linux/genhd.h
··· 113 113 #define GENHD_FL_FAIL 64 114 114 115 115 struct gendisk { 116 - /* major, first_minor and minors are input parameters only, 117 - * don't use directly. Use disk_devt() and disk_max_parts(). 116 + /* major, first_minor, minors and ext_minors are input 117 + * parameters only, don't use directly. Use disk_devt() and 118 + * disk_max_parts(). 118 119 */ 119 120 int major; /* major number of driver */ 120 121 int first_minor; 121 122 int minors; /* maximum number of minors, =1 for 122 123 * disks that can't be partitioned. */ 124 + int ext_minors; /* number of extended dynamic minors */ 123 125 124 126 char disk_name[32]; /* name of major driver */ 125 127 ··· 169 167 170 168 static inline int disk_max_parts(struct gendisk *disk) 171 169 { 172 - return disk->minors - 1; 170 + return disk->minors + disk->ext_minors - 1; 173 171 } 174 172 175 173 static inline dev_t disk_devt(struct gendisk *disk) ··· 556 554 #define ADDPART_FLAG_RAID 1 557 555 #define ADDPART_FLAG_WHOLEDISK 2 558 556 557 + extern int blk_alloc_devt(struct hd_struct *part, dev_t *devt); 558 + extern void blk_free_devt(dev_t devt); 559 559 extern dev_t blk_lookup_devt(const char *name, int partno); 560 560 extern char *disk_name (struct gendisk *hd, int partno, char *buf); 561 561 ··· 568 564 569 565 extern struct gendisk *alloc_disk_node(int minors, int node_id); 570 566 extern struct gendisk *alloc_disk(int minors); 567 + extern struct gendisk *alloc_disk_ext_node(int minors, int ext_minrs, 568 + int node_id); 569 + extern struct gendisk *alloc_disk_ext(int minors, int ext_minors); 571 570 extern struct kobject *get_disk(struct gendisk *disk); 572 571 extern void put_disk(struct gendisk *disk); 573 572 extern void blk_register_region(dev_t devt, unsigned long range,
+2
include/linux/major.h
··· 170 170 171 171 #define VIOTAPE_MAJOR 230 172 172 173 + #define BLOCK_EXT_MAJOR 259 174 + 173 175 #endif