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