1989d42e8SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/drivers/base/map.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * (C) Copyright Al Viro 2002,2003
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * NOTE: data structure needs to be changed. It works, but for large dev_t
81da177e4SLinus Torvalds * it will be too slow. It is isolated, though, so these changes will be
91da177e4SLinus Torvalds * local to that file.
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
1458383af6SJes Sorensen #include <linux/mutex.h>
151da177e4SLinus Torvalds #include <linux/kdev_t.h>
161da177e4SLinus Torvalds #include <linux/kobject.h>
171da177e4SLinus Torvalds #include <linux/kobj_map.h>
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds struct kobj_map {
201da177e4SLinus Torvalds struct probe {
211da177e4SLinus Torvalds struct probe *next;
221da177e4SLinus Torvalds dev_t dev;
231da177e4SLinus Torvalds unsigned long range;
241da177e4SLinus Torvalds struct module *owner;
251da177e4SLinus Torvalds kobj_probe_t *get;
261da177e4SLinus Torvalds int (*lock)(dev_t, void *);
271da177e4SLinus Torvalds void *data;
281da177e4SLinus Torvalds } *probes[255];
2958383af6SJes Sorensen struct mutex *lock;
301da177e4SLinus Torvalds };
311da177e4SLinus Torvalds
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)321da177e4SLinus Torvalds int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
331da177e4SLinus Torvalds struct module *module, kobj_probe_t *probe,
341da177e4SLinus Torvalds int (*lock)(dev_t, void *), void *data)
351da177e4SLinus Torvalds {
36*e7deeb9dSJinchao Wang unsigned int n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
37*e7deeb9dSJinchao Wang unsigned int index = MAJOR(dev);
38*e7deeb9dSJinchao Wang unsigned int i;
391da177e4SLinus Torvalds struct probe *p;
401da177e4SLinus Torvalds
411da177e4SLinus Torvalds if (n > 255)
421da177e4SLinus Torvalds n = 255;
431da177e4SLinus Torvalds
442e4fabecSAndrei Poenaru p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);
451da177e4SLinus Torvalds if (p == NULL)
461da177e4SLinus Torvalds return -ENOMEM;
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds for (i = 0; i < n; i++, p++) {
491da177e4SLinus Torvalds p->owner = module;
501da177e4SLinus Torvalds p->get = probe;
511da177e4SLinus Torvalds p->lock = lock;
521da177e4SLinus Torvalds p->dev = dev;
531da177e4SLinus Torvalds p->range = range;
541da177e4SLinus Torvalds p->data = data;
551da177e4SLinus Torvalds }
5658383af6SJes Sorensen mutex_lock(domain->lock);
571da177e4SLinus Torvalds for (i = 0, p -= n; i < n; i++, p++, index++) {
581da177e4SLinus Torvalds struct probe **s = &domain->probes[index % 255];
591da177e4SLinus Torvalds while (*s && (*s)->range < range)
601da177e4SLinus Torvalds s = &(*s)->next;
611da177e4SLinus Torvalds p->next = *s;
621da177e4SLinus Torvalds *s = p;
631da177e4SLinus Torvalds }
6458383af6SJes Sorensen mutex_unlock(domain->lock);
651da177e4SLinus Torvalds return 0;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds
kobj_unmap(struct kobj_map * domain,dev_t dev,unsigned long range)681da177e4SLinus Torvalds void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range)
691da177e4SLinus Torvalds {
70*e7deeb9dSJinchao Wang unsigned int n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
71*e7deeb9dSJinchao Wang unsigned int index = MAJOR(dev);
72*e7deeb9dSJinchao Wang unsigned int i;
731da177e4SLinus Torvalds struct probe *found = NULL;
741da177e4SLinus Torvalds
751da177e4SLinus Torvalds if (n > 255)
761da177e4SLinus Torvalds n = 255;
771da177e4SLinus Torvalds
7858383af6SJes Sorensen mutex_lock(domain->lock);
791da177e4SLinus Torvalds for (i = 0; i < n; i++, index++) {
801da177e4SLinus Torvalds struct probe **s;
811da177e4SLinus Torvalds for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) {
821da177e4SLinus Torvalds struct probe *p = *s;
831da177e4SLinus Torvalds if (p->dev == dev && p->range == range) {
841da177e4SLinus Torvalds *s = p->next;
851da177e4SLinus Torvalds if (!found)
861da177e4SLinus Torvalds found = p;
871da177e4SLinus Torvalds break;
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds }
9158383af6SJes Sorensen mutex_unlock(domain->lock);
921da177e4SLinus Torvalds kfree(found);
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds
kobj_lookup(struct kobj_map * domain,dev_t dev,int * index)951da177e4SLinus Torvalds struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
961da177e4SLinus Torvalds {
971da177e4SLinus Torvalds struct kobject *kobj;
981da177e4SLinus Torvalds struct probe *p;
991da177e4SLinus Torvalds unsigned long best = ~0UL;
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds retry:
10258383af6SJes Sorensen mutex_lock(domain->lock);
1031da177e4SLinus Torvalds for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
1041da177e4SLinus Torvalds struct kobject *(*probe)(dev_t, int *, void *);
1051da177e4SLinus Torvalds struct module *owner;
1061da177e4SLinus Torvalds void *data;
1071da177e4SLinus Torvalds
1081da177e4SLinus Torvalds if (p->dev > dev || p->dev + p->range - 1 < dev)
1091da177e4SLinus Torvalds continue;
1101da177e4SLinus Torvalds if (p->range - 1 >= best)
1111da177e4SLinus Torvalds break;
1121da177e4SLinus Torvalds if (!try_module_get(p->owner))
1131da177e4SLinus Torvalds continue;
1141da177e4SLinus Torvalds owner = p->owner;
1151da177e4SLinus Torvalds data = p->data;
1161da177e4SLinus Torvalds probe = p->get;
1171da177e4SLinus Torvalds best = p->range - 1;
1181da177e4SLinus Torvalds *index = dev - p->dev;
1191da177e4SLinus Torvalds if (p->lock && p->lock(dev, data) < 0) {
1201da177e4SLinus Torvalds module_put(owner);
1211da177e4SLinus Torvalds continue;
1221da177e4SLinus Torvalds }
12358383af6SJes Sorensen mutex_unlock(domain->lock);
1241da177e4SLinus Torvalds kobj = probe(dev, index, data);
1251da177e4SLinus Torvalds /* Currently ->owner protects _only_ ->probe() itself. */
1261da177e4SLinus Torvalds module_put(owner);
1271da177e4SLinus Torvalds if (kobj)
1281da177e4SLinus Torvalds return kobj;
1291da177e4SLinus Torvalds goto retry;
1301da177e4SLinus Torvalds }
13158383af6SJes Sorensen mutex_unlock(domain->lock);
1321da177e4SLinus Torvalds return NULL;
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds
kobj_map_init(kobj_probe_t * base_probe,struct mutex * lock)13558383af6SJes Sorensen struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
1384aed0644SJiri Slaby struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
1391da177e4SLinus Torvalds int i;
1401da177e4SLinus Torvalds
1411da177e4SLinus Torvalds if ((p == NULL) || (base == NULL)) {
1421da177e4SLinus Torvalds kfree(p);
1431da177e4SLinus Torvalds kfree(base);
1441da177e4SLinus Torvalds return NULL;
1451da177e4SLinus Torvalds }
1461da177e4SLinus Torvalds
1471da177e4SLinus Torvalds base->dev = 1;
1481da177e4SLinus Torvalds base->range = ~0;
1491da177e4SLinus Torvalds base->get = base_probe;
1501da177e4SLinus Torvalds for (i = 0; i < 255; i++)
1511da177e4SLinus Torvalds p->probes[i] = base;
15258383af6SJes Sorensen p->lock = lock;
1531da177e4SLinus Torvalds return p;
1541da177e4SLinus Torvalds }
155