Commit 6abc19b4 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] kobj_map

code responsible for gendisk lookups taken out in drivers/base and
generalized - now it allows to have a range-based mapping from numbers
to kobjects for given struct subsystem.
parent 4460bbfd
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
obj-y := core.o sys.o interface.o power.o bus.o \ obj-y := core.o sys.o interface.o power.o bus.o \
driver.o class.o platform.o \ driver.o class.o platform.o \
cpu.o firmware.o init.o cpu.o firmware.o init.o map.o
obj-$(CONFIG_NUMA) += node.o memblk.o obj-$(CONFIG_NUMA) += node.o memblk.o
/*
* linux/drivers/base/map.c
*
* (C) Copyright Al Viro 2002,2003
* Released under GPL v2.
*
* NOTE: data structure needs to be changed. It works, but for large dev_t
* it will be too slow. It is isolated, though, so these changes will be
* local to that file.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/kobject.h>
#include <linux/kobj_map.h>
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct rw_semaphore *sem;
};
static inline int dev_to_index(dev_t dev)
{
return MAJOR(dev) % 255;
}
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
unsigned index = MAJOR(dev);
unsigned i;
struct probe *p;
if (n > 255)
n = 255;
p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
for (i = 0; i < n; i++, p++) {
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;
p->range = range;
p->data = data;
}
down_write(domain->sem);
for (i = 0, p -= n; i < n; i++, p++, index++) {
struct probe **s = &domain->probes[index % 255];
while (*s && (*s)->range < range)
s = &(*s)->next;
p->next = *s;
*s = p;
}
up_write(domain->sem);
return 0;
}
void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
unsigned index = MAJOR(dev);
unsigned i;
struct probe *found = NULL;
if (n > 255)
n = 255;
down_write(domain->sem);
for (i = 0; i < n; i++, index++) {
struct probe **s;
for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) {
struct probe *p = *s;
if (p->dev == dev && p->range == range) {
*s = p->next;
if (!found)
found = p;
break;
}
}
}
up_write(domain->sem);
kfree(found);
}
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
{
struct kobject *kobj;
struct probe *p;
unsigned best = ~0U;
retry:
down_read(domain->sem);
for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
struct kobject *(*probe)(dev_t, int *, void *);
struct module *owner;
void *data;
if (p->dev > dev || p->dev + p->range - 1 < dev)
continue;
if (p->range - 1 >= best)
break;
if (!try_module_get(p->owner))
continue;
owner = p->owner;
data = p->data;
probe = p->get;
best = p->range - 1;
*index = dev - p->dev;
if (p->lock && p->lock(dev, data) < 0) {
module_put(owner);
continue;
}
up_read(domain->sem);
kobj = probe(dev, index, data);
/* Currently ->owner protects _only_ ->probe() itself. */
module_put(owner);
if (kobj)
return kobj;
goto retry;
}
up_read(domain->sem);
return NULL;
}
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe,
struct subsystem *s)
{
struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
struct probe *base = kmalloc(sizeof(struct probe), GFP_KERNEL);
int i;
memset(base, 0, sizeof(struct probe));
base->dev = 1;
base->range = ~0;
base->get = base_probe;
for (i = 0; i < 255; i++)
p->probes[i] = base;
p->sem = &s->rwsem;
return p;
}
...@@ -13,13 +13,14 @@ ...@@ -13,13 +13,14 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/kobj_map.h>
#define MAX_PROBE_HASH 255 /* random */ #define MAX_PROBE_HASH 255 /* random */
static struct subsystem block_subsys; static struct subsystem block_subsys;
/* /*
* Can be merged with blk_probe or deleted altogether. Later. * Can be deleted altogether. Later.
* *
* Modified under both block_subsys.rwsem and major_names_lock. * Modified under both block_subsys.rwsem and major_names_lock.
*/ */
...@@ -31,27 +32,12 @@ static struct blk_major_name { ...@@ -31,27 +32,12 @@ static struct blk_major_name {
static spinlock_t major_names_lock = SPIN_LOCK_UNLOCKED; static spinlock_t major_names_lock = SPIN_LOCK_UNLOCKED;
static struct blk_probe {
struct blk_probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
struct kobject *(*get)(dev_t dev, int *part, void *data);
int (*lock)(dev_t, void *);
void *data;
} *probes[MAX_PROBE_HASH];
/* index in the above - for now: assume no multimajor ranges */ /* index in the above - for now: assume no multimajor ranges */
static inline int major_to_index(int major) static inline int major_to_index(int major)
{ {
return major % MAX_PROBE_HASH; return major % MAX_PROBE_HASH;
} }
static inline int dev_to_index(dev_t dev)
{
return major_to_index(MAJOR(dev));
}
/* get block device names in somewhat random order */ /* get block device names in somewhat random order */
int get_blkdev_list(char *p) int get_blkdev_list(char *p)
{ {
...@@ -156,6 +142,8 @@ int unregister_blkdev(unsigned int major, const char *name) ...@@ -156,6 +142,8 @@ int unregister_blkdev(unsigned int major, const char *name)
return ret; return ret;
} }
static struct kobj_map *bdev_map;
/* /*
* Register device numbers dev..(dev+range-1) * Register device numbers dev..(dev+range-1)
* range must be nonzero * range must be nonzero
...@@ -165,42 +153,12 @@ void blk_register_region(dev_t dev, unsigned long range, struct module *module, ...@@ -165,42 +153,12 @@ void blk_register_region(dev_t dev, unsigned long range, struct module *module,
struct kobject *(*probe)(dev_t, int *, void *), struct kobject *(*probe)(dev_t, int *, void *),
int (*lock)(dev_t, void *), void *data) int (*lock)(dev_t, void *), void *data)
{ {
int index = dev_to_index(dev); kobj_map(bdev_map, dev, range, module, probe, lock, data);
struct blk_probe *p = kmalloc(sizeof(struct blk_probe), GFP_KERNEL);
struct blk_probe **s;
if (p == NULL)
return;
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;
p->range = range;
p->data = data;
down_write(&block_subsys.rwsem);
for (s = &probes[index]; *s && (*s)->range < range; s = &(*s)->next)
;
p->next = *s;
*s = p;
up_write(&block_subsys.rwsem);
} }
void blk_unregister_region(dev_t dev, unsigned long range) void blk_unregister_region(dev_t dev, unsigned long range)
{ {
int index = dev_to_index(dev); kobj_unmap(bdev_map, dev, range);
struct blk_probe **s;
down_write(&block_subsys.rwsem);
for (s = &probes[index]; *s; s = &(*s)->next) {
struct blk_probe *p = *s;
if (p->dev == dev && p->range == range) {
*s = p->next;
kfree(p);
break;
}
}
up_write(&block_subsys.rwsem);
} }
EXPORT_SYMBOL(blk_register_region); EXPORT_SYMBOL(blk_register_region);
...@@ -256,46 +214,10 @@ void unlink_gendisk(struct gendisk *disk) ...@@ -256,46 +214,10 @@ void unlink_gendisk(struct gendisk *disk)
* This function gets the structure containing partitioning * This function gets the structure containing partitioning
* information for the given device @dev. * information for the given device @dev.
*/ */
struct gendisk * struct gendisk *get_gendisk(dev_t dev, int *part)
get_gendisk(dev_t dev, int *part)
{ {
int index = dev_to_index(dev); struct kobject *kobj = kobj_lookup(bdev_map, dev, part);
struct kobject *kobj; return kobj ? to_disk(kobj) : NULL;
struct blk_probe *p;
unsigned best = ~0U;
retry:
down_read(&block_subsys.rwsem);
for (p = probes[index]; p; p = p->next) {
struct kobject *(*probe)(dev_t, int *, void *);
struct module *owner;
void *data;
if (p->dev > dev || p->dev + p->range - 1 < dev)
continue;
if (p->range - 1 >= best)
break;
if (!try_module_get(p->owner))
continue;
owner = p->owner;
data = p->data;
probe = p->get;
best = p->range - 1;
*part = dev - p->dev;
if (p->lock && p->lock(dev, data) < 0) {
module_put(owner);
continue;
}
up_read(&block_subsys.rwsem);
kobj = probe(dev, part, data);
/* Currently ->owner protects _only_ ->probe() itself. */
module_put(owner);
if (kobj)
return to_disk(kobj);
goto retry; /* this terminates: best decreases */
}
up_read(&block_subsys.rwsem);
return NULL;
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
...@@ -376,14 +298,7 @@ static struct kobject *base_probe(dev_t dev, int *part, void *data) ...@@ -376,14 +298,7 @@ static struct kobject *base_probe(dev_t dev, int *part, void *data)
int __init device_init(void) int __init device_init(void)
{ {
struct blk_probe *base = kmalloc(sizeof(struct blk_probe), GFP_KERNEL); bdev_map = kobj_map_init(base_probe, &block_subsys);
int i;
memset(base, 0, sizeof(struct blk_probe));
base->dev = 1;
base->range = ~0; /* range 1 .. ~0 */
base->get = base_probe;
for (i = 0; i < MAX_PROBE_HASH; i++)
probes[i] = base; /* must remain last in chain */
blk_dev_init(); blk_dev_init();
subsystem_register(&block_subsys); subsystem_register(&block_subsys);
return 0; return 0;
......
#ifdef __KERNEL__
typedef struct kobject *kobj_probe_t(dev_t, int *, void *);
struct kobj_map;
int kobj_map(struct kobj_map *, dev_t, unsigned long, struct module *,
kobj_probe_t *, int (*)(dev_t, void *), void *);
void kobj_unmap(struct kobj_map *, dev_t, unsigned long);
struct kobject *kobj_lookup(struct kobj_map *, dev_t, int *);
struct kobj_map *kobj_map_init(kobj_probe_t *, struct subsystem *);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment