Commit dd77a4ee authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6: (47 commits)
  Driver core: Don't call put methods while holding a spinlock
  Driver core: Remove unneeded routines from driver core
  Driver core: Fix potential deadlock in driver core
  PCI: enable driver multi-threaded probe
  Driver Core: add ability for drivers to do a threaded probe
  sysfs: add proper sysfs_init() prototype
  drivers/base: check errors
  drivers/base: Platform notify needs to occur before drivers attach to the device
  v4l-dev2: handle __must_check
  add CONFIG_ENABLE_MUST_CHECK
  add __must_check to device management code
  Driver core: fixed add_bind_files() definition
  Driver core: fix comments in drivers/base/power/resume.c
  sysfs_remove_bin_file: no return value, dump_stack on error
  kobject: must_check fixes
  Driver core: add ability for devices to create and remove bin files
  Class: add support for class interfaces for devices
  Driver core: create devices/virtual/ tree
  Driver core: add device_rename function
  Driver core: add ability for classes to handle devices properly
  ...
parents e8216dee 7e9f4b2d
What: devfs What: devfs
Date: July 2005 Date: July 2005 (scheduled), finally removed in kernel v2.6.18
Contact: Greg Kroah-Hartman <gregkh@suse.de> Contact: Greg Kroah-Hartman <gregkh@suse.de>
Description: Description:
devfs has been unmaintained for a number of years, has unfixable devfs has been unmaintained for a number of years, has unfixable
races, contains a naming policy within the kernel that is races, contains a naming policy within the kernel that is
against the LSB, and can be replaced by using udev. against the LSB, and can be replaced by using udev.
The files fs/devfs/*, include/linux/devfs_fs*.h will be removed, The files fs/devfs/*, include/linux/devfs_fs*.h were removed,
along with the the assorted devfs function calls throughout the along with the the assorted devfs function calls throughout the
kernel tree. kernel tree.
Users: Users:
What: /sys/power/
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power directory will contain files that will
provide a unified interface to the power management
subsystem.
What: /sys/power/state
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/state file controls the system power state.
Reading from this file returns what states are supported,
which is hard-coded to 'standby' (Power-On Suspend), 'mem'
(Suspend-to-RAM), and 'disk' (Suspend-to-Disk).
Writing to this file one of these strings causes the system to
transition into that state. Please see the file
Documentation/power/states.txt for a description of each of
these states.
What: /sys/power/disk
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/disk file controls the operating mode of the
suspend-to-disk mechanism. Reading from this file returns
the name of the method by which the system will be put to
sleep on the next suspend. There are four methods supported:
'firmware' - means that the memory image will be saved to disk
by some firmware, in which case we also assume that the
firmware will handle the system suspend.
'platform' - the memory image will be saved by the kernel and
the system will be put to sleep by the platform driver (e.g.
ACPI or other PM registers).
'shutdown' - the memory image will be saved by the kernel and
the system will be powered off.
'reboot' - the memory image will be saved by the kernel and
the system will be rebooted.
The suspend-to-disk method may be chosen by writing to this
file one of the accepted strings:
'firmware'
'platform'
'shutdown'
'reboot'
It will only change to 'firmware' or 'platform' if the system
supports that.
What: /sys/power/image_size
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/image_size file controls the size of the image
created by the suspend-to-disk mechanism. It can be written a
string representing a non-negative integer that will be used
as an upper limit of the image size, in bytes. The kernel's
suspend-to-disk code will do its best to ensure the image size
will not exceed this number. However, if it turns out to be
impossible, the kernel will try to suspend anyway using the
smallest image possible. In particular, if "0" is written to
this file, the suspend image will be as small as possible.
Reading from this file will display the current image size
limit, which is set to 500 MB by default.
What: /sys/power/pm_trace
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/pm_trace file controls the code which saves the
last PM event point in the RTC across reboots, so that you can
debug a machine that just hangs during suspend (or more
commonly, during resume). Namely, the RTC is only used to save
the last PM event point if this file contains '1'. Initially
it contains '0' which may be changed to '1' by writing a
string representing a nonzero integer into it.
To use this debugging feature you should attempt to suspend
the machine, then reboot it and run
dmesg -s 1000000 | grep 'hash matches'
CAUTION: Using it will cause your machine's real-time (CMOS)
clock to be set to a random invalid time after a resume.
...@@ -6,6 +6,21 @@ be removed from this file. ...@@ -6,6 +6,21 @@ be removed from this file.
--------------------------- ---------------------------
What: /sys/devices/.../power/state
dev->power.power_state
dpm_runtime_{suspend,resume)()
When: July 2007
Why: Broken design for runtime control over driver power states, confusing
driver-internal runtime power management with: mechanisms to support
system-wide sleep state transitions; event codes that distinguish
different phases of swsusp "sleep" transitions; and userspace policy
inputs. This framework was never widely used, and most attempts to
use it were broken. Drivers should instead be exposing domain-specific
interfaces either to kernel or to userspace.
Who: Pavel Machek <pavel@suse.cz>
---------------------------
What: RAW driver (CONFIG_RAW_DRIVER) What: RAW driver (CONFIG_RAW_DRIVER)
When: December 2005 When: December 2005
Why: declared obsolete since kernel 2.6.3 Why: declared obsolete since kernel 2.6.3
...@@ -294,3 +309,15 @@ Why: The frame diverter is included in most distribution kernels, but is ...@@ -294,3 +309,15 @@ Why: The frame diverter is included in most distribution kernels, but is
It is not clear if anyone is still using it. It is not clear if anyone is still using it.
Who: Stephen Hemminger <shemminger@osdl.org> Who: Stephen Hemminger <shemminger@osdl.org>
---------------------------
What: PHYSDEVPATH, PHYSDEVBUS, PHYSDEVDRIVER in the uevent environment
When: Oktober 2008
Why: The stacking of class devices makes these values misleading and
inconsistent.
Class devices should not carry any of these properties, and bus
devices have SUBSYTEM and DRIVER as a replacement.
Who: Kay Sievers <kay.sievers@suse.de>
---------------------------
This diff is collapsed.
...@@ -16,7 +16,7 @@ extern int cpu_dev_init(void); ...@@ -16,7 +16,7 @@ extern int cpu_dev_init(void);
extern int attribute_container_init(void); extern int attribute_container_init(void);
extern int bus_add_device(struct device * dev); extern int bus_add_device(struct device * dev);
extern void bus_attach_device(struct device * dev); extern int bus_attach_device(struct device * dev);
extern void bus_remove_device(struct device * dev); extern void bus_remove_device(struct device * dev);
extern struct bus_type *get_bus(struct bus_type * bus); extern struct bus_type *get_bus(struct bus_type * bus);
extern void put_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus);
......
...@@ -371,12 +371,20 @@ int bus_add_device(struct device * dev) ...@@ -371,12 +371,20 @@ int bus_add_device(struct device * dev)
if (bus) { if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
error = device_add_attrs(bus, dev); error = device_add_attrs(bus, dev);
if (!error) { if (error)
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); goto out;
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem"); error = sysfs_create_link(&bus->devices.kobj,
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); &dev->kobj, dev->bus_id);
} if (error)
goto out;
error = sysfs_create_link(&dev->kobj,
&dev->bus->subsys.kset.kobj, "subsystem");
if (error)
goto out;
error = sysfs_create_link(&dev->kobj,
&dev->bus->subsys.kset.kobj, "bus");
} }
out:
return error; return error;
} }
...@@ -384,16 +392,24 @@ int bus_add_device(struct device * dev) ...@@ -384,16 +392,24 @@ int bus_add_device(struct device * dev)
* bus_attach_device - add device to bus * bus_attach_device - add device to bus
* @dev: device tried to attach to a driver * @dev: device tried to attach to a driver
* *
* - Add device to bus's list of devices.
* - Try to attach to driver. * - Try to attach to driver.
*/ */
void bus_attach_device(struct device * dev) int bus_attach_device(struct device * dev)
{ {
struct bus_type * bus = dev->bus; struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) { if (bus) {
device_attach(dev); dev->is_registered = 1;
klist_add_tail(&dev->knode_bus, &bus->klist_devices); ret = device_attach(dev);
if (ret >= 0) {
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
ret = 0;
} else
dev->is_registered = 0;
} }
return ret;
} }
/** /**
...@@ -412,7 +428,8 @@ void bus_remove_device(struct device * dev) ...@@ -412,7 +428,8 @@ void bus_remove_device(struct device * dev)
sysfs_remove_link(&dev->kobj, "bus"); sysfs_remove_link(&dev->kobj, "bus");
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
device_remove_attrs(dev->bus, dev); device_remove_attrs(dev->bus, dev);
klist_remove(&dev->knode_bus); dev->is_registered = 0;
klist_del(&dev->knode_bus);
pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id); pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id);
device_release_driver(dev); device_release_driver(dev);
put_bus(dev->bus); put_bus(dev->bus);
...@@ -455,10 +472,17 @@ static void driver_remove_attrs(struct bus_type * bus, struct device_driver * dr ...@@ -455,10 +472,17 @@ static void driver_remove_attrs(struct bus_type * bus, struct device_driver * dr
* Thanks to drivers making their tables __devinit, we can't allow manual * Thanks to drivers making their tables __devinit, we can't allow manual
* bind and unbind from userspace unless CONFIG_HOTPLUG is enabled. * bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
*/ */
static void add_bind_files(struct device_driver *drv) static int __must_check add_bind_files(struct device_driver *drv)
{ {
driver_create_file(drv, &driver_attr_unbind); int ret;
driver_create_file(drv, &driver_attr_bind);
ret = driver_create_file(drv, &driver_attr_unbind);
if (ret == 0) {
ret = driver_create_file(drv, &driver_attr_bind);
if (ret)
driver_remove_file(drv, &driver_attr_unbind);
}
return ret;
} }
static void remove_bind_files(struct device_driver *drv) static void remove_bind_files(struct device_driver *drv)
...@@ -467,7 +491,7 @@ static void remove_bind_files(struct device_driver *drv) ...@@ -467,7 +491,7 @@ static void remove_bind_files(struct device_driver *drv)
driver_remove_file(drv, &driver_attr_unbind); driver_remove_file(drv, &driver_attr_unbind);
} }
#else #else
static inline void add_bind_files(struct device_driver *drv) {} static inline int add_bind_files(struct device_driver *drv) { return 0; }
static inline void remove_bind_files(struct device_driver *drv) {} static inline void remove_bind_files(struct device_driver *drv) {}
#endif #endif
...@@ -476,7 +500,7 @@ static inline void remove_bind_files(struct device_driver *drv) {} ...@@ -476,7 +500,7 @@ static inline void remove_bind_files(struct device_driver *drv) {}
* @drv: driver. * @drv: driver.
* *
*/ */
int bus_add_driver(struct device_driver * drv) int bus_add_driver(struct device_driver *drv)
{ {
struct bus_type * bus = get_bus(drv->bus); struct bus_type * bus = get_bus(drv->bus);
int error = 0; int error = 0;
...@@ -484,27 +508,39 @@ int bus_add_driver(struct device_driver * drv) ...@@ -484,27 +508,39 @@ int bus_add_driver(struct device_driver * drv)
if (bus) { if (bus) {
pr_debug("bus %s: add driver %s\n", bus->name, drv->name); pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name); error = kobject_set_name(&drv->kobj, "%s", drv->name);
if (error) { if (error)
put_bus(bus); goto out_put_bus;
return error;
}
drv->kobj.kset = &bus->drivers; drv->kobj.kset = &bus->drivers;
if ((error = kobject_register(&drv->kobj))) { if ((error = kobject_register(&drv->kobj)))
put_bus(bus); goto out_put_bus;
return error;
}
driver_attach(drv); error = driver_attach(drv);
if (error)
goto out_unregister;
klist_add_tail(&drv->knode_bus, &bus->klist_drivers); klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv); module_add_driver(drv->owner, drv);
driver_add_attrs(bus, drv); error = driver_add_attrs(bus, drv);
add_bind_files(drv); if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__FUNCTION__, drv->name);
}
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__FUNCTION__, drv->name);
}
} }
return error; return error;
out_unregister:
kobject_unregister(&drv->kobj);
out_put_bus:
put_bus(bus);
return error;
} }
/** /**
* bus_remove_driver - delete driver from bus's knowledge. * bus_remove_driver - delete driver from bus's knowledge.
* @drv: driver. * @drv: driver.
...@@ -530,16 +566,21 @@ void bus_remove_driver(struct device_driver * drv) ...@@ -530,16 +566,21 @@ void bus_remove_driver(struct device_driver * drv)
/* Helper for bus_rescan_devices's iter */ /* Helper for bus_rescan_devices's iter */
static int bus_rescan_devices_helper(struct device *dev, void *data) static int __must_check bus_rescan_devices_helper(struct device *dev,
void *data)
{ {
int ret = 0;
if (!dev->driver) { if (!dev->driver) {
if (dev->parent) /* Needed for USB */ if (dev->parent) /* Needed for USB */
down(&dev->parent->sem); down(&dev->parent->sem);
device_attach(dev); ret = device_attach(dev);
if (dev->parent) if (dev->parent)
up(&dev->parent->sem); up(&dev->parent->sem);
if (ret > 0)
ret = 0;
} }
return 0; return ret < 0 ? ret : 0;
} }
/** /**
...@@ -550,9 +591,9 @@ static int bus_rescan_devices_helper(struct device *dev, void *data) ...@@ -550,9 +591,9 @@ static int bus_rescan_devices_helper(struct device *dev, void *data)
* attached and rescan it against existing drivers to see if it matches * attached and rescan it against existing drivers to see if it matches
* any by calling device_attach() for the unbound devices. * any by calling device_attach() for the unbound devices.
*/ */
void bus_rescan_devices(struct bus_type * bus) int bus_rescan_devices(struct bus_type * bus)
{ {
bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper); return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
} }
/** /**
...@@ -564,7 +605,7 @@ void bus_rescan_devices(struct bus_type * bus) ...@@ -564,7 +605,7 @@ void bus_rescan_devices(struct bus_type * bus)
* to use if probing criteria changed during a devices lifetime and * to use if probing criteria changed during a devices lifetime and
* driver attachment should change accordingly. * driver attachment should change accordingly.
*/ */
void device_reprobe(struct device *dev) int device_reprobe(struct device *dev)
{ {
if (dev->driver) { if (dev->driver) {
if (dev->parent) /* Needed for USB */ if (dev->parent) /* Needed for USB */
...@@ -573,14 +614,14 @@ void device_reprobe(struct device *dev) ...@@ -573,14 +614,14 @@ void device_reprobe(struct device *dev)
if (dev->parent) if (dev->parent)
up(&dev->parent->sem); up(&dev->parent->sem);
} }
return bus_rescan_devices_helper(dev, NULL);
bus_rescan_devices_helper(dev, NULL);
} }
EXPORT_SYMBOL_GPL(device_reprobe); EXPORT_SYMBOL_GPL(device_reprobe);
struct bus_type * get_bus(struct bus_type * bus) struct bus_type *get_bus(struct bus_type *bus)
{ {
return bus ? container_of(subsys_get(&bus->subsys), struct bus_type, subsys) : NULL; return bus ? container_of(subsys_get(&bus->subsys),
struct bus_type, subsys) : NULL;
} }
void put_bus(struct bus_type * bus) void put_bus(struct bus_type * bus)
...@@ -655,22 +696,6 @@ static void klist_devices_put(struct klist_node *n) ...@@ -655,22 +696,6 @@ static void klist_devices_put(struct klist_node *n)
put_device(dev); put_device(dev);
} }
static void klist_drivers_get(struct klist_node *n)
{
struct device_driver *drv = container_of(n, struct device_driver,
knode_bus);
get_driver(drv);
}
static void klist_drivers_put(struct klist_node *n)
{
struct device_driver *drv = container_of(n, struct device_driver,
knode_bus);
put_driver(drv);
}
/** /**
* bus_register - register a bus with the system. * bus_register - register a bus with the system.
* @bus: bus. * @bus: bus.
...@@ -706,7 +731,7 @@ int bus_register(struct bus_type * bus) ...@@ -706,7 +731,7 @@ int bus_register(struct bus_type * bus)
goto bus_drivers_fail; goto bus_drivers_fail;
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, klist_drivers_get, klist_drivers_put); klist_init(&bus->klist_drivers, NULL, NULL);
bus_add_attrs(bus); bus_add_attrs(bus);
pr_debug("bus type '%s' registered\n", bus->name); pr_debug("bus type '%s' registered\n", bus->name);
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include "base.h" #include "base.h"
extern struct subsystem devices_subsys;
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj) #define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
...@@ -197,7 +199,7 @@ static int class_device_create_uevent(struct class_device *class_dev, ...@@ -197,7 +199,7 @@ static int class_device_create_uevent(struct class_device *class_dev,
* Note, the pointer created here is to be destroyed when finished by * Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy(). * making a call to class_destroy().
*/ */
struct class *class_create(struct module *owner, char *name) struct class *class_create(struct module *owner, const char *name)
{ {
struct class *cls; struct class *cls;
int retval; int retval;
...@@ -361,7 +363,7 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp, ...@@ -361,7 +363,7 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id); pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
if (class_dev->dev) { if (class_dev->dev) {
/* add physical device, backing this device */ /* add device, backing this class device (deprecated) */
struct device *dev = class_dev->dev; struct device *dev = class_dev->dev;
char *path = kobject_get_path(&dev->kobj, GFP_KERNEL); char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
...@@ -679,7 +681,8 @@ int class_device_register(struct class_device *class_dev) ...@@ -679,7 +681,8 @@ int class_device_register(struct class_device *class_dev)
struct class_device *class_device_create(struct class *cls, struct class_device *class_device_create(struct class *cls,
struct class_device *parent, struct class_device *parent,
dev_t devt, dev_t devt,
struct device *device, char *fmt, ...) struct device *device,
const char *fmt, ...)
{ {
va_list args; va_list args;
struct class_device *class_dev = NULL; struct class_device *class_dev = NULL;
...@@ -839,6 +842,7 @@ int class_interface_register(struct class_interface *class_intf) ...@@ -839,6 +842,7 @@ int class_interface_register(struct class_interface *class_intf)
{ {
struct class *parent; struct class *parent;
struct class_device *class_dev; struct class_device *class_dev;
struct device *dev;
if (!class_intf || !class_intf->class) if (!class_intf || !class_intf->class)
return -ENODEV; return -ENODEV;
...@@ -853,6 +857,10 @@ int class_interface_register(struct class_interface *class_intf) ...@@ -853,6 +857,10 @@ int class_interface_register(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node) list_for_each_entry(class_dev, &parent->children, node)
class_intf->add(class_dev, class_intf); class_intf->add(class_dev, class_intf);
} }
if (class_intf->add_dev) {
list_for_each_entry(dev, &parent->devices, node)
class_intf->add_dev(dev, class_intf);
}
up(&parent->sem); up(&parent->sem);
return 0; return 0;
...@@ -862,6 +870,7 @@ void class_interface_unregister(struct class_interface *class_intf) ...@@ -862,6 +870,7 @@ void class_interface_unregister(struct class_interface *class_intf)
{ {
struct class * parent = class_intf->class; struct class * parent = class_intf->class;
struct class_device *class_dev; struct class_device *class_dev;
struct device *dev;
if (!parent) if (!parent)
return; return;
...@@ -872,12 +881,31 @@ void class_interface_unregister(struct class_interface *class_intf) ...@@ -872,12 +881,31 @@ void class_interface_unregister(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node) list_for_each_entry(class_dev, &parent->children, node)
class_intf->remove(class_dev, class_intf); class_intf->remove(class_dev, class_intf);
} }
if (class_intf->remove_dev) {
list_for_each_entry(dev, &parent->devices, node)
class_intf->remove_dev(dev, class_intf);
}
up(&parent->sem); up(&parent->sem);
class_put(parent); class_put(parent);
} }
int virtual_device_parent(struct device *dev)
{
if (!dev->class)
return -ENODEV;
if (!dev->class->virtual_dir) {
static struct kobject *virtual_dir = NULL;
if (!virtual_dir)
virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
}
dev->kobj.parent = dev->class->virtual_dir;
return 0;
}
int __init classes_init(void) int __init classes_init(void)
{ {
......
This diff is collapsed.
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kthread.h>
#include "base.h" #include "base.h"
#include "power/power.h" #include "power/power.h"
...@@ -38,66 +39,73 @@ ...@@ -38,66 +39,73 @@
* *
* This function must be called with @dev->sem held. * This function must be called with @dev->sem held.
*/ */
void device_bind_driver(struct device * dev) int device_bind_driver(struct device *dev)
{ {
if (klist_node_attached(&dev->knode_driver)) int ret;
return;
if (klist_node_attached(&dev->knode_driver)) {
printk(KERN_WARNING "%s: device %s already bound\n",
__FUNCTION__, kobject_name(&dev->kobj));
return 0;
}
pr_debug("bound device '%s' to driver '%s'\n", pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id, dev->driver->name); dev->bus_id, dev->driver->name);
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
sysfs_create_link(&dev->driver->kobj, &dev->kobj, ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
kobject_name(&dev->kobj)); kobject_name(&dev->kobj));
sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); if (ret == 0) {
ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj,
"driver");
if (ret)
sysfs_remove_link(&dev->driver->kobj,
kobject_name(&dev->kobj));
}
return ret;
} }
/** struct stupid_thread_structure {
* driver_probe_device - attempt to bind device & driver. struct device_driver *drv;
* @drv: driver. struct device *dev;
* @dev: device. };
*
* First, we call the bus's match function, if one present, which static atomic_t probe_count = ATOMIC_INIT(0);
* should compare the device IDs the driver supports with the static int really_probe(void *void_data)
* device IDs of the device. Note we don't do this ourselves
* because we don't know the format of the ID structures, nor what
* is to be considered a match and what is not.
*
* This function returns 1 if a match is found, an error if one
* occurs (that is not -ENODEV or -ENXIO), and 0 otherwise.
*
* This function must be called with @dev->sem held. When called
* for a USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver * drv, struct device * dev)
{ {
struct stupid_thread_structure *data = void_data;
struct device_driver *drv = data->drv;
struct device *dev = data->dev;
int ret = 0; int ret = 0;
if (drv->bus->match && !drv->bus->match(dev, drv)) atomic_inc(&probe_count);
goto Done; pr_debug("%s: Probing driver %s with device %s\n",
drv->bus->name, drv->name, dev->bus_id);
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
dev->driver = drv; dev->driver = drv;
if (dev->bus->probe) { if (dev->bus->probe) {
ret = dev->bus->probe(dev); ret = dev->bus->probe(dev);
if (ret) { if (ret) {
dev->driver = NULL; dev->driver = NULL;
goto ProbeFailed; goto probe_failed;
} }
} else if (drv->probe) { } else if (drv->probe) {
ret = drv->probe(dev); ret = drv->probe(dev);
if (ret) { if (ret) {
dev->driver = NULL; dev->driver = NULL;
goto ProbeFailed; goto probe_failed;
} }
} }
device_bind_driver(dev); if (device_bind_driver(dev)) {
printk(KERN_ERR "%s: device_bind_driver(%s) failed\n",
__FUNCTION__, dev->bus_id);
/* How does undo a ->probe? We're screwed. */
}
ret = 1; ret = 1;
pr_debug("%s: Bound Device %s to Driver %s\n", pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name); drv->bus->name, dev->bus_id, drv->name);
goto Done; goto done;
ProbeFailed: probe_failed:
if (ret == -ENODEV || ret == -ENXIO) { if (ret == -ENODEV || ret == -ENXIO) {
/* Driver matched, but didn't support device /* Driver matched, but didn't support device
* or device not found. * or device not found.
...@@ -110,7 +118,71 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) ...@@ -110,7 +118,71 @@ int driver_probe_device(struct device_driver * drv, struct device * dev)
"%s: probe of %s failed with error %d\n", "%s: probe of %s failed with error %d\n",
drv->name, dev->bus_id, ret); drv->name, dev->bus_id, ret);
} }
Done: done:
kfree(data);
atomic_dec(&probe_count);
return ret;
}
/**
* driver_probe_done
* Determine if the probe sequence is finished or not.
*
* Should somehow figure out how to use a semaphore, not an atomic variable...
*/
int driver_probe_done(void)
{
pr_debug("%s: probe_count = %d\n", __FUNCTION__,
atomic_read(&probe_count));
if (atomic_read(&probe_count))
return -EBUSY;
return 0;
}
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* First, we call the bus's match function, if one present, which should
* compare the device IDs the driver supports with the device IDs of the
* device. Note we don't do this ourselves because we don't know the
* format of the ID structures, nor what is to be considered a match and
* what is not.
*
* This function returns 1 if a match is found, an error if one occurs
* (that is not -ENODEV or -ENXIO), and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
struct stupid_thread_structure *data;
struct task_struct *probe_task;
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
data = kmalloc(sizeof(*data), GFP_KERNEL);
data->drv = drv;
data->dev = dev;
if (drv->multithread_probe) {
probe_task = kthread_run(really_probe, data,
"probe-%s", dev->bus_id);
if (IS_ERR(probe_task))
ret = PTR_ERR(probe_task);
} else
ret = really_probe(data);
done:
return ret; return ret;
} }
...@@ -139,8 +211,9 @@ int device_attach(struct device * dev) ...@@ -139,8 +211,9 @@ int device_attach(struct device * dev)
down(&dev->sem); down(&dev->sem);
if (dev->driver) { if (dev->driver) {
device_bind_driver(dev); ret = device_bind_driver(dev);
ret = 1; if (ret == 0)
ret = 1;
} else } else
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
up(&dev->sem); up(&dev->sem);
...@@ -182,9 +255,9 @@ static int __driver_attach(struct device * dev, void * data) ...@@ -182,9 +255,9 @@ static int __driver_attach(struct device * dev, void * data)
* returns 0 and the @dev->driver is set, we've found a * returns 0 and the @dev->driver is set, we've found a
* compatible pair. * compatible pair.
*/ */
void driver_attach(struct device_driver * drv) int driver_attach(struct device_driver * drv)
{ {
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
} }
/** /**
......
...@@ -142,20 +142,6 @@ void put_driver(struct device_driver * drv) ...@@ -142,20 +142,6 @@ void put_driver(struct device_driver * drv)
kobject_put(&drv->kobj); kobject_put(&drv->kobj);
} }
static void klist_devices_get(struct klist_node *n)
{
struct device *dev = container_of(n, struct device, knode_driver);
get_device(dev);
}
static void klist_devices_put(struct klist_node *n)
{
struct device *dev = container_of(n, struct device, knode_driver);
put_device(dev);
}
/** /**
* driver_register - register driver with bus * driver_register - register driver with bus
* @drv: driver to register * @drv: driver to register
...@@ -175,7 +161,7 @@ int driver_register(struct device_driver * drv) ...@@ -175,7 +161,7 @@ int driver_register(struct device_driver * drv)
(drv->bus->shutdown && drv->shutdown)) { (drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
} }
klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&drv->klist_devices, NULL, NULL);
init_completion(&drv->unloaded); init_completion(&drv->unloaded);
return bus_add_driver(drv); return bus_add_driver(drv);
} }
......
...@@ -505,12 +505,36 @@ static int platform_match(struct device * dev, struct device_driver * drv) ...@@ -505,12 +505,36 @@ static int platform_match(struct device * dev, struct device_driver * drv)
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
} }
static int platform_suspend(struct device * dev, pm_message_t state) static int platform_suspend(struct device *dev, pm_message_t mesg)
{ {
int ret = 0; int ret = 0;
if (dev->driver && dev->driver->suspend) if (dev->driver && dev->driver->suspend)
ret = dev->driver->suspend(dev, state); ret = dev->driver->suspend(dev, mesg);
return ret;
}
static int platform_suspend_late(struct device *dev, pm_message_t mesg)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
int ret = 0;
if (dev->driver && drv->suspend_late)
ret = drv->suspend_late(pdev, mesg);
return ret;
}
static int platform_resume_early(struct device *dev)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
int ret = 0;
if (dev->driver && drv->resume_early)
ret = drv->resume_early(pdev);
return ret; return ret;
} }
...@@ -531,6 +555,8 @@ struct bus_type platform_bus_type = { ...@@ -531,6 +555,8 @@ struct bus_type platform_bus_type = {
.match = platform_match, .match = platform_match,
.uevent = platform_uevent, .uevent = platform_uevent,
.suspend = platform_suspend, .suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume, .resume = platform_resume,
}; };
EXPORT_SYMBOL_GPL(platform_bus_type); EXPORT_SYMBOL_GPL(platform_bus_type);
......
...@@ -38,13 +38,35 @@ int resume_device(struct device * dev) ...@@ -38,13 +38,35 @@ int resume_device(struct device * dev)
dev_dbg(dev,"resuming\n"); dev_dbg(dev,"resuming\n");
error = dev->bus->resume(dev); error = dev->bus->resume(dev);
} }
if (dev->class && dev->class->resume) {
dev_dbg(dev,"class resume\n");
error = dev->class->resume(dev);
}
up(&dev->sem); up(&dev->sem);
TRACE_RESUME(error); TRACE_RESUME(error);
return error; return error;
} }
static int resume_device_early(struct device * dev)
{
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
if (dev->bus && dev->bus->resume_early) {
dev_dbg(dev,"EARLY resume\n");
error = dev->bus->resume_early(dev);
}
TRACE_RESUME(error);
return error;
}
/*
* Resume the devices that have either not gone through
* the late suspend, or that did go through it but also
* went through the early resume
*/
void dpm_resume(void) void dpm_resume(void)
{ {
down(&dpm_list_sem); down(&dpm_list_sem);
...@@ -74,6 +96,7 @@ void dpm_resume(void) ...@@ -74,6 +96,7 @@ void dpm_resume(void)
void device_resume(void) void device_resume(void)
{ {
might_sleep();
down(&dpm_sem); down(&dpm_sem);
dpm_resume(); dpm_resume();
up(&dpm_sem); up(&dpm_sem);
...@@ -83,12 +106,12 @@ EXPORT_SYMBOL_GPL(device_resume); ...@@ -83,12 +106,12 @@ EXPORT_SYMBOL_GPL(device_resume);
/** /**
* device_power_up_irq - Power on some devices. * dpm_power_up - Power on some devices.
* *
* Walk the dpm_off_irq list and power each device up. This * Walk the dpm_off_irq list and power each device up. This
* is used for devices that required they be powered down with * is used for devices that required they be powered down with
* interrupts disabled. As devices are powered on, they are moved to * interrupts disabled. As devices are powered on, they are moved
* the dpm_suspended list. * to the dpm_active list.
* *
* Interrupts must be disabled when calling this. * Interrupts must be disabled when calling this.
*/ */
...@@ -99,16 +122,14 @@ void dpm_power_up(void) ...@@ -99,16 +122,14 @@ void dpm_power_up(void)
struct list_head * entry = dpm_off_irq.next; struct list_head * entry = dpm_off_irq.next;
struct device * dev = to_device(entry); struct device * dev = to_device(entry);
get_device(dev); list_move_tail(entry, &dpm_off);
list_move_tail(entry, &dpm_active); resume_device_early(dev);
resume_device(dev);
put_device(dev);
} }
} }
/** /**
* device_pm_power_up - Turn on all devices that need special attention. * device_power_up - Turn on all devices that need special attention.
* *
* Power on system devices then devices that required we shut them down * Power on system devices then devices that required we shut them down
* with interrupts disabled. * with interrupts disabled.
......
...@@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event) ...@@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event)
switch (event) { switch (event) {
case PM_EVENT_SUSPEND: return "suspend"; case PM_EVENT_SUSPEND: return "suspend";
case PM_EVENT_FREEZE: return "freeze"; case PM_EVENT_FREEZE: return "freeze";
case PM_EVENT_PRETHAW: return "prethaw";
default: return "(unknown suspend event)"; default: return "(unknown suspend event)";
} }
} }
...@@ -65,7 +66,19 @@ int suspend_device(struct device * dev, pm_message_t state) ...@@ -65,7 +66,19 @@ int suspend_device(struct device * dev, pm_message_t state)
dev->power.prev_state = dev->power.power_state; dev->power.prev_state = dev->power.power_state;
if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "class %s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
&& device_may_wakeup(dev))
? ", may wakeup"
: ""
);
error = dev->class->suspend(dev, state);
suspend_report_result(dev->class->suspend, error);
}
if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "%s%s\n", dev_dbg(dev, "%s%s\n",
suspend_verb(state.event), suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND) ((state.event == PM_EVENT_SUSPEND)
...@@ -81,15 +94,42 @@ int suspend_device(struct device * dev, pm_message_t state) ...@@ -81,15 +94,42 @@ int suspend_device(struct device * dev, pm_message_t state)
} }
/*
* This is called with interrupts off, only a single CPU
* running. We can't do down() on a semaphore (and we don't
* need the protection)
*/
static int suspend_device_late(struct device *dev, pm_message_t state)
{
int error = 0;
if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
dev_dbg(dev, "LATE %s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
&& device_may_wakeup(dev))
? ", may wakeup"
: ""
);
error = dev->bus->suspend_late(dev, state);
suspend_report_result(dev->bus->suspend_late, error);
}
return error;
}
/** /**
* device_suspend - Save state and stop all devices in system. * device_suspend - Save state and stop all devices in system.
* @state: Power state to put each device in. * @state: Power state to put each device in.
* *
* Walk the dpm_active list, call ->suspend() for each device, and move * Walk the dpm_active list, call ->suspend() for each device, and move
* it to dpm_off. * it to the dpm_off list.
* Check the return value for each. If it returns 0, then we move the *
* the device to the dpm_off list. If it returns -EAGAIN, we move it to * (For historical reasons, if it returns -EAGAIN, that used to mean
* the dpm_off_irq list. If we get a different error, try and back out. * that the device would be called again with interrupts disabled.
* These days, we use the "suspend_late()" callback for that, so we
* print a warning and consider it an error).
*
* If we get a different error, try and back out.
* *
* If we hit a failure with any of the devices, call device_resume() * If we hit a failure with any of the devices, call device_resume()
* above to bring the suspended devices back to life. * above to bring the suspended devices back to life.
...@@ -100,6 +140,7 @@ int device_suspend(pm_message_t state) ...@@ -100,6 +140,7 @@ int device_suspend(pm_message_t state)
{ {
int error = 0; int error = 0;
might_sleep();
down(&dpm_sem); down(&dpm_sem);
down(&dpm_list_sem); down(&dpm_list_sem);
while (!list_empty(&dpm_active) && error == 0) { while (!list_empty(&dpm_active) && error == 0) {
...@@ -115,39 +156,27 @@ int device_suspend(pm_message_t state) ...@@ -115,39 +156,27 @@ int device_suspend(pm_message_t state)
/* Check if the device got removed */ /* Check if the device got removed */
if (!list_empty(&dev->power.entry)) { if (!list_empty(&dev->power.entry)) {
/* Move it to the dpm_off or dpm_off_irq list */ /* Move it to the dpm_off list */
if (!error) if (!error)
list_move(&dev->power.entry, &dpm_off); list_move(&dev->power.entry, &dpm_off);
else if (error == -EAGAIN) {
list_move(&dev->power.entry, &dpm_off_irq);
error = 0;
}
} }
if (error) if (error)
printk(KERN_ERR "Could not suspend device %s: " printk(KERN_ERR "Could not suspend device %s: "
"error %d\n", kobject_name(&dev->kobj), error); "error %d%s\n",
kobject_name(&dev->kobj), error,
error == -EAGAIN ? " (please convert to suspend_late)" : "");
put_device(dev); put_device(dev);
} }
up(&dpm_list_sem); up(&dpm_list_sem);
if (error) { if (error)
/* we failed... before resuming, bring back devices from
* dpm_off_irq list back to main dpm_off list, we do want
* to call resume() on them, in case they partially suspended
* despite returning -EAGAIN
*/
while (!list_empty(&dpm_off_irq)) {
struct list_head * entry = dpm_off_irq.next;
list_move(entry, &dpm_off);
}
dpm_resume(); dpm_resume();
}
up(&dpm_sem); up(&dpm_sem);
return error; return error;
} }
EXPORT_SYMBOL_GPL(device_suspend); EXPORT_SYMBOL_GPL(device_suspend);
/** /**
* device_power_down - Shut down special devices. * device_power_down - Shut down special devices.
* @state: Power state to enter. * @state: Power state to enter.
...@@ -162,14 +191,17 @@ int device_power_down(pm_message_t state) ...@@ -162,14 +191,17 @@ int device_power_down(pm_message_t state)
int error = 0; int error = 0;
struct device * dev; struct device * dev;
list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) { while (!list_empty(&dpm_off)) {
if ((error = suspend_device(dev, state))) struct list_head * entry = dpm_off.prev;
break;
dev = to_device(entry);
error = suspend_device_late(dev, state);
if (error)
goto Error;
list_move(&dev->power.entry, &dpm_off_irq);
} }
if (error)
goto Error; error = sysdev_suspend(state);
if ((error = sysdev_suspend(state)))
goto Error;
Done: Done:
return error; return error;
Error: Error:
......
...@@ -7,22 +7,29 @@ ...@@ -7,22 +7,29 @@
#include "power.h" #include "power.h"
#ifdef CONFIG_PM_SYSFS_DEPRECATED
/** /**
* state - Control current power state of device * state - Control current power state of device
* *
* show() returns the current power state of the device. '0' indicates * show() returns the current power state of the device. '0' indicates
* the device is on. Other values (1-3) indicate the device is in a low * the device is on. Other values (2) indicate the device is in some low
* power state. * power state.
* *
* store() sets the current power state, which is an integer value * store() sets the current power state, which is an integer valued
* between 0-3. If the device is on ('0'), and the value written is * 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early()
* greater than 0, then the device is placed directly into the low-power * methods fail this operation; those methods couldn't be called.
* state (via its driver's ->suspend() method). * Otherwise,
* If the device is currently in a low-power state, and the value is 0, *
* the device is powered back on (via the ->resume() method). * - If the recorded dev->power.power_state.event matches the
* If the device is in a low-power state, and a different low-power state * target value, nothing is done.
* is requested, the device is first resumed, then suspended into the new * - If the recorded event code is nonzero, the device is reactivated
* low-power state. * by calling bus.resume() and/or class.resume().
* - If the target value is nonzero, the device is suspended by
* calling class.suspend() and/or bus.suspend() with event code
* PM_EVENT_SUSPEND.
*
* This mechanism is DEPRECATED and should only be used for testing.
*/ */
static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
...@@ -38,6 +45,10 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c ...@@ -38,6 +45,10 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
pm_message_t state; pm_message_t state;
int error = -EINVAL; int error = -EINVAL;
/* disallow incomplete suspend sequences */
if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early))
return error;
state.event = PM_EVENT_SUSPEND; state.event = PM_EVENT_SUSPEND;
/* Older apps expected to write "3" here - confused with PCI D3 */ /* Older apps expected to write "3" here - confused with PCI D3 */
if ((n == 1) && !strcmp(buf, "3")) if ((n == 1) && !strcmp(buf, "3"))
...@@ -57,6 +68,8 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c ...@@ -57,6 +68,8 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
static DEVICE_ATTR(state, 0644, state_show, state_store); static DEVICE_ATTR(state, 0644, state_show, state_store);
#endif /* CONFIG_PM_SYSFS_DEPRECATED */
/* /*
* wakeup - Report/change current wakeup option for device * wakeup - Report/change current wakeup option for device
* *
...@@ -130,7 +143,9 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); ...@@ -130,7 +143,9 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
static struct attribute * power_attrs[] = { static struct attribute * power_attrs[] = {
#ifdef CONFIG_PM_SYSFS_DEPRECATED
&dev_attr_state.attr, &dev_attr_state.attr,
#endif
&dev_attr_wakeup.attr, &dev_attr_wakeup.attr,
NULL, NULL,
}; };
......
...@@ -1207,7 +1207,7 @@ int system_bus_clock (void) ...@@ -1207,7 +1207,7 @@ int system_bus_clock (void)
EXPORT_SYMBOL(system_bus_clock); EXPORT_SYMBOL(system_bus_clock);
static int generic_ide_suspend(struct device *dev, pm_message_t state) static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
{ {
ide_drive_t *drive = dev->driver_data; ide_drive_t *drive = dev->driver_data;
struct request rq; struct request rq;
...@@ -1221,7 +1221,9 @@ static int generic_ide_suspend(struct device *dev, pm_message_t state) ...@@ -1221,7 +1221,9 @@ static int generic_ide_suspend(struct device *dev, pm_message_t state)
rq.special = &args; rq.special = &args;
rq.end_io_data = &rqpm; rq.end_io_data = &rqpm;
rqpm.pm_step = ide_pm_state_start_suspend; rqpm.pm_step = ide_pm_state_start_suspend;
rqpm.pm_state = state.event; if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
rqpm.pm_state = mesg.event;
return ide_do_drive_cmd(drive, &rq, ide_wait); return ide_do_drive_cmd(drive, &rq, ide_wait);
} }
......
...@@ -1369,15 +1369,16 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) ...@@ -1369,15 +1369,16 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
} }
static int static int
pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t state) pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
{ {
ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
int rc = 0; int rc = 0;
if (state.event != mdev->ofdev.dev.power.power_state.event && state.event >= PM_EVENT_SUSPEND) { if (mesg.event != mdev->ofdev.dev.power.power_state.event
&& mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif); rc = pmac_ide_do_suspend(hwif);
if (rc == 0) if (rc == 0)
mdev->ofdev.dev.power.power_state = state; mdev->ofdev.dev.power.power_state = mesg;
} }
return rc; return rc;
...@@ -1473,15 +1474,16 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1473,15 +1474,16 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
} }
static int static int
pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t state) pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{ {
ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev); ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
int rc = 0; int rc = 0;
if (state.event != pdev->dev.power.power_state.event && state.event >= 2) { if (mesg.event != pdev->dev.power.power_state.event
&& mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif); rc = pmac_ide_do_suspend(hwif);
if (rc == 0) if (rc == 0)
pdev->dev.power.power_state = state; pdev->dev.power.power_state = mesg;
} }
return rc; return rc;
......
...@@ -981,7 +981,7 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state) ...@@ -981,7 +981,7 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS; return -ERESTARTSYS;
if (state.event > PM_EVENT_ON) { if (1) {
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
cinergyt2_suspend_rc(cinergyt2); cinergyt2_suspend_rc(cinergyt2);
......
...@@ -17,6 +17,31 @@ config PCI_MSI ...@@ -17,6 +17,31 @@ config PCI_MSI
If you don't know what to do here, say N. If you don't know what to do here, say N.
config PCI_MULTITHREAD_PROBE
bool "PCI Multi-threaded probe (EXPERIMENTAL)"
depends on PCI && EXPERIMENTAL
help
Say Y here if you want the PCI core to spawn a new thread for
every PCI device that is probed. This can cause a huge
speedup in boot times on multiprocessor machines, and even a
smaller speedup on single processor machines.
But it can also cause lots of bad things to happen. A number
of PCI drivers can not properly handle running in this way,
some will just not work properly at all, while others might
decide to blow up power supplies with a huge load all at once,
so use this option at your own risk.
It is very unwise to use this option if you are not using a
boot process that can handle devices being created in any
order. A program that can create persistant block and network
device names (like udev) is a good idea if you wish to use
this option.
Again, use this option at your own risk, you have been warned!
When in doubt, say N.
config PCI_DEBUG config PCI_DEBUG
bool "PCI Debugging" bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL depends on PCI && DEBUG_KERNEL
......
...@@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void) ...@@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void)
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
err("%s: Notification handler removal failed\n", __FUNCTION__); err("%s: Notification handler removal failed\n", __FUNCTION__);
/* remove the /sys entries */ /* remove the /sys entries */
if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr)) sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
err("%s: removal of sysfs file apci_table failed\n",
__FUNCTION__);
} }
module_init(ibm_acpiphp_init); module_init(ibm_acpiphp_init);
......
...@@ -17,6 +17,16 @@ ...@@ -17,6 +17,16 @@
* Registration of PCI drivers and handling of hot-pluggable devices. * Registration of PCI drivers and handling of hot-pluggable devices.
*/ */
/* multithreaded probe logic */
static int pci_multithread_probe =
#ifdef CONFIG_PCI_MULTITHREAD_PROBE
1;
#else
0;
#endif
__module_param_call("", pci_multithread_probe, param_set_bool, param_get_bool, &pci_multithread_probe, 0644);
/* /*
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG * Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/ */
...@@ -279,6 +289,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) ...@@ -279,6 +289,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
return i; return i;
} }
static int pci_device_suspend_late(struct device * dev, pm_message_t state)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
int i = 0;
if (drv && drv->suspend_late) {
i = drv->suspend_late(pci_dev, state);
suspend_report_result(drv->suspend_late, i);
}
return i;
}
/* /*
* Default resume method for devices that have no driver provided resume, * Default resume method for devices that have no driver provided resume,
...@@ -313,6 +335,17 @@ static int pci_device_resume(struct device * dev) ...@@ -313,6 +335,17 @@ static int pci_device_resume(struct device * dev)
return error; return error;
} }
static int pci_device_resume_early(struct device * dev)
{
int error = 0;
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
if (drv && drv->resume_early)
error = drv->resume_early(pci_dev);
return error;
}
static void pci_device_shutdown(struct device *dev) static void pci_device_shutdown(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
...@@ -385,6 +418,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner) ...@@ -385,6 +418,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner)
drv->driver.bus = &pci_bus_type; drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner; drv->driver.owner = owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type; drv->driver.kobj.ktype = &pci_driver_kobj_type;
drv->driver.multithread_probe = pci_multithread_probe;
spin_lock_init(&drv->dynids.lock); spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list); INIT_LIST_HEAD(&drv->dynids.list);
...@@ -509,8 +543,10 @@ struct bus_type pci_bus_type = { ...@@ -509,8 +543,10 @@ struct bus_type pci_bus_type = {
.probe = pci_device_probe, .probe = pci_device_probe,
.remove = pci_device_remove, .remove = pci_device_remove,
.suspend = pci_device_suspend, .suspend = pci_device_suspend,
.shutdown = pci_device_shutdown, .suspend_late = pci_device_suspend_late,
.resume_early = pci_device_resume_early,
.resume = pci_device_resume, .resume = pci_device_resume,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs, .dev_attrs = pci_dev_attrs,
}; };
......
...@@ -432,10 +432,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) ...@@ -432,10 +432,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
case PM_EVENT_ON: case PM_EVENT_ON:
return PCI_D0; return PCI_D0;
case PM_EVENT_FREEZE: case PM_EVENT_FREEZE:
case PM_EVENT_PRETHAW:
/* REVISIT both freeze and pre-thaw "should" use D0 */
case PM_EVENT_SUSPEND: case PM_EVENT_SUSPEND:
return PCI_D3hot; return PCI_D3hot;
default: default:
printk("They asked me for state %d\n", state.event); printk("Unrecognized suspend event %d\n", state.event);
BUG(); BUG();
} }
return PCI_D0; return PCI_D0;
......
...@@ -1756,16 +1756,23 @@ static void set_mesh_power(struct mesh_state *ms, int state) ...@@ -1756,16 +1756,23 @@ static void set_mesh_power(struct mesh_state *ms, int state)
pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0); pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
msleep(10); msleep(10);
} }
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int mesh_suspend(struct macio_dev *mdev, pm_message_t state) static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
{ {
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
unsigned long flags; unsigned long flags;
if (state.event == mdev->ofdev.dev.power.power_state.event || state.event < 2) switch (mesg.event) {
case PM_EVENT_SUSPEND:
case PM_EVENT_FREEZE:
break;
default:
return 0;
}
if (mesg.event == mdev->ofdev.dev.power.power_state.event)
return 0; return 0;
scsi_block_requests(ms->host); scsi_block_requests(ms->host);
...@@ -1780,7 +1787,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t state) ...@@ -1780,7 +1787,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t state)
disable_irq(ms->meshintr); disable_irq(ms->meshintr);
set_mesh_power(ms, 0); set_mesh_power(ms, 0);
mdev->ofdev.dev.power.power_state = state; mdev->ofdev.dev.power.power_state = mesg;
return 0; return 0;
} }
......
...@@ -281,7 +281,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) ...@@ -281,7 +281,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
(void) usb_hcd_pci_resume (dev); (void) usb_hcd_pci_resume (dev);
} }
} else { } else if (hcd->state != HC_STATE_HALT) {
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state); hcd->state);
WARN_ON(1); WARN_ON(1);
......
...@@ -238,6 +238,12 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message) ...@@ -238,6 +238,12 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
writel (0, &ehci->regs->intr_enable); writel (0, &ehci->regs->intr_enable);
(void)readl(&ehci->regs->intr_enable); (void)readl(&ehci->regs->intr_enable);
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW) {
ehci_halt(ehci);
ehci_reset(ehci);
}
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail: bail:
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
......
...@@ -135,6 +135,11 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) ...@@ -135,6 +135,11 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
} }
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
(void)ohci_readl(ohci, &ohci->regs->intrdisable); (void)ohci_readl(ohci, &ohci->regs->intrdisable);
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW)
ohci_usb_reset(ohci);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail: bail:
spin_unlock_irqrestore (&ohci->lock, flags); spin_unlock_irqrestore (&ohci->lock, flags);
......
...@@ -1783,10 +1783,15 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state) ...@@ -1783,10 +1783,15 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
struct sl811 *sl811 = hcd_to_sl811(hcd); struct sl811 *sl811 = hcd_to_sl811(hcd);
int retval = 0; int retval = 0;
if (state.event == PM_EVENT_FREEZE) switch (state.event) {
case PM_EVENT_FREEZE:
retval = sl811h_bus_suspend(hcd); retval = sl811h_bus_suspend(hcd);
else if (state.event == PM_EVENT_SUSPEND) break;
case PM_EVENT_SUSPEND:
case PM_EVENT_PRETHAW: /* explicitly discard hw state */
port_power(sl811, 0); port_power(sl811, 0);
break;
}
if (retval == 0) if (retval == 0)
dev->dev.power.power_state = state; dev->dev.power.power_state = state;
return retval; return retval;
......
...@@ -734,6 +734,10 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) ...@@ -734,6 +734,10 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
/* FIXME: Enable non-PME# remote wakeup? */ /* FIXME: Enable non-PME# remote wakeup? */
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW)
uhci_hc_died(uhci);
done_okay: done_okay:
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
done: done:
......
...@@ -2621,25 +2621,28 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo) ...@@ -2621,25 +2621,28 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo)
} }
int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{ {
struct fb_info *info = pci_get_drvdata(pdev); struct fb_info *info = pci_get_drvdata(pdev);
struct radeonfb_info *rinfo = info->par; struct radeonfb_info *rinfo = info->par;
int i; int i;
if (state.event == pdev->dev.power.power_state.event) if (mesg.event == pdev->dev.power.power_state.event)
return 0; return 0;
printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n", printk(KERN_DEBUG "radeonfb (%s): suspending for event: %d...\n",
pci_name(pdev), state.event); pci_name(pdev), mesg.event);
/* For suspend-to-disk, we cheat here. We don't suspend anything and /* For suspend-to-disk, we cheat here. We don't suspend anything and
* let fbcon continue drawing until we are all set. That shouldn't * let fbcon continue drawing until we are all set. That shouldn't
* really cause any problem at this point, provided that the wakeup * really cause any problem at this point, provided that the wakeup
* code knows that any state in memory may not match the HW * code knows that any state in memory may not match the HW
*/ */
if (state.event == PM_EVENT_FREEZE) switch (mesg.event) {
case PM_EVENT_FREEZE: /* about to take snapshot */
case PM_EVENT_PRETHAW: /* before restoring snapshot */
goto done; goto done;
}
acquire_console_sem(); acquire_console_sem();
...@@ -2706,7 +2709,7 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -2706,7 +2709,7 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
release_console_sem(); release_console_sem();
done: done:
pdev->dev.power.power_state = state; pdev->dev.power.power_state = mesg;
return 0; return 0;
} }
......
...@@ -1554,15 +1554,17 @@ static struct fb_ops i810fb_ops __devinitdata = { ...@@ -1554,15 +1554,17 @@ static struct fb_ops i810fb_ops __devinitdata = {
/*********************************************************************** /***********************************************************************
* Power Management * * Power Management *
***********************************************************************/ ***********************************************************************/
static int i810fb_suspend(struct pci_dev *dev, pm_message_t state) static int i810fb_suspend(struct pci_dev *dev, pm_message_t mesg)
{ {
struct fb_info *info = pci_get_drvdata(dev); struct fb_info *info = pci_get_drvdata(dev);
struct i810fb_par *par = info->par; struct i810fb_par *par = info->par;
par->cur_state = state.event; par->cur_state = mesg.event;
if (state.event == PM_EVENT_FREEZE) { switch (mesg.event) {
dev->dev.power.power_state = state; case PM_EVENT_FREEZE:
case PM_EVENT_PRETHAW:
dev->dev.power.power_state = mesg;
return 0; return 0;
} }
...@@ -1578,7 +1580,7 @@ static int i810fb_suspend(struct pci_dev *dev, pm_message_t state) ...@@ -1578,7 +1580,7 @@ static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
pci_save_state(dev); pci_save_state(dev);
pci_disable_device(dev); pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state)); pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem(); release_console_sem();
return 0; return 0;
......
...@@ -950,24 +950,25 @@ static struct fb_ops nvidia_fb_ops = { ...@@ -950,24 +950,25 @@ static struct fb_ops nvidia_fb_ops = {
}; };
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state) static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
{ {
struct fb_info *info = pci_get_drvdata(dev); struct fb_info *info = pci_get_drvdata(dev);
struct nvidia_par *par = info->par; struct nvidia_par *par = info->par;
if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
acquire_console_sem(); acquire_console_sem();
par->pm_state = state.event; par->pm_state = mesg.event;
if (state.event == PM_EVENT_FREEZE) { if (mesg.event == PM_EVENT_SUSPEND) {
dev->dev.power.power_state = state;
} else {
fb_set_suspend(info, 1); fb_set_suspend(info, 1);
nvidiafb_blank(FB_BLANK_POWERDOWN, info); nvidiafb_blank(FB_BLANK_POWERDOWN, info);
nvidia_write_regs(par, &par->SavedReg); nvidia_write_regs(par, &par->SavedReg);
pci_save_state(dev); pci_save_state(dev);
pci_disable_device(dev); pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state)); pci_set_power_state(dev, pci_choose_state(dev, mesg));
} }
dev->dev.power.power_state = mesg;
release_console_sem(); release_console_sem();
return 0; return 0;
......
...@@ -2323,24 +2323,24 @@ static void __devexit savagefb_remove(struct pci_dev *dev) ...@@ -2323,24 +2323,24 @@ static void __devexit savagefb_remove(struct pci_dev *dev)
} }
} }
static int savagefb_suspend(struct pci_dev* dev, pm_message_t state) static int savagefb_suspend(struct pci_dev *dev, pm_message_t mesg)
{ {
struct fb_info *info = pci_get_drvdata(dev); struct fb_info *info = pci_get_drvdata(dev);
struct savagefb_par *par = info->par; struct savagefb_par *par = info->par;
DBG("savagefb_suspend"); DBG("savagefb_suspend");
if (mesg.event == PM_EVENT_PRETHAW)
par->pm_state = state.event; mesg.event = PM_EVENT_FREEZE;
par->pm_state = mesg.event;
dev->dev.power.power_state = mesg;
/* /*
* For PM_EVENT_FREEZE, do not power down so the console * For PM_EVENT_FREEZE, do not power down so the console
* can remain active. * can remain active.
*/ */
if (state.event == PM_EVENT_FREEZE) { if (mesg.event == PM_EVENT_FREEZE)
dev->dev.power.power_state = state;
return 0; return 0;
}
acquire_console_sem(); acquire_console_sem();
fb_set_suspend(info, 1); fb_set_suspend(info, 1);
...@@ -2353,7 +2353,7 @@ static int savagefb_suspend(struct pci_dev* dev, pm_message_t state) ...@@ -2353,7 +2353,7 @@ static int savagefb_suspend(struct pci_dev* dev, pm_message_t state)
savage_disable_mmio(par); savage_disable_mmio(par);
pci_save_state(dev); pci_save_state(dev);
pci_disable_device(dev); pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state)); pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem(); release_console_sem();
return 0; return 0;
......
...@@ -55,12 +55,11 @@ static u64 debugfs_u8_get(void *data) ...@@ -55,12 +55,11 @@ static u64 debugfs_u8_get(void *data)
DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n"); DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
/** /**
* debugfs_create_u8 - create a file in the debugfs filesystem that is used to read and write an unsigned 8 bit value. * debugfs_create_u8 - create a debugfs file that is used to read and write an unsigned 8-bit value
*
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have * @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a * @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the * directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem. * file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write * @value: a pointer to the variable that the file should read to and write
* from. * from.
...@@ -72,11 +71,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n"); ...@@ -72,11 +71,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
* This function will return a pointer to a dentry if it succeeds. This * This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is * pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded, * to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned. * you are responsible here.) If an error occurs, %NULL will be returned.
* *
* If debugfs is not enabled in the kernel, the value -ENODEV will be * If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for * returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code. * code.
*/ */
struct dentry *debugfs_create_u8(const char *name, mode_t mode, struct dentry *debugfs_create_u8(const char *name, mode_t mode,
...@@ -97,12 +96,11 @@ static u64 debugfs_u16_get(void *data) ...@@ -97,12 +96,11 @@ static u64 debugfs_u16_get(void *data)
DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n"); DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
/** /**
* debugfs_create_u16 - create a file in the debugfs filesystem that is used to read and write an unsigned 16 bit value. * debugfs_create_u16 - create a debugfs file that is used to read and write an unsigned 16-bit value
*
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have * @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a * @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the * directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem. * file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write * @value: a pointer to the variable that the file should read to and write
* from. * from.
...@@ -114,11 +112,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n"); ...@@ -114,11 +112,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
* This function will return a pointer to a dentry if it succeeds. This * This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is * pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded, * to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned. * you are responsible here.) If an error occurs, %NULL will be returned.
* *
* If debugfs is not enabled in the kernel, the value -ENODEV will be * If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for * returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code. * code.
*/ */
struct dentry *debugfs_create_u16(const char *name, mode_t mode, struct dentry *debugfs_create_u16(const char *name, mode_t mode,
...@@ -139,12 +137,11 @@ static u64 debugfs_u32_get(void *data) ...@@ -139,12 +137,11 @@ static u64 debugfs_u32_get(void *data)
DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n"); DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
/** /**
* debugfs_create_u32 - create a file in the debugfs filesystem that is used to read and write an unsigned 32 bit value. * debugfs_create_u32 - create a debugfs file that is used to read and write an unsigned 32-bit value
*
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have * @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a * @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the * directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem. * file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write * @value: a pointer to the variable that the file should read to and write
* from. * from.
...@@ -156,11 +153,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n"); ...@@ -156,11 +153,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
* This function will return a pointer to a dentry if it succeeds. This * This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is * pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded, * to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned. * you are responsible here.) If an error occurs, %NULL will be returned.
* *
* If debugfs is not enabled in the kernel, the value -ENODEV will be * If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for * returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code. * code.
*/ */
struct dentry *debugfs_create_u32(const char *name, mode_t mode, struct dentry *debugfs_create_u32(const char *name, mode_t mode,
...@@ -219,12 +216,11 @@ static const struct file_operations fops_bool = { ...@@ -219,12 +216,11 @@ static const struct file_operations fops_bool = {
}; };
/** /**
* debugfs_create_bool - create a file in the debugfs filesystem that is used to read and write a boolean value. * debugfs_create_bool - create a debugfs file that is used to read and write a boolean value
*
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have * @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a * @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the * directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem. * file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write * @value: a pointer to the variable that the file should read to and write
* from. * from.
...@@ -236,11 +232,11 @@ static const struct file_operations fops_bool = { ...@@ -236,11 +232,11 @@ static const struct file_operations fops_bool = {
* This function will return a pointer to a dentry if it succeeds. This * This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is * pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded, * to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned. * you are responsible here.) If an error occurs, %NULL will be returned.
* *
* If debugfs is not enabled in the kernel, the value -ENODEV will be * If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for * returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code. * code.
*/ */
struct dentry *debugfs_create_bool(const char *name, mode_t mode, struct dentry *debugfs_create_bool(const char *name, mode_t mode,
...@@ -264,13 +260,11 @@ static struct file_operations fops_blob = { ...@@ -264,13 +260,11 @@ static struct file_operations fops_blob = {
}; };
/** /**
* debugfs_create_blob - create a file in the debugfs filesystem that is * debugfs_create_blob - create a debugfs file that is used to read and write a binary blob
* used to read and write a binary blob.
*
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have * @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a * @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the * directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem. * file will be created in the root of the debugfs filesystem.
* @blob: a pointer to a struct debugfs_blob_wrapper which contains a pointer * @blob: a pointer to a struct debugfs_blob_wrapper which contains a pointer
* to the blob data and the size of the data. * to the blob data and the size of the data.
...@@ -282,11 +276,11 @@ static struct file_operations fops_blob = { ...@@ -282,11 +276,11 @@ static struct file_operations fops_blob = {
* This function will return a pointer to a dentry if it succeeds. This * This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is * pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded, * to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned. * you are responsible here.) If an error occurs, %NULL will be returned.
* *
* If debugfs is not enabled in the kernel, the value -ENODEV will be * If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for * returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code. * code.
*/ */
struct dentry *debugfs_create_blob(const char *name, mode_t mode, struct dentry *debugfs_create_blob(const char *name, mode_t mode,
......
...@@ -162,7 +162,6 @@ static int debugfs_create_by_name(const char *name, mode_t mode, ...@@ -162,7 +162,6 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
/** /**
* debugfs_create_file - create a file in the debugfs filesystem * debugfs_create_file - create a file in the debugfs filesystem
*
* @name: a pointer to a string containing the name of the file to create. * @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have * @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a * @parent: a pointer to the parent dentry for this file. This should be a
...@@ -182,11 +181,11 @@ static int debugfs_create_by_name(const char *name, mode_t mode, ...@@ -182,11 +181,11 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
* This function will return a pointer to a dentry if it succeeds. This * This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is * pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded, * to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned. * you are responsible here.) If an error occurs, %NULL will be returned.
* *
* If debugfs is not enabled in the kernel, the value -ENODEV will be * If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for * returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code. * code.
*/ */
struct dentry *debugfs_create_file(const char *name, mode_t mode, struct dentry *debugfs_create_file(const char *name, mode_t mode,
...@@ -221,7 +220,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_file); ...@@ -221,7 +220,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_file);
/** /**
* debugfs_create_dir - create a directory in the debugfs filesystem * debugfs_create_dir - create a directory in the debugfs filesystem
*
* @name: a pointer to a string containing the name of the directory to * @name: a pointer to a string containing the name of the directory to
* create. * create.
* @parent: a pointer to the parent dentry for this file. This should be a * @parent: a pointer to the parent dentry for this file. This should be a
...@@ -233,11 +231,11 @@ EXPORT_SYMBOL_GPL(debugfs_create_file); ...@@ -233,11 +231,11 @@ EXPORT_SYMBOL_GPL(debugfs_create_file);
* This function will return a pointer to a dentry if it succeeds. This * This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is * pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded, * to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned. * you are responsible here.) If an error occurs, %NULL will be returned.
* *
* If debugfs is not enabled in the kernel, the value -ENODEV will be * If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for * returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code. * code.
*/ */
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
...@@ -250,7 +248,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir); ...@@ -250,7 +248,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir);
/** /**
* debugfs_remove - removes a file or directory from the debugfs filesystem * debugfs_remove - removes a file or directory from the debugfs filesystem
*
* @dentry: a pointer to a the dentry of the file or directory to be * @dentry: a pointer to a the dentry of the file or directory to be
* removed. * removed.
* *
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/acct.h> #include <linux/acct.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/namespace.h> #include <linux/namespace.h>
#include <linux/namei.h> #include <linux/namei.h>
...@@ -28,15 +29,6 @@ ...@@ -28,15 +29,6 @@
extern int __init init_rootfs(void); extern int __init init_rootfs(void);
#ifdef CONFIG_SYSFS
extern int __init sysfs_init(void);
#else
static inline int sysfs_init(void)
{
return 0;
}
#endif
/* spinlock for vfsmount related operations, inplace of dcache_lock */ /* spinlock for vfsmount related operations, inplace of dcache_lock */
__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -176,7 +177,6 @@ const struct file_operations bin_fops = { ...@@ -176,7 +177,6 @@ const struct file_operations bin_fops = {
* sysfs_create_bin_file - create binary file for object. * sysfs_create_bin_file - create binary file for object.
* @kobj: object. * @kobj: object.
* @attr: attribute descriptor. * @attr: attribute descriptor.
*
*/ */
int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
...@@ -191,13 +191,16 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) ...@@ -191,13 +191,16 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
* sysfs_remove_bin_file - remove binary file for object. * sysfs_remove_bin_file - remove binary file for object.
* @kobj: object. * @kobj: object.
* @attr: attribute descriptor. * @attr: attribute descriptor.
*
*/ */
int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr) void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
{ {
sysfs_hash_and_remove(kobj->dentry,attr->attr.name); if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
return 0; printk(KERN_ERR "%s: "
"bad dentry or inode or no such file: \"%s\"\n",
__FUNCTION__, attr->attr.name);
dump_stack();
}
} }
EXPORT_SYMBOL_GPL(sysfs_create_bin_file); EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
......
...@@ -43,7 +43,7 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, ...@@ -43,7 +43,7 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd,
memset(sd, 0, sizeof(*sd)); memset(sd, 0, sizeof(*sd));
atomic_set(&sd->s_count, 1); atomic_set(&sd->s_count, 1);
atomic_set(&sd->s_event, 0); atomic_set(&sd->s_event, 1);
INIT_LIST_HEAD(&sd->s_children); INIT_LIST_HEAD(&sd->s_children);
list_add(&sd->s_sibling, &parent_sd->s_children); list_add(&sd->s_sibling, &parent_sd->s_children);
sd->s_element = element; sd->s_element = element;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/errno.h>
#include "sysfs.h" #include "sysfs.h"
extern struct super_block * sysfs_sb; extern struct super_block * sysfs_sb;
...@@ -234,17 +235,18 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) ...@@ -234,17 +235,18 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
} }
} }
void sysfs_hash_and_remove(struct dentry * dir, const char * name) int sysfs_hash_and_remove(struct dentry * dir, const char * name)
{ {
struct sysfs_dirent * sd; struct sysfs_dirent * sd;
struct sysfs_dirent * parent_sd; struct sysfs_dirent * parent_sd;
int found = 0;
if (!dir) if (!dir)
return; return -ENOENT;
if (dir->d_inode == NULL) if (dir->d_inode == NULL)
/* no inode means this hasn't been made visible yet */ /* no inode means this hasn't been made visible yet */
return; return -ENOENT;
parent_sd = dir->d_fsdata; parent_sd = dir->d_fsdata;
mutex_lock(&dir->d_inode->i_mutex); mutex_lock(&dir->d_inode->i_mutex);
...@@ -255,8 +257,11 @@ void sysfs_hash_and_remove(struct dentry * dir, const char * name) ...@@ -255,8 +257,11 @@ void sysfs_hash_and_remove(struct dentry * dir, const char * name)
list_del_init(&sd->s_sibling); list_del_init(&sd->s_sibling);
sysfs_drop_dentry(sd, dir); sysfs_drop_dentry(sd, dir);
sysfs_put(sd); sysfs_put(sd);
found = 1;
break; break;
} }
} }
mutex_unlock(&dir->d_inode->i_mutex); mutex_unlock(&dir->d_inode->i_mutex);
return found ? 0 : -ENOENT;
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
*/ */
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mount.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/namei.h> #include <linux/namei.h>
...@@ -82,10 +83,19 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj ...@@ -82,10 +83,19 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj
*/ */
int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
{ {
struct dentry * dentry = kobj->dentry; struct dentry *dentry = NULL;
int error = -EEXIST; int error = -EEXIST;
BUG_ON(!kobj || !kobj->dentry || !name); BUG_ON(!name);
if (!kobj) {
if (sysfs_mount && sysfs_mount->mnt_sb)
dentry = sysfs_mount->mnt_sb->s_root;
} else
dentry = kobj->dentry;
if (!dentry)
return -EFAULT;
mutex_lock(&dentry->d_inode->i_mutex); mutex_lock(&dentry->d_inode->i_mutex);
if (!sysfs_dirent_exist(dentry->d_fsdata, name)) if (!sysfs_dirent_exist(dentry->d_fsdata, name))
......
...@@ -10,7 +10,7 @@ extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, ...@@ -10,7 +10,7 @@ extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *,
umode_t, int); umode_t, int);
extern int sysfs_add_file(struct dentry *, const struct attribute *, int); extern int sysfs_add_file(struct dentry *, const struct attribute *, int);
extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); extern int sysfs_hash_and_remove(struct dentry * dir, const char * name);
extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name); extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name);
extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
......
...@@ -99,6 +99,11 @@ extern void __chk_io_ptr(void __iomem *); ...@@ -99,6 +99,11 @@ extern void __chk_io_ptr(void __iomem *);
#define __must_check #define __must_check
#endif #endif
#ifndef CONFIG_ENABLE_MUST_CHECK
#undef __must_check
#define __must_check
#endif
/* /*
* Allow us to avoid 'defined but not used' warnings on functions and data, * Allow us to avoid 'defined but not used' warnings on functions and data,
* as well as force them to be emitted to the assembly file. * as well as force them to be emitted to the assembly file.
......
This diff is collapsed.
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/compiler.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/kref.h> #include <linux/kref.h>
...@@ -71,12 +72,12 @@ static inline const char * kobject_name(const struct kobject * kobj) ...@@ -71,12 +72,12 @@ static inline const char * kobject_name(const struct kobject * kobj)
extern void kobject_init(struct kobject *); extern void kobject_init(struct kobject *);
extern void kobject_cleanup(struct kobject *); extern void kobject_cleanup(struct kobject *);
extern int kobject_add(struct kobject *); extern int __must_check kobject_add(struct kobject *);
extern void kobject_del(struct kobject *); extern void kobject_del(struct kobject *);
extern int kobject_rename(struct kobject *, const char *new_name); extern int __must_check kobject_rename(struct kobject *, const char *new_name);
extern int kobject_register(struct kobject *); extern int __must_check kobject_register(struct kobject *);
extern void kobject_unregister(struct kobject *); extern void kobject_unregister(struct kobject *);
extern struct kobject * kobject_get(struct kobject *); extern struct kobject * kobject_get(struct kobject *);
...@@ -128,8 +129,8 @@ struct kset { ...@@ -128,8 +129,8 @@ struct kset {
extern void kset_init(struct kset * k); extern void kset_init(struct kset * k);
extern int kset_add(struct kset * k); extern int __must_check kset_add(struct kset * k);
extern int kset_register(struct kset * k); extern int __must_check kset_register(struct kset * k);
extern void kset_unregister(struct kset * k); extern void kset_unregister(struct kset * k);
static inline struct kset * to_kset(struct kobject * kobj) static inline struct kset * to_kset(struct kobject * kobj)
...@@ -239,7 +240,7 @@ extern struct subsystem hypervisor_subsys; ...@@ -239,7 +240,7 @@ extern struct subsystem hypervisor_subsys;
(obj)->subsys.kset.kobj.kset = &(_subsys).kset (obj)->subsys.kset.kobj.kset = &(_subsys).kset
extern void subsystem_init(struct subsystem *); extern void subsystem_init(struct subsystem *);
extern int subsystem_register(struct subsystem *); extern int __must_check subsystem_register(struct subsystem *);
extern void subsystem_unregister(struct subsystem *); extern void subsystem_unregister(struct subsystem *);
static inline struct subsystem * subsys_get(struct subsystem * s) static inline struct subsystem * subsys_get(struct subsystem * s)
...@@ -258,7 +259,8 @@ struct subsys_attribute { ...@@ -258,7 +259,8 @@ struct subsys_attribute {
ssize_t (*store)(struct subsystem *, const char *, size_t); ssize_t (*store)(struct subsystem *, const char *, size_t);
}; };
extern int subsys_create_file(struct subsystem * , struct subsys_attribute *); extern int __must_check subsys_create_file(struct subsystem * ,
struct subsys_attribute *);
#if defined(CONFIG_HOTPLUG) #if defined(CONFIG_HOTPLUG)
void kobject_uevent(struct kobject *kobj, enum kobject_action action); void kobject_uevent(struct kobject *kobj, enum kobject_action action);
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/compiler.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -346,6 +347,8 @@ struct pci_driver { ...@@ -346,6 +347,8 @@ struct pci_driver {
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */ int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */ void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */ int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */ int (*resume) (struct pci_dev *dev); /* Device woken up */
int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */ int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */
void (*shutdown) (struct pci_dev *dev); void (*shutdown) (struct pci_dev *dev);
...@@ -401,7 +404,7 @@ extern struct list_head pci_root_buses; /* list of all known PCI buses */ ...@@ -401,7 +404,7 @@ extern struct list_head pci_root_buses; /* list of all known PCI buses */
extern struct list_head pci_devices; /* list of all devices */ extern struct list_head pci_devices; /* list of all devices */
void pcibios_fixup_bus(struct pci_bus *); void pcibios_fixup_bus(struct pci_bus *);
int pcibios_enable_device(struct pci_dev *, int mask); int __must_check pcibios_enable_device(struct pci_dev *, int mask);
char *pcibios_setup (char *str); char *pcibios_setup (char *str);
/* Used only when drivers/pci/setup.c is used */ /* Used only when drivers/pci/setup.c is used */
...@@ -488,19 +491,19 @@ static inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val ...@@ -488,19 +491,19 @@ static inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val
return pci_bus_write_config_dword (dev->bus, dev->devfn, where, val); return pci_bus_write_config_dword (dev->bus, dev->devfn, where, val);
} }
int pci_enable_device(struct pci_dev *dev); int __must_check pci_enable_device(struct pci_dev *dev);
int pci_enable_device_bars(struct pci_dev *dev, int mask); int __must_check pci_enable_device_bars(struct pci_dev *dev, int mask);
void pci_disable_device(struct pci_dev *dev); void pci_disable_device(struct pci_dev *dev);
void pci_set_master(struct pci_dev *dev); void pci_set_master(struct pci_dev *dev);
#define HAVE_PCI_SET_MWI #define HAVE_PCI_SET_MWI
int pci_set_mwi(struct pci_dev *dev); int __must_check pci_set_mwi(struct pci_dev *dev);
void pci_clear_mwi(struct pci_dev *dev); void pci_clear_mwi(struct pci_dev *dev);
void pci_intx(struct pci_dev *dev, int enable); void pci_intx(struct pci_dev *dev, int enable);
int pci_set_dma_mask(struct pci_dev *dev, u64 mask); int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask); int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno); void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno);
int pci_assign_resource(struct pci_dev *dev, int i); int __must_check pci_assign_resource(struct pci_dev *dev, int i);
int pci_assign_resource_fixed(struct pci_dev *dev, int i); int __must_check pci_assign_resource_fixed(struct pci_dev *dev, int i);
void pci_restore_bars(struct pci_dev *dev); void pci_restore_bars(struct pci_dev *dev);
/* ROM control related routines */ /* ROM control related routines */
...@@ -526,23 +529,24 @@ void pdev_sort_resources(struct pci_dev *, struct resource_list *); ...@@ -526,23 +529,24 @@ void pdev_sort_resources(struct pci_dev *, struct resource_list *);
void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *), void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
int (*)(struct pci_dev *, u8, u8)); int (*)(struct pci_dev *, u8, u8));
#define HAVE_PCI_REQ_REGIONS 2 #define HAVE_PCI_REQ_REGIONS 2
int pci_request_regions(struct pci_dev *, const char *); int __must_check pci_request_regions(struct pci_dev *, const char *);
void pci_release_regions(struct pci_dev *); void pci_release_regions(struct pci_dev *);
int pci_request_region(struct pci_dev *, int, const char *); int __must_check pci_request_region(struct pci_dev *, int, const char *);
void pci_release_region(struct pci_dev *, int); void pci_release_region(struct pci_dev *, int);
/* drivers/pci/bus.c */ /* drivers/pci/bus.c */
int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
resource_size_t size, resource_size_t align, struct resource *res, resource_size_t size,
resource_size_t min, unsigned int type_mask, resource_size_t align, resource_size_t min,
void (*alignf)(void *, struct resource *, unsigned int type_mask,
resource_size_t, resource_size_t), void (*alignf)(void *, struct resource *,
void *alignf_data); resource_size_t, resource_size_t),
void *alignf_data);
void pci_enable_bridges(struct pci_bus *bus); void pci_enable_bridges(struct pci_bus *bus);
/* Proper probing supporting hot-pluggable devices */ /* Proper probing supporting hot-pluggable devices */
int __pci_register_driver(struct pci_driver *, struct module *); int __must_check __pci_register_driver(struct pci_driver *, struct module *);
static inline int pci_register_driver(struct pci_driver *driver) static inline int __must_check pci_register_driver(struct pci_driver *driver)
{ {
return __pci_register_driver(driver, THIS_MODULE); return __pci_register_driver(driver, THIS_MODULE);
} }
......
...@@ -49,6 +49,8 @@ struct platform_driver { ...@@ -49,6 +49,8 @@ struct platform_driver {
int (*remove)(struct platform_device *); int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *); void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state); int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *); int (*resume)(struct platform_device *);
struct device_driver driver; struct device_driver driver;
}; };
......
...@@ -142,29 +142,61 @@ typedef struct pm_message { ...@@ -142,29 +142,61 @@ typedef struct pm_message {
} pm_message_t; } pm_message_t;
/* /*
* There are 4 important states driver can be in: * Several driver power state transitions are externally visible, affecting
* ON -- driver is working * the state of pending I/O queues and (for drivers that touch hardware)
* FREEZE -- stop operations and apply whatever policy is applicable to a * interrupts, wakeups, DMA, and other hardware state. There may also be
* suspended driver of that class, freeze queues for block like IDE * internal transitions to various low power modes, which are transparent
* does, drop packets for ethernet, etc... stop DMA engine too etc... * to the rest of the driver stack (such as a driver that's ON gating off
* so a consistent image can be saved; but do not power any hardware * clocks which are not in active use).
* down.
* SUSPEND - like FREEZE, but hardware is doing as much powersaving as
* possible. Roughly pci D3.
* *
* Unfortunately, current drivers only recognize numeric values 0 (ON) and 3 * One transition is triggered by resume(), after a suspend() call; the
* (SUSPEND). We'll need to fix the drivers. So yes, putting 3 to all different * message is implicit:
* defines is intentional, and will go away as soon as drivers are fixed. Also *
* note that typedef is neccessary, we'll probably want to switch to * ON Driver starts working again, responding to hardware events
* typedef struct pm_message_t { int event; int flags; } pm_message_t * and software requests. The hardware may have gone through
* or something similar soon. * a power-off reset, or it may have maintained state from the
* previous suspend() which the driver will rely on while
* resuming. On most platforms, there are no restrictions on
* availability of resources like clocks during resume().
*
* Other transitions are triggered by messages sent using suspend(). All
* these transitions quiesce the driver, so that I/O queues are inactive.
* That commonly entails turning off IRQs and DMA; there may be rules
* about how to quiesce that are specific to the bus or the device's type.
* (For example, network drivers mark the link state.) Other details may
* differ according to the message:
*
* SUSPEND Quiesce, enter a low power device state appropriate for
* the upcoming system state (such as PCI_D3hot), and enable
* wakeup events as appropriate.
*
* FREEZE Quiesce operations so that a consistent image can be saved;
* but do NOT otherwise enter a low power device state, and do
* NOT emit system wakeup events.
*
* PRETHAW Quiesce as if for FREEZE; additionally, prepare for restoring
* the system from a snapshot taken after an earlier FREEZE.
* Some drivers will need to reset their hardware state instead
* of preserving it, to ensure that it's never mistaken for the
* state which that earlier snapshot had set up.
*
* A minimally power-aware driver treats all messages as SUSPEND, fully
* reinitializes its device during resume() -- whether or not it was reset
* during the suspend/resume cycle -- and can't issue wakeup events.
*
* More power-aware drivers may also use low power states at runtime as
* well as during system sleep states like PM_SUSPEND_STANDBY. They may
* be able to use wakeup events to exit from runtime low-power states,
* or from system low-power states such as standby or suspend-to-RAM.
*/ */
#define PM_EVENT_ON 0 #define PM_EVENT_ON 0
#define PM_EVENT_FREEZE 1 #define PM_EVENT_FREEZE 1
#define PM_EVENT_SUSPEND 2 #define PM_EVENT_SUSPEND 2
#define PM_EVENT_PRETHAW 3
#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) #define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
#define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) #define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) #define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
...@@ -190,6 +222,7 @@ extern void device_resume(void); ...@@ -190,6 +222,7 @@ extern void device_resume(void);
extern suspend_disk_method_t pm_disk_mode; extern suspend_disk_method_t pm_disk_mode;
extern int device_suspend(pm_message_t state); extern int device_suspend(pm_message_t state);
extern int device_prepare_suspend(pm_message_t state);
#define device_set_wakeup_enable(dev,val) \ #define device_set_wakeup_enable(dev,val) \
((dev)->power.should_wakeup = !!(val)) ((dev)->power.should_wakeup = !!(val))
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#ifndef _SYSFS_H_ #ifndef _SYSFS_H_
#define _SYSFS_H_ #define _SYSFS_H_
#include <linux/compiler.h>
#include <asm/atomic.h> #include <asm/atomic.h>
struct kobject; struct kobject;
...@@ -86,40 +87,44 @@ struct sysfs_dirent { ...@@ -86,40 +87,44 @@ struct sysfs_dirent {
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
extern int extern int __must_check
sysfs_create_dir(struct kobject *); sysfs_create_dir(struct kobject *);
extern void extern void
sysfs_remove_dir(struct kobject *); sysfs_remove_dir(struct kobject *);
extern int extern int __must_check
sysfs_rename_dir(struct kobject *, const char *new_name); sysfs_rename_dir(struct kobject *, const char *new_name);
extern int extern int __must_check
sysfs_create_file(struct kobject *, const struct attribute *); sysfs_create_file(struct kobject *, const struct attribute *);
extern int extern int __must_check
sysfs_update_file(struct kobject *, const struct attribute *); sysfs_update_file(struct kobject *, const struct attribute *);
extern int extern int __must_check
sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode); sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode);
extern void extern void
sysfs_remove_file(struct kobject *, const struct attribute *); sysfs_remove_file(struct kobject *, const struct attribute *);
extern int extern int __must_check
sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name); sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name);
extern void extern void
sysfs_remove_link(struct kobject *, const char * name); sysfs_remove_link(struct kobject *, const char * name);
int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr); int __must_check sysfs_create_bin_file(struct kobject *kobj,
int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr); struct bin_attribute *attr);
void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
int sysfs_create_group(struct kobject *, const struct attribute_group *); int __must_check sysfs_create_group(struct kobject *,
const struct attribute_group *);
void sysfs_remove_group(struct kobject *, const struct attribute_group *); void sysfs_remove_group(struct kobject *, const struct attribute_group *);
void sysfs_notify(struct kobject * k, char *dir, char *attr); void sysfs_notify(struct kobject * k, char *dir, char *attr);
extern int __must_check sysfs_init(void);
#else /* CONFIG_SYSFS */ #else /* CONFIG_SYSFS */
static inline int sysfs_create_dir(struct kobject * k) static inline int sysfs_create_dir(struct kobject * k)
...@@ -191,6 +196,11 @@ static inline void sysfs_notify(struct kobject * k, char *dir, char *attr) ...@@ -191,6 +196,11 @@ static inline void sysfs_notify(struct kobject * k, char *dir, char *attr)
{ {
} }
static inline int __must_check sysfs_init(void)
{
return 0;
}
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_SYSFS */
#endif /* _SYSFS_H_ */ #endif /* _SYSFS_H_ */
...@@ -341,7 +341,7 @@ extern int video_usercopy(struct inode *inode, struct file *file, ...@@ -341,7 +341,7 @@ extern int video_usercopy(struct inode *inode, struct file *file,
extern struct video_device* video_devdata(struct file*); extern struct video_device* video_devdata(struct file*);
#define to_video_device(cd) container_of(cd, struct video_device, class_dev) #define to_video_device(cd) container_of(cd, struct video_device, class_dev)
static inline int static inline int __must_check
video_device_create_file(struct video_device *vfd, video_device_create_file(struct video_device *vfd,
struct class_device_attribute *attr) struct class_device_attribute *attr)
{ {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/security.h> #include <linux/security.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/device.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_fs_sb.h> #include <linux/nfs_fs_sb.h>
...@@ -403,6 +404,10 @@ void __init prepare_namespace(void) ...@@ -403,6 +404,10 @@ void __init prepare_namespace(void)
ssleep(root_delay); ssleep(root_delay);
} }
/* wait for the known devices to complete their probing */
while (driver_probe_done() != 0)
msleep(100);
md_run_setup(); md_run_setup();
if (saved_root_name[0]) { if (saved_root_name[0]) {
......
...@@ -64,6 +64,17 @@ config PM_TRACE ...@@ -64,6 +64,17 @@ config PM_TRACE
CAUTION: this option will cause your machine's real-time clock to be CAUTION: this option will cause your machine's real-time clock to be
set to an invalid time after a resume. set to an invalid time after a resume.
config PM_SYSFS_DEPRECATED
bool "Driver model /sys/devices/.../power/state files (DEPRECATED)"
depends on PM && SYSFS
default n
help
The driver model started out with a sysfs file intended to provide
a userspace hook for device power management. This feature has never
worked very well, except for limited testing purposes, and so it will
be removed. It's not clear that a generic mechanism could really
handle the wide variability of device power states; any replacements
are likely to be bus or driver specific.
config SOFTWARE_SUSPEND config SOFTWARE_SUSPEND
bool "Software Suspend" bool "Software Suspend"
......
...@@ -103,7 +103,7 @@ static void unprepare_processes(void) ...@@ -103,7 +103,7 @@ static void unprepare_processes(void)
} }
/** /**
* pm_suspend_disk - The granpappy of power management. * pm_suspend_disk - The granpappy of hibernation power management.
* *
* If we're going through the firmware, then get it over with quickly. * If we're going through the firmware, then get it over with quickly.
* *
...@@ -212,7 +212,7 @@ static int software_resume(void) ...@@ -212,7 +212,7 @@ static int software_resume(void)
pr_debug("PM: Preparing devices for restore.\n"); pr_debug("PM: Preparing devices for restore.\n");
if ((error = device_suspend(PMSG_FREEZE))) { if ((error = device_suspend(PMSG_PRETHAW))) {
printk("Some devices failed to suspend\n"); printk("Some devices failed to suspend\n");
swsusp_free(); swsusp_free();
goto Thaw; goto Thaw;
......
...@@ -247,6 +247,9 @@ int swsusp_suspend(void) ...@@ -247,6 +247,9 @@ int swsusp_suspend(void)
restore_processor_state(); restore_processor_state();
Restore_highmem: Restore_highmem:
restore_highmem(); restore_highmem();
/* NOTE: device_power_up() is just a resume() for devices
* that suspended with irqs off ... no overall powerup.
*/
device_power_up(); device_power_up();
Enable_irqs: Enable_irqs:
local_irq_enable(); local_irq_enable();
...@@ -256,8 +259,12 @@ int swsusp_suspend(void) ...@@ -256,8 +259,12 @@ int swsusp_suspend(void)
int swsusp_resume(void) int swsusp_resume(void)
{ {
int error; int error;
local_irq_disable(); local_irq_disable();
if (device_power_down(PMSG_FREEZE)) /* NOTE: device_power_down() is just a suspend() with irqs off;
* it has no special "power things down" semantics
*/
if (device_power_down(PMSG_PRETHAW))
printk(KERN_ERR "Some devices failed to power down, very bad\n"); printk(KERN_ERR "Some devices failed to power down, very bad\n");
/* We'll ignore saved state, but this gets preempt count (etc) right */ /* We'll ignore saved state, but this gets preempt count (etc) right */
save_processor_state(); save_processor_state();
......
...@@ -196,7 +196,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, ...@@ -196,7 +196,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
snapshot_free_unused_memory(&data->handle); snapshot_free_unused_memory(&data->handle);
down(&pm_sem); down(&pm_sem);
pm_prepare_console(); pm_prepare_console();
error = device_suspend(PMSG_FREEZE); error = device_suspend(PMSG_PRETHAW);
if (!error) { if (!error) {
error = swsusp_resume(); error = swsusp_resume();
device_resume(); device_resume();
......
...@@ -8,6 +8,13 @@ config PRINTK_TIME ...@@ -8,6 +8,13 @@ config PRINTK_TIME
operations. This is useful for identifying long delays operations. This is useful for identifying long delays
in kernel startup. in kernel startup.
config ENABLE_MUST_CHECK
bool "Enable __must_check logic"
default y
help
Enable the __must_check logic in the kernel build. Disable this to
suppress the "warning: ignoring return value of 'foo', declared with
attribute warn_unused_result" messages.
config MAGIC_SYSRQ config MAGIC_SYSRQ
bool "Magic SysRq key" bool "Magic SysRq key"
......
...@@ -123,12 +123,10 @@ EXPORT_SYMBOL_GPL(klist_add_tail); ...@@ -123,12 +123,10 @@ EXPORT_SYMBOL_GPL(klist_add_tail);
static void klist_release(struct kref * kref) static void klist_release(struct kref * kref)
{ {
struct klist_node * n = container_of(kref, struct klist_node, n_ref); struct klist_node * n = container_of(kref, struct klist_node, n_ref);
void (*put)(struct klist_node *) = n->n_klist->put;
list_del(&n->n_node); list_del(&n->n_node);
complete(&n->n_removed); complete(&n->n_removed);
n->n_klist = NULL; n->n_klist = NULL;
if (put)
put(n);
} }
static int klist_dec_and_del(struct klist_node * n) static int klist_dec_and_del(struct klist_node * n)
...@@ -145,10 +143,14 @@ static int klist_dec_and_del(struct klist_node * n) ...@@ -145,10 +143,14 @@ static int klist_dec_and_del(struct klist_node * n)
void klist_del(struct klist_node * n) void klist_del(struct klist_node * n)
{ {
struct klist * k = n->n_klist; struct klist * k = n->n_klist;
void (*put)(struct klist_node *) = k->put;
spin_lock(&k->k_lock); spin_lock(&k->k_lock);
klist_dec_and_del(n); if (!klist_dec_and_del(n))
put = NULL;
spin_unlock(&k->k_lock); spin_unlock(&k->k_lock);
if (put)
put(n);
} }
EXPORT_SYMBOL_GPL(klist_del); EXPORT_SYMBOL_GPL(klist_del);
...@@ -161,10 +163,7 @@ EXPORT_SYMBOL_GPL(klist_del); ...@@ -161,10 +163,7 @@ EXPORT_SYMBOL_GPL(klist_del);
void klist_remove(struct klist_node * n) void klist_remove(struct klist_node * n)
{ {
struct klist * k = n->n_klist; klist_del(n);
spin_lock(&k->k_lock);
klist_dec_and_del(n);
spin_unlock(&k->k_lock);
wait_for_completion(&n->n_removed); wait_for_completion(&n->n_removed);
} }
...@@ -260,12 +259,15 @@ static struct klist_node * to_klist_node(struct list_head * n) ...@@ -260,12 +259,15 @@ static struct klist_node * to_klist_node(struct list_head * n)
struct klist_node * klist_next(struct klist_iter * i) struct klist_node * klist_next(struct klist_iter * i)
{ {
struct list_head * next; struct list_head * next;
struct klist_node * lnode = i->i_cur;
struct klist_node * knode = NULL; struct klist_node * knode = NULL;
void (*put)(struct klist_node *) = i->i_klist->put;
spin_lock(&i->i_klist->k_lock); spin_lock(&i->i_klist->k_lock);
if (i->i_cur) { if (lnode) {
next = i->i_cur->n_node.next; next = lnode->n_node.next;
klist_dec_and_del(i->i_cur); if (!klist_dec_and_del(lnode))
put = NULL;
} else } else
next = i->i_head->next; next = i->i_head->next;
...@@ -275,6 +277,8 @@ struct klist_node * klist_next(struct klist_iter * i) ...@@ -275,6 +277,8 @@ struct klist_node * klist_next(struct klist_iter * i)
} }
i->i_cur = knode; i->i_cur = knode;
spin_unlock(&i->i_klist->k_lock); spin_unlock(&i->i_klist->k_lock);
if (put && lnode)
put(lnode);
return knode; return knode;
} }
......
...@@ -407,6 +407,7 @@ static struct kobj_type dir_ktype = { ...@@ -407,6 +407,7 @@ static struct kobj_type dir_ktype = {
struct kobject *kobject_add_dir(struct kobject *parent, const char *name) struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
{ {
struct kobject *k; struct kobject *k;
int ret;
if (!parent) if (!parent)
return NULL; return NULL;
...@@ -418,7 +419,13 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name) ...@@ -418,7 +419,13 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
k->parent = parent; k->parent = parent;
k->ktype = &dir_ktype; k->ktype = &dir_ktype;
kobject_set_name(k, name); kobject_set_name(k, name);
kobject_register(k); ret = kobject_register(k);
if (ret < 0) {
printk(KERN_WARNING "kobject_add_dir: "
"kobject_register error: %d\n", ret);
kobject_del(k);
return NULL;
}
return k; return k;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment