kobject.c 15 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
/*
 * kobject.c - library routines for handling generic kernel objects
 *
 * Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org>
5
6
 * Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (c) 2006-2007 Novell Inc.
Linus Torvalds's avatar
Linus Torvalds committed
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This file is released under the GPLv2.
 *
 *
 * Please see the file Documentation/kobject.txt for critical information
 * about using the kobject interface.
 */

#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/stat.h>
Tim Schmielau's avatar
Tim Schmielau committed
19
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

/**
 *	populate_dir - populate directory with attributes.
 *	@kobj:	object we're working on.
 *
 *	Most subsystems have a set of default attributes that 
 *	are associated with an object that registers with them.
 *	This is a helper called during object registration that 
 *	loops through the default attributes of the subsystem 
 *	and creates attributes files for them in sysfs.
 *
 */

static int populate_dir(struct kobject * kobj)
{
	struct kobj_type * t = get_ktype(kobj);
	struct attribute * attr;
	int error = 0;
	int i;
	
	if (t && t->default_attrs) {
		for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
			if ((error = sysfs_create_file(kobj,attr)))
				break;
		}
	}
	return error;
}

49
static int create_dir(struct kobject * kobj)
Linus Torvalds's avatar
Linus Torvalds committed
50
51
52
{
	int error = 0;
	if (kobject_name(kobj)) {
53
		error = sysfs_create_dir(kobj);
Linus Torvalds's avatar
Linus Torvalds committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
		if (!error) {
			if ((error = populate_dir(kobj)))
				sysfs_remove_dir(kobj);
		}
	}
	return error;
}

static inline struct kobject * to_kobj(struct list_head * entry)
{
	return container_of(entry,struct kobject,entry);
}

static int get_kobj_path_length(struct kobject *kobj)
{
	int length = 1;
	struct kobject * parent = kobj;

	/* walk up the ancestors until we hit the one pointing to the 
	 * root.
	 * Add 1 to strlen for leading '/' of each level.
	 */
	do {
77
78
		if (kobject_name(parent) == NULL)
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
		length += strlen(kobject_name(parent)) + 1;
		parent = parent->parent;
	} while (parent);
	return length;
}

static void fill_kobj_path(struct kobject *kobj, char *path, int length)
{
	struct kobject * parent;

	--length;
	for (parent = kobj; parent; parent = parent->parent) {
		int cur = strlen(kobject_name(parent));
		/* back up enough to print this name with '/' */
		length -= cur;
		strncpy (path + length, kobject_name(parent), cur);
		*(path + --length) = '/';
	}

	pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
}

/**
102
 * kobject_get_path - generate and return the path associated with a given kobj and kset pair.
Linus Torvalds's avatar
Linus Torvalds committed
103
104
105
 *
 * @kobj:	kobject in question, with which to build the path
 * @gfp_mask:	the allocation type used to allocate the path
106
107
 *
 * The result must be freed by the caller with kfree().
Linus Torvalds's avatar
Linus Torvalds committed
108
 */
Al Viro's avatar
Al Viro committed
109
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
Linus Torvalds's avatar
Linus Torvalds committed
110
111
112
113
114
{
	char *path;
	int len;

	len = get_kobj_path_length(kobj);
115
116
	if (len == 0)
		return NULL;
117
	path = kzalloc(len, gfp_mask);
Linus Torvalds's avatar
Linus Torvalds committed
118
119
120
121
122
123
	if (!path)
		return NULL;
	fill_kobj_path(kobj, path, len);

	return path;
}
124
EXPORT_SYMBOL_GPL(kobject_get_path);
Linus Torvalds's avatar
Linus Torvalds committed
125
126
127
128
129
130
131

/**
 *	kobject_init - initialize object.
 *	@kobj:	object in question.
 */
void kobject_init(struct kobject * kobj)
{
132
133
	if (!kobj)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
	kref_init(&kobj->kref);
	INIT_LIST_HEAD(&kobj->entry);
	kobj->kset = kset_get(kobj->kset);
}


/**
 *	unlink - remove kobject from kset list.
 *	@kobj:	kobject.
 *
 *	Remove the kobject from the kset list and decrement
 *	its parent's refcount.
 *	This is separated out, so we can use it in both 
 *	kobject_del() and kobject_add() on error.
 */

static void unlink(struct kobject * kobj)
{
	if (kobj->kset) {
		spin_lock(&kobj->kset->list_lock);
		list_del_init(&kobj->entry);
		spin_unlock(&kobj->kset->list_lock);
	}
	kobject_put(kobj);
}

