aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/core.c17
-rw-r--r--fs/kernfs/dir.c72
-rw-r--r--fs/sysfs/file.c23
-rw-r--r--include/linux/device.h2
-rw-r--r--include/linux/kernfs.h6
-rw-r--r--include/linux/sysfs.h7
6 files changed, 127 insertions, 0 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2b567177ef78..9db57afcf81f 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -571,6 +571,23 @@ void device_remove_file(struct device *dev,
571EXPORT_SYMBOL_GPL(device_remove_file); 571EXPORT_SYMBOL_GPL(device_remove_file);
572 572
573/** 573/**
574 * device_remove_file_self - remove sysfs attribute file from its own method.
575 * @dev: device.
576 * @attr: device attribute descriptor.
577 *
578 * See kernfs_remove_self() for details.
579 */
580bool device_remove_file_self(struct device *dev,
581 const struct device_attribute *attr)
582{
583 if (dev)
584 return sysfs_remove_file_self(&dev->kobj, &attr->attr);
585 else
586 return false;
587}
588EXPORT_SYMBOL_GPL(device_remove_file_self);
589
590/**
574 * device_create_bin_file - create sysfs binary attribute file for device. 591 * device_create_bin_file - create sysfs binary attribute file for device.
575 * @dev: device. 592 * @dev: device.
576 * @attr: device binary attribute descriptor. 593 * @attr: device binary attribute descriptor.
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 1aeb57969bff..a8028be6cdb7 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -986,6 +986,78 @@ void kernfs_remove(struct kernfs_node *kn)
986} 986}
987 987
988/** 988/**
989 * kernfs_remove_self - remove a kernfs_node from its own method
990 * @kn: the self kernfs_node to remove
991 *
992 * The caller must be running off of a kernfs operation which is invoked
993 * with an active reference - e.g. one of kernfs_ops. This can be used to
994 * implement a file operation which deletes itself.
995 *
996 * For example, the "delete" file for a sysfs device directory can be
997 * implemented by invoking kernfs_remove_self() on the "delete" file
998 * itself. This function breaks the circular dependency of trying to
999 * deactivate self while holding an active ref itself. It isn't necessary
1000 * to modify the usual removal path to use kernfs_remove_self(). The
1001 * "delete" implementation can simply invoke kernfs_remove_self() on self
1002 * before proceeding with the usual removal path. kernfs will ignore later
1003 * kernfs_remove() on self.
1004 *
1005 * kernfs_remove_self() can be called multiple times concurrently on the
1006 * same kernfs_node. Only the first one actually performs removal and
1007 * returns %true. All others will wait until the kernfs operation which
1008 * won self-removal finishes and return %false. Note that the losers wait
1009 * for the completion of not only the winning kernfs_remove_self() but also
1010 * the whole kernfs_ops which won the arbitration. This can be used to
1011 * guarantee, for example, all concurrent writes to a "delete" file to
1012 * finish only after the whole operation is complete.
1013 */
1014bool kernfs_remove_self(struct kernfs_node *kn)
1015{
1016 bool ret;
1017
1018 mutex_lock(&kernfs_mutex);
1019 __kernfs_deactivate_self(kn);
1020
1021 /*
1022 * SUICIDAL is used to arbitrate among competing invocations. Only
1023 * the first one will actually perform removal. When the removal
1024 * is complete, SUICIDED is set and the active ref is restored
1025 * while holding kernfs_mutex. The ones which lost arbitration
1026 * waits for SUICDED && drained which can happen only after the
1027 * enclosing kernfs operation which executed the winning instance
1028 * of kernfs_remove_self() finished.
1029 */
1030 if (!(kn->flags & KERNFS_SUICIDAL)) {
1031 kn->flags |= KERNFS_SUICIDAL;
1032 __kernfs_remove(kn);
1033 kn->flags |= KERNFS_SUICIDED;
1034 ret = true;
1035 } else {
1036 wait_queue_head_t *waitq = &kernfs_root(kn)->deactivate_waitq;
1037 DEFINE_WAIT(wait);
1038
1039 while (true) {
1040 prepare_to_wait(waitq, &wait, TASK_UNINTERRUPTIBLE);
1041
1042 if ((kn->flags & KERNFS_SUICIDED) &&
1043 atomic_read(&kn->active) == KN_DEACTIVATED_BIAS)
1044 break;
1045
1046 mutex_unlock(&kernfs_mutex);
1047 schedule();
1048 mutex_lock(&kernfs_mutex);
1049 }
1050 finish_wait(waitq, &wait);
1051 WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb));
1052 ret = false;
1053 }
1054
1055 __kernfs_reactivate_self(kn);
1056 mutex_unlock(&kernfs_mutex);
1057 return ret;
1058}
1059
1060/**
989 * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it 1061 * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it
990 * @parent: parent of the target 1062 * @parent: parent of the target
991 * @name: name of the kernfs_node to remove 1063 * @name: name of the kernfs_node to remove
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 810cf6e613e5..1b8b91b67fdb 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;
diff --git a/include/linux/device.h b/include/linux/device.h
index 952b01033c32..1ff3f1697513 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -560,6 +560,8 @@ extern int device_create_file(struct device *device,
560 const struct device_attribute *entry); 560 const struct device_attribute *entry);
561extern void device_remove_file(struct device *dev, 561extern void device_remove_file(struct device *dev,
562 const struct device_attribute *attr); 562 const struct device_attribute *attr);
563extern bool device_remove_file_self(struct device *dev,
564 const struct device_attribute *attr);
563extern int __must_check device_create_bin_file(struct device *dev, 565extern int __must_check device_create_bin_file(struct device *dev,
564 const struct bin_attribute *attr); 566 const struct bin_attribute *attr);
565extern void device_remove_bin_file(struct device *dev, 567extern void device_remove_bin_file(struct device *dev,
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index ac8693027058..0b7b7cc352eb 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -43,6 +43,8 @@ enum kernfs_node_flag {
43 KERNFS_HAS_MMAP = 0x0080, 43 KERNFS_HAS_MMAP = 0x0080,
44 KERNFS_LOCKDEP = 0x0100, 44 KERNFS_LOCKDEP = 0x0100,
45 KERNFS_STATIC_NAME = 0x0200, 45 KERNFS_STATIC_NAME = 0x0200,
46 KERNFS_SUICIDAL = 0x0400,
47 KERNFS_SUICIDED = 0x0800,
46}; 48};
47 49
48/* type-specific structures for kernfs_node union members */ 50/* type-specific structures for kernfs_node union members */
@@ -239,6 +241,7 @@ void kernfs_reactivate(struct kernfs_node *kn);
239void kernfs_deactivate_self(struct kernfs_node *kn); 241void kernfs_deactivate_self(struct kernfs_node *kn);
240void kernfs_reactivate_self(struct kernfs_node *kn); 242void kernfs_reactivate_self(struct kernfs_node *kn);
241void kernfs_remove(struct kernfs_node *kn); 243void kernfs_remove(struct kernfs_node *kn);
244bool kernfs_remove_self(struct kernfs_node *kn);
242int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, 245int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
243 const void *ns); 246 const void *ns);
244int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, 247int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
@@ -296,6 +299,9 @@ kernfs_create_link(struct kernfs_node *parent, const char *name,
296 299
297static inline void kernfs_remove(struct kernfs_node *kn) { } 300static inline void kernfs_remove(struct kernfs_node *kn) { }
298 301
302static inline bool kernfs_remove_self(struct kernfs_node *kn)
303{ return false; }
304
299static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn, 305static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn,
300 const char *name, const void *ns) 306 const char *name, const void *ns)
301{ return -ENOSYS; } 307{ return -ENOSYS; }
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 30b2ebee6439..bd96c603ab6c 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -198,6 +198,7 @@ int __must_check sysfs_chmod_file(struct kobject *kobj,
198 const struct attribute *attr, umode_t mode); 198 const struct attribute *attr, umode_t mode);
199void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, 199void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
200 const void *ns); 200 const void *ns);
201bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr);
201void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr); 202void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr);
202 203
203int __must_check sysfs_create_bin_file(struct kobject *kobj, 204int __must_check sysfs_create_bin_file(struct kobject *kobj,
@@ -301,6 +302,12 @@ static inline void sysfs_remove_file_ns(struct kobject *kobj,
301{ 302{
302} 303}
303 304
305static inline bool sysfs_remove_file_self(struct kobject *kobj,
306 const struct attribute *attr)
307{
308 return false;
309}
310
304static inline void sysfs_remove_files(struct kobject *kobj, 311static inline void sysfs_remove_files(struct kobject *kobj,
305 const struct attribute **attr) 312 const struct attribute **attr)
306{ 313{