aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
context:
space:
mode:
Diffstat (limited to 'block')
-rw-r--r--block/genhd.c120
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);
24struct kobject *block_depr; 25struct 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 */
34static DEFINE_MUTEX(ext_devt_mutex);
35static DEFINE_IDR(ext_devt_idr);
36
27static struct device_type disk_type; 37static struct device_type disk_type;
28 38
29/** 39/**
@@ -288,6 +298,74 @@ EXPORT_SYMBOL(unregister_blkdev);
288 298
289static struct kobj_map *bdev_map; 299static 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 */
316int 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 */
355void 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 */
372struct gendisk *get_gendisk(dev_t devt, int *partno) 450struct 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
879struct gendisk *alloc_disk_node(int minors, int node_id) 974struct gendisk *alloc_disk_node(int minors, int node_id)
880{ 975{
976 return alloc_disk_ext_node(minors, 0, node_id);
977}
978
979struct gendisk *alloc_disk_ext(int minors, int ext_minors)
980{
981 return alloc_disk_ext_node(minors, ext_minors, -1);
982}
983
984struct 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
911EXPORT_SYMBOL(alloc_disk); 1019EXPORT_SYMBOL(alloc_disk);
912EXPORT_SYMBOL(alloc_disk_node); 1020EXPORT_SYMBOL(alloc_disk_node);
1021EXPORT_SYMBOL(alloc_disk_ext);
1022EXPORT_SYMBOL(alloc_disk_ext_node);
913 1023
914struct kobject *get_disk(struct gendisk *disk) 1024struct kobject *get_disk(struct gendisk *disk)
915{ 1025{