|  | /* | 
|  | * This tries to keep block devices away from devfs as much as possible. | 
|  | */ | 
|  | #include <linux/fs.h> | 
|  | #include <linux/devfs_fs_kernel.h> | 
|  | #include <linux/vmalloc.h> | 
|  | #include <linux/genhd.h> | 
|  | #include <linux/bitops.h> | 
|  | #include <linux/mutex.h> | 
|  |  | 
|  |  | 
|  | struct unique_numspace { | 
|  | u32		  num_free;          /*  Num free in bits       */ | 
|  | u32		  length;            /*  Array length in bytes  */ | 
|  | unsigned long	  *bits; | 
|  | struct semaphore  mutex; | 
|  | }; | 
|  |  | 
|  | static DEFINE_MUTEX(numspace_mutex); | 
|  |  | 
|  | static int expand_numspace(struct unique_numspace *s) | 
|  | { | 
|  | u32 length; | 
|  | void *bits; | 
|  |  | 
|  | if (s->length < 16) | 
|  | length = 16; | 
|  | else | 
|  | length = s->length << 1; | 
|  |  | 
|  | bits = vmalloc(length); | 
|  | if (!bits) | 
|  | return -ENOMEM; | 
|  | if (s->bits) { | 
|  | memcpy(bits, s->bits, s->length); | 
|  | vfree(s->bits); | 
|  | } | 
|  |  | 
|  | s->num_free = (length - s->length) << 3; | 
|  | s->bits = bits; | 
|  | memset(bits + s->length, 0, length - s->length); | 
|  | s->length = length; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int alloc_unique_number(struct unique_numspace *s) | 
|  | { | 
|  | int rval = 0; | 
|  |  | 
|  | mutex_lock(&numspace_mutex); | 
|  | if (s->num_free < 1) | 
|  | rval = expand_numspace(s); | 
|  | if (!rval) { | 
|  | rval = find_first_zero_bit(s->bits, s->length << 3); | 
|  | --s->num_free; | 
|  | __set_bit(rval, s->bits); | 
|  | } | 
|  | mutex_unlock(&numspace_mutex); | 
|  |  | 
|  | return rval; | 
|  | } | 
|  |  | 
|  | static void dealloc_unique_number(struct unique_numspace *s, int number) | 
|  | { | 
|  | int old_val; | 
|  |  | 
|  | if (number >= 0) { | 
|  | mutex_lock(&numspace_mutex); | 
|  | old_val = __test_and_clear_bit(number, s->bits); | 
|  | if (old_val) | 
|  | ++s->num_free; | 
|  | mutex_unlock(&numspace_mutex); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct unique_numspace disc_numspace; | 
|  | static struct unique_numspace cdrom_numspace; | 
|  |  | 
|  | void devfs_add_partitioned(struct gendisk *disk) | 
|  | { | 
|  | char dirname[64], symlink[16]; | 
|  |  | 
|  | devfs_mk_dir(disk->devfs_name); | 
|  | devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), | 
|  | S_IFBLK|S_IRUSR|S_IWUSR, | 
|  | "%s/disc", disk->devfs_name); | 
|  |  | 
|  | disk->number = alloc_unique_number(&disc_numspace); | 
|  |  | 
|  | sprintf(symlink, "discs/disc%d", disk->number); | 
|  | sprintf(dirname, "../%s", disk->devfs_name); | 
|  | devfs_mk_symlink(symlink, dirname); | 
|  |  | 
|  | } | 
|  |  | 
|  | void devfs_add_disk(struct gendisk *disk) | 
|  | { | 
|  | devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), | 
|  | (disk->flags & GENHD_FL_CD) ? | 
|  | S_IFBLK|S_IRUGO|S_IWUGO : | 
|  | S_IFBLK|S_IRUSR|S_IWUSR, | 
|  | "%s", disk->devfs_name); | 
|  |  | 
|  | if (disk->flags & GENHD_FL_CD) { | 
|  | char dirname[64], symlink[16]; | 
|  |  | 
|  | disk->number = alloc_unique_number(&cdrom_numspace); | 
|  |  | 
|  | sprintf(symlink, "cdroms/cdrom%d", disk->number); | 
|  | sprintf(dirname, "../%s", disk->devfs_name); | 
|  | devfs_mk_symlink(symlink, dirname); | 
|  | } | 
|  | } | 
|  |  | 
|  | void devfs_remove_disk(struct gendisk *disk) | 
|  | { | 
|  | if (disk->minors != 1) { | 
|  | devfs_remove("discs/disc%d", disk->number); | 
|  | dealloc_unique_number(&disc_numspace, disk->number); | 
|  | devfs_remove("%s/disc", disk->devfs_name); | 
|  | } | 
|  | if (disk->flags & GENHD_FL_CD) { | 
|  | devfs_remove("cdroms/cdrom%d", disk->number); | 
|  | dealloc_unique_number(&cdrom_numspace, disk->number); | 
|  | } | 
|  | devfs_remove(disk->devfs_name); | 
|  | } | 
|  |  | 
|  |  |