Merge tag 'driver-core-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / base / core.c
index 04bbcd779e114ef1ecad0df77591c0801a0bf84b..0073b09bb99f9d0650abd11589fcb2285bc0b69e 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright (c) 2006 Novell, Inc.
  */
 
+#include <linux/acpi.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/fwnode.h>
@@ -728,6 +729,26 @@ static inline int device_is_not_partition(struct device *dev)
 }
 #endif
 
+static int
+device_platform_notify(struct device *dev, enum kobject_action action)
+{
+       int ret;
+
+       ret = acpi_platform_notify(dev, action);
+       if (ret)
+               return ret;
+
+       ret = software_node_notify(dev, action);
+       if (ret)
+               return ret;
+
+       if (platform_notify && action == KOBJ_ADD)
+               platform_notify(dev);
+       else if (platform_notify_remove && action == KOBJ_REMOVE)
+               platform_notify_remove(dev);
+       return 0;
+}
+
 /**
  * dev_driver_string - Return a device's driver name, if at all possible
  * @dev: struct device to get the name of
@@ -794,10 +815,12 @@ ssize_t device_store_ulong(struct device *dev,
                           const char *buf, size_t size)
 {
        struct dev_ext_attribute *ea = to_ext_attr(attr);
-       char *end;
-       unsigned long new = simple_strtoul(buf, &end, 0);
-       if (end == buf)
-               return -EINVAL;
+       int ret;
+       unsigned long new;
+
+       ret = kstrtoul(buf, 0, &new);
+       if (ret)
+               return ret;
        *(unsigned long *)(ea->var) = new;
        /* Always return full write size even if we didn't consume all */
        return size;
@@ -818,9 +841,14 @@ ssize_t device_store_int(struct device *dev,
                         const char *buf, size_t size)
 {
        struct dev_ext_attribute *ea = to_ext_attr(attr);
-       char *end;
-       long new = simple_strtol(buf, &end, 0);
-       if (end == buf || new > INT_MAX || new < INT_MIN)
+       int ret;
+       long new;
+
+       ret = kstrtol(buf, 0, &new);
+       if (ret)
+               return ret;
+
+       if (new > INT_MAX || new < INT_MIN)
                return -EINVAL;
        *(int *)(ea->var) = new;
        /* Always return full write size even if we didn't consume all */
@@ -890,8 +918,7 @@ static void device_release(struct kobject *kobj)
        else if (dev->class && dev->class->dev_release)
                dev->class->dev_release(dev);
        else
-               WARN(1, KERN_ERR "Device '%s' does not have a release() "
-                       "function, it is broken and must be fixed.\n",
+               WARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/kobject.txt.\n",
                        dev_name(dev));
        kfree(p);
 }
@@ -1067,8 +1094,14 @@ out:
 static ssize_t uevent_store(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       if (kobject_synth_uevent(&dev->kobj, buf, count))
+       int rc;
+
+       rc = kobject_synth_uevent(&dev->kobj, buf, count);
+
+       if (rc) {
                dev_err(dev, "uevent: failed to send synthetic uevent\n");
+               return rc;
+       }
 
        return count;
 }
@@ -1883,8 +1916,9 @@ int device_add(struct device *dev)
        }
 
        /* notify platform of device entry */
-       if (platform_notify)
-               platform_notify(dev);
+       error = device_platform_notify(dev, KOBJ_ADD);
+       if (error)
+               goto platform_error;
 
        error = device_create_file(dev, &dev_attr_uevent);
        if (error)
@@ -1960,6 +1994,8 @@ done:
  SymlinkError:
        device_remove_file(dev, &dev_attr_uevent);
  attrError:
+       device_platform_notify(dev, KOBJ_REMOVE);
+platform_error:
        kobject_uevent(&dev->kobj, KOBJ_REMOVE);
        glue_dir = get_glue_dir(dev);
        kobject_del(&dev->kobj);
@@ -2077,14 +2113,10 @@ void device_del(struct device *dev)
        bus_remove_device(dev);
        device_pm_remove(dev);
        driver_deferred_probe_del(dev);
+       device_platform_notify(dev, KOBJ_REMOVE);
        device_remove_properties(dev);
        device_links_purge(dev);
 
-       /* Notify the platform of the removal, in case they
-        * need to do anything...
-        */
-       if (platform_notify_remove)
-               platform_notify_remove(dev);
        if (dev->bus)
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_REMOVED_DEVICE, dev);