Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * This tries to keep block devices away from devfs as much as possible. |
| 3 | */ |
| 4 | #include <linux/fs.h> |
| 5 | #include <linux/devfs_fs_kernel.h> |
| 6 | #include <linux/vmalloc.h> |
| 7 | #include <linux/genhd.h> |
| 8 | #include <linux/bitops.h> |
Ingo Molnar | 353ab6e | 2006-03-26 01:37:12 -0800 | [diff] [blame] | 9 | #include <linux/mutex.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 10 | |
| 11 | |
| 12 | struct unique_numspace { |
| 13 | u32 num_free; /* Num free in bits */ |
| 14 | u32 length; /* Array length in bytes */ |
| 15 | unsigned long *bits; |
| 16 | struct semaphore mutex; |
| 17 | }; |
| 18 | |
Ingo Molnar | 353ab6e | 2006-03-26 01:37:12 -0800 | [diff] [blame] | 19 | static DEFINE_MUTEX(numspace_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | |
| 21 | static int expand_numspace(struct unique_numspace *s) |
| 22 | { |
| 23 | u32 length; |
| 24 | void *bits; |
| 25 | |
| 26 | if (s->length < 16) |
| 27 | length = 16; |
| 28 | else |
| 29 | length = s->length << 1; |
| 30 | |
| 31 | bits = vmalloc(length); |
| 32 | if (!bits) |
| 33 | return -ENOMEM; |
| 34 | if (s->bits) { |
| 35 | memcpy(bits, s->bits, s->length); |
| 36 | vfree(s->bits); |
| 37 | } |
| 38 | |
| 39 | s->num_free = (length - s->length) << 3; |
| 40 | s->bits = bits; |
| 41 | memset(bits + s->length, 0, length - s->length); |
| 42 | s->length = length; |
| 43 | |
| 44 | return 0; |
| 45 | } |
| 46 | |
| 47 | static int alloc_unique_number(struct unique_numspace *s) |
| 48 | { |
| 49 | int rval = 0; |
| 50 | |
Ingo Molnar | 353ab6e | 2006-03-26 01:37:12 -0800 | [diff] [blame] | 51 | mutex_lock(&numspace_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | if (s->num_free < 1) |
| 53 | rval = expand_numspace(s); |
| 54 | if (!rval) { |
| 55 | rval = find_first_zero_bit(s->bits, s->length << 3); |
| 56 | --s->num_free; |
| 57 | __set_bit(rval, s->bits); |
| 58 | } |
Ingo Molnar | 353ab6e | 2006-03-26 01:37:12 -0800 | [diff] [blame] | 59 | mutex_unlock(&numspace_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | |
| 61 | return rval; |
| 62 | } |
| 63 | |
| 64 | static void dealloc_unique_number(struct unique_numspace *s, int number) |
| 65 | { |
| 66 | int old_val; |
| 67 | |
| 68 | if (number >= 0) { |
Ingo Molnar | 353ab6e | 2006-03-26 01:37:12 -0800 | [diff] [blame] | 69 | mutex_lock(&numspace_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | old_val = __test_and_clear_bit(number, s->bits); |
| 71 | if (old_val) |
| 72 | ++s->num_free; |
Ingo Molnar | 353ab6e | 2006-03-26 01:37:12 -0800 | [diff] [blame] | 73 | mutex_unlock(&numspace_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 74 | } |
| 75 | } |
| 76 | |
| 77 | static struct unique_numspace disc_numspace; |
| 78 | static struct unique_numspace cdrom_numspace; |
| 79 | |
| 80 | void devfs_add_partitioned(struct gendisk *disk) |
| 81 | { |
| 82 | char dirname[64], symlink[16]; |
| 83 | |
| 84 | devfs_mk_dir(disk->devfs_name); |
| 85 | devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), |
| 86 | S_IFBLK|S_IRUSR|S_IWUSR, |
| 87 | "%s/disc", disk->devfs_name); |
| 88 | |
| 89 | disk->number = alloc_unique_number(&disc_numspace); |
| 90 | |
| 91 | sprintf(symlink, "discs/disc%d", disk->number); |
| 92 | sprintf(dirname, "../%s", disk->devfs_name); |
| 93 | devfs_mk_symlink(symlink, dirname); |
| 94 | |
| 95 | } |
| 96 | |
| 97 | void devfs_add_disk(struct gendisk *disk) |
| 98 | { |
| 99 | devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), |
| 100 | (disk->flags & GENHD_FL_CD) ? |
| 101 | S_IFBLK|S_IRUGO|S_IWUGO : |
| 102 | S_IFBLK|S_IRUSR|S_IWUSR, |
| 103 | "%s", disk->devfs_name); |
| 104 | |
| 105 | if (disk->flags & GENHD_FL_CD) { |
| 106 | char dirname[64], symlink[16]; |
| 107 | |
| 108 | disk->number = alloc_unique_number(&cdrom_numspace); |
| 109 | |
| 110 | sprintf(symlink, "cdroms/cdrom%d", disk->number); |
| 111 | sprintf(dirname, "../%s", disk->devfs_name); |
| 112 | devfs_mk_symlink(symlink, dirname); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | void devfs_remove_disk(struct gendisk *disk) |
| 117 | { |
| 118 | if (disk->minors != 1) { |
| 119 | devfs_remove("discs/disc%d", disk->number); |
| 120 | dealloc_unique_number(&disc_numspace, disk->number); |
| 121 | devfs_remove("%s/disc", disk->devfs_name); |
| 122 | } |
| 123 | if (disk->flags & GENHD_FL_CD) { |
| 124 | devfs_remove("cdroms/cdrom%d", disk->number); |
| 125 | dealloc_unique_number(&cdrom_numspace, disk->number); |
| 126 | } |
| 127 | devfs_remove(disk->devfs_name); |
| 128 | } |
| 129 | |
| 130 | |