/*
* gendisk handling
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/kobj_map.h>
#include <linux/buffer_head.h>
#define MAX_PROBE_HASH 255 /* random */
static struct subsystem block_subsys;
static DECLARE_MUTEX(block_subsys_sem);
/*
* Can be deleted altogether. Later.
*
*/
static struct blk_major_name {
struct blk_major_name *next;
int major;
char name[16];
} *major_names[MAX_PROBE_HASH];
/* index in the above - for now: assume no multimajor ranges */
static inline int major_to_index(int major)
{
return major % MAX_PROBE_HASH;
}
struct blkdev_info {
int index;
struct blk_major_name *bd;
};
/*
* iterate over a list of blkdev_info structures. allows
* the major_names array to be iterated over from outside this file
* must be called with the block_subsys_sem held
*/
void *get_next_blkdev(void *dev)
{
struct blkdev_info *info;
if (dev == NULL) {
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
goto out;
info->index=0;
info->bd = major_names[info->index];
if (info->bd)
goto out;
} else {
info = dev;
}
while (info->index < ARRAY_SIZE(major_names)) {
if (info->bd)
info->bd = info->bd->next;
if (info->bd)
goto out;
/*
* No devices on this chain, move to the next
*/
info->index++;
info->bd = (info->index < ARRAY_SIZE(major_names)) ?
major_names[info->index] : NULL;
if (info->bd)
goto out;
}
out:
return info;
}
void *acquire_blkdev_list(void)
{
down(&block_subsys_sem);
return get_next_blkdev(NULL);
}
void release_blkdev_list(void *dev)
{
up(&block_subsys_sem);
kfree(dev);
}
/*
* Count the number of records in the blkdev_list.
* must be called with the block_subsys_sem held
*/
int count_blkdev_list(void)
{
struct blk_major_name *n;
int i, count;
count = 0;
for (i = 0; i < ARRAY_SIZE(major_names); i++) {
for (n = major_names[i]; n; n = n->next)
count++;
}
return count;
}
/*
* extract the major and name values from a blkdev_info struct
* passed in as a void to *dev. Must be called with
* block_subsys_sem held
*/
int get_blkdev_info(void *dev, int *major, char **name)
{
struct blkdev_info *info = dev;
if (info->bd == NULL)
return 1;
*major = info->bd->major;
*name = info->bd->name;
return 0;
}
int register_blkdev(unsigned int major, const char *name)
{
struct blk_major_name **n, *p;
int index, ret = 0;
down(&block_subsys_sem);
/* temporary */
if (major == 0) {
for (index = ARRAY_SIZE(major_names)-1; index > 0; index--) {
if (major_names[index] == NULL)
break;
}
if (index == 0) {
printk("register_blkdev: failed to get major for %s\n",
name);
ret = -EBUSY;
goto out;
}
major = index;
ret = major;
}
p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
if (p == NULL) {
ret = -ENOMEM;
goto out;
}
p->major = major;
strlcpy(p->name, name, sizeof(p->name));
p->next = NULL;
index = major_to_index(major);
for (n = &major_names[index]; *n; n = &(*n)->next) {
if ((*n)->major == major)
break;
}
if (!*n)
*n = p;
else
ret = -EBUSY;
if (ret < 0) {
printk("register_blkdev: cannot get major %d for %s\n",
major, name);
kfree(p);
}
out:
up(&block_subsys_sem);
return ret;
}
EXPORT_SYMBOL(register_blkdev);
/* todo: make void - error printk here */
int unregister_blkdev(unsigned int major, const char *name)
{
struct blk_major_name **n;
struct blk_major_name *p = NULL;
int index = major_to_index(major);
int ret = 0;
down(&block_subsys_sem);
for (n = &major_names[index]; *n; n = &(*n)->next)
if ((*n)->major == major)
break;
if (!*n || strcmp((*n)->name, name))
ret = -EINVAL;
else {
p = *n;