/**
161
 *	kobject_add - add an object to the hierarchy.
Linus Torvalds's avatar
Linus Torvalds committed
162
163
164
 *	@kobj:	object.
 */

165
int kobject_add(struct kobject * kobj)
Linus Torvalds's avatar
Linus Torvalds committed
166
167
168
169
170
171
172
{
	int error = 0;
	struct kobject * parent;

	if (!(kobj = kobject_get(kobj)))
		return -ENOENT;
	if (!kobj->k_name)
173
		kobject_set_name(kobj, "NO_NAME");
174
	if (!*kobj->k_name) {
175
176
		pr_debug("kobject attempted to be registered with no name!\n");
		WARN_ON(1);
177
		kobject_put(kobj);
178
179
		return -EINVAL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
180
181
182
183
	parent = kobject_get(kobj->parent);

	pr_debug("kobject %s: registering. parent: %s, set: %s\n",
		 kobject_name(kobj), parent ? kobject_name(parent) : "<NULL>", 
184
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>" );
Linus Torvalds's avatar
Linus Torvalds committed
185
186
187
188
189
190
191
192
193

	if (kobj->kset) {
		spin_lock(&kobj->kset->list_lock);

		if (!parent)
			parent = kobject_get(&kobj->kset->kobj);

		list_add_tail(&kobj->entry,&kobj->kset->list);
		spin_unlock(&kobj->kset->list_lock);
194
		kobj->parent = parent;
Linus Torvalds's avatar
Linus Torvalds committed
195
196
	}

197
	error = create_dir(kobj);
Linus Torvalds's avatar
Linus Torvalds committed
198
199
200
	if (error) {
		/* unlink does the kobject_put() for us */
		unlink(kobj);
201
		kobject_put(parent);
202
203
204

		/* be noisy on error issues */
		if (error == -EEXIST)
205
206
207
			printk(KERN_ERR "kobject_add failed for %s with "
			       "-EEXIST, don't try to register things with "
			       "the same name in the same directory.\n",
208
209
			       kobject_name(kobj));
		else
210
			printk(KERN_ERR "kobject_add failed for %s (%d)\n",
211
			       kobject_name(kobj), error);
212
		dump_stack();
Linus Torvalds's avatar
Linus Torvalds committed
213
214
215
216
217
218
219
220
221
222
223
224
	}

	return error;
}

/**
 *	kobject_register - initialize and add an object.
 *	@kobj:	object in question.
 */

int kobject_register(struct kobject * kobj)
{
225
	int error = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
226
227
228
	if (kobj) {
		kobject_init(kobj);
		error = kobject_add(kobj);
229
		if (!error)
230
			kobject_uevent(kobj, KOBJ_ADD);
231
	}
Linus Torvalds's avatar
Linus Torvalds committed
232
233
234
	return error;
}

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/**
 * kobject_set_name_vargs - Set the name of an kobject
 * @kobj: struct kobject to set the name of
 * @fmt: format string used to build the name
 * @vargs: vargs to format the string.
 */
static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
				  va_list vargs)
{
	va_list aq;
	char *name;

	va_copy(aq, vargs);
	name = kvasprintf(GFP_KERNEL, fmt, vargs);
	va_end(aq);

	if (!name)
		return -ENOMEM;

	/* Free the old name, if necessary. */
	kfree(kobj->k_name);

	/* Now, set the new name */
	kobj->k_name = name;

	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
262
263

/**
264
 * kobject_set_name - Set the name of a kobject
265
 * @kobj: struct kobject to set the name of
266
 * @fmt: format string used to build the name
Linus Torvalds's avatar
Linus Torvalds committed
267
 *
268
269
270
 * This sets the name of the kobject.  If you have already added the
 * kobject to the system, you must call kobject_rename() in order to
 * change the name of the kobject.
Linus Torvalds's avatar
Linus Torvalds committed
271
 */
272
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
Linus Torvalds's avatar
Linus Torvalds committed
273
274
{
	va_list args;
275
	int retval;
Linus Torvalds's avatar
Linus Torvalds committed
276

277
	va_start(args, fmt);
278
	retval = kobject_set_name_vargs(kobj, fmt, args);
Linus Torvalds's avatar
Linus Torvalds committed
279
280
	va_end(args);

281
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
282
283
284
}
EXPORT_SYMBOL(kobject_set_name);

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/**
 * kobject_init_ng - initialize a kobject structure
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 *
 * This function will properly initialize a kobject such that it can then
 * be passed to the kobject_add() call.
 *
 * After this function is called, the kobject MUST be cleaned up by a call
 * to kobject_put(), not by a call to kfree directly to ensure that all of
 * the memory is cleaned up properly.
 */
void kobject_init_ng(struct kobject *kobj, struct kobj_type *ktype)
{
	char *err_str;

	if (!kobj) {
		err_str = "invalid kobject pointer!";
		goto error;
	}
	if (!ktype) {
		err_str = "must have a ktype to be initialized properly!\n";
		goto error;
	}
	if (atomic_read(&kobj->kref.refcount)) {
		/* do not error out as sometimes we can recover */
		printk(KERN_ERR "kobject: reference count is already set, "
		       "something is seriously wrong.\n");
		dump_stack();
	}

	kref_init(&kobj->kref);
	INIT_LIST_HEAD(&kobj->entry);
	kobj->ktype = ktype;
	return;

error:
	printk(KERN_ERR "kobject: %s\n", err_str);
	dump_stack();
}
EXPORT_SYMBOL(kobject_init_ng);

Linus Torvalds's avatar
Linus Torvalds committed
327
328
329
330
331
332
/**
 *	kobject_rename - change the name of an object
 *	@kobj:	object in question.
 *	@new_name: object's new name
 */

333
int kobject_rename(struct kobject * kobj, const char *new_name)
Linus Torvalds's avatar
Linus Torvalds committed
334
335
{
	int error = 0;
336
337
338
	const char *devpath = NULL;
	char *devpath_string = NULL;
	char *envp[2];
Linus Torvalds's avatar
Linus Torvalds committed
339
340
341
342

	kobj = kobject_get(kobj);
	if (!kobj)
		return -EINVAL;
343
344
	if (!kobj->parent)
		return -EINVAL;
345

346
347
348
349
350
	/* see if this name is already in use */
	if (kobj->kset) {
		struct kobject *temp_kobj;
		temp_kobj = kset_find_obj(kobj->kset, new_name);
		if (temp_kobj) {
Johannes Berg's avatar
Johannes Berg committed
351
352
			printk(KERN_WARNING "kobject '%s' cannot be renamed "
			       "to '%s' as '%s' is already in existence.\n",
353
354
355
356
357
358
			       kobject_name(kobj), new_name, new_name);
			kobject_put(temp_kobj);
			return -EINVAL;
		}
	}

359
360
361
362
363
364
365
366
367
368
369
370
371
372
	devpath = kobject_get_path(kobj, GFP_KERNEL);
	if (!devpath) {
		error = -ENOMEM;
		goto out;
	}
	devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
	if (!devpath_string) {
		error = -ENOMEM;
		goto out;
	}
	sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
	envp[0] = devpath_string;
	envp[1] = NULL;

373
	error = sysfs_rename_dir(kobj, new_name);
374
375
376
377
378
379
380
381
382
383

	/* This function is mostly/only used for network interface.
	 * Some hotplug package track interfaces by their name and
	 * therefore want to know when the name is changed by the user. */
	if (!error)
		kobject_uevent_env(kobj, KOBJ_MOVE, envp);

out:
	kfree(devpath_string);
	kfree(devpath);
384
385
386
387
388
	kobject_put(kobj);

	return error;
}

389
390
391
/**
 *	kobject_move - move object to another parent
 *	@kobj:	object in question.
392
 *	@new_parent: object's new parent (can be NULL)
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
 */

int kobject_move(struct kobject *kobj, struct kobject *new_parent)
{
	int error;
	struct kobject *old_parent;
	const char *devpath = NULL;
	char *devpath_string = NULL;
	char *envp[2];

	kobj = kobject_get(kobj);
	if (!kobj)
		return -EINVAL;
	new_parent = kobject_get(new_parent);
	if (!new_parent) {
408
409
		if (kobj->kset)
			new_parent = kobject_get(&kobj->kset->kobj);
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
	}
	/* old object path */
	devpath = kobject_get_path(kobj, GFP_KERNEL);
	if (!devpath) {
		error = -ENOMEM;
		goto out;
	}
	devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
	if (!devpath_string) {
		error = -ENOMEM;
		goto out;
	}
	sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
	envp[0] = devpath_string;
	envp[1] = NULL;
	error = sysfs_move_dir(kobj, new_parent);
	if (error)
		goto out;
	old_parent = kobj->parent;
	kobj->parent = new_parent;
430
	new_parent = NULL;
431
432
433
	kobject_put(old_parent);
	kobject_uevent_env(kobj, KOBJ_MOVE, envp);
out:
434
	kobject_put(new_parent);
435
436
437
438
439
440
	kobject_put(kobj);
	kfree(devpath_string);
	kfree(devpath);
	return error;
}

Linus Torvalds's avatar
Linus Torvalds committed
441
442
443
444
445
446
447
/**
 *	kobject_del - unlink kobject from hierarchy.
 * 	@kobj:	object.
 */

void kobject_del(struct kobject * kobj)
{
448
449
	if (!kobj)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
450
451
452
453
454
455
456
457
458
459
460
	sysfs_remove_dir(kobj);
	unlink(kobj);
}

/**
 *	kobject_unregister - remove object from hierarchy and decrement refcount.
 *	@kobj:	object going away.
 */

void kobject_unregister(struct kobject * kobj)
{
461
462
	if (!kobj)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
463
	pr_debug("kobject %s: unregistering\n",kobject_name(kobj));
464
	kobject_uevent(kobj, KOBJ_REMOVE);
Linus Torvalds's avatar
Linus Torvalds committed
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
	kobject_del(kobj);
	kobject_put(kobj);
}

/**
 *	kobject_get - increment refcount for object.
 *	@kobj:	object.
 */

struct kobject * kobject_get(struct kobject * kobj)
{
	if (kobj)
		kref_get(&kobj->kref);
	return kobj;
}

481
482
483
/*
 * kobject_cleanup - free kobject resources.
 * @kobj: object to cleanup
Linus Torvalds's avatar
Linus Torvalds committed
484
 */
485
static void kobject_cleanup(struct kobject *kobj)
Linus Torvalds's avatar
Linus Torvalds committed
486
487
488
489
{
	struct kobj_type * t = get_ktype(kobj);
	struct kset * s = kobj->kset;
	struct kobject * parent = kobj->parent;
490
	const char *name = kobj->k_name;
Linus Torvalds's avatar
Linus Torvalds committed
491
492

	pr_debug("kobject %s: cleaning up\n",kobject_name(kobj));
493
	if (t && t->release) {
Linus Torvalds's avatar
Linus Torvalds committed
494
		t->release(kobj);
495
496
497
498
499
		/* If we have a release function, we can guess that this was
		 * not a statically allocated kobject, so we should be safe to
		 * free the name */
		kfree(name);
	}
Linus Torvalds's avatar
Linus Torvalds committed
500
501
	if (s)
		kset_put(s);
502
	kobject_put(parent);
Linus Torvalds's avatar
Linus Torvalds committed
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
}

static void kobject_release(struct kref *kref)
{
	kobject_cleanup(container_of(kref, struct kobject, kref));
}

/**
 *	kobject_put - decrement refcount for object.
 *	@kobj:	object.
 *
 *	Decrement the refcount, and if 0, call kobject_cleanup().
 */
void kobject_put(struct kobject * kobj)
{
	if (kobj)
		kref_put(&kobj->kref, kobject_release);
}


Jun'ichi Nomura's avatar
Jun'ichi Nomura committed
523
524
525
526
527
528
529
530
531
532
533
534
static void dir_release(struct kobject *kobj)
{
	kfree(kobj);
}

static struct kobj_type dir_ktype = {
	.release	= dir_release,
	.sysfs_ops	= NULL,
	.default_attrs	= NULL,
};

/**
535
 *	kobject_kset_add_dir - add sub directory of object.
536
 *	@kset:		kset the directory is belongs to.
Jun'ichi Nomura's avatar
Jun'ichi Nomura committed
537
538
539
540
541
 *	@parent:	object in which a directory is created.
 *	@name:	directory name.
 *
 *	Add a plain directory object as child of given object.
 */
542
543
struct kobject *kobject_kset_add_dir(struct kset *kset,
				     struct kobject *parent, const char *name)
Jun'ichi Nomura's avatar
Jun'ichi Nomura committed
544
545
{
	struct kobject *k;
Randy Dunlap's avatar
Randy Dunlap committed
546
	int ret;
Jun'ichi Nomura's avatar
Jun'ichi Nomura committed
547
548
549
550
551
552
553
554

	if (!parent)
		return NULL;

	k = kzalloc(sizeof(*k), GFP_KERNEL);
	if (!k)
		return NULL;

555
	k->kset = kset;
Jun'ichi Nomura's avatar
Jun'ichi Nomura committed
556
557
558
	k->parent = parent;
	k->ktype = &dir_ktype;
	kobject_set_name(k, name);
Randy Dunlap's avatar
Randy Dunlap committed
559
560
	ret = kobject_register(k);
	if (ret < 0) {
561
562
		printk(KERN_WARNING "%s: kobject_register error: %d\n",
			__func__, ret);
Randy Dunlap's avatar
Randy Dunlap committed
563
564
565
		kobject_del(k);
		return NULL;
	}
Jun'ichi Nomura's avatar
Jun'ichi Nomura committed
566
567
568
569

	return k;
}

570
571
572
573
574
575
576
/**
 *	kobject_add_dir - add sub directory of object.
 *	@parent:	object in which a directory is created.
 *	@name:	directory name.
 *
 *	Add a plain directory object as child of given object.
 */
577
578
579
580
581
struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
{
	return kobject_kset_add_dir(NULL, parent, name);
}

Linus Torvalds's avatar
Linus Torvalds committed
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
/**
 *	kset_init - initialize a kset for use
 *	@k:	kset 
 */

void kset_init(struct kset * k)
{
	kobject_init(&k->kobj);
	INIT_LIST_HEAD(&k->list);
	spin_lock_init(&k->list_lock);
}


/**
 *	kset_add - add a kset object to the hierarchy.
 *	@k:	kset.
 */

int kset_add(struct kset * k)
{
	return kobject_add(&k->kobj);
}


/**
 *	kset_register - initialize and add a kset.
 *	@k:	kset.
 */

int kset_register(struct kset * k)
{
613
614
	int err;

615
616
	if (!k)
		return -EINVAL;
617

Linus Torvalds's avatar
Linus Torvalds committed
618
	kset_init(k);
619
620
621
622
623
	err = kset_add(k);
	if (err)
		return err;
	kobject_uevent(&k->kobj, KOBJ_ADD);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
624
625
626
627
628
629
630
631
632
633
}


/**
 *	kset_unregister - remove a kset.
 *	@k:	kset.
 */

void kset_unregister(struct kset * k)
{
634
635
	if (!k)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
	kobject_unregister(&k->kobj);
}


/**
 *	kset_find_obj - search for object in kset.
 *	@kset:	kset we're looking in.
 *	@name:	object's name.
 *
 *	Lock kset via @kset->subsys, and iterate over @kset->list,
 *	looking for a matching kobject. If matching object is found
 *	take a reference and return the object.
 */

struct kobject * kset_find_obj(struct kset * kset, const char * name)
{
	struct list_head * entry;
	struct kobject * ret = NULL;

	spin_lock(&kset->list_lock);
	list_for_each(entry,&kset->list) {
		struct kobject * k = to_kobj(entry);
		if (kobject_name(k) && !strcmp(kobject_name(k),name)) {
			ret = kobject_get(k);
			break;
		}
	}
	spin_unlock(&kset->list_lock);
	return ret;
}

667
int subsystem_register(struct kset *s)
Linus Torvalds's avatar
Linus Torvalds committed
668
{
669
	return kset_register(s);
Linus Torvalds's avatar
Linus Torvalds committed
670
671
}

672
void subsystem_unregister(struct kset *s)
Linus Torvalds's avatar
Linus Torvalds committed
673
{
674
	kset_unregister(s);
Linus Torvalds's avatar
Linus Torvalds committed
675
676
677
678
679
680
681
682
}

/**
 *	subsystem_create_file - export sysfs attribute file.
 *	@s:	subsystem.
 *	@a:	subsystem attribute descriptor.
 */

683
int subsys_create_file(struct kset *s, struct subsys_attribute *a)
Linus Torvalds's avatar
Linus Torvalds committed
684
685
{
	int error = 0;
686
687
688
689

	if (!s || !a)
		return -EINVAL;

690
	if (kset_get(s)) {
691
		error = sysfs_create_file(&s->kobj, &a->attr);
692
		kset_put(s);
Linus Torvalds's avatar
Linus Torvalds committed
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
	}
	return error;
}

EXPORT_SYMBOL(kobject_init);
EXPORT_SYMBOL(kobject_register);
EXPORT_SYMBOL(kobject_unregister);
EXPORT_SYMBOL(kobject_get);
EXPORT_SYMBOL(kobject_put);
EXPORT_SYMBOL(kobject_add);
EXPORT_SYMBOL(kobject_del);

EXPORT_SYMBOL(kset_register);
EXPORT_SYMBOL(kset_unregister);

EXPORT_SYMBOL(subsystem_register);
EXPORT_SYMBOL(subsystem_unregister);
EXPORT_SYMBOL(subsys_create_file);