aboutsummaryrefslogtreecommitdiffstats
path: root/block/genhd.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2008-08-25 06:47:22 -0400
committerJens Axboe <jens.axboe@oracle.com>2008-10-09 02:56:06 -0400
commitbcce3de1be61e424deef35d1e86e86a35c4b6e65 (patch)
treecfdefb52bc37c61dfac160951a9beb86d5cd9ba0 /block/genhd.c
parentc9959059161ddd7bf4670cf47367033d6b2f79c4 (diff)
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>
Diffstat (limited to 'block/genhd.c')
-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{