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 --- include/linux/device.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index a28642975053..c0bd23048be0 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(+) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index c0bd23048be0..3f33f17f556c 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 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(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index 5b76bf55d05c..2ad91ddad8e2 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 99dfe96fffcb..a992985d1fab 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 8ae0f63602e0..ed2ebd3c287d 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 0589dfbbd7db..d8372b432826 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 97e656af2d22..9d0dfcbe2c1c 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 5a76d056b9d0..dbdf6fadfc79 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 5b635aa5947e..fb2d83c5bf01 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 6ba4861dd6ac..b3d7efcdf021 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 a6c8efbdc0c9..b8cb2f145ae4 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 7c237e6ac711..851791d955f3 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 7b039306037f..e9eae4a78402 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 06084dbf1277..2fb9d5f271ea 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 cd81c395e164..7ad646fe077e 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 00ebf7af7467..69007a6eff50 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 e995123fd805..393c73c47f87 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 0f95405c2c5e..6527f3f34493 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 9215fbbccc08..a4b2c576144b 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 021e503c8c44..1fbf7c78bba0 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 91482f2dcc50..fde54537d715 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 bc39fc40bbde..fdfaa7885515 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 b5c72e458943..7ec3041ae79e 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 e2a8cfaade1d..c0e0ae2bb8e7 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 e9fe1bb7eb23..1097e81b56d0 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 619ba99dfe39..fbeaddf595d3 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 794ad74b1d61..c3ab81428c66 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 3f33f17f556c..e19e40a3dcbe 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 65ee1929b2b1..a9aa4b5917d7 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 eaec1ea9558e..9ae8da3e6407 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 95f7a7a544b4..7f939ce29801 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 ad91e9e5f475..7d4c57523b09 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 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 (limited to 'include') diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index 8f6e3b2403c7..4d4ce0e61e42 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 837b5985ac40..01193a4fe30e 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 45200fd68534..8aa1955f35ed 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 5c2586d75797..73b2e7516729 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 000000000000..313da35984af --- /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 fcaee42c7ac2..dd0bed4f1cf0 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(+) (limited to 'include') diff --git a/drivers/base/class.c b/drivers/base/class.c index eb85e4312301..161746deab4b 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 e19e40a3dcbe..62ff53a67931 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 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 (limited to 'include') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 8f006f96ff53..ee377270beb9 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 1b2640ce74f0..c12c7f2f2a6f 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 503d59c57501..2ca7f5b7b824 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 a992985d1fab..390e664ec1c7 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 000000000000..fd488ad4263a --- /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 7bd9b6a5b01f..c8a934e79421 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 62ff53a67931..847b763e40e9 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 6d3f2f449ead..deee7afd8d66 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 093f65915501..bb008d064c1a 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 b34fd8e5edef..8e6a7846bd07 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 5a0b3d4055f3..bd20f8bb02aa 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