Rev 455 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
437 | giacomo | 1 | /* |
2 | * kobject.c - library routines for handling generic kernel objects |
||
3 | * |
||
4 | * Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org> |
||
5 | * |
||
6 | * This file is released under the GPLv2. |
||
7 | * |
||
8 | * |
||
9 | * Please see the file Documentation/kobject.txt for critical information |
||
10 | * about using the kobject interface. |
||
11 | */ |
||
12 | |||
13 | #undef DEBUG |
||
14 | |||
15 | #include <ll/stdarg.h> |
||
16 | |||
17 | #include <linuxcomp.h> |
||
18 | |||
19 | #include <linux/kobject.h> |
||
20 | #include <linux/string.h> |
||
21 | #include <linux/module.h> |
||
22 | #include <linux/stat.h> |
||
23 | |||
24 | /** |
||
25 | * populate_dir - populate directory with attributes. |
||
26 | * @kobj: object we're working on. |
||
27 | * |
||
28 | * Most subsystems have a set of default attributes that |
||
29 | * are associated with an object that registers with them. |
||
30 | * This is a helper called during object registration that |
||
31 | * loops through the default attributes of the subsystem |
||
32 | * and creates attributes files for them in sysfs. |
||
33 | * |
||
34 | */ |
||
35 | |||
36 | static int create_dir(struct kobject * kobj) |
||
37 | { |
||
38 | int error = 0; |
||
39 | if (kobject_name(kobj)) { |
||
40 | } |
||
41 | return error; |
||
42 | } |
||
43 | |||
44 | |||
45 | static inline struct kobject * to_kobj(struct list_head * entry) |
||
46 | { |
||
47 | return container_of(entry,struct kobject,entry); |
||
48 | } |
||
49 | |||
50 | |||
51 | #ifdef CONFIG_HOTPLUG |
||
52 | static int get_kobj_path_length(struct kset *kset, struct kobject *kobj) |
||
53 | { |
||
54 | int length = 1; |
||
55 | struct kobject * parent = kobj; |
||
56 | |||
57 | /* walk up the ancestors until we hit the one pointing to the |
||
58 | * root. |
||
59 | * Add 1 to strlen for leading '/' of each level. |
||
60 | */ |
||
61 | do { |
||
62 | length += strlen(kobject_name(parent)) + 1; |
||
63 | parent = parent->parent; |
||
64 | } while (parent); |
||
65 | return length; |
||
66 | } |
||
67 | |||
68 | static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, int length) |
||
69 | { |
||
70 | struct kobject * parent; |
||
71 | |||
72 | --length; |
||
73 | for (parent = kobj; parent; parent = parent->parent) { |
||
74 | int cur = strlen(kobject_name(parent)); |
||
75 | /* back up enough to print this name with '/' */ |
||
76 | length -= cur; |
||
77 | strncpy (path + length, kobject_name(parent), cur); |
||
78 | *(path + --length) = '/'; |
||
79 | } |
||
80 | |||
81 | pr_debug("%s: path = '%s'\n",__FUNCTION__,path); |
||
82 | } |
||
83 | |||
84 | #define BUFFER_SIZE 1024 /* should be enough memory for the env */ |
||
85 | #define NUM_ENVP 32 /* number of env pointers */ |
||
86 | static unsigned long sequence_num; |
||
87 | static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED; |
||
88 | |||
89 | static void kset_hotplug(const char *action, struct kset *kset, |
||
90 | struct kobject *kobj) |
||
91 | { |
||
92 | char *argv [3]; |
||
93 | char **envp = NULL; |
||
94 | char *buffer = NULL; |
||
95 | char *scratch; |
||
96 | int i = 0; |
||
97 | int retval; |
||
98 | int kobj_path_length; |
||
99 | char *kobj_path = NULL; |
||
100 | char *name = NULL; |
||
101 | unsigned long seq; |
||
102 | |||
103 | /* If the kset has a filter operation, call it. If it returns |
||
104 | failure, no hotplug event is required. */ |
||
105 | if (kset->hotplug_ops->filter) { |
||
106 | if (!kset->hotplug_ops->filter(kset, kobj)) |
||
107 | return; |
||
108 | } |
||
109 | |||
110 | pr_debug ("%s\n", __FUNCTION__); |
||
111 | |||
112 | if (!hotplug_path[0]) |
||
113 | return; |
||
114 | |||
115 | envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); |
||
116 | if (!envp) |
||
117 | return; |
||
118 | memset (envp, 0x00, NUM_ENVP * sizeof (char *)); |
||
119 | |||
120 | buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); |
||
121 | if (!buffer) |
||
122 | goto exit; |
||
123 | |||
124 | if (kset->hotplug_ops->name) |
||
125 | name = kset->hotplug_ops->name(kset, kobj); |
||
126 | if (name == NULL) |
||
127 | name = kset->kobj.name; |
||
128 | |||
129 | argv [0] = hotplug_path; |
||
130 | argv [1] = name; |
||
131 | argv [2] = 0; |
||
132 | |||
133 | /* minimal command environment */ |
||
134 | envp [i++] = "HOME=/"; |
||
135 | envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; |
||
136 | |||
137 | scratch = buffer; |
||
138 | |||
139 | envp [i++] = scratch; |
||
140 | scratch += sprintf(scratch, "ACTION=%s", action) + 1; |
||
141 | |||
142 | spin_lock(&sequence_lock); |
||
143 | seq = sequence_num++; |
||
144 | spin_unlock(&sequence_lock); |
||
145 | |||
146 | envp [i++] = scratch; |
||
147 | scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1; |
||
148 | |||
149 | kobj_path_length = get_kobj_path_length (kset, kobj); |
||
150 | kobj_path = kmalloc (kobj_path_length, GFP_KERNEL); |
||
151 | if (!kobj_path) |
||
152 | goto exit; |
||
153 | memset (kobj_path, 0x00, kobj_path_length); |
||
154 | fill_kobj_path (kset, kobj, kobj_path, kobj_path_length); |
||
155 | |||
156 | envp [i++] = scratch; |
||
157 | scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1; |
||
158 | |||
159 | if (kset->hotplug_ops->hotplug) { |
||
160 | /* have the kset specific function add its stuff */ |
||
161 | retval = kset->hotplug_ops->hotplug (kset, kobj, |
||
162 | &envp[i], NUM_ENVP - i, scratch, |
||
163 | BUFFER_SIZE - (scratch - buffer)); |
||
164 | if (retval) { |
||
165 | pr_debug ("%s - hotplug() returned %d\n", |
||
166 | __FUNCTION__, retval); |
||
167 | goto exit; |
||
168 | } |
||
169 | } |
||
170 | |||
171 | pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1], |
||
172 | envp[0], envp[1], envp[2], envp[3]); |
||
173 | retval = call_usermodehelper (argv[0], argv, envp, 0); |
||
174 | if (retval) |
||
175 | pr_debug ("%s - call_usermodehelper returned %d\n", |
||
176 | __FUNCTION__, retval); |
||
177 | |||
178 | exit: |
||
179 | kfree(kobj_path); |
||
180 | kfree(buffer); |
||
181 | kfree(envp); |
||
182 | return; |
||
183 | } |
||
184 | #else |
||
185 | static void kset_hotplug(const char *action, struct kset *kset, |
||
186 | struct kobject *kobj) |
||
187 | { |
||
188 | return; |
||
189 | } |
||
190 | #endif /* CONFIG_HOTPLUG */ |
||
191 | |||
192 | /** |
||
193 | * kobject_init - initialize object. |
||
194 | * @kobj: object in question. |
||
195 | */ |
||
196 | |||
197 | void kobject_init(struct kobject * kobj) |
||
198 | { |
||
199 | atomic_set(&kobj->refcount,1); |
||
200 | INIT_LIST_HEAD(&kobj->entry); |
||
201 | kobj->kset = kset_get(kobj->kset); |
||
202 | } |
||
203 | |||
204 | |||
205 | /** |
||
206 | * unlink - remove kobject from kset list. |
||
207 | * @kobj: kobject. |
||
208 | * |
||
209 | * Remove the kobject from the kset list and decrement |
||
210 | * its parent's refcount. |
||
211 | * This is separated out, so we can use it in both |
||
212 | * kobject_del() and kobject_add() on error. |
||
213 | */ |
||
214 | |||
215 | static void unlink(struct kobject * kobj) |
||
216 | { |
||
217 | if (kobj->kset) { |
||
218 | list_del_init(&kobj->entry); |
||
219 | } |
||
220 | kobject_put(kobj); |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * kobject_add - add an object to the hierarchy. |
||
225 | * @kobj: object. |
||
226 | */ |
||
227 | |||
228 | int kobject_add(struct kobject * kobj) |
||
229 | { |
||
230 | int error = 0; |
||
231 | struct kobject * parent; |
||
232 | struct kobject * top_kobj; |
||
233 | |||
234 | if (!(kobj = kobject_get(kobj))) |
||
235 | return -ENOENT; |
||
236 | if (!kobj->k_name) |
||
237 | kobj->k_name = kobj->name; |
||
238 | parent = kobject_get(kobj->parent); |
||
239 | |||
240 | pr_debug("kobject %s: registering. parent: %s, set: %s\n", |
||
241 | kobject_name(kobj), parent ? kobject_name(parent) : "<NULL>", |
||
242 | kobj->kset ? kobj->kset->kobj.name : "<NULL>" ); |
||
243 | |||
244 | if (kobj->kset) { |
||
245 | |||
246 | if (!parent) |
||
247 | parent = kobject_get(&kobj->kset->kobj); |
||
248 | |||
249 | list_add_tail(&kobj->entry,&kobj->kset->list); |
||
250 | } |
||
251 | kobj->parent = parent; |
||
252 | |||
253 | error = create_dir(kobj); |
||
254 | if (error) { |
||
255 | unlink(kobj); |
||
256 | if (parent) |
||
257 | kobject_put(parent); |
||
258 | } else { |
||
259 | /* If this kobj does not belong to a kset, |
||
260 | try to find a parent that does. */ |
||
261 | top_kobj = kobj; |
||
262 | if (!top_kobj->kset && top_kobj->parent) { |
||
263 | do { |
||
264 | top_kobj = top_kobj->parent; |
||
265 | } while (!top_kobj->kset && top_kobj->parent); |
||
266 | } |
||
267 | |||
268 | if (top_kobj->kset && top_kobj->kset->hotplug_ops) |
||
269 | kset_hotplug("add", top_kobj->kset, kobj); |
||
270 | } |
||
271 | return error; |
||
272 | } |
||
273 | |||
274 | |||
275 | /** |
||
276 | * kobject_register - initialize and add an object. |
||
277 | * @kobj: object in question. |
||
278 | */ |
||
279 | |||
280 | int kobject_register(struct kobject * kobj) |
||
281 | { |
||
282 | int error = 0; |
||
283 | if (kobj) { |
||
284 | kobject_init(kobj); |
||
285 | error = kobject_add(kobj); |
||
286 | if (error) { |
||
287 | printk("kobject_register failed for %s (%d)\n", |
||
288 | kobject_name(kobj),error); |
||
289 | dump_stack(); |
||
290 | } |
||
291 | } else |
||
292 | error = -EINVAL; |
||
293 | return error; |
||
294 | } |
||
295 | |||
296 | |||
297 | /** |
||
298 | * kobject_set_name - Set the name of an object |
||
299 | * @kobj: object. |
||
300 | * @name: name. |
||
301 | * |
||
302 | * If strlen(name) < KOBJ_NAME_LEN, then use a dynamically allocated |
||
303 | * string that @kobj->k_name points to. Otherwise, use the static |
||
304 | * @kobj->name array. |
||
305 | */ |
||
306 | |||
307 | int kobject_set_name(struct kobject * kobj, const char * fmt, ...) |
||
308 | { |
||
309 | int error = 0; |
||
310 | int limit = KOBJ_NAME_LEN; |
||
311 | int need; |
||
312 | va_list args; |
||
313 | char * name; |
||
314 | |||
315 | va_start(args,fmt); |
||
316 | /* |
||
317 | * First, try the static array |
||
318 | */ |
||
319 | need = vsnprintf(kobj->name,limit,fmt,args); |
||
320 | if (need < limit) |
||
321 | name = kobj->name; |
||
322 | else { |
||
323 | /* |
||
324 | * Need more space? Allocate it and try again |
||
325 | */ |
||
326 | name = kmalloc(need,GFP_KERNEL); |
||
327 | if (!name) { |
||
328 | error = -ENOMEM; |
||
329 | goto Done; |
||
330 | } |
||
331 | limit = need; |
||
332 | need = vsnprintf(name,limit,fmt,args); |
||
333 | |||
334 | /* Still? Give up. */ |
||
335 | if (need > limit) { |
||
336 | kfree(name); |
||
337 | error = -EFAULT; |
||
338 | goto Done; |
||
339 | } |
||
340 | } |
||
341 | |||
342 | /* Free the old name, if necessary. */ |
||
343 | if (kobj->k_name && kobj->k_name != kobj->name) |
||
344 | kfree(kobj->k_name); |
||
345 | |||
346 | /* Now, set the new name */ |
||
347 | kobj->k_name = name; |
||
348 | Done: |
||
349 | va_end(args); |
||
350 | return error; |
||
351 | } |
||
352 | |||
353 | EXPORT_SYMBOL(kobject_set_name); |
||
354 | |||
355 | |||
356 | /** |
||
357 | * kobject_rename - change the name of an object |
||
358 | * @kobj: object in question. |
||
359 | * @new_name: object's new name |
||
360 | */ |
||
361 | |||
362 | void kobject_rename(struct kobject * kobj, char *new_name) |
||
363 | { |
||
364 | kobj = kobject_get(kobj); |
||
365 | if (!kobj) |
||
366 | return; |
||
367 | kobject_put(kobj); |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * kobject_del - unlink kobject from hierarchy. |
||
372 | * @kobj: object. |
||
373 | */ |
||
374 | |||
375 | void kobject_del(struct kobject * kobj) |
||
376 | { |
||
377 | struct kobject * top_kobj; |
||
378 | |||
379 | /* If this kobj does not belong to a kset, |
||
380 | try to find a parent that does. */ |
||
381 | top_kobj = kobj; |
||
382 | if (!top_kobj->kset && top_kobj->parent) { |
||
383 | do { |
||
384 | top_kobj = top_kobj->parent; |
||
385 | } while (!top_kobj->kset && top_kobj->parent); |
||
386 | } |
||
387 | |||
388 | if (top_kobj->kset && top_kobj->kset->hotplug_ops) |
||
389 | kset_hotplug("remove", top_kobj->kset, kobj); |
||
390 | |||
391 | unlink(kobj); |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * kobject_unregister - remove object from hierarchy and decrement refcount. |
||
396 | * @kobj: object going away. |
||
397 | */ |
||
398 | |||
399 | void kobject_unregister(struct kobject * kobj) |
||
400 | { |
||
401 | pr_debug("kobject %s: unregistering\n",kobject_name(kobj)); |
||
402 | kobject_del(kobj); |
||
403 | kobject_put(kobj); |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * kobject_get - increment refcount for object. |
||
408 | * @kobj: object. |
||
409 | */ |
||
410 | |||
411 | struct kobject * kobject_get(struct kobject * kobj) |
||
412 | { |
||
413 | struct kobject * ret = kobj; |
||
414 | |||
415 | if (kobj) { |
||
416 | WARN_ON(!atomic_read(&kobj->refcount)); |
||
417 | atomic_inc(&kobj->refcount); |
||
418 | } else |
||
419 | ret = NULL; |
||
420 | return ret; |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * kobject_cleanup - free kobject resources. |
||
425 | * @kobj: object. |
||
426 | */ |
||
427 | |||
428 | void kobject_cleanup(struct kobject * kobj) |
||
429 | { |
||
430 | struct kobj_type * t = get_ktype(kobj); |
||
431 | struct kset * s = kobj->kset; |
||
432 | struct kobject * parent = kobj->parent; |
||
433 | |||
434 | pr_debug("kobject %s: cleaning up\n",kobject_name(kobj)); |
||
435 | if (kobj->k_name != kobj->name) |
||
436 | kfree(kobj->k_name); |
||
437 | kobj->k_name = NULL; |
||
438 | if (t && t->release) |
||
439 | t->release(kobj); |
||
440 | if (s) |
||
441 | kset_put(s); |
||
442 | if (parent) |
||
443 | kobject_put(parent); |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * kobject_put - decrement refcount for object. |
||
448 | * @kobj: object. |
||
449 | * |
||
450 | * Decrement the refcount, and if 0, call kobject_cleanup(). |
||
451 | */ |
||
452 | |||
453 | void kobject_put(struct kobject * kobj) |
||
454 | { |
||
455 | if (atomic_dec_and_test(&kobj->refcount)) |
||
456 | kobject_cleanup(kobj); |
||
457 | } |
||
458 | |||
459 | |||
460 | /** |
||
461 | * kset_init - initialize a kset for use |
||
462 | * @k: kset |
||
463 | */ |
||
464 | |||
465 | void kset_init(struct kset * k) |
||
466 | { |
||
467 | kobject_init(&k->kobj); |
||
468 | INIT_LIST_HEAD(&k->list); |
||
469 | } |
||
470 | |||
471 | |||
472 | /** |
||
473 | * kset_add - add a kset object to the hierarchy. |
||
474 | * @k: kset. |
||
475 | * |
||
476 | * Simply, this adds the kset's embedded kobject to the |
||
477 | * hierarchy. |
||
478 | * We also try to make sure that the kset's embedded kobject |
||
479 | * has a parent before it is added. We only care if the embedded |
||
480 | * kobject is not part of a kset itself, since kobject_add() |
||
481 | * assigns a parent in that case. |
||
482 | * If that is the case, and the kset has a controlling subsystem, |
||
483 | * then we set the kset's parent to be said subsystem. |
||
484 | */ |
||
485 | |||
486 | int kset_add(struct kset * k) |
||
487 | { |
||
488 | if (!k->kobj.parent && !k->kobj.kset && k->subsys) |
||
489 | k->kobj.parent = &k->subsys->kset.kobj; |
||
490 | |||
491 | return kobject_add(&k->kobj); |
||
492 | } |
||
493 | |||
494 | |||
495 | /** |
||
496 | * kset_register - initialize and add a kset. |
||
497 | * @k: kset. |
||
498 | */ |
||
499 | |||
500 | int kset_register(struct kset * k) |
||
501 | { |
||
502 | kset_init(k); |
||
503 | return kset_add(k); |
||
504 | } |
||
505 | |||
506 | |||
507 | /** |
||
508 | * kset_unregister - remove a kset. |
||
509 | * @k: kset. |
||
510 | */ |
||
511 | |||
512 | void kset_unregister(struct kset * k) |
||
513 | { |
||
514 | kobject_unregister(&k->kobj); |
||
515 | } |
||
516 | |||
517 | |||
518 | /** |
||
519 | * kset_find_obj - search for object in kset. |
||
520 | * @kset: kset we're looking in. |
||
521 | * @name: object's name. |
||
522 | * |
||
523 | * Lock kset via @kset->subsys, and iterate over @kset->list, |
||
524 | * looking for a matching kobject. Return object if found. |
||
525 | */ |
||
526 | |||
527 | struct kobject * kset_find_obj(struct kset * kset, const char * name) |
||
528 | { |
||
529 | struct list_head * entry; |
||
530 | struct kobject * ret = NULL; |
||
531 | |||
532 | list_for_each(entry,&kset->list) { |
||
533 | struct kobject * k = to_kobj(entry); |
||
534 | if (!strcmp(kobject_name(k),name)) { |
||
535 | ret = k; |
||
536 | break; |
||
537 | } |
||
538 | } |
||
539 | return ret; |
||
540 | } |
||
541 | |||
542 | |||
543 | void subsystem_init(struct subsystem * s) |
||
544 | { |
||
545 | kset_init(&s->kset); |
||
546 | } |
||
547 | |||
548 | /** |
||
549 | * subsystem_register - register a subsystem. |
||
550 | * @s: the subsystem we're registering. |
||
551 | * |
||
552 | * Once we register the subsystem, we want to make sure that |
||
553 | * the kset points back to this subsystem for correct usage of |
||
554 | * the rwsem. |
||
555 | */ |
||
556 | |||
557 | int subsystem_register(struct subsystem * s) |
||
558 | { |
||
559 | int error; |
||
560 | |||
561 | subsystem_init(s); |
||
562 | pr_debug("subsystem %s: registering\n",s->kset.kobj.name); |
||
563 | |||
564 | if (!(error = kset_add(&s->kset))) { |
||
565 | if (!s->kset.subsys) |
||
566 | s->kset.subsys = s; |
||
567 | } |
||
568 | return error; |
||
569 | } |
||
570 | |||
571 | void subsystem_unregister(struct subsystem * s) |
||
572 | { |
||
573 | pr_debug("subsystem %s: unregistering\n",s->kset.kobj.name); |
||
574 | kset_unregister(&s->kset); |
||
575 | } |
||
576 | |||
577 | |||
578 | /** |
||
579 | * subsystem_create_file - export sysfs attribute file. |
||
580 | * @s: subsystem. |
||
581 | * @a: subsystem attribute descriptor. |
||
582 | */ |
||
583 | |||
584 | int subsys_create_file(struct subsystem * s, struct subsys_attribute * a) |
||
585 | { |
||
586 | int error = 0; |
||
587 | if (subsys_get(s)) { |
||
588 | subsys_put(s); |
||
589 | } |
||
590 | return error; |
||
591 | } |
||
592 | |||
593 | |||
594 | /** |
||
595 | * subsystem_remove_file - remove sysfs attribute file. |
||
596 | * @s: subsystem. |
||
597 | * @a: attribute desciptor. |
||
598 | */ |
||
599 | |||
600 | void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a) |
||
601 | { |
||
602 | if (subsys_get(s)) { |
||
603 | subsys_put(s); |
||
604 | } |
||
605 | } |
||
606 | |||
607 | |||
608 | EXPORT_SYMBOL(kobject_init); |
||
609 | EXPORT_SYMBOL(kobject_register); |
||
610 | EXPORT_SYMBOL(kobject_unregister); |
||
611 | EXPORT_SYMBOL(kobject_get); |
||
612 | EXPORT_SYMBOL(kobject_put); |
||
613 | |||
614 | EXPORT_SYMBOL(kset_register); |
||
615 | EXPORT_SYMBOL(kset_unregister); |
||
616 | EXPORT_SYMBOL(kset_find_obj); |
||
617 | |||
618 | EXPORT_SYMBOL(subsystem_init); |
||
619 | EXPORT_SYMBOL(subsystem_register); |
||
620 | EXPORT_SYMBOL(subsystem_unregister); |
||
621 | EXPORT_SYMBOL(subsys_create_file); |
||
622 | EXPORT_SYMBOL(subsys_remove_file); |