From 2023c610dc54a4f4130b0494309a9bd668ca3df8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 30 Jul 2009 15:27:18 -0400 Subject: Driver core: add new device to bus's list before probing This patch (as1271) affects when new devices get linked into their bus's list of devices. Currently this happens after probing, and it doesn't happen at all if probing fails. Clearly this is wrong, because at that point quite a few symbolic links have already been created in sysfs. We are committed to adding the device, so it should be linked into the bus's list regardless. In addition, this needs to happen before the uevent announcing the new device gets issued. Otherwise user programs might try to access the device before it has been added to the bus. To fix both these problems, the patch moves the call to klist_add_tail() forward from bus_attach_device() to bus_add_device(). Since bus_attach_device() now does nothing but probe for drivers, it has been renamed to bus_probe_device(). And lastly, the kerneldoc is updated. Signed-off-by: Alan Stern CC: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 +- drivers/base/bus.c | 23 ++++++++++------------- drivers/base/core.c | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index b528145a078..1e52c125f43 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -104,7 +104,7 @@ extern int system_bus_init(void); extern int cpu_dev_init(void); extern int bus_add_device(struct device *dev); -extern void bus_attach_device(struct device *dev); +extern void bus_probe_device(struct device *dev); extern void bus_remove_device(struct device *dev); extern int bus_add_driver(struct device_driver *drv); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 4b04a15146d..973bf2ad4e0 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -459,8 +459,9 @@ static inline void remove_deprecated_bus_links(struct device *dev) { } * bus_add_device - add device to bus * @dev: device being added * + * - Add device's bus attributes. + * - Create links to device's bus. * - Add the device to its bus's list of devices. - * - Create link to device's bus. */ int bus_add_device(struct device *dev) { @@ -483,6 +484,7 @@ int bus_add_device(struct device *dev) error = make_deprecated_bus_links(dev); if (error) goto out_deprecated; + klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); } return 0; @@ -498,24 +500,19 @@ out_put: } /** - * bus_attach_device - add device to bus - * @dev: device tried to attach to a driver + * bus_probe_device - probe drivers for a new device + * @dev: device to probe * - * - Add device to bus's list of devices. - * - Try to attach to driver. + * - Automatically probe for a driver if the bus allows it. */ -void bus_attach_device(struct device *dev) +void bus_probe_device(struct device *dev) { struct bus_type *bus = dev->bus; - int ret = 0; + int ret; - if (bus) { - if (bus->p->drivers_autoprobe) - ret = device_attach(dev); + if (bus && bus->p->drivers_autoprobe) { + ret = device_attach(dev); WARN_ON(ret < 0); - if (ret >= 0) - klist_add_tail(&dev->p->knode_bus, - &bus->p->klist_devices); } } diff --git a/drivers/base/core.c b/drivers/base/core.c index 7ecb1938e59..c34774d0b9d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -945,7 +945,7 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); - bus_attach_device(dev); + bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); -- cgit v1.2.3 From b4028437876866aba4747a655ede00f892089e14 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 May 2009 14:16:57 -0700 Subject: Driver core: move dev_get/set_drvdata to drivers/base/dd.c No one should directly access the driver_data field, so remove the field and make it private. We dynamically create the private field now if it is needed, to handle drivers that call get/set before they are registered with the driver core. Also update the copyright notices on these files while we are there. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 5 +++++ drivers/base/core.c | 20 ++++++++++++++------ drivers/base/dd.c | 31 +++++++++++++++++++++++++++++-- include/linux/device.h | 16 ++++------------ 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 1e52c125f43..503d59c5750 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -70,6 +70,8 @@ struct class_private { * @knode_parent - node in sibling list * @knode_driver - node in driver list * @knode_bus - node in bus list + * @driver_data - private pointer for driver specific info. Will turn into a + * list soon. * @device - pointer back to the struct class that this structure is * associated with. * @@ -80,6 +82,7 @@ struct device_private { struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; + void *driver_data; struct device *device; }; #define to_device_private_parent(obj) \ @@ -89,6 +92,8 @@ struct device_private { #define to_device_private_bus(obj) \ container_of(obj, struct device_private, knode_bus) +extern int device_private_init(struct device *dev); + /* initialisation functions */ extern int devices_init(void); extern int buses_init(void); diff --git a/drivers/base/core.c b/drivers/base/core.c index c34774d0b9d..99dfe96fffc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -843,6 +843,17 @@ static void device_remove_sys_dev_entry(struct device *dev) } } +int device_private_init(struct device *dev) +{ + dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); + if (!dev->p) + return -ENOMEM; + dev->p->device = dev; + klist_init(&dev->p->klist_children, klist_children_get, + klist_children_put); + return 0; +} + /** * device_add - add device to device hierarchy. * @dev: device. @@ -868,14 +879,11 @@ int device_add(struct device *dev) if (!dev) goto done; - dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); if (!dev->p) { - error = -ENOMEM; - goto done; + error = device_private_init(dev); + if (error) + goto done; } - dev->p->device = dev; - klist_init(&dev->p->klist_children, klist_children_get, - klist_children_put); /* * for statically allocated devices, which should all be converted diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 7b34b3a48f6..979d159b5cd 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -11,8 +11,8 @@ * * Copyright (c) 2002-5 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs - * Copyright (c) 2007 Greg Kroah-Hartman - * Copyright (c) 2007 Novell Inc. + * Copyright (c) 2007-2009 Greg Kroah-Hartman + * Copyright (c) 2007-2009 Novell Inc. * * This file is released under the GPLv2 */ @@ -391,3 +391,30 @@ void driver_detach(struct device_driver *drv) put_device(dev); } } + +/* + * These exports can't be _GPL due to .h files using this within them, and it + * might break something that was previously working... + */ +void *dev_get_drvdata(const struct device *dev) +{ + if (dev && dev->p) + return dev->p->driver_data; + return NULL; +} +EXPORT_SYMBOL(dev_get_drvdata); + +void dev_set_drvdata(struct device *dev, void *data) +{ + int error; + + if (!dev) + return; + if (!dev->p) { + error = device_private_init(dev); + if (error) + return; + } + dev->p->driver_data = data; +} +EXPORT_SYMBOL(dev_set_drvdata); diff --git a/include/linux/device.h b/include/linux/device.h index a2864297505..c0bd23048be 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -2,7 +2,8 @@ * device.h - generic, centralized driver model * * Copyright (c) 2001-2003 Patrick Mochel - * Copyright (c) 2004-2007 Greg Kroah-Hartman + * Copyright (c) 2004-2009 Greg Kroah-Hartman + * Copyright (c) 2008-2009 Novell Inc. * * This file is released under the GPLv2 * @@ -381,7 +382,6 @@ struct device { struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ - void *driver_data; /* data private to the driver */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; @@ -447,16 +447,6 @@ static inline void set_dev_node(struct device *dev, int node) } #endif -static inline void *dev_get_drvdata(const struct device *dev) -{ - return dev->driver_data; -} - -static inline void dev_set_drvdata(struct device *dev, void *data) -{ - dev->driver_data = data; -} - static inline unsigned int dev_get_uevent_suppress(const struct device *dev) { return dev->kobj.uevent_suppress; @@ -490,6 +480,8 @@ extern int device_rename(struct device *dev, char *new_name); extern int device_move(struct device *dev, struct device *new_parent, enum dpm_order dpm_order); extern const char *device_get_nodename(struct device *dev, const char **tmp); +extern void *dev_get_drvdata(const struct device *dev); +extern void dev_set_drvdata(struct device *dev, void *data); /* * Root device objects for grouping under /sys/devices -- cgit v1.2.3 From a5b8b1ada6dd76503884f5492b995cd29eefae0f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Jul 2009 15:06:08 +0100 Subject: Driver core: Add accessor for device platform data For consistency with driver data provide a dev_get_platdata() accessor for reading the platform data from a device. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/device.h b/include/linux/device.h index c0bd23048be..3f33f17f556 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -494,6 +494,11 @@ static inline struct device *root_device_register(const char *name) } extern void root_device_unregister(struct device *root); +static inline void *dev_get_platdata(const struct device *dev) +{ + return dev->platform_data; +} + /* * Manual binding of a device to driver. See drivers/base/bus.c * for information on use. -- cgit v1.2.3 From 5b2ea2f10dbb2fa91d8033993000f8664309395f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 24 Jun 2009 17:30:25 +0200 Subject: UIO: remove 'default n' from Kconfig 'default n' is the default, there is no need for these lines. Signed-off-by: Daniel Mack Cc: Hans J. Koch Signed-off-by: Greg Kroah-Hartman --- drivers/uio/Kconfig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 7f86534de26..45200fd6853 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -1,7 +1,6 @@ menuconfig UIO tristate "Userspace I/O drivers" depends on !S390 - default n help Enable this to allow the userspace driver core code to be built. This code allows userspace programs easy access to @@ -16,7 +15,6 @@ if UIO config UIO_CIF tristate "generic Hilscher CIF Card driver" depends on PCI - default n help Driver for Hilscher CIF DeviceNet and Profibus cards. This driver requires a userspace component that handles all of the @@ -48,7 +46,6 @@ config UIO_PDRV_GENIRQ config UIO_SMX tristate "SMX cryptengine UIO interface" - default n help Userspace IO interface to the Cryptography engine found on the Nias Digital SMX boards. These will be available from Q4 2008 @@ -61,7 +58,6 @@ config UIO_SMX config UIO_AEC tristate "AEC video timestamp device" depends on PCI - default n help UIO driver for the Adrienne Electronics Corporation PCI time @@ -78,7 +74,6 @@ config UIO_AEC config UIO_SERCOS3 tristate "Automata Sercos III PCI card driver" - default n help Userspace I/O interface for the Sercos III PCI card from Automata GmbH. The userspace part of this driver will be -- cgit v1.2.3 From a4dbd6740df0872cdf0a86841f75beec8381964d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 24 Jun 2009 10:06:31 -0700 Subject: driver model: constify attribute groups Let attribute group vectors be declared "const". We'd like to let most attribute metadata live in read-only sections... this is a start. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- block/genhd.c | 2 +- drivers/base/core.c | 4 ++-- drivers/base/driver.c | 4 ++-- drivers/block/cciss.c | 2 +- drivers/firewire/core-device.c | 2 +- drivers/firmware/dmi-id.c | 2 +- drivers/infiniband/hw/ehca/ehca_main.c | 2 +- drivers/infiniband/hw/ipath/ipath_kernel.h | 2 +- drivers/infiniband/hw/ipath/ipath_sysfs.c | 2 +- drivers/input/input.c | 2 +- drivers/misc/enclosure.c | 4 ++-- drivers/mmc/core/mmc.c | 2 +- drivers/mmc/core/sd.c | 2 +- drivers/mtd/mtdcore.c | 2 +- drivers/s390/cio/css.c | 2 +- drivers/s390/cio/device.c | 2 +- drivers/s390/net/netiucv.c | 2 +- drivers/scsi/scsi_priv.h | 2 +- drivers/scsi/scsi_sysfs.c | 4 ++-- drivers/usb/core/endpoint.c | 2 +- drivers/usb/core/sysfs.c | 4 ++-- drivers/usb/core/usb.h | 4 ++-- drivers/uwb/lc-dev.c | 2 +- fs/partitions/check.c | 2 +- include/linux/attribute_container.h | 2 +- include/linux/device.h | 6 +++--- include/linux/netdevice.h | 2 +- include/linux/transport_class.h | 2 +- net/bluetooth/hci_sysfs.c | 4 ++-- net/core/net-sysfs.c | 2 +- 30 files changed, 39 insertions(+), 39 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index 5b76bf55d05..2ad91ddad8e 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -903,7 +903,7 @@ static struct attribute_group disk_attr_group = { .attrs = disk_attrs, }; -static struct attribute_group *disk_attr_groups[] = { +static const struct attribute_group *disk_attr_groups[] = { &disk_attr_group, NULL }; diff --git a/drivers/base/core.c b/drivers/base/core.c index 99dfe96fffc..a992985d1fa 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -341,7 +341,7 @@ static void device_remove_attributes(struct device *dev, } static int device_add_groups(struct device *dev, - struct attribute_group **groups) + const struct attribute_group **groups) { int error = 0; int i; @@ -361,7 +361,7 @@ static int device_add_groups(struct device *dev, } static void device_remove_groups(struct device *dev, - struct attribute_group **groups) + const struct attribute_group **groups) { int i; diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 8ae0f63602e..ed2ebd3c287 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -181,7 +181,7 @@ void put_driver(struct device_driver *drv) EXPORT_SYMBOL_GPL(put_driver); static int driver_add_groups(struct device_driver *drv, - struct attribute_group **groups) + const struct attribute_group **groups) { int error = 0; int i; @@ -201,7 +201,7 @@ static int driver_add_groups(struct device_driver *drv, } static void driver_remove_groups(struct device_driver *drv, - struct attribute_group **groups) + const struct attribute_group **groups) { int i; diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 0589dfbbd7d..d8372b43282 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -572,7 +572,7 @@ static struct attribute_group cciss_dev_attr_group = { .attrs = cciss_dev_attrs, }; -static struct attribute_group *cciss_dev_attr_groups[] = { +static const struct attribute_group *cciss_dev_attr_groups[] = { &cciss_dev_attr_group, NULL }; diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 97e656af2d2..9d0dfcbe2c1 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -312,7 +312,7 @@ static void init_fw_attribute_group(struct device *dev, group->groups[0] = &group->group; group->groups[1] = NULL; group->group.attrs = group->attrs; - dev->groups = group->groups; + dev->groups = (const struct attribute_group **) group->groups; } static ssize_t modalias_show(struct device *dev, diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index 5a76d056b9d..dbdf6fadfc7 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -139,7 +139,7 @@ static struct attribute_group sys_dmi_attribute_group = { .attrs = sys_dmi_attributes, }; -static struct attribute_group* sys_dmi_attribute_groups[] = { +static const struct attribute_group* sys_dmi_attribute_groups[] = { &sys_dmi_attribute_group, NULL }; diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index 5b635aa5947..fb2d83c5bf0 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -623,7 +623,7 @@ static struct attribute_group ehca_drv_attr_grp = { .attrs = ehca_drv_attrs }; -static struct attribute_group *ehca_drv_attr_groups[] = { +static const struct attribute_group *ehca_drv_attr_groups[] = { &ehca_drv_attr_grp, NULL, }; diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index 6ba4861dd6a..b3d7efcdf02 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h @@ -1286,7 +1286,7 @@ struct device_driver; extern const char ib_ipath_version[]; -extern struct attribute_group *ipath_driver_attr_groups[]; +extern const struct attribute_group *ipath_driver_attr_groups[]; int ipath_device_create_group(struct device *, struct ipath_devdata *); void ipath_device_remove_group(struct device *, struct ipath_devdata *); diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c index a6c8efbdc0c..b8cb2f145ae 100644 --- a/drivers/infiniband/hw/ipath/ipath_sysfs.c +++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c @@ -1069,7 +1069,7 @@ static ssize_t show_tempsense(struct device *dev, return ret; } -struct attribute_group *ipath_driver_attr_groups[] = { +const struct attribute_group *ipath_driver_attr_groups[] = { &driver_attr_group, NULL, }; diff --git a/drivers/input/input.c b/drivers/input/input.c index 7c237e6ac71..851791d955f 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1144,7 +1144,7 @@ static struct attribute_group input_dev_caps_attr_group = { .attrs = input_dev_caps_attrs, }; -static struct attribute_group *input_dev_attr_groups[] = { +static const struct attribute_group *input_dev_attr_groups[] = { &input_dev_attr_group, &input_dev_id_attr_group, &input_dev_caps_attr_group, diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 7b039306037..e9eae4a7840 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -238,7 +238,7 @@ static void enclosure_component_release(struct device *dev) put_device(dev->parent); } -static struct attribute_group *enclosure_groups[]; +static const struct attribute_group *enclosure_groups[]; /** * enclosure_component_register - add a particular component to an enclosure @@ -536,7 +536,7 @@ static struct attribute_group enclosure_group = { .attrs = enclosure_component_attrs, }; -static struct attribute_group *enclosure_groups[] = { +static const struct attribute_group *enclosure_groups[] = { &enclosure_group, NULL }; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 06084dbf127..2fb9d5f271e 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -276,7 +276,7 @@ static struct attribute_group mmc_std_attr_group = { .attrs = mmc_std_attrs, }; -static struct attribute_group *mmc_attr_groups[] = { +static const struct attribute_group *mmc_attr_groups[] = { &mmc_std_attr_group, NULL, }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index cd81c395e16..7ad646fe077 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -314,7 +314,7 @@ static struct attribute_group sd_std_attr_group = { .attrs = sd_std_attrs, }; -static struct attribute_group *sd_attr_groups[] = { +static const struct attribute_group *sd_attr_groups[] = { &sd_std_attr_group, NULL, }; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 00ebf7af746..69007a6eff5 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -217,7 +217,7 @@ struct attribute_group mtd_group = { .attrs = mtd_attrs, }; -struct attribute_group *mtd_groups[] = { +const struct attribute_group *mtd_groups[] = { &mtd_group, NULL, }; diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index e995123fd80..393c73c47f8 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -266,7 +266,7 @@ static struct attribute_group subch_attr_group = { .attrs = subch_attrs, }; -static struct attribute_group *default_subch_attr_groups[] = { +static const struct attribute_group *default_subch_attr_groups[] = { &subch_attr_group, NULL, }; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 0f95405c2c5..6527f3f3449 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -656,7 +656,7 @@ static struct attribute_group ccwdev_attr_group = { .attrs = ccwdev_attrs, }; -static struct attribute_group *ccwdev_attr_groups[] = { +static const struct attribute_group *ccwdev_attr_groups[] = { &ccwdev_attr_group, NULL, }; diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 9215fbbccc0..a4b2c576144 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -2159,7 +2159,7 @@ static struct attribute_group netiucv_drv_attr_group = { .attrs = netiucv_drv_attrs, }; -static struct attribute_group *netiucv_drv_attr_groups[] = { +static const struct attribute_group *netiucv_drv_attr_groups[] = { &netiucv_drv_attr_group, NULL, }; diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 021e503c8c4..1fbf7c78bba 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -132,7 +132,7 @@ extern struct scsi_transport_template blank_transport_template; extern void __scsi_remove_device(struct scsi_device *); extern struct bus_type scsi_bus_type; -extern struct attribute_group *scsi_sysfs_shost_attr_groups[]; +extern const struct attribute_group *scsi_sysfs_shost_attr_groups[]; /* scsi_netlink.c */ #ifdef CONFIG_SCSI_NETLINK diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 91482f2dcc5..fde54537d71 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -275,7 +275,7 @@ struct attribute_group scsi_shost_attr_group = { .attrs = scsi_sysfs_shost_attrs, }; -struct attribute_group *scsi_sysfs_shost_attr_groups[] = { +const struct attribute_group *scsi_sysfs_shost_attr_groups[] = { &scsi_shost_attr_group, NULL }; @@ -745,7 +745,7 @@ static struct attribute_group scsi_sdev_attr_group = { .attrs = scsi_sdev_attrs, }; -static struct attribute_group *scsi_sdev_attr_groups[] = { +static const struct attribute_group *scsi_sdev_attr_groups[] = { &scsi_sdev_attr_group, NULL }; diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index bc39fc40bbd..fdfaa788551 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -154,7 +154,7 @@ static struct attribute *ep_dev_attrs[] = { static struct attribute_group ep_dev_attr_grp = { .attrs = ep_dev_attrs, }; -static struct attribute_group *ep_dev_groups[] = { +static const struct attribute_group *ep_dev_groups[] = { &ep_dev_attr_grp, NULL }; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index b5c72e45894..7ec3041ae79 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -573,7 +573,7 @@ static struct attribute_group dev_string_attr_grp = { .is_visible = dev_string_attrs_are_visible, }; -struct attribute_group *usb_device_groups[] = { +const struct attribute_group *usb_device_groups[] = { &dev_attr_grp, &dev_string_attr_grp, NULL @@ -799,7 +799,7 @@ static struct attribute_group intf_assoc_attr_grp = { .is_visible = intf_assoc_attrs_are_visible, }; -struct attribute_group *usb_interface_groups[] = { +const struct attribute_group *usb_interface_groups[] = { &intf_attr_grp, &intf_assoc_attr_grp, NULL diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index e2a8cfaade1..c0e0ae2bb8e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -152,8 +152,8 @@ static inline int is_active(const struct usb_interface *f) extern const char *usbcore_name; /* sysfs stuff */ -extern struct attribute_group *usb_device_groups[]; -extern struct attribute_group *usb_interface_groups[]; +extern const struct attribute_group *usb_device_groups[]; +extern const struct attribute_group *usb_interface_groups[]; /* usbfs stuff */ extern struct mutex usbfs_mutex; diff --git a/drivers/uwb/lc-dev.c b/drivers/uwb/lc-dev.c index e9fe1bb7eb2..1097e81b56d 100644 --- a/drivers/uwb/lc-dev.c +++ b/drivers/uwb/lc-dev.c @@ -255,7 +255,7 @@ static struct attribute_group dev_attr_group = { .attrs = dev_attrs, }; -static struct attribute_group *groups[] = { +static const struct attribute_group *groups[] = { &dev_attr_group, NULL, }; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 619ba99dfe3..fbeaddf595d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -312,7 +312,7 @@ static struct attribute_group part_attr_group = { .attrs = part_attrs, }; -static struct attribute_group *part_attr_groups[] = { +static const struct attribute_group *part_attr_groups[] = { &part_attr_group, #ifdef CONFIG_BLK_DEV_IO_TRACE &blk_trace_attr_group, diff --git a/include/linux/attribute_container.h b/include/linux/attribute_container.h index 794ad74b1d6..c3ab81428c6 100644 --- a/include/linux/attribute_container.h +++ b/include/linux/attribute_container.h @@ -17,7 +17,7 @@ struct attribute_container { struct list_head node; struct klist containers; struct class *class; - struct attribute_group *grp; + const struct attribute_group *grp; struct device_attribute **attrs; int (*match)(struct attribute_container *, struct device *); #define ATTRIBUTE_CONTAINER_NO_CLASSDEVS 0x01 diff --git a/include/linux/device.h b/include/linux/device.h index 3f33f17f556..e19e40a3dcb 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -131,7 +131,7 @@ struct device_driver { void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); - struct attribute_group **groups; + const struct attribute_group **groups; const struct dev_pm_ops *pm; @@ -288,7 +288,7 @@ extern void class_destroy(struct class *cls); */ struct device_type { const char *name; - struct attribute_group **groups; + const struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); char *(*nodename)(struct device *dev); void (*release)(struct device *dev); @@ -412,7 +412,7 @@ struct device { struct klist_node knode_class; struct class *class; - struct attribute_group **groups; /* optional groups */ + const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); }; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 65ee1929b2b..a9aa4b5917d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -895,7 +895,7 @@ struct net_device /* class/net/name entry */ struct device dev; /* space for optional statistics and wireless sysfs groups */ - struct attribute_group *sysfs_groups[3]; + const struct attribute_group *sysfs_groups[3]; /* rtnetlink link ops */ const struct rtnl_link_ops *rtnl_link_ops; diff --git a/include/linux/transport_class.h b/include/linux/transport_class.h index eaec1ea9558..9ae8da3e640 100644 --- a/include/linux/transport_class.h +++ b/include/linux/transport_class.h @@ -55,7 +55,7 @@ struct anon_transport_class cls = { \ struct transport_container { struct attribute_container ac; - struct attribute_group *statistics; + const struct attribute_group *statistics; }; #define attribute_container_to_transport_container(x) \ diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 95f7a7a544b..7f939ce2980 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -68,7 +68,7 @@ static struct attribute_group bt_link_group = { .attrs = bt_link_attrs, }; -static struct attribute_group *bt_link_groups[] = { +static const struct attribute_group *bt_link_groups[] = { &bt_link_group, NULL }; @@ -392,7 +392,7 @@ static struct attribute_group bt_host_group = { .attrs = bt_host_attrs, }; -static struct attribute_group *bt_host_groups[] = { +static const struct attribute_group *bt_host_groups[] = { &bt_host_group, NULL }; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index ad91e9e5f47..7d4c57523b0 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -493,7 +493,7 @@ void netdev_unregister_kobject(struct net_device * net) int netdev_register_kobject(struct net_device *net) { struct device *dev = &(net->dev); - struct attribute_group **groups = net->sysfs_groups; + const struct attribute_group **groups = net->sysfs_groups; dev->class = &net_class; dev->platform_data = net; -- cgit v1.2.3 From 389e0cb9a1dec22ec37a104dec5009dd7a33dd59 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 4 Jul 2009 16:51:29 +0200 Subject: mem_class: use minor as index instead of searching the array Declare the device list with the minor numbers as the index, which saves us from searching for a matching list entry. Remove old devfs permissions declaration. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 82 ++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 645237bda68..bed3503184e 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -864,71 +864,67 @@ static const struct file_operations kmsg_fops = { .write = kmsg_write, }; -static const struct { - unsigned int minor; - char *name; - umode_t mode; - const struct file_operations *fops; - struct backing_dev_info *dev_info; -} devlist[] = { /* list of minor devices */ - {1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops, - &directly_mappable_cdev_bdi}, +static const struct memdev { + const char *name; + const struct file_operations *fops; + struct backing_dev_info *dev_info; +} devlist[] = { + [ 1] = { "mem", &mem_fops, &directly_mappable_cdev_bdi }, #ifdef CONFIG_DEVKMEM - {2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops, - &directly_mappable_cdev_bdi}, + [ 2] = { "kmem", &kmem_fops, &directly_mappable_cdev_bdi }, #endif - {3, "null", S_IRUGO | S_IWUGO, &null_fops, NULL}, + [ 3] = {"null", &null_fops, NULL }, #ifdef CONFIG_DEVPORT - {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops, NULL}, + [ 4] = { "port", &port_fops, NULL }, #endif - {5, "zero", S_IRUGO | S_IWUGO, &zero_fops, &zero_bdi}, - {7, "full", S_IRUGO | S_IWUGO, &full_fops, NULL}, - {8, "random", S_IRUGO | S_IWUSR, &random_fops, NULL}, - {9, "urandom", S_IRUGO | S_IWUSR, &urandom_fops, NULL}, - {11,"kmsg", S_IRUGO | S_IWUSR, &kmsg_fops, NULL}, + [ 5] = { "zero", &zero_fops, &zero_bdi }, + [ 6] = { "full", &full_fops, NULL }, + [ 7] = { "random", &random_fops, NULL }, + [ 9] = { "urandom", &urandom_fops, NULL }, + [11] = { "kmsg", &kmsg_fops, NULL }, #ifdef CONFIG_CRASH_DUMP - {12,"oldmem", S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops, NULL}, + [12] = { "oldmem", &oldmem_fops, NULL }, #endif }; static int memory_open(struct inode *inode, struct file *filp) { - int ret = 0; - int i; + int minor; + const struct memdev *dev; + int ret = -ENXIO; lock_kernel(); - for (i = 0; i < ARRAY_SIZE(devlist); i++) { - if (devlist[i].minor == iminor(inode)) { - filp->f_op = devlist[i].fops; - if (devlist[i].dev_info) { - filp->f_mapping->backing_dev_info = - devlist[i].dev_info; - } + minor = iminor(inode); + if (minor >= ARRAY_SIZE(devlist)) + goto out; - break; - } - } + dev = &devlist[minor]; + if (!dev->fops) + goto out; - if (i == ARRAY_SIZE(devlist)) - ret = -ENXIO; - else - if (filp->f_op && filp->f_op->open) - ret = filp->f_op->open(inode, filp); + filp->f_op = dev->fops; + if (dev->dev_info) + filp->f_mapping->backing_dev_info = dev->dev_info; + if (dev->fops->open) + ret = dev->fops->open(inode, filp); + else + ret = 0; +out: unlock_kernel(); return ret; } static const struct file_operations memory_fops = { - .open = memory_open, /* just a selector for the real open */ + .open = memory_open, }; static struct class *mem_class; static int __init chr_dev_init(void) { - int i; + int minor; int err; err = bdi_init(&zero_bdi); @@ -939,10 +935,12 @@ static int __init chr_dev_init(void) printk("unable to get major %d for memory devs\n", MEM_MAJOR); mem_class = class_create(THIS_MODULE, "mem"); - for (i = 0; i < ARRAY_SIZE(devlist); i++) - device_create(mem_class, NULL, - MKDEV(MEM_MAJOR, devlist[i].minor), NULL, - devlist[i].name); + for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) { + if (!devlist[minor].name) + continue; + device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor), + NULL, devlist[minor].name); + } return 0; } -- cgit v1.2.3 From 162dd4212409fd2d36ff22547ea821bf3e86bcc9 Mon Sep 17 00:00:00 2001 From: Jin Dongming Date: Mon, 14 Sep 2009 16:02:26 +0900 Subject: mem_class: fix bug When I build and boot -next on fedora 10, I can not login anymore. When I input the user name and password, the system does not output any message and requires user to input the user name and password again and again. I find the patch which caused this problem with "GIT BISECT" command. And the patch is commit 7c4b7daa1878972ed0137c95f23569124bd6e2b1 "mem_class: use minor as index instead of searching the array". Though I don't know the real reason why user could not login, I confirmed the patch I made as following could resolve the problem on fedora 10. Signed-off-by: Jin Dongming Acked-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index bed3503184e..0491cdf63f2 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -878,8 +878,8 @@ static const struct memdev { [ 4] = { "port", &port_fops, NULL }, #endif [ 5] = { "zero", &zero_fops, &zero_bdi }, - [ 6] = { "full", &full_fops, NULL }, - [ 7] = { "random", &random_fops, NULL }, + [ 7] = { "full", &full_fops, NULL }, + [ 8] = { "random", &random_fops, NULL }, [ 9] = { "urandom", &urandom_fops, NULL }, [11] = { "kmsg", &kmsg_fops, NULL }, #ifdef CONFIG_CRASH_DUMP -- cgit v1.2.3 From a56af87648054089d89874b52e3fc23ed4f274ad Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 12 Jul 2009 21:44:55 +0800 Subject: driver-core: move dma-coherent.c from kernel to driver/base Placing dma-coherent.c in driver/base is better than in kernel, since it contains code to do per-device coherent dma memory handling. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/Makefile | 1 + drivers/base/dma-coherent.c | 176 ++++++++++++++++++++++++++++++++++++++++++++ kernel/Makefile | 1 - kernel/dma-coherent.c | 176 -------------------------------------------- 4 files changed, 177 insertions(+), 177 deletions(-) create mode 100644 drivers/base/dma-coherent.c delete mode 100644 kernel/dma-coherent.c diff --git a/drivers/base/Makefile b/drivers/base/Makefile index b5b8ba512b2..1b2640ce74f 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -6,6 +6,7 @@ obj-y := core.o sys.o bus.o dd.o \ attribute_container.o transport_class.o obj-y += power/ obj-$(CONFIG_HAS_DMA) += dma-mapping.o +obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o obj-$(CONFIG_ISA) += isa.o obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_NUMA) += node.o diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c new file mode 100644 index 00000000000..962a3b574f2 --- /dev/null +++ b/drivers/base/dma-coherent.c @@ -0,0 +1,176 @@ +/* + * Coherent per-device memory handling. + * Borrowed from i386 + */ +#include +#include + +struct dma_coherent_mem { + void *virt_base; + u32 device_base; + int size; + int flags; + unsigned long *bitmap; +}; + +int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + void __iomem *mem_base = NULL; + int pages = size >> PAGE_SHIFT; + int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); + + if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) + goto out; + if (!size) + goto out; + if (dev->dma_mem) + goto out; + + /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ + + mem_base = ioremap(bus_addr, size); + if (!mem_base) + goto out; + + dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); + if (!dev->dma_mem) + goto out; + dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!dev->dma_mem->bitmap) + goto free1_out; + + dev->dma_mem->virt_base = mem_base; + dev->dma_mem->device_base = device_addr; + dev->dma_mem->size = pages; + dev->dma_mem->flags = flags; + + if (flags & DMA_MEMORY_MAP) + return DMA_MEMORY_MAP; + + return DMA_MEMORY_IO; + + free1_out: + kfree(dev->dma_mem); + out: + if (mem_base) + iounmap(mem_base); + return 0; +} +EXPORT_SYMBOL(dma_declare_coherent_memory); + +void dma_release_declared_memory(struct device *dev) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + + if (!mem) + return; + dev->dma_mem = NULL; + iounmap(mem->virt_base); + kfree(mem->bitmap); + kfree(mem); +} +EXPORT_SYMBOL(dma_release_declared_memory); + +void *dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + int pos, err; + + size += device_addr & ~PAGE_MASK; + + if (!mem) + return ERR_PTR(-EINVAL); + + pos = (device_addr - mem->device_base) >> PAGE_SHIFT; + err = bitmap_allocate_region(mem->bitmap, pos, get_order(size)); + if (err != 0) + return ERR_PTR(err); + return mem->virt_base + (pos << PAGE_SHIFT); +} +EXPORT_SYMBOL(dma_mark_declared_memory_occupied); + +/** + * dma_alloc_from_coherent() - try to allocate memory from the per-device coherent area + * + * @dev: device from which we allocate memory + * @size: size of requested memory area + * @dma_handle: This will be filled with the correct dma handle + * @ret: This pointer will be filled with the virtual address + * to allocated area. + * + * This function should be only called from per-arch dma_alloc_coherent() + * to support allocation from per-device coherent memory pools. + * + * Returns 0 if dma_alloc_coherent should continue with allocating from + * generic memory areas, or !0 if dma_alloc_coherent should return @ret. + */ +int dma_alloc_from_coherent(struct device *dev, ssize_t size, + dma_addr_t *dma_handle, void **ret) +{ + struct dma_coherent_mem *mem; + int order = get_order(size); + int pageno; + + if (!dev) + return 0; + mem = dev->dma_mem; + if (!mem) + return 0; + + *ret = NULL; + + if (unlikely(size > (mem->size << PAGE_SHIFT))) + goto err; + + pageno = bitmap_find_free_region(mem->bitmap, mem->size, order); + if (unlikely(pageno < 0)) + goto err; + + /* + * Memory was found in the per-device area. + */ + *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); + *ret = mem->virt_base + (pageno << PAGE_SHIFT); + memset(*ret, 0, size); + + return 1; + +err: + /* + * In the case where the allocation can not be satisfied from the + * per-device area, try to fall back to generic memory if the + * constraints allow it. + */ + return mem->flags & DMA_MEMORY_EXCLUSIVE; +} +EXPORT_SYMBOL(dma_alloc_from_coherent); + +/** + * dma_release_from_coherent() - try to free the memory allocated from per-device coherent memory pool + * @dev: device from which the memory was allocated + * @order: the order of pages allocated + * @vaddr: virtual address of allocated pages + * + * This checks whether the memory was allocated from the per-device + * coherent memory pool and if so, releases that memory. + * + * Returns 1 if we correctly released the memory, or 0 if + * dma_release_coherent() should proceed with releasing memory from + * generic pools. + */ +int dma_release_from_coherent(struct device *dev, int order, void *vaddr) +{ + struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; + + if (mem && vaddr >= mem->virt_base && vaddr < + (mem->virt_base + (mem->size << PAGE_SHIFT))) { + int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; + + bitmap_release_region(mem->bitmap, page, order); + return 1; + } + return 0; +} +EXPORT_SYMBOL(dma_release_from_coherent); diff --git a/kernel/Makefile b/kernel/Makefile index 961379caf66..3d9c7e27e3f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -90,7 +90,6 @@ obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o obj-$(CONFIG_MARKERS) += marker.o obj-$(CONFIG_TRACEPOINTS) += tracepoint.o obj-$(CONFIG_LATENCYTOP) += latencytop.o -obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o obj-$(CONFIG_FUNCTION_TRACER) += trace/ obj-$(CONFIG_TRACING) += trace/ obj-$(CONFIG_X86_DS) += trace/ diff --git a/kernel/dma-coherent.c b/kernel/dma-coherent.c deleted file mode 100644 index 962a3b574f2..00000000000 --- a/kernel/dma-coherent.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Coherent per-device memory handling. - * Borrowed from i386 - */ -#include -#include - -struct dma_coherent_mem { - void *virt_base; - u32 device_base; - int size; - int flags; - unsigned long *bitmap; -}; - -int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, - dma_addr_t device_addr, size_t size, int flags) -{ - void __iomem *mem_base = NULL; - int pages = size >> PAGE_SHIFT; - int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); - - if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) - goto out; - if (!size) - goto out; - if (dev->dma_mem) - goto out; - - /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ - - mem_base = ioremap(bus_addr, size); - if (!mem_base) - goto out; - - dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); - if (!dev->dma_mem) - goto out; - dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!dev->dma_mem->bitmap) - goto free1_out; - - dev->dma_mem->virt_base = mem_base; - dev->dma_mem->device_base = device_addr; - dev->dma_mem->size = pages; - dev->dma_mem->flags = flags; - - if (flags & DMA_MEMORY_MAP) - return DMA_MEMORY_MAP; - - return DMA_MEMORY_IO; - - free1_out: - kfree(dev->dma_mem); - out: - if (mem_base) - iounmap(mem_base); - return 0; -} -EXPORT_SYMBOL(dma_declare_coherent_memory); - -void dma_release_declared_memory(struct device *dev) -{ - struct dma_coherent_mem *mem = dev->dma_mem; - - if (!mem) - return; - dev->dma_mem = NULL; - iounmap(mem->virt_base); - kfree(mem->bitmap); - kfree(mem); -} -EXPORT_SYMBOL(dma_release_declared_memory); - -void *dma_mark_declared_memory_occupied(struct device *dev, - dma_addr_t device_addr, size_t size) -{ - struct dma_coherent_mem *mem = dev->dma_mem; - int pos, err; - - size += device_addr & ~PAGE_MASK; - - if (!mem) - return ERR_PTR(-EINVAL); - - pos = (device_addr - mem->device_base) >> PAGE_SHIFT; - err = bitmap_allocate_region(mem->bitmap, pos, get_order(size)); - if (err != 0) - return ERR_PTR(err); - return mem->virt_base + (pos << PAGE_SHIFT); -} -EXPORT_SYMBOL(dma_mark_declared_memory_occupied); - -/** - * dma_alloc_from_coherent() - try to allocate memory from the per-device coherent area - * - * @dev: device from which we allocate memory - * @size: size of requested memory area - * @dma_handle: This will be filled with the correct dma handle - * @ret: This pointer will be filled with the virtual address - * to allocated area. - * - * This function should be only called from per-arch dma_alloc_coherent() - * to support allocation from per-device coherent memory pools. - * - * Returns 0 if dma_alloc_coherent should continue with allocating from - * generic memory areas, or !0 if dma_alloc_coherent should return @ret. - */ -int dma_alloc_from_coherent(struct device *dev, ssize_t size, - dma_addr_t *dma_handle, void **ret) -{ - struct dma_coherent_mem *mem; - int order = get_order(size); - int pageno; - - if (!dev) - return 0; - mem = dev->dma_mem; - if (!mem) - return 0; - - *ret = NULL; - - if (unlikely(size > (mem->size << PAGE_SHIFT))) - goto err; - - pageno = bitmap_find_free_region(mem->bitmap, mem->size, order); - if (unlikely(pageno < 0)) - goto err; - - /* - * Memory was found in the per-device area. - */ - *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); - *ret = mem->virt_base + (pageno << PAGE_SHIFT); - memset(*ret, 0, size); - - return 1; - -err: - /* - * In the case where the allocation can not be satisfied from the - * per-device area, try to fall back to generic memory if the - * constraints allow it. - */ - return mem->flags & DMA_MEMORY_EXCLUSIVE; -} -EXPORT_SYMBOL(dma_alloc_from_coherent); - -/** - * dma_release_from_coherent() - try to free the memory allocated from per-device coherent memory pool - * @dev: device from which the memory was allocated - * @order: the order of pages allocated - * @vaddr: virtual address of allocated pages - * - * This checks whether the memory was allocated from the per-device - * coherent memory pool and if so, releases that memory. - * - * Returns 1 if we correctly released the memory, or 0 if - * dma_release_coherent() should proceed with releasing memory from - * generic pools. - */ -int dma_release_from_coherent(struct device *dev, int order, void *vaddr) -{ - struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; - - if (mem && vaddr >= mem->virt_base && vaddr < - (mem->virt_base + (mem->size << PAGE_SHIFT))) { - int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; - - bitmap_release_region(mem->bitmap, page, order); - return 1; - } - return 0; -} -EXPORT_SYMBOL(dma_release_from_coherent); -- cgit v1.2.3 From ccb86a6907c9ba7b5be5f521362fc308e80bed34 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 20 Jul 2009 10:29:34 +0300 Subject: uio: add generic driver for PCI 2.3 devices This adds a generic uio driver that can bind to any PCI device. First user will be virtualization where a qemu userspace process needs to give guest OS access to the device. Interrupts are handled using the Interrupt Disable bit in the PCI command register and Interrupt Status bit in the PCI status register. All devices compliant to PCI 2.3 (circa 2002) and all compliant PCI Express devices should support these bits. Driver detects this support, and won't bind to devices which do not support the Interrupt Disable Bit in the command register. It's expected that more features of interest to virtualization will be added to this driver in the future. Possibilities are: mmap for device resources, MSI/MSI-X, eventfd (to interface with kvm), iommu. Signed-off-by: Michael S. Tsirkin Acked-by: Chris Wright Signed-off-by: Hans J. Koch Acked-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/uio-howto.tmpl | 163 +++++++++++++++++++++++++++ MAINTAINERS | 7 ++ drivers/uio/Kconfig | 10 ++ drivers/uio/Makefile | 1 + drivers/uio/uio_pci_generic.c | 207 +++++++++++++++++++++++++++++++++++ include/linux/pci_regs.h | 1 + 6 files changed, 389 insertions(+) create mode 100644 drivers/uio/uio_pci_generic.c diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index 8f6e3b2403c..4d4ce0e61e4 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -25,6 +25,10 @@ 2006-2008 Hans-Jürgen Koch. + + 2009 + Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) + @@ -41,6 +45,13 @@ GPL version 2. + + 0.9 + 2009-07-16 + mst + Added generic pci driver + + 0.8 2008-12-24 @@ -809,6 +820,158 @@ framework to set up sysfs files for this region. Simply leave it alone. + + +Generic PCI UIO driver + + The generic driver is a kernel module named uio_pci_generic. + It can work with any device compliant to PCI 2.3 (circa 2002) and + any compliant PCI Express device. Using this, you only need to + write the userspace driver, removing the need to write + a hardware-specific kernel module. + + + +Making the driver recognize the device + +Since the driver does not declare any device ids, it will not get loaded +automatically and will not automatically bind to any devices, you must load it +and allocate id to the driver yourself. For example: + + modprobe uio_pci_generic + echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id + + + +If there already is a hardware specific kernel driver for your device, the +generic driver still won't bind to it, in this case if you want to use the +generic driver (why would you?) you'll have to manually unbind the hardware +specific driver and bind the generic driver, like this: + + echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind + echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind + + + +You can verify that the device has been bound to the driver +by looking for it in sysfs, for example like the following: + + ls -l /sys/bus/pci/devices/0000:00:19.0/driver + +Which if successful should print + + .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic + +Note that the generic driver will not bind to old PCI 2.2 devices. +If binding the device failed, run the following command: + + dmesg + +and look in the output for failure reasons + + + + +Things to know about uio_pci_generic + +Interrupts are handled using the Interrupt Disable bit in the PCI command +register and Interrupt Status bit in the PCI status register. All devices +compliant to PCI 2.3 (circa 2002) and all compliant PCI Express devices should +support these bits. uio_pci_generic detects this support, and won't bind to +devices which do not support the Interrupt Disable Bit in the command register. + + +On each interrupt, uio_pci_generic sets the Interrupt Disable bit. +This prevents the device from generating further interrupts +until the bit is cleared. The userspace driver should clear this +bit before blocking and waiting for more interrupts. + + + +Writing userspace driver using uio_pci_generic + +Userspace driver can use pci sysfs interface, or the +libpci libray that wraps it, to talk to the device and to +re-enable interrupts by writing to the command register. + + + +Example code using uio_pci_generic + +Here is some sample userspace driver code using uio_pci_generic: + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +int main() +{ + int uiofd; + int configfd; + int err; + int i; + unsigned icount; + unsigned char command_high; + + uiofd = open("/dev/uio0", O_RDONLY); + if (uiofd < 0) { + perror("uio open:"); + return errno; + } + configfd = open("/sys/class/uio/uio0/device/config", O_RDWR); + if (uiofd < 0) { + perror("config open:"); + return errno; + } + + /* Read and cache command value */ + err = pread(configfd, &command_high, 1, 5); + if (err != 1) { + perror("command config read:"); + return errno; + } + command_high &= ~0x4; + + for(i = 0;; ++i) { + /* Print out a message, for debugging. */ + if (i == 0) + fprintf(stderr, "Started uio test driver.\n"); + else + fprintf(stderr, "Interrupts: %d\n", icount); + + /****************************************/ + /* Here we got an interrupt from the + device. Do something to it. */ + /****************************************/ + + /* Re-enable interrupts. */ + err = pwrite(configfd, &command_high, 1, 5); + if (err != 1) { + perror("config write:"); + break; + } + + /* Wait for next interrupt. */ + err = read(uiofd, &icount, 4); + if (err != 4) { + perror("uio read:"); + break; + } + + } + return errno; +} + + + + + + + Further information diff --git a/MAINTAINERS b/MAINTAINERS index 837b5985ac4..01193a4fe30 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2218,6 +2218,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/arnd/asm-generic.git S: Maintained F: include/asm-generic +GENERIC UIO DRIVER FOR PCI DEVICES +M: Michael S. Tsirkin +L: kvm@vger.kernel.org +L: linux-kernel@vger.kernel.org +S: Supported +F: drivers/uio/uio_pci_generic.c + GFS2 FILE SYSTEM M: Steven Whitehouse L: cluster-devel@redhat.com diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 45200fd6853..8aa1955f35e 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -84,4 +84,14 @@ config UIO_SERCOS3 If you compile this as a module, it will be called uio_sercos3. +config UIO_PCI_GENERIC + tristate "Generic driver for PCI 2.3 and PCI Express cards" + depends on PCI + default n + help + Generic driver that you can bind, dynamically, to any + PCI 2.3 compliant and PCI Express card. It is useful, + primarily, for virtualization scenarios. + If you compile this as a module, it will be called uio_pci_generic. + endif diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index 5c2586d7579..73b2e751672 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o obj-$(CONFIG_UIO_SMX) += uio_smx.o obj-$(CONFIG_UIO_AEC) += uio_aec.o obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o +obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c new file mode 100644 index 00000000000..313da35984a --- /dev/null +++ b/drivers/uio/uio_pci_generic.c @@ -0,0 +1,207 @@ +/* uio_pci_generic - generic UIO driver for PCI 2.3 devices + * + * Copyright (C) 2009 Red Hat, Inc. + * Author: Michael S. Tsirkin + * + * This work is licensed under the terms of the GNU GPL, version 2. + * + * Since the driver does not declare any device ids, you must allocate + * id and bind the device to the driver yourself. For example: + * + * # echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind + * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver + * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic + * + * Driver won't bind to devices which do not support the Interrupt Disable Bit + * in the command register. All devices compliant to PCI 2.3 (circa 2002) and + * all compliant PCI Express devices should support this bit. + */ + +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.01.0" +#define DRIVER_AUTHOR "Michael S. Tsirkin " +#define DRIVER_DESC "Generic UIO driver for PCI 2.3 devices" + +struct uio_pci_generic_dev { + struct uio_info info; + struct pci_dev *pdev; + spinlock_t lock; /* guards command register accesses */ +}; + +static inline struct uio_pci_generic_dev * +to_uio_pci_generic_dev(struct uio_info *info) +{ + return container_of(info, struct uio_pci_generic_dev, info); +} + +/* Interrupt handler. Read/modify/write the command register to disable + * the interrupt. */ +static irqreturn_t irqhandler(int irq, struct uio_info *info) +{ + struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info); + struct pci_dev *pdev = gdev->pdev; + irqreturn_t ret = IRQ_NONE; + u32 cmd_status_dword; + u16 origcmd, newcmd, status; + + /* We do a single dword read to retrieve both command and status. + * Document assumptions that make this possible. */ + BUILD_BUG_ON(PCI_COMMAND % 4); + BUILD_BUG_ON(PCI_COMMAND + 2 != PCI_STATUS); + + spin_lock_irq(&gdev->lock); + pci_block_user_cfg_access(pdev); + + /* Read both command and status registers in a single 32-bit operation. + * Note: we could cache the value for command and move the status read + * out of the lock if there was a way to get notified of user changes + * to command register through sysfs. Should be good for shared irqs. */ + pci_read_config_dword(pdev, PCI_COMMAND, &cmd_status_dword); + origcmd = cmd_status_dword; + status = cmd_status_dword >> 16; + + /* Check interrupt status register to see whether our device + * triggered the interrupt. */ + if (!(status & PCI_STATUS_INTERRUPT)) + goto done; + + /* We triggered the interrupt, disable it. */ + newcmd = origcmd | PCI_COMMAND_INTX_DISABLE; + if (newcmd != origcmd) + pci_write_config_word(pdev, PCI_COMMAND, newcmd); + + /* UIO core will signal the user process. */ + ret = IRQ_HANDLED; +done: + + pci_unblock_user_cfg_access(pdev); + spin_unlock_irq(&gdev->lock); + return ret; +} + +/* Verify that the device supports Interrupt Disable bit in command register, + * per PCI 2.3, by flipping this bit and reading it back: this bit was readonly + * in PCI 2.2. */ +static int __devinit verify_pci_2_3(struct pci_dev *pdev) +{ + u16 orig, new; + int err = 0; + + pci_block_user_cfg_access(pdev); + pci_read_config_word(pdev, PCI_COMMAND, &orig); + pci_write_config_word(pdev, PCI_COMMAND, + orig ^ PCI_COMMAND_INTX_DISABLE); + pci_read_config_word(pdev, PCI_COMMAND, &new); + /* There's no way to protect against + * hardware bugs or detect them reliably, but as long as we know + * what the value should be, let's go ahead and check it. */ + if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) { + err = -EBUSY; + dev_err(&pdev->dev, "Command changed from 0x%x to 0x%x: " + "driver or HW bug?\n", orig, new); + goto err; + } + if (!((new ^ orig) & PCI_COMMAND_INTX_DISABLE)) { + dev_warn(&pdev->dev, "Device does not support " + "disabling interrupts: unable to bind.\n"); + err = -ENODEV; + goto err; + } + /* Now restore the original value. */ + pci_write_config_word(pdev, PCI_COMMAND, orig); +err: + pci_unblock_user_cfg_access(pdev); + return err; +} + +static int __devinit probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct uio_pci_generic_dev *gdev; + int err; + + if (!pdev->irq) { + dev_warn(&pdev->dev, "No IRQ assigned to device: " + "no support for interrupts?\n"); + return -ENODEV; + } + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n", + __func__, err); + return err; + } + + err = verify_pci_2_3(pdev); + if (err) + goto err_verify; + + gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL); + if (!gdev) { + err = -ENOMEM; + goto err_alloc; + } + + gdev->info.name = "uio_pci_generic"; + gdev->info.version = DRIVER_VERSION; + gdev->info.irq = pdev->irq; + gdev->info.irq_flags = IRQF_SHARED; + gdev->info.handler = irqhandler; + gdev->pdev = pdev; + spin_lock_init(&gdev->lock); + + if (uio_register_device(&pdev->dev, &gdev->info)) + goto err_register; + pci_set_drvdata(pdev, gdev); + + return 0; +err_register: + kfree(gdev); +err_alloc: +err_verify: + pci_disable_device(pdev); + return err; +} + +static void remove(struct pci_dev *pdev) +{ + struct uio_pci_generic_dev *gdev = pci_get_drvdata(pdev); + + uio_unregister_device(&gdev->info); + pci_disable_device(pdev); + kfree(gdev); +} + +static struct pci_driver driver = { + .name = "uio_pci_generic", + .id_table = NULL, /* only dynamic id's */ + .probe = probe, + .remove = remove, +}; + +static int __init init(void) +{ + pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + return pci_register_driver(&driver); +} + +static void __exit cleanup(void) +{ + pci_unregister_driver(&driver); +} + +module_init(init); +module_exit(cleanup); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index fcaee42c7ac..dd0bed4f1cf 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -42,6 +42,7 @@ #define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ #define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */ #define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ #define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ #define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ -- cgit v1.2.3 From 4622709445705c1e423d2addcfd8ccae052fe0ba Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 4 Aug 2009 12:55:34 +0200 Subject: Driver core: Add support for compatibility classes When turning class devices into bus devices, we may need to temporarily add links in sysfs so that user-space applications are not confused. This is done by adding the following API: * Functions to register and unregister compatibility classes. These appear in sysfs at the same location as regular classes, but instead of class devices, they contain links to bus devices. * Functions to create and delete such links. Additionally, the caller can optionally pass a target device to which a "device" link should point (typically that would be the device's parent), to fully emulate the original class device. The i2c subsystem will be the first user of this API, as i2c adapters are being converted from class devices to bus devices. Signed-off-by: Jean Delvare Signed-off-by: Kay Sievers --- drivers/base/class.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 8 +++++ 2 files changed, 95 insertions(+) diff --git a/drivers/base/class.c b/drivers/base/class.c index eb85e431230..161746deab4 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -488,6 +488,93 @@ void class_interface_unregister(struct class_interface *class_intf) class_put(parent); } +struct class_compat { + struct kobject *kobj; +}; + +/** + * class_compat_register - register a compatibility class + * @name: the name of the class + * + * Compatibility class are meant as a temporary user-space compatibility + * workaround when converting a family of class devices to a bus devices. + */ +struct class_compat *class_compat_register(const char *name) +{ + struct class_compat *cls; + + cls = kmalloc(sizeof(struct class_compat), GFP_KERNEL); + if (!cls) + return NULL; + cls->kobj = kobject_create_and_add(name, &class_kset->kobj); + if (!cls->kobj) { + kfree(cls); + return NULL; + } + return cls; +} +EXPORT_SYMBOL_GPL(class_compat_register); + +/** + * class_compat_unregister - unregister a compatibility class + * @cls: the class to unregister + */ +void class_compat_unregister(struct class_compat *cls) +{ + kobject_put(cls->kobj); + kfree(cls); +} +EXPORT_SYMBOL_GPL(class_compat_unregister); + +/** + * class_compat_create_link - create a compatibility class device link to + * a bus device + * @cls: the compatibility class + * @dev: the target bus device + * @device_link: an optional device to which a "device" link should be created + */ +int class_compat_create_link(struct class_compat *cls, struct device *dev, + struct device *device_link) +{ + int error; + + error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev)); + if (error) + return error; + + /* + * Optionally add a "device" link (typically to the parent), as a + * class device would have one and we want to provide as much + * backwards compatibility as possible. + */ + if (device_link) { + error = sysfs_create_link(&dev->kobj, &device_link->kobj, + "device"); + if (error) + sysfs_remove_link(cls->kobj, dev_name(dev)); + } + + return error; +} +EXPORT_SYMBOL_GPL(class_compat_create_link); + +/** + * class_compat_remove_link - remove a compatibility class device link to + * a bus device + * @cls: the compatibility class + * @dev: the target bus device + * @device_link: an optional device to which a "device" link was previously + * created + */ +void class_compat_remove_link(struct class_compat *cls, struct device *dev, + struct device *device_link) +{ + if (device_link) + sysfs_remove_link(&dev->kobj, "device"); + sysfs_remove_link(cls->kobj, dev_name(dev)); +} +EXPORT_SYMBOL_GPL(class_compat_remove_link); + int __init classes_init(void) { class_kset = kset_create_and_add("class", NULL, NULL); diff --git a/include/linux/device.h b/include/linux/device.h index e19e40a3dcb..62ff53a6793 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -225,6 +225,14 @@ extern void class_unregister(struct class *class); __class_register(class, &__key); \ }) +struct class_compat; +struct class_compat *class_compat_register(const char *name); +void class_compat_unregister(struct class_compat *cls); +int class_compat_create_link(struct class_compat *cls, struct device *dev, + struct device *device_link); +void class_compat_remove_link(struct class_compat *cls, struct device *dev, + struct device *device_link); + extern void class_dev_iter_init(struct class_dev_iter *iter, struct class *class, struct device *start, -- cgit v1.2.3 From daa4122673f002911122cac2b996bd36b6c01b32 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 6 Aug 2009 16:00:44 -0700 Subject: driver core: platform_device_add_data(): use kmemdup() Instead of open-coding it. Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 0f7d434ce98..ed156a13aa4 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -10,6 +10,7 @@ * information. */ +#include #include #include #include @@ -213,14 +214,13 @@ EXPORT_SYMBOL_GPL(platform_device_add_resources); int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size) { - void *d; + void *d = kmemdup(data, size, GFP_KERNEL); - d = kmalloc(size, GFP_KERNEL); if (d) { - memcpy(d, data, size); pdev->dev.platform_data = d; + return 0; } - return d ? 0 : -ENOMEM; + return -ENOMEM; } EXPORT_SYMBOL_GPL(platform_device_add_data); -- cgit v1.2.3 From 66d5e5169c96f3e0175235e2bcbaedc8bc1c728f Mon Sep 17 00:00:00 2001 From: David Altobelli Date: Mon, 17 Aug 2009 17:07:03 -0600 Subject: hpilo: staging for interrupt handling Refactor some hpilo routines in order to allow for locking changes in interrupt handling. Should not be functionally different. Signed-off-by: David Altobelli Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 120 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 880ccf39e23..35ed12379cd 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -151,6 +151,7 @@ static inline void doorbell_clr(struct ccb *ccb) { iowrite8(2, ccb->ccb_u5.db_base); } + static inline int ctrl_set(int l2sz, int idxmask, int desclim) { int active = 0, go = 1; @@ -160,6 +161,7 @@ static inline int ctrl_set(int l2sz, int idxmask, int desclim) active << CTRL_BITPOS_A | go << CTRL_BITPOS_G; } + static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) { /* for simplicity, use the same parameters for send and recv ctrls */ @@ -192,13 +194,10 @@ static void fifo_setup(void *base_addr, int nr_entry) static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) { - struct ccb *driver_ccb; - struct ccb __iomem *device_ccb; + struct ccb *driver_ccb = &data->driver_ccb; + struct ccb __iomem *device_ccb = data->mapped_ccb; int retries; - driver_ccb = &data->driver_ccb; - device_ccb = data->mapped_ccb; - /* complicated dance to tell the hw we are stopping */ doorbell_clr(driver_ccb); iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G), @@ -225,26 +224,22 @@ static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); } -static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) +static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) { char *dma_va, *dma_pa; - int pkt_id, pkt_sz, i, error; struct ccb *driver_ccb, *ilo_ccb; - struct pci_dev *pdev; driver_ccb = &data->driver_ccb; ilo_ccb = &data->ilo_ccb; - pdev = hw->ilo_dev; data->dma_size = 2 * fifo_sz(NR_QENTRY) + 2 * desc_mem_sz(NR_QENTRY) + ILO_START_ALIGN + ILO_CACHE_SZ; - error = -ENOMEM; - data->dma_va = pci_alloc_consistent(pdev, data->dma_size, + data->dma_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size, &data->dma_pa); if (!data->dma_va) - goto out; + return -ENOMEM; dma_va = (char *)data->dma_va; dma_pa = (char *)data->dma_pa; @@ -290,10 +285,18 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE); ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */ + return 0; +} + +static void ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) +{ + int pkt_id, pkt_sz; + struct ccb *driver_ccb = &data->driver_ccb; + /* copy the ccb with physical addrs to device memory */ data->mapped_ccb = (struct ccb __iomem *) (hw->ram_vaddr + (slot * ILOHW_CCB_SZ)); - memcpy_toio(data->mapped_ccb, ilo_ccb, sizeof(struct ccb)); + memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb)); /* put packets on the send and receive queues */ pkt_sz = 0; @@ -306,7 +309,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); + /* the ccb is ready to use */ doorbell_clr(driver_ccb); +} + +static int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data) +{ + int pkt_id, i; + struct ccb *driver_ccb = &data->driver_ccb; /* make sure iLO is really handling requests */ for (i = MAX_WAIT; i > 0; i--) { @@ -315,20 +325,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) udelay(WAIT_TIME); } - if (i) { - ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); - doorbell_set(driver_ccb); - } else { - dev_err(&pdev->dev, "Open could not dequeue a packet\n"); - error = -EBUSY; - goto free; + if (i == 0) { + dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n"); + return -EBUSY; } + ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); + doorbell_set(driver_ccb); return 0; -free: - ilo_ccb_close(pdev, data); -out: - return error; } static inline int is_channel_reset(struct ccb *ccb) @@ -343,16 +347,31 @@ static inline void set_channel_reset(struct ccb *ccb) FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; } +static inline int get_device_outbound(struct ilo_hwinfo *hw) +{ + return ioread32(&hw->mmio_vaddr[DB_OUT]); +} + +static inline int is_db_reset(int db_out) +{ + return db_out & (1 << DB_RESET); +} + static inline int is_device_reset(struct ilo_hwinfo *hw) { /* check for global reset condition */ - return ioread32(&hw->mmio_vaddr[DB_OUT]) & (1 << DB_RESET); + return is_db_reset(get_device_outbound(hw)); +} + +static inline void clear_pending_db(struct ilo_hwinfo *hw, int clr) +{ + iowrite32(clr, &hw->mmio_vaddr[DB_OUT]); } static inline void clear_device(struct ilo_hwinfo *hw) { /* clear the device (reset bits, pending channel entries) */ - iowrite32(-1, &hw->mmio_vaddr[DB_OUT]); + clear_pending_db(hw, -1); } static void ilo_locked_reset(struct ilo_hwinfo *hw) @@ -387,15 +406,11 @@ static ssize_t ilo_read(struct file *fp, char __user *buf, size_t len, loff_t *off) { int err, found, cnt, pkt_id, pkt_len; - struct ccb_data *data; - struct ccb *driver_ccb; - struct ilo_hwinfo *hw; + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + struct ilo_hwinfo *hw = data->ilo_hw; void *pkt; - data = fp->private_data; - driver_ccb = &data->driver_ccb; - hw = data->ilo_hw; - if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { /* * If the device has been reset, applications @@ -442,15 +457,11 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, size_t len, loff_t *off) { int err, pkt_id, pkt_len; - struct ccb_data *data; - struct ccb *driver_ccb; - struct ilo_hwinfo *hw; + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + struct ilo_hwinfo *hw = data->ilo_hw; void *pkt; - data = fp->private_data; - driver_ccb = &data->driver_ccb; - hw = data->ilo_hw; - if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { /* * If the device has been reset, applications @@ -532,14 +543,28 @@ static int ilo_open(struct inode *ip, struct file *fp) /* each fd private_data holds sw/hw view of ccb */ if (hw->ccb_alloc[slot] == NULL) { /* create a channel control block for this minor */ - error = ilo_ccb_open(hw, data, slot); - if (!error) { - hw->ccb_alloc[slot] = data; - hw->ccb_alloc[slot]->ccb_cnt = 1; - hw->ccb_alloc[slot]->ccb_excl = fp->f_flags & O_EXCL; - hw->ccb_alloc[slot]->ilo_hw = hw; - } else + error = ilo_ccb_setup(hw, data, slot); + if (error) { + kfree(data); + goto out; + } + + /* write the ccb to hw */ + ilo_ccb_open(hw, data, slot); + + /* make sure the channel is functional */ + error = ilo_ccb_verify(hw, data); + if (error) { + ilo_ccb_close(hw->ilo_dev, data); kfree(data); + goto out; + } + + data->ccb_cnt = 1; + data->ccb_excl = fp->f_flags & O_EXCL; + data->ilo_hw = hw; + hw->ccb_alloc[slot] = data; + } else { kfree(data); if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { @@ -554,6 +579,7 @@ static int ilo_open(struct inode *ip, struct file *fp) error = 0; } } +out: spin_unlock(&hw->alloc_lock); if (!error) -- cgit v1.2.3 From 9f7048412163d8f8175ba01063f3db02d42cc6a7 Mon Sep 17 00:00:00 2001 From: David Altobelli Date: Mon, 17 Aug 2009 17:07:33 -0600 Subject: hpilo: add interrupt handler Add interrupt handler to hpilo. This is enablement for poll handler, and it also simplifies the logic for handling an iLO reset, because now only the interrupt handler needs to look for reset, the file system interfaces only need to return failure when a reset has happened. Signed-off-by: David Altobelli Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 138 +++++++++++++++++++++++++++++++++++---------------- drivers/misc/hpilo.h | 8 ++- 2 files changed, 101 insertions(+), 45 deletions(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 35ed12379cd..9a370d5acf8 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include "hpilo.h" static struct class *ilo_class; @@ -61,9 +63,10 @@ static inline int desc_mem_sz(int nr_entry) static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) { struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + unsigned long flags; int ret = 0; - spin_lock(&hw->fifo_lock); + spin_lock_irqsave(&hw->fifo_lock, flags); if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] & ENTRY_MASK_O)) { fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= @@ -71,7 +74,7 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) fifo_q->tail += 1; ret = 1; } - spin_unlock(&hw->fifo_lock); + spin_unlock_irqrestore(&hw->fifo_lock, flags); return ret; } @@ -79,10 +82,11 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) { struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + unsigned long flags; int ret = 0; u64 c; - spin_lock(&hw->fifo_lock); + spin_lock_irqsave(&hw->fifo_lock, flags); c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; if (c & ENTRY_MASK_C) { if (entry) @@ -93,7 +97,7 @@ static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) fifo_q->head += 1; ret = 1; } - spin_unlock(&hw->fifo_lock); + spin_unlock_irqrestore(&hw->fifo_lock, flags); return ret; } @@ -374,7 +378,18 @@ static inline void clear_device(struct ilo_hwinfo *hw) clear_pending_db(hw, -1); } -static void ilo_locked_reset(struct ilo_hwinfo *hw) +static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw) +{ + iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]); +} + +static inline void ilo_disable_interrupts(struct ilo_hwinfo *hw) +{ + iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1, + &hw->mmio_vaddr[DB_IRQ]); +} + +static void ilo_set_reset(struct ilo_hwinfo *hw) { int slot; @@ -387,19 +402,6 @@ static void ilo_locked_reset(struct ilo_hwinfo *hw) continue; set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); } - - clear_device(hw); -} - -static void ilo_reset(struct ilo_hwinfo *hw) -{ - spin_lock(&hw->alloc_lock); - - /* reset might have been handled after lock was taken */ - if (is_device_reset(hw)) - ilo_locked_reset(hw); - - spin_unlock(&hw->alloc_lock); } static ssize_t ilo_read(struct file *fp, char __user *buf, @@ -411,12 +413,11 @@ static ssize_t ilo_read(struct file *fp, char __user *buf, struct ilo_hwinfo *hw = data->ilo_hw; void *pkt; - if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { + if (is_channel_reset(driver_ccb)) { /* * If the device has been reset, applications * need to close and reopen all ccbs. */ - ilo_reset(hw); return -ENODEV; } @@ -462,14 +463,8 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, struct ilo_hwinfo *hw = data->ilo_hw; void *pkt; - if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { - /* - * If the device has been reset, applications - * need to close and reopen all ccbs. - */ - ilo_reset(hw); + if (is_channel_reset(driver_ccb)) return -ENODEV; - } /* get a packet to send the user command */ if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) @@ -496,27 +491,28 @@ static int ilo_close(struct inode *ip, struct file *fp) int slot; struct ccb_data *data; struct ilo_hwinfo *hw; + unsigned long flags; slot = iminor(ip) % MAX_CCB; hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); - spin_lock(&hw->alloc_lock); - - if (is_device_reset(hw)) - ilo_locked_reset(hw); + spin_lock(&hw->open_lock); if (hw->ccb_alloc[slot]->ccb_cnt == 1) { data = fp->private_data; + spin_lock_irqsave(&hw->alloc_lock, flags); + hw->ccb_alloc[slot] = NULL; + spin_unlock_irqrestore(&hw->alloc_lock, flags); + ilo_ccb_close(hw->ilo_dev, data); kfree(data); - hw->ccb_alloc[slot] = NULL; } else hw->ccb_alloc[slot]->ccb_cnt--; - spin_unlock(&hw->alloc_lock); + spin_unlock(&hw->open_lock); return 0; } @@ -526,6 +522,7 @@ static int ilo_open(struct inode *ip, struct file *fp) int slot, error; struct ccb_data *data; struct ilo_hwinfo *hw; + unsigned long flags; slot = iminor(ip) % MAX_CCB; hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); @@ -535,10 +532,7 @@ static int ilo_open(struct inode *ip, struct file *fp) if (!data) return -ENOMEM; - spin_lock(&hw->alloc_lock); - - if (is_device_reset(hw)) - ilo_locked_reset(hw); + spin_lock(&hw->open_lock); /* each fd private_data holds sw/hw view of ccb */ if (hw->ccb_alloc[slot] == NULL) { @@ -549,22 +543,31 @@ static int ilo_open(struct inode *ip, struct file *fp) goto out; } + data->ccb_cnt = 1; + data->ccb_excl = fp->f_flags & O_EXCL; + data->ilo_hw = hw; + init_waitqueue_head(&data->ccb_waitq); + /* write the ccb to hw */ + spin_lock_irqsave(&hw->alloc_lock, flags); ilo_ccb_open(hw, data, slot); + hw->ccb_alloc[slot] = data; + spin_unlock_irqrestore(&hw->alloc_lock, flags); /* make sure the channel is functional */ error = ilo_ccb_verify(hw, data); if (error) { + + spin_lock_irqsave(&hw->alloc_lock, flags); + hw->ccb_alloc[slot] = NULL; + spin_unlock_irqrestore(&hw->alloc_lock, flags); + ilo_ccb_close(hw->ilo_dev, data); + kfree(data); goto out; } - data->ccb_cnt = 1; - data->ccb_excl = fp->f_flags & O_EXCL; - data->ilo_hw = hw; - hw->ccb_alloc[slot] = data; - } else { kfree(data); if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { @@ -580,7 +583,7 @@ static int ilo_open(struct inode *ip, struct file *fp) } } out: - spin_unlock(&hw->alloc_lock); + spin_unlock(&hw->open_lock); if (!error) fp->private_data = hw->ccb_alloc[slot]; @@ -596,6 +599,41 @@ static const struct file_operations ilo_fops = { .release = ilo_close, }; +static irqreturn_t ilo_isr(int irq, void *data) +{ + struct ilo_hwinfo *hw = data; + int pending, i; + + spin_lock(&hw->alloc_lock); + + /* check for ccbs which have data */ + pending = get_device_outbound(hw); + if (!pending) { + spin_unlock(&hw->alloc_lock); + return IRQ_NONE; + } + + if (is_db_reset(pending)) { + /* wake up all ccbs if the device was reset */ + pending = -1; + ilo_set_reset(hw); + } + + for (i = 0; i < MAX_CCB; i++) { + if (!hw->ccb_alloc[i]) + continue; + if (pending & (1 << i)) + wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq); + } + + /* clear the device of the channels that have been handled */ + clear_pending_db(hw, pending); + + spin_unlock(&hw->alloc_lock); + + return IRQ_HANDLED; +} + static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) { pci_iounmap(pdev, hw->db_vaddr); @@ -649,6 +687,8 @@ static void ilo_remove(struct pci_dev *pdev) device_destroy(ilo_class, MKDEV(ilo_major, i)); cdev_del(&ilo_hw->cdev); + ilo_disable_interrupts(ilo_hw); + free_irq(pdev->irq, ilo_hw); ilo_unmap_device(pdev, ilo_hw); pci_release_regions(pdev); pci_disable_device(pdev); @@ -684,6 +724,7 @@ static int __devinit ilo_probe(struct pci_dev *pdev, ilo_hw->ilo_dev = pdev; spin_lock_init(&ilo_hw->alloc_lock); spin_lock_init(&ilo_hw->fifo_lock); + spin_lock_init(&ilo_hw->open_lock); error = pci_enable_device(pdev); if (error) @@ -702,13 +743,19 @@ static int __devinit ilo_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, ilo_hw); clear_device(ilo_hw); + error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw); + if (error) + goto unmap; + + ilo_enable_interrupts(ilo_hw); + cdev_init(&ilo_hw->cdev, &ilo_fops); ilo_hw->cdev.owner = THIS_MODULE; start = devnum * MAX_CCB; error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); if (error) { dev_err(&pdev->dev, "Could not add cdev\n"); - goto unmap; + goto remove_isr; } for (minor = 0 ; minor < MAX_CCB; minor++) { @@ -721,6 +768,9 @@ static int __devinit ilo_probe(struct pci_dev *pdev, } return 0; +remove_isr: + ilo_disable_interrupts(ilo_hw); + free_irq(pdev->irq, ilo_hw); unmap: ilo_unmap_device(pdev, ilo_hw); free_regions: diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index 03a14c82aad..38576050776 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h @@ -46,11 +46,14 @@ struct ilo_hwinfo { spinlock_t alloc_lock; spinlock_t fifo_lock; + spinlock_t open_lock; struct cdev cdev; }; -/* offset from mmio_vaddr */ +/* offset from mmio_vaddr for enabling doorbell interrupts */ +#define DB_IRQ 0xB2 +/* offset from mmio_vaddr for outbound communications */ #define DB_OUT 0xD4 /* DB_OUT reset bit */ #define DB_RESET 26 @@ -131,6 +134,9 @@ struct ccb_data { /* pointer to hardware device info */ struct ilo_hwinfo *ilo_hw; + /* queue for this ccb to wait for recv data */ + wait_queue_head_t ccb_waitq; + /* usage count, to allow for shared ccb's */ int ccb_cnt; -- cgit v1.2.3 From 4a351471df352474956f1fdc7bc4e7cf6836503a Mon Sep 17 00:00:00 2001 From: David Altobelli Date: Mon, 17 Aug 2009 17:08:01 -0600 Subject: hpilo: add poll f_op Add poll handler to hpilo, to allow applications a low overhead method of waiting for data. Signed-off-by: David Altobelli Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 9a370d5acf8..1ad27c6abcc 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "hpilo.h" static struct class *ilo_class; @@ -102,6 +103,22 @@ static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) return ret; } +static int fifo_check_recv(struct ilo_hwinfo *hw, char *fifobar) +{ + struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + unsigned long flags; + int ret = 0; + u64 c; + + spin_lock_irqsave(&hw->fifo_lock, flags); + c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; + if (c & ENTRY_MASK_C) + ret = 1; + spin_unlock_irqrestore(&hw->fifo_lock, flags); + + return ret; +} + static int ilo_pkt_enqueue(struct ilo_hwinfo *hw, struct ccb *ccb, int dir, int id, int len) { @@ -146,6 +163,13 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, return ret; } +static int ilo_pkt_recv(struct ilo_hwinfo *hw, struct ccb *ccb) +{ + char *fifobar = ccb->ccb_u3.recv_fifobar; + + return fifo_check_recv(hw, fifobar); +} + static inline void doorbell_set(struct ccb *ccb) { iowrite8(1, ccb->ccb_u5.db_base); @@ -486,6 +510,21 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, return err ? -EFAULT : len; } +static unsigned int ilo_poll(struct file *fp, poll_table *wait) +{ + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + + poll_wait(fp, &data->ccb_waitq, wait); + + if (is_channel_reset(driver_ccb)) + return POLLERR; + else if (ilo_pkt_recv(data->ilo_hw, driver_ccb)) + return POLLIN | POLLRDNORM; + + return 0; +} + static int ilo_close(struct inode *ip, struct file *fp) { int slot; @@ -595,6 +634,7 @@ static const struct file_operations ilo_fops = { .owner = THIS_MODULE, .read = ilo_read, .write = ilo_write, + .poll = ilo_poll, .open = ilo_open, .release = ilo_close, }; @@ -835,7 +875,7 @@ static void __exit ilo_exit(void) class_destroy(ilo_class); } -MODULE_VERSION("1.1"); +MODULE_VERSION("1.2"); MODULE_ALIAS(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME); MODULE_AUTHOR("David Altobelli "); -- cgit v1.2.3 From 52ad51e7a93558a218cd00059bd69881a82eb2bb Mon Sep 17 00:00:00 2001 From: GeunSik Lim Date: Mon, 7 Sep 2009 21:37:17 +0900 Subject: debugfs: Fix mount directory of debugfs by default in events.txt We need common default directory of denbugfs for consistency. (debugfs's default directory is /sys/kernel/debug/ by debugfs.) Signed-off-by: GeunSik Lim Signed-off-by: Greg Kroah-Hartman --- Documentation/trace/events.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Documentation/trace/events.txt b/Documentation/trace/events.txt index 2bcc8d4dea2..90e8b3383ba 100644 --- a/Documentation/trace/events.txt +++ b/Documentation/trace/events.txt @@ -22,12 +22,12 @@ tracing information should be printed. --------------------------------- The events which are available for tracing can be found in the file -/debug/tracing/available_events. +/sys/kernel/debug/tracing/available_events. To enable a particular event, such as 'sched_wakeup', simply echo it -to /debug/tracing/set_event. For example: +to /sys/kernel/debug/tracing/set_event. For example: - # echo sched_wakeup >> /debug/tracing/set_event + # echo sched_wakeup >> /sys/kernel/debug/tracing/set_event [ Note: '>>' is necessary, otherwise it will firstly disable all the events. ] @@ -35,15 +35,15 @@ to /debug/tracing/set_event. For example: To disable an event, echo the event name to the set_event file prefixed with an exclamation point: - # echo '!sched_wakeup' >> /debug/tracing/set_event + # echo '!sched_wakeup' >> /sys/kernel/debug/tracing/set_event To disable all events, echo an empty line to the set_event file: - # echo > /debug/tracing/set_event + # echo > /sys/kernel/debug/tracing/set_event To enable all events, echo '*:*' or '*:' to the set_event file: - # echo *:* > /debug/tracing/set_event + # echo *:* > /sys/kernel/debug/tracing/set_event The events are organized into subsystems, such as ext4, irq, sched, etc., and a full event name looks like this: :. The @@ -52,29 +52,29 @@ file. All of the events in a subsystem can be specified via the syntax ":*"; for example, to enable all irq events, you can use the command: - # echo 'irq:*' > /debug/tracing/set_event + # echo 'irq:*' > /sys/kernel/debug/tracing/set_event 2.2 Via the 'enable' toggle --------------------------- -The events available are also listed in /debug/tracing/events/ hierarchy +The events available are also listed in /sys/kernel/debug/tracing/events/ hierarchy of directories. To enable event 'sched_wakeup': - # echo 1 > /debug/tracing/events/sched/sched_wakeup/enable + # echo 1 > /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable To disable it: - # echo 0 > /debug/tracing/events/sched/sched_wakeup/enable + # echo 0 > /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable To enable all events in sched subsystem: - # echo 1 > /debug/tracing/events/sched/enable + # echo 1 > /sys/kernel/debug/tracing/events/sched/enable To eanble all events: - # echo 1 > /debug/tracing/events/enable + # echo 1 > /sys/kernel/debug/tracing/events/enable When reading one of these enable files, there are four results: -- cgit v1.2.3 From 088a4eed282c9f75a2c165ba4909433c6dd6f959 Mon Sep 17 00:00:00 2001 From: GeunSik Lim Date: Mon, 7 Sep 2009 21:37:37 +0900 Subject: debugfs: Change debuhgfs directory of trace-events-sample.h Default directory of debug filesystem for ftrace is /sys/kernel/debug/. Signed-off-by: GeunSik Lim Signed-off-by: Greg Kroah-Hartman --- samples/trace_events/trace-events-sample.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h index f24ae370e51..6af373236d7 100644 --- a/samples/trace_events/trace-events-sample.h +++ b/samples/trace_events/trace-events-sample.h @@ -1,6 +1,6 @@ /* * If TRACE_SYSTEM is defined, that will be the directory created - * in the ftrace directory under /debugfs/tracing/events/ + * in the ftrace directory under /sys/kernel/debug/tracing/events/ * * The define_trace.h below will also look for a file name of * TRACE_SYSTEM.h where TRACE_SYSTEM is what is defined here. -- cgit v1.2.3 From 9d480b88d830e6d6eb90767b0722def8fffc0365 Mon Sep 17 00:00:00 2001 From: GeunSik Lim Date: Mon, 7 Sep 2009 21:37:53 +0900 Subject: debugfs: Change debugfs directory of IWMC3200 Change default debugfs directory that mount for debugging IWMC3200(Intel Wireless Multicomm 3200 WiFi driver). As we all know, We need change default directory for consistency of debugfs by Greg.K.H Signed-off-by: GeunSik Lim Acked-by: Zhu Yi Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwmc3200wifi/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig index c62da435285..c25a04371ca 100644 --- a/drivers/net/wireless/iwmc3200wifi/Kconfig +++ b/drivers/net/wireless/iwmc3200wifi/Kconfig @@ -24,8 +24,8 @@ config IWM_DEBUG To see the list of debug modules and levels, see iwm/debug.h For example, if you want the full MLME debug output: - echo 0xff > /debug/iwm/phyN/debug/mlme + echo 0xff > /sys/kernel/debug/iwm/phyN/debug/mlme Or, if you want the full debug, for all modules: - echo 0xff > /debug/iwm/phyN/debug/level - echo 0xff > /debug/iwm/phyN/debug/modules + echo 0xff > /sys/kernel/debug/iwm/phyN/debug/level + echo 0xff > /sys/kernel/debug/iwm/phyN/debug/modules -- cgit v1.2.3 From 837cbb07618d61114354596931dd1998b672c5d8 Mon Sep 17 00:00:00 2001 From: GeunSik Lim Date: Mon, 7 Sep 2009 21:39:05 +0900 Subject: debugfs: Modified default dir of debugfs for debugging UHCI. Change default debugfs directory as mounting point for debugging UHCI(Universal Host Controller Interface driver) for USB. As we all know, We need change default directory for consistency of debugfs by Greg K-H Signed-off-by: GeunSik Lim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 274751b4409..5cd0e48f67f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -67,7 +67,7 @@ MODULE_PARM_DESC(ignore_oc, "ignore hardware overcurrent indications"); * debug = 0, no debugging messages * debug = 1, dump failed URBs except for stalls * debug = 2, dump all failed URBs (including stalls) - * show all queues in /debug/uhci/[pci_addr] + * show all queues in /sys/kernel/debug/uhci/[pci_addr] * debug = 3, show all TDs in URBs when dumping */ #ifdef DEBUG -- cgit v1.2.3 From ea5ffff57dce2f4c85ab056f4b0a202f71db2bdf Mon Sep 17 00:00:00 2001 From: GeunSik Lim Date: Mon, 7 Sep 2009 21:39:25 +0900 Subject: debugfs: Modify default debugfs directory for debugging pktcdvd. As we all know, We need change default directory for consistency of debugfs by Greg K-H Signed-off-by: GeunSik Lim Signed-off-by: Greg Kroah-Hartman --- drivers/block/pktcdvd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 99a506f619b..95f11cdef20 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -92,7 +92,7 @@ static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ static mempool_t *psd_pool; static struct class *class_pktcdvd = NULL; /* /sys/class/pktcdvd */ -static struct dentry *pkt_debugfs_root = NULL; /* /debug/pktcdvd */ +static struct dentry *pkt_debugfs_root = NULL; /* /sys/kernel/debug/pktcdvd */ /* forward declaration */ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev); -- cgit v1.2.3 From 2b2af54a5bb6f7e80ccf78f20084b93c398c3a8b Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 30 Apr 2009 15:23:42 +0200 Subject: Driver Core: devtmpfs - kernel-maintained tmpfs-based /dev Devtmpfs lets the kernel create a tmpfs instance called devtmpfs very early at kernel initialization, before any driver-core device is registered. Every device with a major/minor will provide a device node in devtmpfs. Devtmpfs can be changed and altered by userspace at any time, and in any way needed - just like today's udev-mounted tmpfs. Unmodified udev versions will run just fine on top of it, and will recognize an already existing kernel-created device node and use it. The default node permissions are root:root 0600. Proper permissions and user/group ownership, meaningful symlinks, all other policy still needs to be applied by userspace. If a node is created by devtmps, devtmpfs will remove the device node when the device goes away. If the device node was created by userspace, or the devtmpfs created node was replaced by userspace, it will no longer be removed by devtmpfs. If it is requested to auto-mount it, it makes init=/bin/sh work without any further userspace support. /dev will be fully populated and dynamic, and always reflect the current device state of the kernel. With the commonly used dynamic device numbers, it solves the problem where static devices nodes may point to the wrong devices. It is intended to make the initial bootup logic simpler and more robust, by de-coupling the creation of the inital environment, to reliably run userspace processes, from a complex userspace bootstrap logic to provide a working /dev. Signed-off-by: Kay Sievers Signed-off-by: Jan Blunck Tested-By: Harald Hoyer Tested-By: Scott James Remnant Signed-off-by: Greg Kroah-Hartman --- drivers/base/Kconfig | 25 ++++ drivers/base/Makefile | 1 + drivers/base/base.h | 6 + drivers/base/core.c | 3 + drivers/base/devtmpfs.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/base/init.c | 1 + include/linux/device.h | 10 ++ include/linux/shmem_fs.h | 3 + init/do_mounts.c | 2 +- init/main.c | 2 + mm/shmem.c | 9 +- 11 files changed, 422 insertions(+), 7 deletions(-) create mode 100644 drivers/base/devtmpfs.c diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 8f006f96ff5..ee377270beb 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -8,6 +8,31 @@ config UEVENT_HELPER_PATH Path to uevent helper program forked by the kernel for every uevent. +config DEVTMPFS + bool "Create a kernel maintained /dev tmpfs (EXPERIMENTAL)" + depends on HOTPLUG && SHMEM && TMPFS + help + This creates a tmpfs filesystem, and mounts it at bootup + and mounts it at /dev. The kernel driver core creates device + nodes for all registered devices in that filesystem. All device + nodes are owned by root and have the default mode of 0600. + Userspace can add and delete the nodes as needed. This is + intended to simplify bootup, and make it possible to delay + the initial coldplug at bootup done by udev in userspace. + It should also provide a simpler way for rescue systems + to bring up a kernel with dynamic major/minor numbers. + Meaningful symlinks, permissions and device ownership must + still be handled by userspace. + If unsure, say N here. + +config DEVTMPFS_MOUNT + bool "Automount devtmpfs at /dev" + depends on DEVTMPFS + help + This will mount devtmpfs at /dev if the kernel mounts the root + filesystem. It will not affect initramfs based mounting. + If unsure, say N here. + config STANDALONE bool "Select only drivers that don't need compile-time external firmware" if EXPERIMENTAL default y diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 1b2640ce74f..c12c7f2f2a6 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -4,6 +4,7 @@ obj-y := core.o sys.o bus.o dd.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o +obj-$(CONFIG_DEVTMPFS) += devtmpfs.o obj-y += power/ obj-$(CONFIG_HAS_DMA) += dma-mapping.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o diff --git a/drivers/base/base.h b/drivers/base/base.h index 503d59c5750..2ca7f5b7b82 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -139,3 +139,9 @@ static inline void module_add_driver(struct module *mod, struct device_driver *drv) { } static inline void module_remove_driver(struct device_driver *drv) { } #endif + +#ifdef CONFIG_DEVTMPFS +extern int devtmpfs_init(void); +#else +static inline int devtmpfs_init(void) { return 0; } +#endif diff --git a/drivers/base/core.c b/drivers/base/core.c index a992985d1fa..390e664ec1c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -929,6 +929,8 @@ int device_add(struct device *dev) error = device_create_sys_dev_entry(dev); if (error) goto devtattrError; + + devtmpfs_create_node(dev); } error = device_add_class_symlinks(dev); @@ -1075,6 +1077,7 @@ void device_del(struct device *dev) if (parent) klist_del(&dev->p->knode_parent); if (MAJOR(dev->devt)) { + devtmpfs_delete_node(dev); device_remove_sys_dev_entry(dev); device_remove_file(dev, &devt_attr); } diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c new file mode 100644 index 00000000000..fd488ad4263 --- /dev/null +++ b/drivers/base/devtmpfs.c @@ -0,0 +1,367 @@ +/* + * devtmpfs - kernel-maintained tmpfs-based /dev + * + * Copyright (C) 2009, Kay Sievers + * + * During bootup, before any driver core device is registered, + * devtmpfs, a tmpfs-based filesystem is created. Every driver-core + * device which requests a device node, will add a node in this + * filesystem. The node is named after the the name of the device, + * or the susbsytem can provide a custom name. All devices are + * owned by root and have a mode of 0600. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct vfsmount *dev_mnt; + +#if defined CONFIG_DEVTMPFS_MOUNT +static int dev_mount = 1; +#else +static int dev_mount; +#endif + +static int __init mount_param(char *str) +{ + dev_mount = simple_strtoul(str, NULL, 0); + return 1; +} +__setup("devtmpfs.mount=", mount_param); + +static int dev_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, data, shmem_fill_super, mnt); +} + +static struct file_system_type dev_fs_type = { + .name = "devtmpfs", + .get_sb = dev_get_sb, + .kill_sb = kill_litter_super, +}; + +#ifdef CONFIG_BLOCK +static inline int is_blockdev(struct device *dev) +{ + return dev->class == &block_class; +} +#else +static inline int is_blockdev(struct device *dev) { return 0; } +#endif + +static int dev_mkdir(const char *name, mode_t mode) +{ + struct nameidata nd; + struct dentry *dentry; + int err; + + err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, + name, LOOKUP_PARENT, &nd); + if (err) + return err; + + dentry = lookup_create(&nd, 1); + if (!IS_ERR(dentry)) { + err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); + dput(dentry); + } else { + err = PTR_ERR(dentry); + } + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + + path_put(&nd.path); + return err; +} + +static int create_path(const char *nodepath) +{ + char *path; + struct nameidata nd; + int err = 0; + + path = kstrdup(nodepath, GFP_KERNEL); + if (!path) + return -ENOMEM; + + err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, + path, LOOKUP_PARENT, &nd); + if (err == 0) { + struct dentry *dentry; + + /* create directory right away */ + dentry = lookup_create(&nd, 1); + if (!IS_ERR(dentry)) { + err = vfs_mkdir(nd.path.dentry->d_inode, + dentry, 0755); + dput(dentry); + } + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + + path_put(&nd.path); + } else if (err == -ENOENT) { + char *s; + + /* parent directories do not exist, create them */ + s = path; + while (1) { + s = strchr(s, '/'); + if (!s) + break; + s[0] = '\0'; + err = dev_mkdir(path, 0755); + if (err && err != -EEXIST) + break; + s[0] = '/'; + s++; + } + } + + kfree(path); + return err; +} + +int devtmpfs_create_node(struct device *dev) +{ + const char *tmp = NULL; + const char *nodename; + const struct cred *curr_cred; + mode_t mode; + struct nameidata nd; + struct dentry *dentry; + int err; + + if (!dev_mnt) + return 0; + + nodename = device_get_nodename(dev, &tmp); + if (!nodename) + return -ENOMEM; + + if (is_blockdev(dev)) + mode = S_IFBLK|0600; + else + mode = S_IFCHR|0600; + + curr_cred = override_creds(&init_cred); + err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, + nodename, LOOKUP_PARENT, &nd); + if (err == -ENOENT) { + /* create missing parent directories */ + create_path(nodename); + err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, + nodename, LOOKUP_PARENT, &nd); + if (err) + goto out; + } + + dentry = lookup_create(&nd, 0); + if (!IS_ERR(dentry)) { + err = vfs_mknod(nd.path.dentry->d_inode, + dentry, mode, dev->devt); + /* mark as kernel created inode */ + if (!err) + dentry->d_inode->i_private = &dev_mnt; + dput(dentry); + } else { + err = PTR_ERR(dentry); + } + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + + path_put(&nd.path); +out: + kfree(tmp); + revert_creds(curr_cred); + return err; +} + +static int dev_rmdir(const char *name) +{ + struct nameidata nd; + struct dentry *dentry; + int err; + + err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, + name, LOOKUP_PARENT, &nd); + if (err) + return err; + + mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); + if (!IS_ERR(dentry)) { + if (dentry->d_inode) + err = vfs_rmdir(nd.path.dentry->d_inode, dentry); + else + err = -ENOENT; + dput(dentry); + } else { + err = PTR_ERR(dentry); + } + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + + path_put(&nd.path); + return err; +} + +static int delete_path(const char *nodepath) +{ + const char *path; + int err = 0; + + path = kstrdup(nodepath, GFP_KERNEL); + if (!path) + return -ENOMEM; + + while (1) { + char *base; + + base = strrchr(path, '/'); + if (!base) + break; + base[0] = '\0'; + err = dev_rmdir(path); + if (err) + break; + } + + kfree(path); + return err; +} + +static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat) +{ + /* did we create it */ + if (inode->i_private != &dev_mnt) + return 0; + + /* does the dev_t match */ + if (is_blockdev(dev)) { + if (!S_ISBLK(stat->mode)) + return 0; + } else { + if (!S_ISCHR(stat->mode)) + return 0; + } + if (stat->rdev != dev->devt) + return 0; + + /* ours */ + return 1; +} + +int devtmpfs_delete_node(struct device *dev) +{ + const char *tmp = NULL; + const char *nodename; + const struct cred *curr_cred; + struct nameidata nd; + struct dentry *dentry; + struct kstat stat; + int deleted = 1; + int err; + + if (!dev_mnt) + return 0; + + nodename = device_get_nodename(dev, &tmp); + if (!nodename) + return -ENOMEM; + + curr_cred = override_creds(&init_cred); + err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, + nodename, LOOKUP_PARENT, &nd); + if (err) + goto out; + + mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); + if (!IS_ERR(dentry)) { + if (dentry->d_inode) { + err = vfs_getattr(nd.path.mnt, dentry, &stat); + if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { + err = vfs_unlink(nd.path.dentry->d_inode, + dentry); + if (!err || err == -ENOENT) + deleted = 1; + } + } else { + err = -ENOENT; + } + dput(dentry); + } else { + err = PTR_ERR(dentry); + } + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + + path_put(&nd.path); + if (deleted && strchr(nodename, '/')) + delete_path(nodename); +out: + kfree(tmp); + revert_creds(curr_cred); + return err; +} + +/* + * If configured, or requested by the commandline, devtmpfs will be + * auto-mounted after the kernel mounted the root filesystem. + */ +int devtmpfs_mount(const char *mountpoint) +{ + struct path path; + int err; + + if (!dev_mount) + return 0; + + if (!dev_mnt) + return 0; + + err = kern_path(mountpoint, LOOKUP_FOLLOW, &path); + if (err) + return err; + err = do_add_mount(dev_mnt, &path, 0, NULL); + if (err) + printk(KERN_INFO "devtmpfs: error mounting %i\n", err); + else + printk(KERN_INFO "devtmpfs: mounted\n"); + path_put(&path); + return err; +} + +/* + * Create devtmpfs instance, driver-core devices will add their device + * nodes here. + */ +int __init devtmpfs_init(void) +{ + int err; + struct vfsmount *mnt; + + err = register_filesystem(&dev_fs_type); + if (err) { + printk(KERN_ERR "devtmpfs: unable to register devtmpfs " + "type %i\n", err); + return err; + } + + mnt = kern_mount(&dev_fs_type); + if (IS_ERR(mnt)) { + err = PTR_ERR(mnt); + printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); + unregister_filesystem(&dev_fs_type); + return err; + } + dev_mnt = mnt; + + printk(KERN_INFO "devtmpfs: initialized\n"); + return 0; +} diff --git a/drivers/base/init.c b/drivers/base/init.c index 7bd9b6a5b01..c8a934e7942 100644 --- a/drivers/base/init.c +++ b/drivers/base/init.c @@ -20,6 +20,7 @@ void __init driver_init(void) { /* These are the core pieces */ + devtmpfs_init(); devices_init(); buses_init(); classes_init(); diff --git a/include/linux/device.h b/include/linux/device.h index 62ff53a6793..847b763e40e 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -552,6 +552,16 @@ extern void put_device(struct device *dev); extern void wait_for_device_probe(void); +#ifdef CONFIG_DEVTMPFS +extern int devtmpfs_create_node(struct device *dev); +extern int devtmpfs_delete_node(struct device *dev); +extern int devtmpfs_mount(const char *mountpoint); +#else +static inline int devtmpfs_create_node(struct device *dev) { return 0; } +static inline int devtmpfs_delete_node(struct device *dev) { return 0; } +static inline int devtmpfs_mount(const char *mountpoint) { return 0; } +#endif + /* drivers/base/power/shutdown.c */ extern void device_shutdown(void); diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 6d3f2f449ea..deee7afd8d6 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -38,6 +38,9 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) return container_of(inode, struct shmem_inode_info, vfs_inode); } +extern int init_tmpfs(void); +extern int shmem_fill_super(struct super_block *sb, void *data, int silent); + #ifdef CONFIG_TMPFS_POSIX_ACL int shmem_check_acl(struct inode *, int); int shmem_acl_init(struct inode *, struct inode *); diff --git a/init/do_mounts.c b/init/do_mounts.c index 093f6591550..bb008d064c1 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -415,7 +415,7 @@ void __init prepare_namespace(void) mount_root(); out: + devtmpfs_mount("dev"); sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); } - diff --git a/init/main.c b/init/main.c index b34fd8e5ede..8e6a7846bd0 100644 --- a/init/main.c +++ b/init/main.c @@ -68,6 +68,7 @@ #include #include #include +#include #include #include @@ -809,6 +810,7 @@ static void __init do_basic_setup(void) init_workqueues(); cpuset_init_smp(); usermodehelper_init(); + init_tmpfs(); driver_init(); init_irq_proc(); do_ctors(); diff --git a/mm/shmem.c b/mm/shmem.c index 5a0b3d4055f..bd20f8bb02a 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2298,8 +2298,7 @@ static void shmem_put_super(struct super_block *sb) sb->s_fs_info = NULL; } -static int shmem_fill_super(struct super_block *sb, - void *data, int silent) +int shmem_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct dentry *root; @@ -2519,7 +2518,7 @@ static struct file_system_type tmpfs_fs_type = { .kill_sb = kill_litter_super, }; -static int __init init_tmpfs(void) +int __init init_tmpfs(void) { int error; @@ -2576,7 +2575,7 @@ static struct file_system_type tmpfs_fs_type = { .kill_sb = kill_litter_super, }; -static int __init init_tmpfs(void) +int __init init_tmpfs(void) { BUG_ON(register_filesystem(&tmpfs_fs_type) != 0); @@ -2687,5 +2686,3 @@ int shmem_zero_setup(struct vm_area_struct *vma) vma->vm_ops = &shmem_vm_ops; return 0; } - -module_init(init_tmpfs) -- cgit v1.2.3