Blame view
lib/kobject.c
27.9 KB
d9d16e16a
|
1 |
// SPDX-License-Identifier: GPL-2.0 |
1da177e4c
|
2 3 4 5 |
/* * kobject.c - library routines for handling generic kernel objects * * Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org> |
f0e7e1bd7
|
6 7 |
* Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com> * Copyright (c) 2006-2007 Novell Inc. |
1da177e4c
|
8 |
* |
1da177e4c
|
9 10 11 12 13 14 |
* Please see the file Documentation/kobject.txt for critical information * about using the kobject interface. */ #include <linux/kobject.h> #include <linux/string.h> |
8bc3bcc93
|
15 |
#include <linux/export.h> |
1da177e4c
|
16 |
#include <linux/stat.h> |
4e57b6817
|
17 |
#include <linux/slab.h> |
89c86a64c
|
18 |
#include <linux/random.h> |
1da177e4c
|
19 |
|
e34ff4906
|
20 |
/** |
ed856349d
|
21 |
* kobject_namespace() - Return @kobj's namespace tag. |
e34ff4906
|
22 23 24 25 26 27 28 29 30 31 32 33 |
* @kobj: kobject in question * * Returns namespace tag of @kobj if its parent has namespace ops enabled * and thus @kobj should have a namespace tag associated with it. Returns * %NULL otherwise. */ const void *kobject_namespace(struct kobject *kobj) { const struct kobj_ns_type_operations *ns_ops = kobj_ns_ops(kobj); if (!ns_ops || ns_ops->type == KOBJ_NS_TYPE_NONE) return NULL; |
a1212d278
|
34 |
return kobj->ktype->namespace(kobj); |
e34ff4906
|
35 |
} |
5f81880d5
|
36 |
/** |
ed856349d
|
37 |
* kobject_get_ownership() - Get sysfs ownership data for @kobj. |
5f81880d5
|
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
* @kobj: kobject in question * @uid: kernel user ID for sysfs objects * @gid: kernel group ID for sysfs objects * * Returns initial uid/gid pair that should be used when creating sysfs * representation of given kobject. Normally used to adjust ownership of * objects in a container. */ void kobject_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid) { *uid = GLOBAL_ROOT_UID; *gid = GLOBAL_ROOT_GID; if (kobj->ktype->get_ownership) kobj->ktype->get_ownership(kobj, uid, gid); } |
e374a2bfe
|
54 55 56 |
/* * populate_dir - populate directory with attributes. * @kobj: object we're working on. |
1da177e4c
|
57 |
* |
e374a2bfe
|
58 59 60 61 |
* 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. |
1da177e4c
|
62 |
*/ |
e374a2bfe
|
63 |
static int populate_dir(struct kobject *kobj) |
1da177e4c
|
64 |
{ |
e374a2bfe
|
65 66 |
struct kobj_type *t = get_ktype(kobj); struct attribute *attr; |
1da177e4c
|
67 68 |
int error = 0; int i; |
e374a2bfe
|
69 |
|
1da177e4c
|
70 71 |
if (t && t->default_attrs) { for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) { |
e374a2bfe
|
72 73 |
error = sysfs_create_file(kobj, attr); if (error) |
1da177e4c
|
74 75 76 77 78 |
break; } } return error; } |
e374a2bfe
|
79 |
static int create_dir(struct kobject *kobj) |
1da177e4c
|
80 |
{ |
aa30f47cf
|
81 |
const struct kobj_type *ktype = get_ktype(kobj); |
c84a3b277
|
82 |
const struct kobj_ns_type_operations *ops; |
e34ff4906
|
83 84 85 |
int error; error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); |
c84a3b277
|
86 87 88 89 90 91 92 |
if (error) return error; error = populate_dir(kobj); if (error) { sysfs_remove_dir(kobj); return error; |
1da177e4c
|
93 |
} |
26ea12dec
|
94 |
|
aa30f47cf
|
95 96 97 98 99 100 101 |
if (ktype) { error = sysfs_create_groups(kobj, ktype->default_groups); if (error) { sysfs_remove_dir(kobj); return error; } } |
26ea12dec
|
102 103 104 105 106 |
/* * @kobj->sd may be deleted by an ancestor going away. Hold an * extra reference so that it stays until @kobj is gone. */ sysfs_get(kobj->sd); |
c84a3b277
|
107 108 109 110 111 112 113 114 115 |
/* * If @kobj has ns_ops, its children need to be filtered based on * their namespace tags. Enable namespace support on @kobj->sd. */ ops = kobj_child_ns_ops(kobj); if (ops) { BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE); BUG_ON(ops->type >= KOBJ_NS_TYPES); BUG_ON(!kobj_ns_type_registered(ops->type)); |
fa4cd451c
|
116 |
sysfs_enable_ns(kobj->sd); |
c84a3b277
|
117 118 119 |
} return 0; |
1da177e4c
|
120 |
} |
1da177e4c
|
121 122 123 |
static int get_kobj_path_length(struct kobject *kobj) { int length = 1; |
e374a2bfe
|
124 |
struct kobject *parent = kobj; |
1da177e4c
|
125 |
|
e374a2bfe
|
126 |
/* walk up the ancestors until we hit the one pointing to the |
1da177e4c
|
127 128 129 130 |
* root. * Add 1 to strlen for leading '/' of each level. */ do { |
b365b3daf
|
131 132 |
if (kobject_name(parent) == NULL) return 0; |
1da177e4c
|
133 134 135 136 137 138 139 140 |
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) { |
e374a2bfe
|
141 |
struct kobject *parent; |
1da177e4c
|
142 143 144 145 146 147 |
--length; for (parent = kobj; parent; parent = parent->parent) { int cur = strlen(kobject_name(parent)); /* back up enough to print this name with '/' */ length -= cur; |
77d2a24b6
|
148 |
memcpy(path + length, kobject_name(parent), cur); |
1da177e4c
|
149 150 |
*(path + --length) = '/'; } |
9f66fa2a4
|
151 152 |
pr_debug("kobject: '%s' (%p): %s: path = '%s' ", kobject_name(kobj), |
810304db7
|
153 |
kobj, __func__, path); |
1da177e4c
|
154 155 156 |
} /** |
8fd7c302b
|
157 |
* kobject_get_path() - Allocate memory and fill in the path for @kobj. |
1da177e4c
|
158 159 |
* @kobj: kobject in question, with which to build the path * @gfp_mask: the allocation type used to allocate the path |
72fd4a35a
|
160 |
* |
8fd7c302b
|
161 |
* Return: The newly allocated memory, caller must free with kfree(). |
1da177e4c
|
162 |
*/ |
fd4f2df24
|
163 |
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask) |
1da177e4c
|
164 165 166 167 168 |
{ char *path; int len; len = get_kobj_path_length(kobj); |
b365b3daf
|
169 170 |
if (len == 0) return NULL; |
4668edc33
|
171 |
path = kzalloc(len, gfp_mask); |
1da177e4c
|
172 173 |
if (!path) return NULL; |
1da177e4c
|
174 175 176 177 |
fill_kobj_path(kobj, path, len); return path; } |
80fc9f532
|
178 |
EXPORT_SYMBOL_GPL(kobject_get_path); |
1da177e4c
|
179 |
|
0f4dafc05
|
180 181 |
/* add the kobject to its kset's list */ static void kobj_kset_join(struct kobject *kobj) |
1da177e4c
|
182 |
{ |
0f4dafc05
|
183 |
if (!kobj->kset) |
31b9025aa
|
184 |
return; |
0f4dafc05
|
185 186 187 188 189 |
kset_get(kobj->kset); spin_lock(&kobj->kset->list_lock); list_add_tail(&kobj->entry, &kobj->kset->list); spin_unlock(&kobj->kset->list_lock); |
1da177e4c
|
190 |
} |
0f4dafc05
|
191 192 193 194 195 |
/* remove the kobject from its kset's list */ static void kobj_kset_leave(struct kobject *kobj) { if (!kobj->kset) return; |
1da177e4c
|
196 |
|
0f4dafc05
|
197 198 199 200 201 |
spin_lock(&kobj->kset->list_lock); list_del_init(&kobj->entry); spin_unlock(&kobj->kset->list_lock); kset_put(kobj->kset); } |
1da177e4c
|
202 |
|
e374a2bfe
|
203 |
static void kobject_init_internal(struct kobject *kobj) |
1da177e4c
|
204 |
{ |
0f4dafc05
|
205 206 207 208 |
if (!kobj) return; kref_init(&kobj->kref); INIT_LIST_HEAD(&kobj->entry); |
a4573c488
|
209 210 211 212 |
kobj->state_in_sysfs = 0; kobj->state_add_uevent_sent = 0; kobj->state_remove_uevent_sent = 0; kobj->state_initialized = 1; |
1da177e4c
|
213 |
} |
0f4dafc05
|
214 |
|
9e7bbccd0
|
215 |
static int kobject_add_internal(struct kobject *kobj) |
1da177e4c
|
216 217 |
{ int error = 0; |
e374a2bfe
|
218 |
struct kobject *parent; |
1da177e4c
|
219 |
|
0f4dafc05
|
220 |
if (!kobj) |
1da177e4c
|
221 |
return -ENOENT; |
0f4dafc05
|
222 |
|
af5ca3f4e
|
223 |
if (!kobj->name || !kobj->name[0]) { |
82d1f1178
|
224 225 226 227 |
WARN(1, "kobject: (%p): attempted to be registered with empty name! ", kobj); |
c171fef5c
|
228 229 |
return -EINVAL; } |
1da177e4c
|
230 |
|
0f4dafc05
|
231 |
parent = kobject_get(kobj->parent); |
1da177e4c
|
232 |
|
0f4dafc05
|
233 |
/* join kset if set, use it as parent if we do not already have one */ |
1da177e4c
|
234 |
if (kobj->kset) { |
0f4dafc05
|
235 |
if (!parent) |
1da177e4c
|
236 |
parent = kobject_get(&kobj->kset->kobj); |
0f4dafc05
|
237 |
kobj_kset_join(kobj); |
460f7e9a1
|
238 |
kobj->parent = parent; |
1da177e4c
|
239 |
} |
1da177e4c
|
240 |
|
0f4dafc05
|
241 242 |
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s' ", |
810304db7
|
243 |
kobject_name(kobj), kobj, __func__, |
0f4dafc05
|
244 |
parent ? kobject_name(parent) : "<NULL>", |
e374a2bfe
|
245 |
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); |
0f4dafc05
|
246 |
|
90bc61359
|
247 |
error = create_dir(kobj); |
1da177e4c
|
248 |
if (error) { |
0f4dafc05
|
249 250 251 |
kobj_kset_leave(kobj); kobject_put(parent); kobj->parent = NULL; |
dcd0da002
|
252 253 254 |
/* be noisy on error issues */ if (error == -EEXIST) |
3e14c6abb
|
255 256 257 |
pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory. ", __func__, kobject_name(kobj)); |
dcd0da002
|
258 |
else |
3e14c6abb
|
259 260 261 262 |
pr_err("%s failed for %s (error: %d parent: %s) ", __func__, kobject_name(kobj), error, parent ? kobject_name(parent) : "'none'"); |
0f4dafc05
|
263 264 |
} else kobj->state_in_sysfs = 1; |
1da177e4c
|
265 266 267 |
return error; } |
b592fcfe7
|
268 |
/** |
ed856349d
|
269 |
* kobject_set_name_vargs() - Set the name of a kobject. |
663a47430
|
270 271 272 273 |
* @kobj: struct kobject to set the name of * @fmt: format string used to build the name * @vargs: vargs to format the string. */ |
1fa5ae857
|
274 |
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, |
663a47430
|
275 276 |
va_list vargs) { |
f773f32d7
|
277 |
const char *s; |
663a47430
|
278 |
|
8a577ffc7
|
279 280 |
if (kobj->name && !fmt) return 0; |
f773f32d7
|
281 |
s = kvasprintf_const(GFP_KERNEL, fmt, vargs); |
2abf114fc
|
282 |
if (!s) |
a4ca66174
|
283 |
return -ENOMEM; |
663a47430
|
284 |
|
f773f32d7
|
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
/* * ewww... some of these buggers have '/' in the name ... If * that's the case, we need to make sure we have an actual * allocated copy to modify, since kvasprintf_const may have * returned something from .rodata. */ if (strchr(s, '/')) { char *t; t = kstrdup(s, GFP_KERNEL); kfree_const(s); if (!t) return -ENOMEM; strreplace(t, '/', '!'); s = t; } kfree_const(kobj->name); |
2abf114fc
|
302 |
kobj->name = s; |
9f255651f
|
303 |
|
663a47430
|
304 305 |
return 0; } |
1da177e4c
|
306 307 |
/** |
ed856349d
|
308 |
* kobject_set_name() - Set the name of a kobject. |
663a47430
|
309 |
* @kobj: struct kobject to set the name of |
8c4606b1a
|
310 |
* @fmt: format string used to build the name |
1da177e4c
|
311 |
* |
8c4606b1a
|
312 313 314 |
* 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. |
1da177e4c
|
315 |
*/ |
663a47430
|
316 |
int kobject_set_name(struct kobject *kobj, const char *fmt, ...) |
1da177e4c
|
317 |
{ |
a4ca66174
|
318 |
va_list vargs; |
663a47430
|
319 |
int retval; |
1da177e4c
|
320 |
|
a4ca66174
|
321 322 323 |
va_start(vargs, fmt); retval = kobject_set_name_vargs(kobj, fmt, vargs); va_end(vargs); |
1da177e4c
|
324 |
|
663a47430
|
325 |
return retval; |
1da177e4c
|
326 |
} |
1da177e4c
|
327 |
EXPORT_SYMBOL(kobject_set_name); |
1da177e4c
|
328 |
/** |
ed856349d
|
329 |
* kobject_init() - Initialize a kobject structure. |
e86000d04
|
330 331 332 333 334 335 336 337 338 339 |
* @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. */ |
f9cb074bf
|
340 |
void kobject_init(struct kobject *kobj, struct kobj_type *ktype) |
e86000d04
|
341 342 343 344 345 346 347 348 349 350 351 352 |
{ char *err_str; if (!kobj) { err_str = "invalid kobject pointer!"; goto error; } if (!ktype) { err_str = "must have a ktype to be initialized properly! "; goto error; } |
0f4dafc05
|
353 |
if (kobj->state_initialized) { |
e86000d04
|
354 |
/* do not error out as sometimes we can recover */ |
82d1f1178
|
355 356 357 |
pr_err("kobject (%p): tried to init an initialized object, something is seriously wrong. ", kobj); |
e86000d04
|
358 359 |
dump_stack(); } |
a4573c488
|
360 |
kobject_init_internal(kobj); |
e86000d04
|
361 362 363 364 |
kobj->ktype = ktype; return; error: |
82d1f1178
|
365 366 |
pr_err("kobject (%p): %s ", kobj, err_str); |
e86000d04
|
367 368 |
dump_stack(); } |
f9cb074bf
|
369 |
EXPORT_SYMBOL(kobject_init); |
e86000d04
|
370 |
|
8db148606
|
371 372 373 |
static __printf(3, 0) int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs) |
244f6cee9
|
374 |
{ |
244f6cee9
|
375 |
int retval; |
a4ca66174
|
376 |
retval = kobject_set_name_vargs(kobj, fmt, vargs); |
244f6cee9
|
377 |
if (retval) { |
82d1f1178
|
378 379 |
pr_err("kobject: can not set name properly! "); |
244f6cee9
|
380 381 382 |
return retval; } kobj->parent = parent; |
9e7bbccd0
|
383 |
return kobject_add_internal(kobj); |
244f6cee9
|
384 385 386 |
} /** |
ed856349d
|
387 |
* kobject_add() - The main kobject add function. |
244f6cee9
|
388 389 390 391 392 393 394 395 396 |
* @kobj: the kobject to add * @parent: pointer to the parent of the kobject. * @fmt: format to name the kobject with. * * The kobject name is set and added to the kobject hierarchy in this * function. * * If @parent is set, then the parent of the @kobj will be set to it. * If @parent is NULL, then the parent of the @kobj will be set to the |
9705710e4
|
397 |
* kobject associated with the kset assigned to this kobject. If no kset |
244f6cee9
|
398 399 400 |
* is assigned to the kobject, then the kobject will be located in the * root of the sysfs tree. * |
0f4dafc05
|
401 |
* Note, no "add" uevent will be created with this call, the caller should set |
244f6cee9
|
402 403 404 |
* up all of the necessary sysfs files for the object and then call * kobject_uevent() with the UEVENT_ADD parameter to ensure that * userspace is properly notified of this kobject's creation. |
92067f843
|
405 406 407 408 409 410 411 |
* * Return: If this function returns an error, kobject_put() must be * called to properly clean up the memory associated with the * object. Under no instance should the kobject that is passed * to this function be directly freed with a call to kfree(), * that can leak memory. * |
70e16a620
|
412 413 414 415 416 417 |
* If this function returns success, kobject_put() must also be called * in order to properly clean up the memory associated with the object. * * In short, once this function is called, kobject_put() MUST be called * when the use of the object is finished in order to properly free * everything. |
244f6cee9
|
418 |
*/ |
b2d6db587
|
419 420 |
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) |
244f6cee9
|
421 422 423 424 425 426 |
{ va_list args; int retval; if (!kobj) return -EINVAL; |
0f4dafc05
|
427 |
if (!kobj->state_initialized) { |
82d1f1178
|
428 429 |
pr_err("kobject '%s' (%p): tried to add an uninitialized object, something is seriously wrong. ", |
0f4dafc05
|
430 431 432 433 |
kobject_name(kobj), kobj); dump_stack(); return -EINVAL; } |
244f6cee9
|
434 435 436 437 438 439 |
va_start(args, fmt); retval = kobject_add_varg(kobj, parent, fmt, args); va_end(args); return retval; } |
b2d6db587
|
440 |
EXPORT_SYMBOL(kobject_add); |
244f6cee9
|
441 |
|
e86000d04
|
442 |
/** |
ed856349d
|
443 444 |
* kobject_init_and_add() - Initialize a kobject structure and add it to * the kobject hierarchy. |
c11c4154e
|
445 446 447 448 449 |
* @kobj: pointer to the kobject to initialize * @ktype: pointer to the ktype for this kobject. * @parent: pointer to the parent of this kobject. * @fmt: the name of the kobject. * |
1fd7c3b43
|
450 451 452 453 454 455 |
* This function combines the call to kobject_init() and kobject_add(). * * If this function returns an error, kobject_put() must be called to * properly clean up the memory associated with the object. This is the * same type of error handling after a call to kobject_add() and kobject * lifetime rules are the same here. |
c11c4154e
|
456 457 458 459 460 461 |
*/ int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) { va_list args; int retval; |
f9cb074bf
|
462 |
kobject_init(kobj, ktype); |
c11c4154e
|
463 464 465 466 467 468 469 470 471 472 |
va_start(args, fmt); retval = kobject_add_varg(kobj, parent, fmt, args); va_end(args); return retval; } EXPORT_SYMBOL_GPL(kobject_init_and_add); /** |
ed856349d
|
473 |
* kobject_rename() - Change the name of an object. |
e374a2bfe
|
474 475 |
* @kobj: object in question. * @new_name: object's new name |
030c1d2bf
|
476 477 478 479 480 |
* * It is the responsibility of the caller to provide mutual * exclusion between two different calls of kobject_rename * on the same kobject and to ensure that new_name is valid and * won't conflict with other kobjects. |
1da177e4c
|
481 |
*/ |
e374a2bfe
|
482 |
int kobject_rename(struct kobject *kobj, const char *new_name) |
1da177e4c
|
483 484 |
{ int error = 0; |
ca2f37dbc
|
485 |
const char *devpath = NULL; |
0b4a4fea2
|
486 |
const char *dup_name = NULL, *name; |
ca2f37dbc
|
487 488 |
char *devpath_string = NULL; char *envp[2]; |
1da177e4c
|
489 490 491 492 |
kobj = kobject_get(kobj); if (!kobj) return -EINVAL; |
122f8ec7b
|
493 494 |
if (!kobj->parent) { kobject_put(kobj); |
b592fcfe7
|
495 |
return -EINVAL; |
122f8ec7b
|
496 |
} |
ca2f37dbc
|
497 498 499 500 501 502 503 504 505 506 507 508 509 510 |
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; |
ca2f37dbc
|
511 |
|
f773f32d7
|
512 |
name = dup_name = kstrdup_const(new_name, GFP_KERNEL); |
0b4a4fea2
|
513 514 515 516 |
if (!name) { error = -ENOMEM; goto out; } |
e34ff4906
|
517 |
error = sysfs_rename_dir_ns(kobj, new_name, kobject_namespace(kobj)); |
0b4a4fea2
|
518 519 520 521 522 523 |
if (error) goto out; /* Install the new kobject name */ dup_name = kobj->name; kobj->name = name; |
ca2f37dbc
|
524 525 526 527 |
/* 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. */ |
0b4a4fea2
|
528 |
kobject_uevent_env(kobj, KOBJ_MOVE, envp); |
ca2f37dbc
|
529 530 |
out: |
f773f32d7
|
531 |
kfree_const(dup_name); |
ca2f37dbc
|
532 533 |
kfree(devpath_string); kfree(devpath); |
b592fcfe7
|
534 535 536 537 |
kobject_put(kobj); return error; } |
8344b568f
|
538 |
EXPORT_SYMBOL_GPL(kobject_rename); |
b592fcfe7
|
539 540 |
/** |
ed856349d
|
541 |
* kobject_move() - Move object to another parent. |
e374a2bfe
|
542 543 |
* @kobj: object in question. * @new_parent: object's new parent (can be NULL) |
8a82472f8
|
544 |
*/ |
8a82472f8
|
545 546 547 548 549 550 551 552 553 554 555 556 557 |
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) { |
c744aeae9
|
558 559 |
if (kobj->kset) new_parent = kobject_get(&kobj->kset->kobj); |
8a82472f8
|
560 |
} |
e34ff4906
|
561 |
|
8a82472f8
|
562 563 564 565 566 567 568 569 570 571 572 573 574 575 |
/* 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; |
e34ff4906
|
576 |
error = sysfs_move_dir_ns(kobj, new_parent, kobject_namespace(kobj)); |
8a82472f8
|
577 578 579 580 |
if (error) goto out; old_parent = kobj->parent; kobj->parent = new_parent; |
9e993efb0
|
581 |
new_parent = NULL; |
8a82472f8
|
582 583 584 |
kobject_put(old_parent); kobject_uevent_env(kobj, KOBJ_MOVE, envp); out: |
9e993efb0
|
585 |
kobject_put(new_parent); |
8a82472f8
|
586 587 588 589 590 |
kobject_put(kobj); kfree(devpath_string); kfree(devpath); return error; } |
24199d206
|
591 |
EXPORT_SYMBOL_GPL(kobject_move); |
8a82472f8
|
592 593 |
/** |
ed856349d
|
594 |
* kobject_del() - Unlink kobject from hierarchy. |
e374a2bfe
|
595 |
* @kobj: object. |
92067f843
|
596 597 598 |
* * This is the function that should be called to delete an object * successfully added via kobject_add(). |
1da177e4c
|
599 |
*/ |
e374a2bfe
|
600 |
void kobject_del(struct kobject *kobj) |
1da177e4c
|
601 |
{ |
324a56e16
|
602 |
struct kernfs_node *sd; |
3d378dc71
|
603 |
const struct kobj_type *ktype; |
26ea12dec
|
604 |
|
31b9025aa
|
605 606 |
if (!kobj) return; |
0f4dafc05
|
607 |
|
26ea12dec
|
608 |
sd = kobj->sd; |
3d378dc71
|
609 |
ktype = get_ktype(kobj); |
aa30f47cf
|
610 611 612 |
if (ktype) sysfs_remove_groups(kobj, ktype->default_groups); |
1da177e4c
|
613 |
sysfs_remove_dir(kobj); |
26ea12dec
|
614 |
sysfs_put(sd); |
0f4dafc05
|
615 616 617 618 |
kobj->state_in_sysfs = 0; kobj_kset_leave(kobj); kobject_put(kobj->parent); kobj->parent = NULL; |
1da177e4c
|
619 |
} |
fa40ae344
|
620 |
EXPORT_SYMBOL(kobject_del); |
1da177e4c
|
621 622 |
/** |
ed856349d
|
623 |
* kobject_get() - Increment refcount for object. |
e374a2bfe
|
624 |
* @kobj: object. |
1da177e4c
|
625 |
*/ |
e374a2bfe
|
626 |
struct kobject *kobject_get(struct kobject *kobj) |
1da177e4c
|
627 |
{ |
d82d54af7
|
628 629 |
if (kobj) { if (!kobj->state_initialized) |
82d1f1178
|
630 631 632 633 |
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not initialized, yet kobject_get() is being called. ", kobject_name(kobj), kobj); |
1da177e4c
|
634 |
kref_get(&kobj->kref); |
d82d54af7
|
635 |
} |
1da177e4c
|
636 637 |
return kobj; } |
fa40ae344
|
638 |
EXPORT_SYMBOL(kobject_get); |
1da177e4c
|
639 |
|
c70c176ff
|
640 |
struct kobject * __must_check kobject_get_unless_zero(struct kobject *kobj) |
a49b7e82c
|
641 |
{ |
c70c176ff
|
642 643 |
if (!kobj) return NULL; |
a49b7e82c
|
644 645 646 647 |
if (!kref_get_unless_zero(&kobj->kref)) kobj = NULL; return kobj; } |
c70c176ff
|
648 |
EXPORT_SYMBOL(kobject_get_unless_zero); |
a49b7e82c
|
649 |
|
18041f477
|
650 651 652 |
/* * kobject_cleanup - free kobject resources. * @kobj: object to cleanup |
1da177e4c
|
653 |
*/ |
18041f477
|
654 |
static void kobject_cleanup(struct kobject *kobj) |
1da177e4c
|
655 |
{ |
0f4dafc05
|
656 |
struct kobj_type *t = get_ktype(kobj); |
af5ca3f4e
|
657 |
const char *name = kobj->name; |
1da177e4c
|
658 |
|
c817a67ec
|
659 660 661 |
pr_debug("kobject: '%s' (%p): %s, parent %p ", kobject_name(kobj), kobj, __func__, kobj->parent); |
0f4dafc05
|
662 663 |
if (t && !t->release) |
186bddb28
|
664 665 |
pr_debug("kobject: '%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/kobject.txt. ", |
0f4dafc05
|
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 |
kobject_name(kobj), kobj); /* send "remove" if the caller did not do it but sent "add" */ if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) { pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event ", kobject_name(kobj), kobj); kobject_uevent(kobj, KOBJ_REMOVE); } /* remove from sysfs if the caller did not do it */ if (kobj->state_in_sysfs) { pr_debug("kobject: '%s' (%p): auto cleanup kobject_del ", kobject_name(kobj), kobj); kobject_del(kobj); } |
ce2c9cb02
|
683 |
if (t && t->release) { |
0f4dafc05
|
684 685 686 |
pr_debug("kobject: '%s' (%p): calling ktype release ", kobject_name(kobj), kobj); |
1da177e4c
|
687 |
t->release(kobj); |
0f4dafc05
|
688 689 690 |
} /* free name if we allocated it */ |
af5ca3f4e
|
691 |
if (name) { |
0f4dafc05
|
692 693 |
pr_debug("kobject: '%s': free name ", name); |
f773f32d7
|
694 |
kfree_const(name); |
ce2c9cb02
|
695 |
} |
1da177e4c
|
696 |
} |
c817a67ec
|
697 698 699 700 701 702 703 |
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE static void kobject_delayed_cleanup(struct work_struct *work) { kobject_cleanup(container_of(to_delayed_work(work), struct kobject, release)); } #endif |
1da177e4c
|
704 705 |
static void kobject_release(struct kref *kref) { |
c817a67ec
|
706 707 |
struct kobject *kobj = container_of(kref, struct kobject, kref); #ifdef CONFIG_DEBUG_KOBJECT_RELEASE |
89c86a64c
|
708 709 710 711 |
unsigned long delay = HZ + HZ * (get_random_int() & 0x3); pr_info("kobject: '%s' (%p): %s, parent %p (delayed %ld) ", kobject_name(kobj), kobj, __func__, kobj->parent, delay); |
c817a67ec
|
712 |
INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup); |
89c86a64c
|
713 714 |
schedule_delayed_work(&kobj->release, delay); |
c817a67ec
|
715 716 717 |
#else kobject_cleanup(kobj); #endif |
1da177e4c
|
718 719 720 |
} /** |
ed856349d
|
721 |
* kobject_put() - Decrement refcount for object. |
e374a2bfe
|
722 |
* @kobj: object. |
1da177e4c
|
723 |
* |
e374a2bfe
|
724 |
* Decrement the refcount, and if 0, call kobject_cleanup(). |
1da177e4c
|
725 |
*/ |
e374a2bfe
|
726 |
void kobject_put(struct kobject *kobj) |
1da177e4c
|
727 |
{ |
c1ebdae51
|
728 |
if (kobj) { |
d955c78ac
|
729 |
if (!kobj->state_initialized) |
82d1f1178
|
730 731 732 733 |
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not initialized, yet kobject_put() is being called. ", kobject_name(kobj), kobj); |
1da177e4c
|
734 |
kref_put(&kobj->kref, kobject_release); |
c1ebdae51
|
735 |
} |
1da177e4c
|
736 |
} |
fa40ae344
|
737 |
EXPORT_SYMBOL(kobject_put); |
1da177e4c
|
738 |
|
3f9e3ee9d
|
739 |
static void dynamic_kobj_release(struct kobject *kobj) |
7423172a5
|
740 |
{ |
810304db7
|
741 742 |
pr_debug("kobject: (%p): %s ", kobj, __func__); |
7423172a5
|
743 744 |
kfree(kobj); } |
3f9e3ee9d
|
745 |
static struct kobj_type dynamic_kobj_ktype = { |
386f275f5
|
746 747 |
.release = dynamic_kobj_release, .sysfs_ops = &kobj_sysfs_ops, |
7423172a5
|
748 |
}; |
43968d2f1
|
749 |
/** |
ed856349d
|
750 |
* kobject_create() - Create a struct kobject dynamically. |
3f9e3ee9d
|
751 752 753 754 755 |
* * This function creates a kobject structure dynamically and sets it up * to be a "dynamic" kobject with a default release function set up. * * If the kobject was not able to be created, NULL will be returned. |
43968d2f1
|
756 |
* The kobject structure returned from here must be cleaned up with a |
f9cb074bf
|
757 |
* call to kobject_put() and not kfree(), as kobject_init() has |
43968d2f1
|
758 |
* already been called on this structure. |
3f9e3ee9d
|
759 |
*/ |
43968d2f1
|
760 |
struct kobject *kobject_create(void) |
3f9e3ee9d
|
761 762 763 764 765 766 |
{ struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); if (!kobj) return NULL; |
f9cb074bf
|
767 |
kobject_init(kobj, &dynamic_kobj_ktype); |
3f9e3ee9d
|
768 769 770 771 |
return kobj; } /** |
ed856349d
|
772 773 |
* kobject_create_and_add() - Create a struct kobject dynamically and * register it with sysfs. |
9ff1f838e
|
774 |
* @name: the name for the kobject |
3f9e3ee9d
|
775 776 |
* @parent: the parent kobject of this kobject, if any. * |
f70701a34
|
777 |
* This function creates a kobject structure dynamically and registers it |
3f9e3ee9d
|
778 |
* with sysfs. When you are finished with this structure, call |
78a2d906b
|
779 |
* kobject_put() and the structure will be dynamically freed when |
3f9e3ee9d
|
780 781 782 783 784 785 786 787 788 789 790 791 |
* it is no longer being used. * * If the kobject was not able to be created, NULL will be returned. */ struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) { struct kobject *kobj; int retval; kobj = kobject_create(); if (!kobj) return NULL; |
b2d6db587
|
792 |
retval = kobject_add(kobj, parent, "%s", name); |
3f9e3ee9d
|
793 |
if (retval) { |
82d1f1178
|
794 795 |
pr_warn("%s: kobject_add error: %d ", __func__, retval); |
3f9e3ee9d
|
796 797 798 799 800 801 |
kobject_put(kobj); kobj = NULL; } return kobj; } EXPORT_SYMBOL_GPL(kobject_create_and_add); |
7423172a5
|
802 |
/** |
ed856349d
|
803 |
* kset_init() - Initialize a kset for use. |
e374a2bfe
|
804 |
* @k: kset |
1da177e4c
|
805 |
*/ |
e374a2bfe
|
806 |
void kset_init(struct kset *k) |
1da177e4c
|
807 |
{ |
e1543ddf7
|
808 |
kobject_init_internal(&k->kobj); |
1da177e4c
|
809 810 811 |
INIT_LIST_HEAD(&k->list); spin_lock_init(&k->list_lock); } |
23b5212cc
|
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 |
/* default kobject attribute operations */ static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct kobj_attribute *kattr; ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr); if (kattr->show) ret = kattr->show(kobj, kattr, buf); return ret; } static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct kobj_attribute *kattr; ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr); if (kattr->store) ret = kattr->store(kobj, kattr, buf, count); return ret; } |
52cf25d0a
|
836 |
const struct sysfs_ops kobj_sysfs_ops = { |
23b5212cc
|
837 838 839 |
.show = kobj_attr_show, .store = kobj_attr_store, }; |
29dfe2dc0
|
840 |
EXPORT_SYMBOL_GPL(kobj_sysfs_ops); |
1da177e4c
|
841 842 |
/** |
ed856349d
|
843 |
* kset_register() - Initialize and add a kset. |
e374a2bfe
|
844 |
* @k: kset. |
1da177e4c
|
845 |
*/ |
e374a2bfe
|
846 |
int kset_register(struct kset *k) |
1da177e4c
|
847 |
{ |
80f03e349
|
848 |
int err; |
31b9025aa
|
849 850 |
if (!k) return -EINVAL; |
80f03e349
|
851 |
|
1da177e4c
|
852 |
kset_init(k); |
12e339ac6
|
853 |
err = kobject_add_internal(&k->kobj); |
80f03e349
|
854 855 856 857 |
if (err) return err; kobject_uevent(&k->kobj, KOBJ_ADD); return 0; |
1da177e4c
|
858 |
} |
fa40ae344
|
859 |
EXPORT_SYMBOL(kset_register); |
1da177e4c
|
860 |
|
1da177e4c
|
861 |
/** |
ed856349d
|
862 |
* kset_unregister() - Remove a kset. |
e374a2bfe
|
863 |
* @k: kset. |
1da177e4c
|
864 |
*/ |
e374a2bfe
|
865 |
void kset_unregister(struct kset *k) |
1da177e4c
|
866 |
{ |
31b9025aa
|
867 868 |
if (!k) return; |
35a5fe695
|
869 |
kobject_del(&k->kobj); |
78a2d906b
|
870 |
kobject_put(&k->kobj); |
1da177e4c
|
871 |
} |
fa40ae344
|
872 |
EXPORT_SYMBOL(kset_unregister); |
1da177e4c
|
873 |
|
1da177e4c
|
874 |
/** |
ed856349d
|
875 |
* kset_find_obj() - Search for object in kset. |
e374a2bfe
|
876 877 |
* @kset: kset we're looking in. * @name: object's name. |
1da177e4c
|
878 |
* |
e374a2bfe
|
879 880 881 |
* 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. |
1da177e4c
|
882 |
*/ |
e374a2bfe
|
883 |
struct kobject *kset_find_obj(struct kset *kset, const char *name) |
1da177e4c
|
884 |
{ |
c6a2a3dc2
|
885 |
struct kobject *k; |
e374a2bfe
|
886 |
struct kobject *ret = NULL; |
1da177e4c
|
887 888 |
spin_lock(&kset->list_lock); |
c25d1dfbd
|
889 |
|
c6a2a3dc2
|
890 |
list_for_each_entry(k, &kset->list, entry) { |
e374a2bfe
|
891 |
if (kobject_name(k) && !strcmp(kobject_name(k), name)) { |
a49b7e82c
|
892 |
ret = kobject_get_unless_zero(k); |
1da177e4c
|
893 894 895 |
break; } } |
c25d1dfbd
|
896 |
|
1da177e4c
|
897 898 899 |
spin_unlock(&kset->list_lock); return ret; } |
2fe829aca
|
900 |
EXPORT_SYMBOL_GPL(kset_find_obj); |
1da177e4c
|
901 |
|
b727c7028
|
902 903 904 |
static void kset_release(struct kobject *kobj) { struct kset *kset = container_of(kobj, struct kset, kobj); |
9f66fa2a4
|
905 906 |
pr_debug("kobject: '%s' (%p): %s ", |
810304db7
|
907 |
kobject_name(kobj), kobj, __func__); |
b727c7028
|
908 909 |
kfree(kset); } |
7ab35a14d
|
910 |
static void kset_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid) |
d028b6f70
|
911 912 913 914 |
{ if (kobj->parent) kobject_get_ownership(kobj->parent, uid, gid); } |
386f275f5
|
915 916 |
static struct kobj_type kset_ktype = { .sysfs_ops = &kobj_sysfs_ops, |
d028b6f70
|
917 918 |
.release = kset_release, .get_ownership = kset_get_ownership, |
b727c7028
|
919 920 921 |
}; /** |
ed856349d
|
922 |
* kset_create() - Create a struct kset dynamically. |
b727c7028
|
923 924 925 926 927 928 929 930 931 932 933 934 935 936 |
* * @name: the name for the kset * @uevent_ops: a struct kset_uevent_ops for the kset * @parent_kobj: the parent kobject of this kset, if any. * * This function creates a kset structure dynamically. This structure can * then be registered with the system and show up in sysfs with a call to * kset_register(). When you are finished with this structure, if * kset_register() has been called, call kset_unregister() and the * structure will be dynamically freed when it is no longer being used. * * If the kset was not able to be created, NULL will be returned. */ static struct kset *kset_create(const char *name, |
9cd43611c
|
937 |
const struct kset_uevent_ops *uevent_ops, |
b727c7028
|
938 939 940 |
struct kobject *parent_kobj) { struct kset *kset; |
d9cd8f378
|
941 |
int retval; |
b727c7028
|
942 943 944 945 |
kset = kzalloc(sizeof(*kset), GFP_KERNEL); if (!kset) return NULL; |
b7165ebbf
|
946 |
retval = kobject_set_name(&kset->kobj, "%s", name); |
d9cd8f378
|
947 948 949 950 |
if (retval) { kfree(kset); return NULL; } |
b727c7028
|
951 952 953 954 |
kset->uevent_ops = uevent_ops; kset->kobj.parent = parent_kobj; /* |
386f275f5
|
955 |
* The kobject of this kset will have a type of kset_ktype and belong to |
b727c7028
|
956 957 958 |
* no kset itself. That way we can properly free it when it is * finished being used. */ |
386f275f5
|
959 |
kset->kobj.ktype = &kset_ktype; |
b727c7028
|
960 961 962 963 964 965 |
kset->kobj.kset = NULL; return kset; } /** |
ed856349d
|
966 |
* kset_create_and_add() - Create a struct kset dynamically and add it to sysfs. |
b727c7028
|
967 968 969 970 971 972 973 974 975 976 977 978 979 |
* * @name: the name for the kset * @uevent_ops: a struct kset_uevent_ops for the kset * @parent_kobj: the parent kobject of this kset, if any. * * This function creates a kset structure dynamically and registers it * with sysfs. When you are finished with this structure, call * kset_unregister() and the structure will be dynamically freed when it * is no longer being used. * * If the kset was not able to be created, NULL will be returned. */ struct kset *kset_create_and_add(const char *name, |
9cd43611c
|
980 |
const struct kset_uevent_ops *uevent_ops, |
b727c7028
|
981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 |
struct kobject *parent_kobj) { struct kset *kset; int error; kset = kset_create(name, uevent_ops, parent_kobj); if (!kset) return NULL; error = kset_register(kset); if (error) { kfree(kset); return NULL; } return kset; } EXPORT_SYMBOL_GPL(kset_create_and_add); |
bc451f205
|
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 |
static DEFINE_SPINLOCK(kobj_ns_type_lock); static const struct kobj_ns_type_operations *kobj_ns_ops_tbl[KOBJ_NS_TYPES]; int kobj_ns_type_register(const struct kobj_ns_type_operations *ops) { enum kobj_ns_type type = ops->type; int error; spin_lock(&kobj_ns_type_lock); error = -EINVAL; if (type >= KOBJ_NS_TYPES) goto out; error = -EINVAL; if (type <= KOBJ_NS_TYPE_NONE) goto out; error = -EBUSY; if (kobj_ns_ops_tbl[type]) goto out; error = 0; kobj_ns_ops_tbl[type] = ops; out: spin_unlock(&kobj_ns_type_lock); return error; } int kobj_ns_type_registered(enum kobj_ns_type type) { int registered = 0; spin_lock(&kobj_ns_type_lock); if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES)) registered = kobj_ns_ops_tbl[type] != NULL; spin_unlock(&kobj_ns_type_lock); return registered; } const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent) { const struct kobj_ns_type_operations *ops = NULL; |
41fb96a4b
|
1043 |
if (parent && parent->ktype && parent->ktype->child_ns_type) |
bc451f205
|
1044 1045 1046 1047 1048 1049 1050 1051 1052 |
ops = parent->ktype->child_ns_type(parent); return ops; } const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) { return kobj_child_ns_ops(kobj->parent); } |
7dc5dbc87
|
1053 1054 |
bool kobj_ns_current_may_mount(enum kobj_ns_type type) { |
730d7d339
|
1055 |
bool may_mount = true; |
7dc5dbc87
|
1056 1057 1058 1059 1060 1061 1062 1063 1064 |
spin_lock(&kobj_ns_type_lock); if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && kobj_ns_ops_tbl[type]) may_mount = kobj_ns_ops_tbl[type]->current_may_mount(); spin_unlock(&kobj_ns_type_lock); return may_mount; } |
bc451f205
|
1065 |
|
a685e0898
|
1066 |
void *kobj_ns_grab_current(enum kobj_ns_type type) |
bc451f205
|
1067 |
{ |
a685e0898
|
1068 |
void *ns = NULL; |
bc451f205
|
1069 1070 1071 1072 |
spin_lock(&kobj_ns_type_lock); if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && kobj_ns_ops_tbl[type]) |
a685e0898
|
1073 |
ns = kobj_ns_ops_tbl[type]->grab_current_ns(); |
bc451f205
|
1074 1075 1076 1077 |
spin_unlock(&kobj_ns_type_lock); return ns; } |
172856eac
|
1078 |
EXPORT_SYMBOL_GPL(kobj_ns_grab_current); |
bc451f205
|
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 |
const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk) { const void *ns = NULL; spin_lock(&kobj_ns_type_lock); if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && kobj_ns_ops_tbl[type]) ns = kobj_ns_ops_tbl[type]->netlink_ns(sk); spin_unlock(&kobj_ns_type_lock); return ns; } const void *kobj_ns_initial(enum kobj_ns_type type) { const void *ns = NULL; spin_lock(&kobj_ns_type_lock); if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && kobj_ns_ops_tbl[type]) ns = kobj_ns_ops_tbl[type]->initial_ns(); spin_unlock(&kobj_ns_type_lock); return ns; } |
a685e0898
|
1105 |
void kobj_ns_drop(enum kobj_ns_type type, void *ns) |
bc451f205
|
1106 |
{ |
a685e0898
|
1107 1108 1109 1110 1111 |
spin_lock(&kobj_ns_type_lock); if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && kobj_ns_ops_tbl[type] && kobj_ns_ops_tbl[type]->drop_ns) kobj_ns_ops_tbl[type]->drop_ns(ns); spin_unlock(&kobj_ns_type_lock); |
bc451f205
|
1112 |
} |
172856eac
|
1113 |
EXPORT_SYMBOL_GPL(kobj_ns_drop); |