You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
2.8 KiB
130 lines
2.8 KiB
/*
|
|
* 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);
|
|
}
|
|
|
|
|
|
|