diff options
Diffstat (limited to 'block/genhd.c')
-rw-r--r-- | block/genhd.c | 120 |
1 files changed, 115 insertions, 5 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 | { |