| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  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>
 | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | #include <linux/mutex.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #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]; | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	struct mutex *lock; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	mutex_lock(domain->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	mutex_unlock(domain->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	mutex_lock(domain->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	mutex_unlock(domain->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	kfree(found); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct kobject *kobj; | 
					
						
							|  |  |  | 	struct probe *p; | 
					
						
							|  |  |  | 	unsigned long best = ~0UL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | retry: | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	mutex_lock(domain->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 		mutex_unlock(domain->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		kobj = probe(dev, index, data); | 
					
						
							|  |  |  | 		/* Currently ->owner protects _only_ ->probe() itself. */ | 
					
						
							|  |  |  | 		module_put(owner); | 
					
						
							|  |  |  | 		if (kobj) | 
					
						
							|  |  |  | 			return kobj; | 
					
						
							|  |  |  | 		goto retry; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	mutex_unlock(domain->lock); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2005-09-13 01:25:01 -07:00
										 |  |  | 	struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((p == NULL) || (base == NULL)) { | 
					
						
							|  |  |  | 		kfree(p); | 
					
						
							|  |  |  | 		kfree(base); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	base->dev = 1; | 
					
						
							|  |  |  | 	base->range = ~0; | 
					
						
							|  |  |  | 	base->get = base_probe; | 
					
						
							|  |  |  | 	for (i = 0; i < 255; i++) | 
					
						
							|  |  |  | 		p->probes[i] = base; | 
					
						
							| 
									
										
										
										
											2006-02-06 14:12:43 -08:00
										 |  |  | 	p->lock = lock; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return p; | 
					
						
							|  |  |  | } |