Revert "sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner()"
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Mar 2014 03:54:57 +0000 (20:54 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Mar 2014 03:54:57 +0000 (20:54 -0700)
This reverts commit d1ba277e79889085a2faec3b68b91ce89c63f888.

As reported by Stephen, this patch breaks linux-next as a ppc patch
suddenly (after 2 years) started using this old api call.  So revert it
for now, it will go away in 3.15-rc2 when we can change the PPC call to
the new api.

Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Tejun Heo <tj@kernel.org>
Cc: Stewart Smith <stewart@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/base/core.c
fs/sysfs/file.c
include/linux/device.h
include/linux/sysfs.h

index 20da3ad1696b58ef6aa6a91830070063e1dc21d7..0dd65281cc65bb6a368143bea0373c2c373787dd 100644 (file)
@@ -614,6 +614,39 @@ void device_remove_bin_file(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(device_remove_bin_file);
 
+/**
+ * device_schedule_callback_owner - helper to schedule a callback for a device
+ * @dev: device.
+ * @func: callback function to invoke later.
+ * @owner: module owning the callback routine
+ *
+ * Attribute methods must not unregister themselves or their parent device
+ * (which would amount to the same thing).  Attempts to do so will deadlock,
+ * since unregistration is mutually exclusive with driver callbacks.
+ *
+ * Instead methods can call this routine, which will attempt to allocate
+ * and schedule a workqueue request to call back @func with @dev as its
+ * argument in the workqueue's process context.  @dev will be pinned until
+ * @func returns.
+ *
+ * This routine is usually called via the inline device_schedule_callback(),
+ * which automatically sets @owner to THIS_MODULE.
+ *
+ * Returns 0 if the request was submitted, -ENOMEM if storage could not
+ * be allocated, -ENODEV if a reference to @owner isn't available.
+ *
+ * NOTE: This routine won't work if CONFIG_SYSFS isn't set!  It uses an
+ * underlying sysfs routine (since it is intended for use by attribute
+ * methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
+ */
+int device_schedule_callback_owner(struct device *dev,
+               void (*func)(struct device *), struct module *owner)
+{
+       return sysfs_schedule_callback(&dev->kobj,
+                       (void (*)(void *)) func, dev, owner);
+}
+EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
+
 static void klist_children_get(struct klist_node *n)
 {
        struct device_private *p = to_device_private_parent(n);
index 28cc1acd5439bf8caeb6d93e8bf68804ad738b1b..1b8b91b67fdb7a4b192b9568ad1d85defdcd863a 100644 (file)
@@ -453,3 +453,95 @@ void sysfs_remove_bin_file(struct kobject *kobj,
        kernfs_remove_by_name(kobj->sd, attr->attr.name);
 }
 EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
+
+struct sysfs_schedule_callback_struct {
+       struct list_head        workq_list;
+       struct kobject          *kobj;
+       void                    (*func)(void *);
+       void                    *data;
+       struct module           *owner;
+       struct work_struct      work;
+};
+
+static struct workqueue_struct *sysfs_workqueue;
+static DEFINE_MUTEX(sysfs_workq_mutex);
+static LIST_HEAD(sysfs_workq);
+static void sysfs_schedule_callback_work(struct work_struct *work)
+{
+       struct sysfs_schedule_callback_struct *ss = container_of(work,
+                       struct sysfs_schedule_callback_struct, work);
+
+       (ss->func)(ss->data);
+       kobject_put(ss->kobj);
+       module_put(ss->owner);
+       mutex_lock(&sysfs_workq_mutex);
+       list_del(&ss->workq_list);
+       mutex_unlock(&sysfs_workq_mutex);
+       kfree(ss);
+}
+
+/**
+ * sysfs_schedule_callback - helper to schedule a callback for a kobject
+ * @kobj: object we're acting for.
+ * @func: callback function to invoke later.
+ * @data: argument to pass to @func.
+ * @owner: module owning the callback code
+ *
+ * sysfs attribute methods must not unregister themselves or their parent
+ * kobject (which would amount to the same thing).  Attempts to do so will
+ * deadlock, since unregistration is mutually exclusive with driver
+ * callbacks.
+ *
+ * Instead methods can call this routine, which will attempt to allocate
+ * and schedule a workqueue request to call back @func with @data as its
+ * argument in the workqueue's process context.  @kobj will be pinned
+ * until @func returns.
+ *
+ * Returns 0 if the request was submitted, -ENOMEM if storage could not
+ * be allocated, -ENODEV if a reference to @owner isn't available,
+ * -EAGAIN if a callback has already been scheduled for @kobj.
+ */
+int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
+               void *data, struct module *owner)
+{
+       struct sysfs_schedule_callback_struct *ss, *tmp;
+
+       if (!try_module_get(owner))
+               return -ENODEV;
+
+       mutex_lock(&sysfs_workq_mutex);
+       list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list)
+               if (ss->kobj == kobj) {
+                       module_put(owner);
+                       mutex_unlock(&sysfs_workq_mutex);
+                       return -EAGAIN;
+               }
+       mutex_unlock(&sysfs_workq_mutex);
+
+       if (sysfs_workqueue == NULL) {
+               sysfs_workqueue = create_singlethread_workqueue("sysfsd");
+               if (sysfs_workqueue == NULL) {
+                       module_put(owner);
+                       return -ENOMEM;
+               }
+       }
+
+       ss = kmalloc(sizeof(*ss), GFP_KERNEL);
+       if (!ss) {
+               module_put(owner);
+               return -ENOMEM;
+       }
+       kobject_get(kobj);
+       ss->kobj = kobj;
+       ss->func = func;
+       ss->data = data;
+       ss->owner = owner;
+       INIT_WORK(&ss->work, sysfs_schedule_callback_work);
+       INIT_LIST_HEAD(&ss->workq_list);
+       mutex_lock(&sysfs_workq_mutex);
+       list_add_tail(&ss->workq_list, &sysfs_workq);
+       mutex_unlock(&sysfs_workq_mutex);
+       queue_work(sysfs_workqueue, &ss->work);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sysfs_schedule_callback);
index fb1ba13f7665419c5e6c755db46c7cc60e8b38dd..1ff3f16975131e702a4445e6cfc817803b074d3d 100644 (file)
@@ -566,6 +566,12 @@ extern int __must_check device_create_bin_file(struct device *dev,
                                        const struct bin_attribute *attr);
 extern void device_remove_bin_file(struct device *dev,
                                   const struct bin_attribute *attr);
+extern int device_schedule_callback_owner(struct device *dev,
+               void (*func)(struct device *dev), struct module *owner);
+
+/* This is a macro to avoid include problems with THIS_MODULE */
+#define device_schedule_callback(dev, func)                    \
+       device_schedule_callback_owner(dev, func, THIS_MODULE)
 
 /* device resource management */
 typedef void (*dr_release_t)(struct device *dev, void *res);
@@ -925,7 +931,10 @@ extern int device_online(struct device *dev);
 extern struct device *__root_device_register(const char *name,
                                             struct module *owner);
 
-/* This is a macro to avoid include problems with THIS_MODULE */
+/*
+ * This is a macro to avoid include problems with THIS_MODULE,
+ * just as per what is done for device_schedule_callback() above.
+ */
 #define root_device_register(name) \
        __root_device_register(name, THIS_MODULE)
 
index fdaa0c6fc7a2e37d0455359b36ef9e1288063d81..e0bf210ddffde0108e77820d81adc318332e2f19 100644 (file)
@@ -178,6 +178,9 @@ struct sysfs_ops {
 
 #ifdef CONFIG_SYSFS
 
+int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
+                           void *data, struct module *owner);
+
 int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns);
 void sysfs_remove_dir(struct kobject *kobj);
 int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
@@ -251,6 +254,12 @@ static inline void sysfs_enable_ns(struct kernfs_node *kn)
 
 #else /* CONFIG_SYSFS */
 
+static inline int sysfs_schedule_callback(struct kobject *kobj,
+               void (*func)(void *), void *data, struct module *owner)
+{
+       return -ENOSYS;
+}
+
 static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
 {
        return 0;