aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sysfs')
-rw-r--r--fs/sysfs/Kconfig1
-rw-r--r--fs/sysfs/dir.c44
-rw-r--r--fs/sysfs/file.c115
-rw-r--r--fs/sysfs/group.c7
-rw-r--r--fs/sysfs/mount.c2
5 files changed, 43 insertions, 126 deletions
diff --git a/fs/sysfs/Kconfig b/fs/sysfs/Kconfig
index 8c41feacbac5..b2756014508c 100644
--- a/fs/sysfs/Kconfig
+++ b/fs/sysfs/Kconfig
@@ -1,6 +1,7 @@
1config SYSFS 1config SYSFS
2 bool "sysfs file system support" if EXPERT 2 bool "sysfs file system support" if EXPERT
3 default y 3 default y
4 select KERNFS
4 help 5 help
5 The sysfs filesystem is a virtual filesystem that the kernel uses to 6 The sysfs filesystem is a virtual filesystem that the kernel uses to
6 export internal kernel objects, their attributes, and their 7 export internal kernel objects, their attributes, and their
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index ee0d761c3179..0b45ff42f374 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -19,39 +19,18 @@
19 19
20DEFINE_SPINLOCK(sysfs_symlink_target_lock); 20DEFINE_SPINLOCK(sysfs_symlink_target_lock);
21 21
22/**
23 * sysfs_pathname - return full path to sysfs dirent
24 * @kn: kernfs_node whose path we want
25 * @path: caller allocated buffer of size PATH_MAX
26 *
27 * Gives the name "/" to the sysfs_root entry; any path returned
28 * is relative to wherever sysfs is mounted.
29 */
30static char *sysfs_pathname(struct kernfs_node *kn, char *path)
31{
32 if (kn->parent) {
33 sysfs_pathname(kn->parent, path);
34 strlcat(path, "/", PATH_MAX);
35 }
36 strlcat(path, kn->name, PATH_MAX);
37 return path;
38}
39
40void sysfs_warn_dup(struct kernfs_node *parent, const char *name) 22void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
41{ 23{
42 char *path; 24 char *buf, *path = NULL;
43 25
44 path = kzalloc(PATH_MAX, GFP_KERNEL); 26 buf = kzalloc(PATH_MAX, GFP_KERNEL);
45 if (path) { 27 if (buf)
46 sysfs_pathname(parent, path); 28 path = kernfs_path(parent, buf, PATH_MAX);
47 strlcat(path, "/", PATH_MAX);
48 strlcat(path, name, PATH_MAX);
49 }
50 29
51 WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s'\n", 30 WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n",
52 path ? path : name); 31 path, name);
53 32
54 kfree(path); 33 kfree(buf);
55} 34}
56 35
57/** 36/**
@@ -122,9 +101,13 @@ void sysfs_remove_dir(struct kobject *kobj)
122int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, 101int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
123 const void *new_ns) 102 const void *new_ns)
124{ 103{
125 struct kernfs_node *parent = kobj->sd->parent; 104 struct kernfs_node *parent;
105 int ret;
126 106
127 return kernfs_rename_ns(kobj->sd, parent, new_name, new_ns); 107 parent = kernfs_get_parent(kobj->sd);
108 ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
109 kernfs_put(parent);
110 return ret;
128} 111}
129 112
130int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, 113int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
@@ -133,7 +116,6 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
133 struct kernfs_node *kn = kobj->sd; 116 struct kernfs_node *kn = kobj->sd;
134 struct kernfs_node *new_parent; 117 struct kernfs_node *new_parent;
135 118
136 BUG_ON(!kn->parent);
137 new_parent = new_parent_kobj && new_parent_kobj->sd ? 119 new_parent = new_parent_kobj && new_parent_kobj->sd ?
138 new_parent_kobj->sd : sysfs_root_kn; 120 new_parent_kobj->sd : sysfs_root_kn;
139 121
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 810cf6e613e5..28cc1acd5439 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -372,6 +372,29 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
372} 372}
373EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); 373EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
374 374
375/**
376 * sysfs_remove_file_self - remove an object attribute from its own method
377 * @kobj: object we're acting for
378 * @attr: attribute descriptor
379 *
380 * See kernfs_remove_self() for details.
381 */
382bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr)
383{
384 struct kernfs_node *parent = kobj->sd;
385 struct kernfs_node *kn;
386 bool ret;
387
388 kn = kernfs_find_and_get(parent, attr->name);
389 if (WARN_ON_ONCE(!kn))
390 return false;
391
392 ret = kernfs_remove_self(kn);
393
394 kernfs_put(kn);
395 return ret;
396}
397
375void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr) 398void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr)
376{ 399{
377 int i; 400 int i;
@@ -430,95 +453,3 @@ void sysfs_remove_bin_file(struct kobject *kobj,
430 kernfs_remove_by_name(kobj->sd, attr->attr.name); 453 kernfs_remove_by_name(kobj->sd, attr->attr.name);
431} 454}
432EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); 455EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
433
434struct sysfs_schedule_callback_struct {
435 struct list_head workq_list;
436 struct kobject *kobj;
437 void (*func)(void *);
438 void *data;
439 struct module *owner;
440 struct work_struct work;
441};
442
443static struct workqueue_struct *sysfs_workqueue;
444static DEFINE_MUTEX(sysfs_workq_mutex);
445static LIST_HEAD(sysfs_workq);
446static void sysfs_schedule_callback_work(struct work_struct *work)
447{
448 struct sysfs_schedule_callback_struct *ss = container_of(work,
449 struct sysfs_schedule_callback_struct, work);
450
451 (ss->func)(ss->data);
452 kobject_put(ss->kobj);
453 module_put(ss->owner);
454 mutex_lock(&sysfs_workq_mutex);
455 list_del(&ss->workq_list);
456 mutex_unlock(&sysfs_workq_mutex);
457 kfree(ss);
458}
459
460/**
461 * sysfs_schedule_callback - helper to schedule a callback for a kobject
462 * @kobj: object we're acting for.
463 * @func: callback function to invoke later.
464 * @data: argument to pass to @func.
465 * @owner: module owning the callback code
466 *
467 * sysfs attribute methods must not unregister themselves or their parent
468 * kobject (which would amount to the same thing). Attempts to do so will
469 * deadlock, since unregistration is mutually exclusive with driver
470 * callbacks.
471 *
472 * Instead methods can call this routine, which will attempt to allocate
473 * and schedule a workqueue request to call back @func with @data as its
474 * argument in the workqueue's process context. @kobj will be pinned
475 * until @func returns.
476 *
477 * Returns 0 if the request was submitted, -ENOMEM if storage could not
478 * be allocated, -ENODEV if a reference to @owner isn't available,
479 * -EAGAIN if a callback has already been scheduled for @kobj.
480 */
481int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
482 void *data, struct module *owner)
483{
484 struct sysfs_schedule_callback_struct *ss, *tmp;
485
486 if (!try_module_get(owner))
487 return -ENODEV;
488
489 mutex_lock(&sysfs_workq_mutex);
490 list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list)
491 if (ss->kobj == kobj) {
492 module_put(owner);
493 mutex_unlock(&sysfs_workq_mutex);
494 return -EAGAIN;
495 }
496 mutex_unlock(&sysfs_workq_mutex);
497
498 if (sysfs_workqueue == NULL) {
499 sysfs_workqueue = create_singlethread_workqueue("sysfsd");
500 if (sysfs_workqueue == NULL) {
501 module_put(owner);
502 return -ENOMEM;
503 }
504 }
505
506 ss = kmalloc(sizeof(*ss), GFP_KERNEL);
507 if (!ss) {
508 module_put(owner);
509 return -ENOMEM;
510 }
511 kobject_get(kobj);
512 ss->kobj = kobj;
513 ss->func = func;
514 ss->data = data;
515 ss->owner = owner;
516 INIT_WORK(&ss->work, sysfs_schedule_callback_work);
517 INIT_LIST_HEAD(&ss->workq_list);
518 mutex_lock(&sysfs_workq_mutex);
519 list_add_tail(&ss->workq_list, &sysfs_workq);
520 mutex_unlock(&sysfs_workq_mutex);
521 queue_work(sysfs_workqueue, &ss->work);
522 return 0;
523}
524EXPORT_SYMBOL_GPL(sysfs_schedule_callback);
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index 6b579387c67a..aa0406895b53 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -70,8 +70,11 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
70 if (grp->bin_attrs) { 70 if (grp->bin_attrs) {
71 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { 71 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
72 if (update) 72 if (update)
73 sysfs_remove_bin_file(kobj, *bin_attr); 73 kernfs_remove_by_name(parent,
74 error = sysfs_create_bin_file(kobj, *bin_attr); 74 (*bin_attr)->attr.name);
75 error = sysfs_add_file_mode_ns(parent,
76 &(*bin_attr)->attr, true,
77 (*bin_attr)->attr.mode, NULL);
75 if (error) 78 if (error)
76 break; 79 break;
77 } 80 }
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index 3eaf5c6622eb..a66ad6196f59 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -63,7 +63,7 @@ int __init sysfs_init(void)
63{ 63{
64 int err; 64 int err;
65 65
66 sysfs_root = kernfs_create_root(NULL, NULL); 66 sysfs_root = kernfs_create_root(NULL, 0, NULL);
67 if (IS_ERR(sysfs_root)) 67 if (IS_ERR(sysfs_root))
68 return PTR_ERR(sysfs_root); 68 return PTR_ERR(sysfs_root);
69 69