Skip to contentSkip to conversation

Managed device resources, take #2

PATCH In review Tejun Heo 4 participants 22 messages Jan 10, 2007 – Apr 11, 2008

Subsystems (8)
lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

PATCH 1/1 Managed device resources, take #2

Hello,

This is the second take of devres patchset. Changes from the last take[L] are...

* generic managed iomap interface functions implemented
- devm_ioport_map/unmap()
- devm_ioremap[_nocache](), devm_iounmap()

* libata-sff legacy mode now uses generic managed iomap interface intead of implementing its own devres

* all pata drivers have been converted

* Documentation/driver-model/devres.txt added

Git tree is available at the following URLs.

http://htj.dyndns.org/git/?p=libata-tj.git;a=shortlog;h=devres
git://htj.dyndns.org/libata-tj devres

For detailed info, please read Documentation/driver-model/devres.txt. Similar info can also be found from the last post.

Thanks.

--
tejun

[L] http://thread.gmane.org/gmane.linux.ide/14690

author
#1 lore raw

PATCH 1/13 devres: device resource management core

Implement device resource management core, in short, devres. A device driver can allocate arbirary size of devres data which is associated with a release function. On driver detach, release function is invoked on the devres data, then, devres data is freed.

devreses are typed by associated release functions. Some devreses are better represented by single instance of the type while others need multiple instances sharing the same release function. Both usages are supported.

devreses can be grouped using devres group such that a device driver can easily release acquired resources halfway through initialization or selectively release resources (e.g. resources for port 1 out of 4 ports).

In addition, managed kzalloc() and kfree() are implemented by this patch.

---
 drivers/base/Kconfig   |   12 +
 drivers/base/Makefile  |    2 +-
 drivers/base/base.h    |    1 +
 drivers/base/core.c    |    2 +
 drivers/base/dd.c      |    3 +
 drivers/base/devres.c  |  644 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/device.h |   38 +++
 7 files changed, 701 insertions(+), 1 deletions(-)
drivers/base/Kconfig +12 -0
index 1429f3a..5d6312e 100644
@@ -37,6 +37,18 @@ config DEBUG_DRIVER
 
 	  If you are unsure about this, say N here.
 
+config DEBUG_DEVRES
+	bool "Managed device resources verbose debug messages"
+	depends on DEBUG_KERNEL
+	help
+	  This option enables kernel parameter devres.log. If set to
+	  non-zero, devres debug messages are printed. Select this if
+	  you are having a problem with devres or want to debug
+	  resource management for a managed device. devres.log can be
+	  switched on and off from sysfs node.
+
+	  If you are unsure about this, Say N here.
+
 config SYS_HYPERVISOR
 	bool
 	default n
drivers/base/Makefile +1 -1
index 7bbb9ee..e236f42 100644
@@ -2,7 +2,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 dmapool.o \
+			   cpu.o firmware.o init.o map.o dmapool.o devres.o \
 			   attribute_container.o transport_class.o
 obj-y			+= power/
 obj-$(CONFIG_ISA)	+= isa.o
drivers/base/base.h +1 -0
index d26644a..de7e144 100644
@@ -44,3 +44,4 @@ struct class_device_attribute *to_class_dev_attr(struct attribute *_attr)
 
 extern char *make_class_name(const char *name, struct kobject *kobj);
 
+extern void devres_release_all(struct device *dev);
drivers/base/core.c +2 -0
index 67b79a7..d1c94e3 100644
@@ -385,6 +385,8 @@ void device_initialize(struct device *dev)
 	INIT_LIST_HEAD(&dev->dma_pools);
 	INIT_LIST_HEAD(&dev->node);
 	init_MUTEX(&dev->sem);
+	spin_lock_init(&dev->devres_lock);
+	INIT_LIST_HEAD(&dev->devres_head);
 	device_init_wakeup(dev, 0);
 	set_dev_node(dev, -1);
 }
drivers/base/dd.c +3 -0
index 510e788..437e046 100644
@@ -108,6 +108,7 @@ static int really_probe(void *void_data)
 	atomic_inc(&probe_count);
 	pr_debug("%s: Probing driver %s with device %s\n",
 		 drv->bus->name, drv->name, dev->bus_id);
+	WARN_ON(!list_empty(&dev->devres_head));
 
 	dev->driver = drv;
 	if (driver_sysfs_add(dev)) {
@@ -133,6 +134,7 @@ static int really_probe(void *void_data)
 	goto done;
 
 probe_failed:
+	devres_release_all(dev);
 	driver_sysfs_remove(dev);
 	dev->driver = NULL;
 
@@ -324,6 +326,7 @@ static void __device_release_driver(struct device * dev)
 			dev->bus->remove(dev);
 		else if (drv->remove)
 			drv->remove(dev);
+		devres_release_all(dev);
 		dev->driver = NULL;
 		put_driver(drv);
 	}
drivers/base/devres.c new file +644 -0

644 lines not on this page · raw message

include/linux/device.h +38 -0
index f44247f..6c83917 100644
@@ -346,6 +346,41 @@ extern int __must_check device_create_bin_file(struct device *dev,
 					       struct bin_attribute *attr);
 extern void device_remove_bin_file(struct device *dev,
 				   struct bin_attribute *attr);
+
+/* device resource management */
+typedef void (*dr_release_t)(struct device *dev, void *res);
+typedef int (*dr_match_t)(struct device *dev, void *res, void *match_data);
+
+#ifdef CONFIG_DEBUG_DEVRES
+extern void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
+			     const char *name);
+#define devres_alloc(release, size, gfp) \
+	__devres_alloc(release, size, gfp, #release)
+#else
+extern void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
+#endif
+extern void devres_free(void *res);
+extern void devres_add(struct device *dev, void *res);
+extern void * devres_find(struct device *dev, dr_release_t release,
+			  dr_match_t match, void *match_data);
+extern void * devres_get(struct device *dev, void *new_res,
+			 dr_match_t match, void *match_data);
+extern void * devres_remove(struct device *dev, dr_release_t release,
+			    dr_match_t match, void *match_data);
+extern int devres_destroy(struct device *dev, dr_release_t release,
+			  dr_match_t match, void *match_data);
+
+/* devres group */
+extern void * __must_check devres_open_group(struct device *dev, void *id,
+					     gfp_t gfp);
+extern void devres_close_group(struct device *dev, void *id);
+extern void devres_remove_group(struct device *dev, void *id);
+extern int devres_release_group(struct device *dev, void *id);
+
+/* managed kzalloc/kfree for device drivers, no kmalloc, always use kzalloc */
+extern void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp);
+extern void devm_kfree(struct device *dev, void *p);
+
 struct device {
 	struct klist		klist_children;
 	struct klist_node	knode_parent;		/* node in sibling list */
@@ -388,6 +423,9 @@ struct device {
 	/* arch specific additions */
 	struct dev_archdata	archdata;
 
+	spinlock_t		devres_lock;
+	struct list_head	devres_head;
+
 	/* class_device migration path */
 	struct list_head	node;
 	struct class		*class;		/* optional*/
Signature · 1 line
1.4.4.3
author
#2 lore raw

PATCH 2/13 devres: implement managed IO region interface

In reply to Tejun Heo

Implement managed IO region interface - devm_request_region() and devm_release_region(). Except for the first @dev argument and being managed, these take the same arguments and have the same effect as non-managed coutnerparts.

---
 include/linux/ioport.h |   20 +++++++++++++++
 kernel/resource.c      |   62 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+), 0 deletions(-)
include/linux/ioport.h +20 -0
index 15228d7..6859a3b 100644
@@ -137,4 +137,24 @@ static inline int __deprecated check_region(resource_size_t s,
 {
 	return __check_region(&ioport_resource, s, n);
 }
+
+/* Wrappers for managed devices */
+struct device;
+#define devm_request_region(dev,start,n,name) \
+	__devm_request_region(dev, &ioport_resource, (start), (n), (name))
+#define devm_request_mem_region(dev,start,n,name) \
+	__devm_request_region(dev, &iomem_resource, (start), (n), (name))
+
+extern struct resource * __devm_request_region(struct device *dev,
+				struct resource *parent, resource_size_t start,
+				resource_size_t n, const char *name);
+
+#define devm_release_region(start,n) \
+	__devm_release_region(dev, &ioport_resource, (start), (n))
+#define devm_release_mem_region(start,n) \
+	__devm_release_region(dev, &iomem_resource, (start), (n))
+
+extern void __devm_release_region(struct device *dev, struct resource *parent,
+				  resource_size_t start, resource_size_t n);
+
 #endif	/* _LINUX_IOPORT_H */
kernel/resource.c +62 -0

74 lines not on this page · raw message

Signature · 1 line
1.4.4.3
author
#3 lore raw

PATCH 3/13 devres: implement managed IRQ interface

In reply to Tejun Heo

Implement managed IRQ interface - devm_request_irq() and devm_free_irq(). Except for the first @dev argument and being managed, these take the same arguments and have the same effect as non-managed coutnerparts.

---
 include/linux/interrupt.h |    6 +++
 kernel/irq/manage.c       |   86 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 92 insertions(+), 0 deletions(-)
include/linux/interrupt.h +6 -0
index e36e86c..5a8ba0b 100644
@@ -12,6 +12,7 @@
 #include <linux/sched.h>
 #include <linux/irqflags.h>
 #include <linux/bottom_half.h>
+#include <linux/device.h>
 #include <asm/atomic.h>
 #include <asm/ptrace.h>
 #include <asm/system.h>
@@ -83,6 +84,11 @@ extern int request_irq(unsigned int, irq_handler_t handler,
 		       unsigned long, const char *, void *);
 extern void free_irq(unsigned int, void *);
 
+extern int devm_request_irq(struct device *dev, unsigned int irq,
+			    irq_handler_t handler, unsigned long irqflags,
+			    const char *devname, void *dev_id);
+extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);
+
 /*
  * On lockdep we dont want to enable hardirqs in hardirq
  * context. Use local_irq_enable_in_hardirq() to annotate
kernel/irq/manage.c +86 -0

89 lines not on this page · raw message

Signature · 1 line
1.4.4.3
author
#4 lore raw

PATCH 4/13 devres: implement managed DMA interface

In reply to Tejun Heo

Implement managed DMA interface - dmam_alloc_coherent(), dmam_free_coherent(), dmam_declare_coherent_memory(), dmam_pool_create() and dmam_pool_destroy(). Except for being managed, these take the same arguments and have the same effect as non-managed counterparts.

---
 drivers/base/Makefile       |    3 +-
 drivers/base/dma-mapping.c  |  218 +++++++++++++++++++++++++++++++++++++++++++
 drivers/base/dmapool.c      |   59 ++++++++++++
 include/linux/dma-mapping.h |   29 ++++++-
 include/linux/dmapool.h     |    7 ++
 5 files changed, 314 insertions(+), 2 deletions(-)
drivers/base/Makefile +2 -1
index e236f42..e9eb738 100644
@@ -2,7 +2,8 @@
 
 obj-y			:= core.o sys.o bus.o dd.o \
 			   driver.o class.o platform.o \
-			   cpu.o firmware.o init.o map.o dmapool.o devres.o \
+			   cpu.o firmware.o init.o map.o dmapool.o \
+			   dma-mapping.o devres.o \
 			   attribute_container.o transport_class.o
 obj-y			+= power/
 obj-$(CONFIG_ISA)	+= isa.o
drivers/base/dma-mapping.c new file +218 -0

218 lines not on this page · raw message

drivers/base/dmapool.c +59 -0

67 lines not on this page · raw message

include/linux/dma-mapping.h +28 -1
index ff203c4..9a663c6 100644
@@ -66,6 +66,33 @@ dma_mark_declared_memory_occupied(struct device *dev,
 }
 #endif
 
-#endif
+/*
+ * Managed DMA API
+ */
+extern void *dmam_alloc_coherent(struct device *dev, size_t size,
+				 dma_addr_t *dma_handle, gfp_t gfp);
+extern void dmam_free_coherent(struct device *dev, size_t size, void *vaddr,
+			       dma_addr_t dma_handle);
+extern void *dmam_alloc_noncoherent(struct device *dev, size_t size,
+				    dma_addr_t *dma_handle, gfp_t gfp);
+extern void dmam_free_noncoherent(struct device *dev, size_t size, void *vaddr,
+				  dma_addr_t dma_handle);
+#ifdef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
+extern int dmam_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
+					dma_addr_t device_addr, size_t size,
+					int flags);
+extern void dmam_release_declared_memory(struct device *dev);
+#else /* ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY */
+static inline int dmam_declare_coherent_memory(struct device *dev,
+				dma_addr_t bus_addr, dma_addr_t device_addr,
+				size_t size, gfp_t gfp)
+{
+	return 0;
+}
 
+static inline void dmam_release_declared_memory(struct device *dev)
+{
+}
+#endif /* ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY */
 
+#endif
include/linux/dmapool.h +7 -0
index 76f12f4..022e34f 100644
@@ -24,5 +24,12 @@ void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
 
 void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);
 
+/*
+ * Managed DMA pool
+ */
+struct dma_pool *dmam_pool_create(const char *name, struct device *dev,
+				  size_t size, size_t align, size_t allocation);
+void dmam_pool_destroy(struct dma_pool *pool);
+
 #endif
 
Signature · 1 line
1.4.4.3
author
#5 lore raw

PATCH 5/13 devres: implement managed PCI interface

In reply to Tejun Heo

Implement managed PCI interface - pcim_enable_device(), pcim_pin_device() and pci_is_managed(). pcim_enable_device() is equivalent to pci_enable_device() except that it makes the PCI device managed. After pcim_enable_device(), PCI resources such as enabledness, msi/msix/intx status and BAR regions become managed.

pcim_pin_device() is used by device drivers to ask PCI devres not to disable the device on driver detach. This function is to handle cases where a driver finds out that a device is otherwise busy and thus shouldn't be disabled on probe failure or driver detach.

---
 drivers/pci/pci.c   |  127 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pci.h |    9 ++++
 2 files changed, 135 insertions(+), 1 deletions(-)
drivers/pci/pci.c +126 -1
index 6bfb942..2ab2f8a 100644
@@ -744,6 +744,104 @@ int pci_enable_device(struct pci_dev *dev)
 	return result;
 }
 
+/*
+ * Managed PCI resources.  This manages device on/off, intx/msi/msix
+ * on/off and BAR regions.  pci_dev itself records msi/msix status, so
+ * there's no need to track it separately.  pci_devres is initialized
+ * when a device is enabled using managed PCI device enable interface.
+ */
+struct pci_devres {
+	unsigned int disable:1;
+	unsigned int orig_intx:1;
+	unsigned int restore_intx:1;
+	u32 region_mask;
+};
+
+static void pcim_release(struct device *gendev, void *res)
+{
+	struct pci_dev *dev = container_of(gendev, struct pci_dev, dev);
+	struct pci_devres *this = res;
+	int i;
+
+	if (dev->msi_enabled)
+		pci_disable_msi(dev);
+	if (dev->msix_enabled)
+		pci_disable_msix(dev);
+
+	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
+		if (this->region_mask & (1 << i))
+			pci_release_region(dev, i);
+
+	if (this->restore_intx)
+		pci_intx(dev, this->orig_intx);
+
+	if (this->disable)
+		pci_disable_device(dev);
+}
+
+static struct pci_devres * get_pci_dr(struct pci_dev *pdev)
+{
+	struct pci_devres *dr, *new_dr;
+
+	dr = devres_find(&pdev->dev, pcim_release, NULL, NULL);
+	if (dr)
+		return dr;
+
+	new_dr = devres_alloc(pcim_release, sizeof(*new_dr), GFP_KERNEL);
+	if (!new_dr)
+		return NULL;
+	return devres_get(&pdev->dev, new_dr, NULL, NULL);
+}
+
+static struct pci_devres * find_pci_dr(struct pci_dev *pdev)
+{
+	if (pci_is_managed(pdev))
+		return devres_find(&pdev->dev, pcim_release, NULL, NULL);
+	return NULL;
+}
+
+/**
+ * pcim_enable_device - Managed pci_enable_device()
+ * @pdev: PCI device to be initialized
+ *
+ * Managed pci_enable_device().
+ */
+int pcim_enable_device(struct pci_dev *pdev)
+{
+	struct pci_devres *dr;
+	int rc;
+
+	dr = get_pci_dr(pdev);
+	if (unlikely(!dr))
+		return -ENOMEM;
+	WARN_ON(!!dr->disable);
+
+	rc = pci_enable_device(pdev);
+	if (!rc) {
+		pdev->is_managed = 1;
+		dr->disable = 1;
+	}
+	return rc;
+}
+
+/**
+ * pcim_pin_device - Pin managed PCI device
+ * @pdev: PCI device to pin
+ *
+ * Pin managed PCI device @pdev.  Pinned device won't be disabled on
+ * driver detach.  @pdev must have been enabled with
+ * pcim_enable_device().
+ */
+void pcim_pin_device(struct pci_dev *pdev)
+{
+	struct pci_devres *dr;
+
+	dr = find_pci_dr(pdev);
+	WARN_ON(!dr || !dr->disable);
+	if (dr)
+		dr->disable = 0;
+}
+
 /**
  * pcibios_disable_device - disable arch specific PCI resources for device dev
  * @dev: the PCI device to disable
@@ -767,8 +865,13 @@ void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {}
 void
 pci_disable_device(struct pci_dev *dev)
 {
+	struct pci_devres *dr;
 	u16 pci_command;
 
+	dr = find_pci_dr(dev);
+	if (dr)
+		dr->disable = 0;
+
 	if (atomic_sub_return(1, &dev->enable_cnt) != 0)
 		return;
 
@@ -867,6 +970,8 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
  */
 void pci_release_region(struct pci_dev *pdev, int bar)
 {
+	struct pci_devres *dr;
+
 	if (pci_resource_len(pdev, bar) == 0)
 		return;
 	if (pci_resource_flags(pdev, bar) & IORESOURCE_IO)
@@ -875,6 +980,10 @@ void pci_release_region(struct pci_dev *pdev, int bar)
 	else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM)
 		release_mem_region(pci_resource_start(pdev, bar),
 				pci_resource_len(pdev, bar));
+
+	dr = find_pci_dr(pdev);
+	if (dr)
+		dr->region_mask &= ~(1 << bar);
 }
 
 /**
@@ -893,6 +1002,8 @@ void pci_release_region(struct pci_dev *pdev, int bar)
  */
 int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
 {
+	struct pci_devres *dr;
+
 	if (pci_resource_len(pdev, bar) == 0)
 		return 0;
 		
@@ -906,7 +1017,11 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
 				        pci_resource_len(pdev, bar), res_name))
 			goto err_out;
 	}
-	
+
+	dr = find_pci_dr(pdev);
+	if (dr)
+		dr->region_mask |= 1 << bar;
+
 	return 0;
 
 err_out:
@@ -1117,7 +1232,15 @@ pci_intx(struct pci_dev *pdev, int enable)
 	}
 
 	if (new != pci_command) {
+		struct pci_devres *dr;
+
 		pci_write_config_word(pdev, PCI_COMMAND, new);
+
+		dr = find_pci_dr(pdev);
+		if (dr && !dr->restore_intx) {
+			dr->restore_intx = 1;
+			dr->orig_intx = !enable;
+		}
 	}
 }
 
@@ -1189,6 +1312,8 @@ EXPORT_SYMBOL(isa_bridge);
 EXPORT_SYMBOL_GPL(pci_restore_bars);
 EXPORT_SYMBOL(pci_enable_device_bars);
 EXPORT_SYMBOL(pci_enable_device);
+EXPORT_SYMBOL(pcim_enable_device);
+EXPORT_SYMBOL(pcim_pin_device);
 EXPORT_SYMBOL(pci_disable_device);
 EXPORT_SYMBOL(pci_find_capability);
 EXPORT_SYMBOL(pci_bus_find_capability);
include/linux/pci.h +9 -0
index f3c617e..1f82eb9 100644
@@ -167,6 +167,7 @@ struct pci_dev {
 	unsigned int	broken_parity_status:1;	/* Device generates false positive parity */
 	unsigned int 	msi_enabled:1;
 	unsigned int	msix_enabled:1;
+	unsigned int	is_managed:1;
 	atomic_t	enable_cnt;	/* pci_enable_device has been called */
 
 	u32		saved_config_space[16]; /* config space saved at suspend time */
@@ -521,6 +522,14 @@ static inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val
 
 int __must_check pci_enable_device(struct pci_dev *dev);
 int __must_check pci_enable_device_bars(struct pci_dev *dev, int mask);
+int __must_check pcim_enable_device(struct pci_dev *pdev);
+void pcim_pin_device(struct pci_dev *pdev);
+
+static inline int pci_is_managed(struct pci_dev *pdev)
+{
+	return pdev->is_managed;
+}
+
 void pci_disable_device(struct pci_dev *dev);
 void pci_set_master(struct pci_dev *dev);
 #define HAVE_PCI_SET_MWI
Signature · 1 line
1.4.4.3
author
#6 lore raw

PATCH 6/13 devres: implement managed iomap interface

In reply to Tejun Heo

Implement managed iomap interface - devm_ioport_map(), devm_ioport_unmap(), devm_ioremap(), devm_ioremap_nocache(), devm_iounmap(), pcim_iomap_table(), pcim_iomap() and pcim_iounmap(). Except for being managed and additional gendev argument, these functions take the same arguments and have the same effect as non-managed coutnerparts.

pcim_iomap_table() returns pointer to constant array of void __iomem * which record addresses for all mapped BARs. This function is guaranteed to succeed after successful pcim_iomap() and drivers are free to reference the returned iomap table.

---
 include/linux/io.h |   17 ++++
 lib/Makefile       |    3 +-
 lib/iomap.c        |  246 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 263 insertions(+), 3 deletions(-)
include/linux/io.h +17 -0
index 81877ea..f5edf9c 100644
@@ -28,6 +28,23 @@ void __iowrite64_copy(void __iomem *to, const void *from, size_t count);
 int ioremap_page_range(unsigned long addr, unsigned long end,
 		       unsigned long phys_addr, pgprot_t prot);
 
+/*
+ * Managed iomap interface
+ */
+void __iomem * devm_ioport_map(struct device *dev, unsigned long port,
+			       unsigned int nr);
+void devm_ioport_unmap(struct device *dev, void __iomem *addr);
+
+void __iomem * devm_ioremap(struct device *dev, unsigned long offset,
+			    unsigned long size);
+void __iomem * devm_ioremap_nocache(struct device *dev, unsigned long offset,
+				    unsigned long size);
+void devm_iounmap(struct device *dev, void __iomem *addr);
+
+void __iomem * pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);
+void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr);
+void __iomem * const * pcim_iomap_table(struct pci_dev *pdev);
+
 /**
  *	check_signature		-	find BIOS signatures
  *	@io_addr: mmio address to check
lib/Makefile +1 -2
index 77b4bad..29b2e99 100644
@@ -5,7 +5,7 @@
 lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
 	 idr.o div64.o int_sqrt.o bitmap.o extable.o prio_tree.o \
-	 sha1.o irq_regs.o reciprocal_div.o
+	 sha1.o irq_regs.o reciprocal_div.o iomap.o
 
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
@@ -41,7 +41,6 @@ obj-$(CONFIG_CRC_CCITT)	+= crc-ccitt.o
 obj-$(CONFIG_CRC16)	+= crc16.o
 obj-$(CONFIG_CRC32)	+= crc32.o
 obj-$(CONFIG_LIBCRC32C)	+= libcrc32c.o
-obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
 obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
 
 obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/
lib/iomap.c +245 -1

256 lines not on this page · raw message

Signature · 1 line
1.4.4.3
#7 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 46 lines · Tejun Heo
lib/iomap.c +46 -0
+/**
+ * devm_ioremap - Managed ioremap()
+ * @dev: Generic device to remap IO address for
+ * @offset: BUS offset to map
+ * @size: Size of map
+ *
+ * Managed ioremap().  Map is automatically unmapped on driver detach.
+ */
+void __iomem *devm_ioremap(struct device *dev, unsigned long offset,
+			   unsigned long size)
+{
+	void __iomem **ptr, *addr;
+
+	ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return NULL;
+
+	addr = ioremap(offset, size);
+	if (addr) {
+		*ptr = addr;
+		devres_add(dev, ptr);
+	} else
+		devres_free(ptr);
+
+	return addr;
+}
+EXPORT_SYMBOL(devm_ioremap);
+
+/**
+ * devm_ioremap_nocache - Managed ioremap_nocache()
+ * @dev: Generic device to remap IO address for
+ * @offset: BUS offset to map
+ * @size: Size of map
+ *
+ * Managed ioremap_nocache().  Map is automatically unmapped on driver
+ * detach.
+ */
+void __iomem *devm_ioremap_nocache(struct device *dev, unsigned long offset,
+				   unsigned long size)
+{
+	void __iomem **ptr, *addr;
+
+	ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return NULL;
+

lib/iomap.c void pci_iounmap(struct pci_dev *dev, void __iomem * addr) · line 386

Quoted 10 lines
lib/iomap.c +10 -0
+	addr = ioremap_nocache(offset, size);
+	if (addr) {
+		*ptr = addr;
+		devres_add(dev, ptr);
+	} else
+		devres_free(ptr);
+
+	return addr;
+}
+EXPORT_SYMBOL(devm_ioremap_nocache);

A very late comment but nevertheless... :-)
Those functions are going to break on 32-bit platforms with extended
physical address (well, that's starting with Pentiums which had 36-bit PAE :-) AND devices mapped beyond 4 GB (e.g. PowerPC 44x). You should have used resource_size_t for the 'offset' parameter. As this most probably means that libata is broken on such platforms, I'm going to submit a patch...

WBR, Sergei

author
#8 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 1 line · Sergei Shtylyov

A very late comment but nevertheless... :-)

Better late than never.

Quoted 2 lines

Those functions are going to break on 32-bit platforms with extended
physical address (well, that's starting with Pentiums which had 36-bit PAE :-) AND devices mapped beyond 4 GB (e.g. PowerPC 44x). You should have used resource_size_t for the 'offset' parameter. As this most probably means that libata is broken on such platforms, I'm going to submit a patch...

Yeah, right please go ahead. But I wonder whether any BIOS was actually crazy enough to map mmio region above 4G on 32bit machine.

Signature · 1 line
tejun
#9 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 3 lines · Tejun Heo
Quoted 1 line

A very late comment but nevertheless... :-)

Better late than never.

:-)

Quoted 2 lines
Quoted 2 lines

Those functions are going to break on 32-bit platforms with
extended physical address (well, that's starting with Pentiums which had 36-bit PAE :-) AND devices mapped beyond 4 GB (e.g. PowerPC 44x). You should have used resource_size_t for the 'offset' parameter. As this most probably means that libata is broken on such platforms, I'm going to submit a patch...

It's broken with drivers using MMIO, I meant to say.

Quoted 1 line

Yeah, right please go ahead. But I wonder whether any BIOS was actually crazy enough to map mmio region above 4G on 32bit machine.

This is a *hardware* mapping on some non-x86 platforms (like PPC 44x or
MIPS Alchemy). The arch/ppc/ and arch/mips/ kernels have special hooks called from ioremap() which help create an illusion that the PCI memory space on such platforms (not only it) is mapped below 4 GB; arch/powerpc/ kernel doesn't do this anymore -- hence this newly encountered issue.

WBR, Sergei

author
#10 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 4 lines · Sergei Shtylyov
Quoted 1 line

Yeah, right please go ahead. But I wonder whether any BIOS was actually crazy enough to map mmio region above 4G on 32bit machine.

This is a *hardware* mapping on some non-x86 platforms (like PPC 44x
or MIPS Alchemy). The arch/ppc/ and arch/mips/ kernels have special hooks called from ioremap() which help create an illusion that the PCI memory space on such platforms (not only it) is mapped below 4 GB; arch/powerpc/ kernel doesn't do this anymore -- hence this newly encountered issue.

Ah... I see. Thanks for the clarification.

Signature · 1 line
tejun
#11 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

In reply to Sergei Shtylyov
Quoted 4 lines · Hello, I wrote:
Quoted 2 lines
Quoted 2 lines

Those functions are going to break on 32-bit platforms with
extended physical address (well, that's starting with Pentiums which had 36-bit PAE :-) AND devices mapped beyond 4 GB (e.g. PowerPC 44x). You should have used resource_size_t for the 'offset' parameter. As this most probably means that libata is broken on such platforms, I'm going to submit a patch...

It's broken with drivers using MMIO, I meant to say.

Oops, I meant PCI drivers here, at least for the time being. And it looks
like that was a false alarm. :-]

Quoted 4 lines
Quoted 1 line

Yeah, right please go ahead. But I wonder whether any BIOS was actually crazy enough to map mmio region above 4G on 32bit machine.

This is a *hardware* mapping on some non-x86 platforms (like PPC 44x
or MIPS Alchemy). The arch/ppc/ and arch/mips/ kernels have special hooks called from ioremap() which help create an illusion that the PCI memory space on such platforms (not only it) is mapped below 4 GB; arch/powerpc/ kernel doesn't do this anymore -- hence this newly encountered issue.

I thought that pcim_iomap() used devm_ioremap() or something -- which of
course turned to be wrong. devm_ioremap() alone is yet safe since there are no users for it amongst PPC 44x platform device drivers...

MBR, Sergei

#12 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 10 lines · Sergei Shtylyov
Quoted 4 lines · Hello, I wrote:
Quoted 2 lines
Quoted 2 lines

Those functions are going to break on 32-bit platforms with
extended physical address (well, that's starting with Pentiums which had 36-bit PAE :-) AND devices mapped beyond 4 GB (e.g. PowerPC 44x). You should have used resource_size_t for the 'offset' parameter. As this most probably means that libata is broken on such platforms, I'm going to submit a patch...

It's broken with drivers using MMIO, I meant to say.

Oops, I meant PCI drivers here, at least for the time being. And
it looks like that was a false alarm. :-]

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Yeah, right please go ahead. But I wonder whether any BIOS was actually crazy enough to map mmio region above 4G on 32bit machine.

This is a *hardware* mapping on some non-x86 platforms (like PPC
44x or MIPS Alchemy). The arch/ppc/ and arch/mips/ kernels have special hooks called from ioremap() which help create an illusion that the PCI memory space on such platforms (not only it) is mapped below 4 GB; arch/powerpc/ kernel doesn't do this anymore -- hence this newly encountered issue.

I thought that pcim_iomap() used devm_ioremap() or something --
which of course turned to be wrong. devm_ioremap() alone is yet safe since there are no users for it amongst PPC 44x platform device drivers...

but there is no reason not to make it work properly. For example I believe libata uses devm_* and the fsl SATA driver (non-PCI) will need to work in cases similar to the 44x.

- k

#13 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 10 lines · Kumar Gala
Quoted 9 lines
Quoted 4 lines
Quoted 2 lines
Quoted 2 lines

Those functions are going to break on 32-bit platforms with
extended physical address (well, that's starting with Pentiums which had 36-bit PAE :-) AND devices mapped beyond 4 GB (e.g. PowerPC 44x). You should have used resource_size_t for the 'offset' parameter. As this most probably means that libata is broken on such platforms, I'm going to submit a patch...

It's broken with drivers using MMIO, I meant to say.

Oops, I meant PCI drivers here, at least for the time being. And it
looks like that was a false alarm. :-]

Quoted 1 line
Quoted 1 line

Yeah, right please go ahead. But I wonder whether any BIOS was actually crazy enough to map mmio region above 4G on 32bit machine.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

This is a *hardware* mapping on some non-x86 platforms (like PPC
44x or MIPS Alchemy). The arch/ppc/ and arch/mips/ kernels have special hooks called from ioremap() which help create an illusion that the PCI memory space on such platforms (not only it) is mapped below 4 GB; arch/powerpc/ kernel doesn't do this anymore -- hence this newly encountered issue.

I thought that pcim_iomap() used devm_ioremap() or something --
which of course turned to be wrong. devm_ioremap() alone is yet safe since there are no users for it amongst PPC 44x platform device drivers...

but there is no reason not to make it work properly. For example I believe libata uses devm_* and the fsl SATA driver (non-PCI) will need to work in cases similar to the 44x.

Well, as for sata_fsl, it calls of_iomap() which does The Right Thing.

Quoted 1 line

- k

WBR, Sergei

#14 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 15 lines · Sergei Shtylyov
Quoted 12 lines · Kumar Gala
Quoted 12 lines
Quoted 4 lines
Quoted 2 lines
Quoted 2 lines

Those functions are going to break on 32-bit platforms with
extended physical address (well, that's starting with Pentiums which had 36-bit PAE :-) AND devices mapped beyond 4 GB (e.g. PowerPC 44x). You should have used resource_size_t for the 'offset' parameter. As this most probably means that libata is broken on such platforms, I'm going to submit a patch...

It's broken with drivers using MMIO, I meant to say.

Oops, I meant PCI drivers here, at least for the time being. And
it looks like that was a false alarm. :-]

Quoted 4 lines
Quoted 1 line

Yeah, right please go ahead. But I wonder whether any BIOS was actually crazy enough to map mmio region above 4G on 32bit machine.

This is a *hardware* mapping on some non-x86 platforms (like
PPC 44x or MIPS Alchemy). The arch/ppc/ and arch/mips/ kernels have special hooks called from ioremap() which help create an illusion that the PCI memory space on such platforms (not only it) is mapped below 4 GB; arch/powerpc/ kernel doesn't do this anymore -- hence this newly encountered issue.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I thought that pcim_iomap() used devm_ioremap() or something --
which of course turned to be wrong. devm_ioremap() alone is yet safe since there are no users for it amongst PPC 44x platform device drivers...

but there is no reason not to make it work properly. For example I believe libata uses devm_* and the fsl SATA driver (non-PCI) will need to work in cases similar to the 44x.

Well, as for sata_fsl, it calls of_iomap() which does The Right
Thing.

Fair, but I don't see why we should introduce new APIs that are already "broken". We went through a lot of effort to clean up and introduce resource_t (and clearly still have some bugs) for the >32-bit physical address problem.

- k

author
#15 lore raw

Re: [PATCH 6/13] devres: implement managed iomap interface

Quoted 5 lines · Kumar Gala
Quoted 3 lines
Quoted 1 line

but there is no reason not to make it work properly. For example I believe libata uses devm_* and the fsl SATA driver (non-PCI) will need to work in cases similar to the 44x.

Well, as for sata_fsl, it calls of_iomap() which does The Right Thing.

Fair, but I don't see why we should introduce new APIs that are already "broken". We went through a lot of effort to clean up and introduce resource_t (and clearly still have some bugs) for the >32-bit physical address problem.

Yes, please go ahead. In case it wasn't clear, I wasn't objecting to the fix at all. I was just curious what could actually happen on x86.

Signature · 1 line
tejun
author
#16 lore raw

PATCH 7/13 devres: add Documentation/driver-model/devres.txt

In reply to Tejun Heo

Add Documentation/driver-model/drvres.txt.

---
 Documentation/driver-model/devres.txt |  268 +++++++++++++++++++++++++++++++++
 1 files changed, 268 insertions(+), 0 deletions(-)
Documentation/driver-model/devres.txt new file +268 -0
new file mode 100644
index 0000000..5163b85
@@ -0,0 +1,268 @@
+Devres - Managed Device Resource
+================================
+
+Tejun Heo	<[email protected]>
+
+First draft	10 January 2007
+
+
+1. Intro			: Huh? Devres?
+2. Devres			: Devres in a nutshell
+3. Devres Group			: Group devres'es and release them together
+4. Details			: Life time rules, calling context, ...
+5. Overhead			: How much do we have to pay for this?
+6. List of managed interfaces	: Currently implemented managed interfaces
+
+
+  1. Intro
+  --------
+
+devres came up while trying to convert libata to use iomap.  Each
+iomapped address should be kept and unmapped on driver detach.  For
+example, a plain SFF ATA controller (that is, good old PCI IDE) in
+native mode makes use of 5 PCI BARs and all of them should be
+maintained.
+
+As with many other device drivers, libata low level drivers have
+sufficient bugs in ->remove and ->probe failure path.  Well, yes,
+that's probably because libata low level driver developers are lazy
+bunch, but aren't all low level driver developers?  After spending a
+day fiddling with braindamaged hardware with no document or
+braindamaged document, if it's finally working, well, it's working.
+
+For one reason or another, low level drivers don't receive as much
+attention or testing as core code, and bugs on driver detach or
+initilaization failure doesn't happen often enough to be noticeable.
+Init failure path is worse because it's much less travelled while
+needs to handle multiple entry points.
+
+So, many low level drivers end up leaking resources on driver detach
+and having half broken failure path implementation in ->probe() which
+would leak resources or even cause oops when failure occurs.  iomap
+adds more to this mix.  So do msi and msix.
+
+
+  2. Devres
+  ---------
+
+devres is basically linked list of arbitrarily sized memory areas
+associated with a struct device.  Each devres entry is associated with
+a release function.  A devres can be released in several ways.  No
+matter what, all devres entries are released on driver detach.  On
+release, the associated release function is invoked and then the
+devres entry is freed.
+
+Managed interface is created for resources commonly used by device
+drivers using devres.  For example, coherent DMA memory is acquired
+using dma_alloc_coherent().  The managed version is called
+dmam_alloc_coherent().  It is identical to dma_alloc_coherent() except
+for the DMA memory allocated using it is managed and will be
+automatically released on driver detach.  Implementation looks like
+the following.
+
+  struct dma_devres {
+	size_t		size;
+	void		*vaddr;
+	dma_addr_t	dma_handle;
+  };
+
+  static void dmam_coherent_release(struct device *dev, void *res)
+  {
+	struct dma_devres *this = res;
+
+	dma_free_coherent(dev, this->size, this->vaddr, this->dma_handle);
+  }
+
+  dmam_alloc_coherent(dev, size, dma_handle, gfp)
+  {
+	struct dma_devres *dr;
+	void *vaddr;
+
+	dr = devres_alloc(dmam_coherent_release, sizeof(*dr), gfp);
+	...
+
+	/* alloc DMA memory as usual */
+	vaddr = dma_alloc_coherent(...);
+	...
+
+	/* record size, vaddr, dma_handle in dr */
+	dr->vaddr = vaddr;
+	...
+
+	devres_add(dev, dr);
+
+	return vaddr;
+  }
+
+If a driver uses dmam_alloc_coherent(), the area is guaranteed to be
+freed whether initialization fails half-way or the device gets
+detached.  If most resources are acquired using managed interface, a
+driver can have much simpler init and exit code.  Init path basically
+looks like the following.
+
+  my_init_one()
+  {
+	struct mydev *d;
+
+	d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
+
+	d->ring = dmam_alloc_coherent(...);
+	if (!d->ring)
+		return -ENOMEM;
+
+	if (check something)
+		return -EINVAL;
+	...
+
+	return register_to_upper_layer(d);
+  }
+
+And exit path,
+
+  my_remove_one()
+  {
+	unregister_from_upper_layer(d);
+	shutdown_my_hardware();
+  }
+
+As shown above, low level drivers can be simplified a lot by using
+devres.  Complexity is shifted from less maintained low level drivers
+to better maintained higher layer.  Also, as init failure path is
+shared with exit path, both can get more testing.
+
+
+  3. Devres group
+  ---------------
+
+Devres entries can be grouped using devres group.  When a group is
+released, all contained normal devres entries and properly nested
+groups are released.  One usage is to rollback series of acquired
+resources on failure.  For example,
+
+  if (!devres_open_group(dev, NULL, GFP_KERNEL))
+	return -ENOMEM;
+
+  acquire A;
+  if (failed)
+	goto err;
+
+  acquire B;
+  if (failed)
+	goto err;
+  ...
+
+  devres_remove_group(dev, NULL);
+  return 0;
+
+ err:
+  devres_release_group(dev, NULL);
+  return err_code;
+
+As resource acquision failure usually means probe failure, constructs
+like above are usually useful in midlayer driver (e.g. libata core
+layer) where interface function shouldn't have side effect on failure.
+For LLDs, just returning error code suffices in most cases.
+
+Each group is identified by void *id.  It can either be explicitly
+specified by @id argument to devres_open_group() or automatically
+created by passing NULL as @id as in the above example.  In both
+cases, devres_open_group() returns the group's id.  The returned id
+can be passed to other devres functions to select the target group.
+If NULL is given to those functions, the latest open group is
+selected.
+
+For example, you can do something like the following.
+
+  int my_midlayer_create_something()
+  {
+	if (!devres_open_group(dev, my_midlayer_create_something, GFP_KERNEL))
+		return -ENOMEM;
+
+	...
+
+	devres_close_group(dev, my_midlayer_something);
+	return 0;
+  }
+
+  void my_midlayer_destroy_something()
+  {
+	devres_release_group(dev, my_midlayer_create_soemthing);
+  }
+
+
+  4. Details
+  ----------
+
+Lifetime of a devres entry begins on devres allocation and finishes
+when it is released or destroyed (removed and freed) - no reference
+counting.
+
+devres core guarantees atomicity to all basic devres operations and
+has support for single-instance devres types (atomic
+lookup-and-add-if-not-found).  Other than that, synchronizing
+concurrent accesses to allocated devres data is caller's
+responsibility.  This is usually non-issue because bus ops and
+resource allocations already do the job.
+
+For an example of single-instance devres type, read pcim_iomap_table()
+in lib/iomap.c.
+
+All devres interface functions can be called without context if the
+right gfp mask is given.
+
+
+  5. Overhead
+  -----------
+
+Each devres bookkeeping info is allocated together with requested data
+area.  With debug option turned off, bookkeeping info occupies 16
+bytes on 32bit machines and 24 bytes on 64bit (three pointers rounded
+up to ull alignment).  If singly linked list is used, it can be
+reduced to two pointers (8 bytes on 32bit, 16 bytes on 64bit).
+
+Each devres group occupies 8 pointers.  It can be reduced to 6 if
+singly linked list is used.
+
+Memory space overhead on ahci controller with two ports is between 300
+and 400 bytes on 32bit machine after naive conversion (we can
+certainly invest a bit more effort into libata core layer).
+
+
+  6. List of managed interfaces
+  -----------------------------
+
+IO region
+  devm_request_region()
+  devm_request_mem_region()
+  devm_release_region()
+  devm_release_mem_region()
+
+IRQ
+  devm_request_irq()
+  devm_free_irq()
+
+DMA
+  dmam_alloc_coherent()
+  dmam_free_coherent()
+  dmam_alloc_noncoherent()
+  dmam_free_noncoherent()
+  dmam_declare_coherent_memory()
+  dmam_pool_create()
+  dmam_pool_destroy()
+
+PCI
+  pcim_enable_device()	: after success, all PCI ops become managed
+  pcim_pin_device()	: keep PCI device enabled after release
+
+IOMAP
+  devm_ioport_map()
+  devm_ioport_unmap()
+  devm_ioremap()
+  devm_ioremap_nocache()
+  devm_iounmap()
+  pcim_iomap()
+  pcim_iounmap()
+  pcim_iomap_table()	: array of mapped addresses indexed by BAR
+  pcim_iomap_regions()	: do request_region() and iomap() on multiple BARs
Signature · 1 line
1.4.4.3
author
#17 lore raw

PATCH 8/13 libata: implement ata_host_detach()

In reply to Tejun Heo

Implement ata_host_detach() which calls ata_port_detach() for each port in the host and export it. ata_port_detach() is now internal and thus un-exported. ata_host_detach() will be used as the 'deregister from libata layer' function after devres conversion.

---
 drivers/ata/ahci.c        |    3 +--
 drivers/ata/libata-core.c |   22 +++++++++++++++++++---
 include/linux/libata.h    |    2 +-
 3 files changed, 21 insertions(+), 6 deletions(-)
drivers/ata/ahci.c +1 -2
index 5998f74..d089217 100644
@@ -1767,8 +1767,7 @@ static void ahci_remove_one (struct pci_dev *pdev)
 	unsigned int i;
 	int have_msi;
 
-	for (i = 0; i < host->n_ports; i++)
-		ata_port_detach(host->ports[i]);
+	ata_host_detach(host);
 
 	have_msi = hpriv->flags & AHCI_FLAG_MSI;
 	free_irq(host->irq, host);
drivers/ata/libata-core.c +19 -3
index 2cc7a17..c63fe10 100644
@@ -6001,6 +6001,23 @@ void ata_port_detach(struct ata_port *ap)
 }
 
 /**
+ *	ata_host_detach - Detach all ports of an ATA host
+ *	@host: Host to detach
+ *
+ *	Detach all ports of @host.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ */
+void ata_host_detach(struct ata_host *host)
+{
+	int i;
+
+	for (i = 0; i < host->n_ports; i++)
+		ata_port_detach(host->ports[i]);
+}
+
+/**
  *	ata_host_remove - PCI layer callback for device removal
  *	@host: ATA host set that was removed
  *
@@ -6015,8 +6032,7 @@ void ata_host_remove(struct ata_host *host)
 {
 	unsigned int i;
 
-	for (i = 0; i < host->n_ports; i++)
-		ata_port_detach(host->ports[i]);
+	ata_host_detach(host);
 
 	free_irq(host->irq, host);
 	if (host->irq2)
@@ -6391,7 +6407,7 @@ EXPORT_SYMBOL_GPL(ata_std_bios_param);
 EXPORT_SYMBOL_GPL(ata_std_ports);
 EXPORT_SYMBOL_GPL(ata_host_init);
 EXPORT_SYMBOL_GPL(ata_device_add);
-EXPORT_SYMBOL_GPL(ata_port_detach);
+EXPORT_SYMBOL_GPL(ata_host_detach);
 EXPORT_SYMBOL_GPL(ata_host_remove);
 EXPORT_SYMBOL_GPL(ata_sg_init);
 EXPORT_SYMBOL_GPL(ata_sg_init_one);
include/linux/libata.h +1 -1
index 7cfc18f..816d6ef 100644
@@ -721,7 +721,7 @@ extern int ata_pci_device_resume(struct pci_dev *pdev);
 extern int ata_pci_clear_simplex(struct pci_dev *pdev);
 #endif /* CONFIG_PCI */
 extern int ata_device_add(const struct ata_probe_ent *ent);
-extern void ata_port_detach(struct ata_port *ap);
+extern void ata_host_detach(struct ata_host *host);
 extern void ata_host_init(struct ata_host *, struct device *,
 			  unsigned long, const struct ata_port_operations *);
 extern void ata_host_remove(struct ata_host *host);
Signature · 1 line
1.4.4.3
author
#18 lore raw

PATCH 9/13 libata: update libata core layer to use devres

In reply to Tejun Heo

Update libata core layer to use devres.

* ata_device_add() acquires all resources in managed mode.

* ata_host is allocated as devres associated with ata_host_release.

* Port attached status is handled as devres associated with ata_host_attach_release().

* Initialization failure and host removal is handedl by releasing devres group.

* Except for ata_scsi_release() removal, LLD interface remains the same. Some functions use hacky is_managed test to support both managed and unmanaged devices. These will go away once all LLDs are updated to use devres.

---
 drivers/ata/ahci.c        |   21 +-----
 drivers/ata/libata-core.c |  177 +++++++++++++++++++--------------------------
 drivers/ata/libata-scsi.c |    3 +-
 drivers/ata/libata-sff.c  |   56 ++++++---------
 include/linux/libata.h    |    9 +--
 5 files changed, 106 insertions(+), 160 deletions(-)
drivers/ata/ahci.c +4 -17
index d089217..7abe138 100644
@@ -1759,37 +1759,24 @@ err_out:
 	return rc;
 }
 
-static void ahci_remove_one (struct pci_dev *pdev)
+static void ahci_remove_one(struct pci_dev *pdev)
 {
 	struct device *dev = pci_dev_to_dev(pdev);
 	struct ata_host *host = dev_get_drvdata(dev);
 	struct ahci_host_priv *hpriv = host->private_data;
-	unsigned int i;
-	int have_msi;
 
-	ata_host_detach(host);
+	ata_host_remove(host);
 
-	have_msi = hpriv->flags & AHCI_FLAG_MSI;
-	free_irq(host->irq, host);
-
-	for (i = 0; i < host->n_ports; i++) {
-		struct ata_port *ap = host->ports[i];
-
-		ata_scsi_release(ap->scsi_host);
-		scsi_host_put(ap->scsi_host);
-	}
-
-	kfree(hpriv);
 	pci_iounmap(pdev, host->mmio_base);
-	kfree(host);
 
-	if (have_msi)
+	if (hpriv->flags & AHCI_FLAG_MSI)
 		pci_disable_msi(pdev);
 	else
 		pci_intx(pdev, 0);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
 	dev_set_drvdata(dev, NULL);
+	kfree(hpriv);
 }
 
 static int __init ahci_init(void)
drivers/ata/libata-core.c +75 -102

290 lines not on this page · raw message

drivers/ata/libata-scsi.c +2 -1
index 0c43043..6d0b1fc 100644
@@ -3226,7 +3226,8 @@ EXPORT_SYMBOL_GPL(ata_sas_port_init);
 
 void ata_sas_port_destroy(struct ata_port *ap)
 {
-	ap->ops->port_stop(ap);
+	if (ap->ops->port_stop)
+		ap->ops->port_stop(ap);
 	kfree(ap);
 }
 EXPORT_SYMBOL_GPL(ata_sas_port_destroy);
drivers/ata/libata-sff.c +21 -35

142 lines not on this page · raw message

include/linux/libata.h +4 -5
index 816d6ef..f6a6bef 100644
@@ -31,7 +31,7 @@
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
 #include <asm/scatterlist.h>
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/ata.h>
 #include <linux/workqueue.h>
 #include <scsi/scsi_host.h>
@@ -728,7 +728,6 @@ extern void ata_host_remove(struct ata_host *host);
 extern int ata_scsi_detect(struct scsi_host_template *sht);
 extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
 extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *));
-extern int ata_scsi_release(struct Scsi_Host *host);
 extern void ata_sas_port_destroy(struct ata_port *);
 extern struct ata_port *ata_sas_port_alloc(struct ata_host *,
 					   struct ata_port_info *, struct Scsi_Host *);
@@ -1225,14 +1224,14 @@ static inline unsigned int __ac_err_mask(u8 status)
 static inline int ata_pad_alloc(struct ata_port *ap, struct device *dev)
 {
 	ap->pad_dma = 0;
-	ap->pad = dma_alloc_coherent(dev, ATA_DMA_PAD_BUF_SZ,
-				     &ap->pad_dma, GFP_KERNEL);
+	ap->pad = dmam_alloc_coherent(dev, ATA_DMA_PAD_BUF_SZ,
+				      &ap->pad_dma, GFP_KERNEL);
 	return (ap->pad == NULL) ? -ENOMEM : 0;
 }
 
 static inline void ata_pad_free(struct ata_port *ap, struct device *dev)
 {
-	dma_free_coherent(dev, ATA_DMA_PAD_BUF_SZ, ap->pad, ap->pad_dma);
+	dmam_free_coherent(dev, ATA_DMA_PAD_BUF_SZ, ap->pad, ap->pad_dma);
 }
 
 static inline struct ata_port *ata_shost_to_port(struct Scsi_Host *host)
Signature · 1 line
1.4.4.3
author
#19 lore raw

PATCH 11/13 libata: remove unused functions

In reply to Tejun Heo

Now that all LLDs are converted to use devres, default stop callbacks are unused. Remove them.

---
 drivers/ata/libata-core.c |   81 +++-----------------------------------------
 include/linux/libata.h    |    4 --
 2 files changed, 6 insertions(+), 79 deletions(-)
drivers/ata/libata-core.c +6 -75
index e476574..b5538dd 100644
@@ -5515,31 +5515,6 @@ int ata_port_start(struct ata_port *ap)
 }
 
 /**
- *	ata_port_stop - Undo ata_port_start()
- *	@ap: Port to shut down
- *
- *	Frees the PRD table.
- *
- *	May be used as the port_stop() entry in ata_port_operations.
- *
- *	LOCKING:
- *	Inherited from caller.
- */
-void ata_port_stop (struct ata_port *ap)
-{
-	struct device *dev = ap->dev;
-
-	dmam_free_coherent(dev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma);
-	ata_pad_free(ap, dev);
-}
-
-void ata_host_stop (struct ata_host *host)
-{
-	if (host->mmio_base)
-		iounmap(host->mmio_base);
-}
-
-/**
  *	ata_dev_init - Initialize an ata_device structure
  *	@dev: Device structure to initialize
  *
@@ -5878,7 +5853,7 @@ int ata_device_add(const struct ata_probe_ent *ent)
 	}
 
 	/* resource acquisition complete */
-	devres_close_group(dev, ata_device_add);
+	devres_remove_group(dev, ata_device_add);
 
 	/* perform each probe synchronously */
 	DPRINTK("probe begin\n");
@@ -6033,22 +6008,6 @@ void ata_host_detach(struct ata_host *host)
 		ata_port_detach(host->ports[i]);
 }
 
-/**
- *	ata_host_remove - PCI layer callback for device removal
- *	@host: ATA host set that was removed
- *
- *	Unregister all objects associated with this host set. Free those
- *	objects.
- *
- *	LOCKING:
- *	Inherited from calling layer (may sleep).
- */
-void ata_host_remove(struct ata_host *host)
-{
-	ata_host_detach(host);
-	devres_release_group(host->dev, ata_device_add);
-}
-
 struct ata_probe_ent *
 ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port)
 {
@@ -6108,26 +6067,13 @@ void ata_std_ports(struct ata_ioports *ioaddr)
 
 #ifdef CONFIG_PCI
 
-void ata_pci_host_stop (struct ata_host *host)
-{
-	struct pci_dev *pdev = to_pci_dev(host->dev);
-
-	/* XXX - the following if can go away once all LLDs are managed */
-	if (!list_empty(&host->dev->devres_head))
-		pcim_iounmap(pdev, host->mmio_base);
-	else
-		pci_iounmap(pdev, host->mmio_base);
-}
-
 /**
  *	ata_pci_remove_one - PCI layer callback for device removal
  *	@pdev: PCI device that was removed
  *
- *	PCI layer indicates to libata via this hook that
- *	hot-unplug or module unload event has occurred.
- *	Handle this by unregistering all objects associated
- *	with this PCI device.  Free those objects.  Then finally
- *	release PCI resources and disable device.
+ *	PCI layer indicates to libata via this hook that hot-unplug or
+ *	module unload event has occurred.  Detach all ports.  Resource
+ *	release is handled via devres.
  *
  *	LOCKING:
  *	Inherited from PCI layer (may sleep).
@@ -6137,14 +6083,7 @@ void ata_pci_remove_one(struct pci_dev *pdev)
 	struct device *dev = pci_dev_to_dev(pdev);
 	struct ata_host *host = dev_get_drvdata(dev);
 
-	/* XXX - the following if can go away once all LLDs are managed */
-	if (!list_empty(&host->dev->devres_head)) {
-		ata_host_remove(host);
-		pci_release_regions(pdev);
-		pci_disable_device(pdev);
-		dev_set_drvdata(dev, NULL);
-	} else
-		ata_host_detach(host);
+	ata_host_detach(host);
 }
 
 /* move to PCI subsystem */
@@ -6198,11 +6137,7 @@ int ata_pci_device_do_resume(struct pci_dev *pdev)
 	pci_set_power_state(pdev, PCI_D0);
 	pci_restore_state(pdev);
 
-	/* XXX - the following if can go away once all LLDs are managed */
-	if (!list_empty(&pdev->dev.devres_head))
-		rc = pcim_enable_device(pdev);
-	else
-		rc = pci_enable_device(pdev);
+	rc = pcim_enable_device(pdev);
 	if (rc) {
 		dev_printk(KERN_ERR, &pdev->dev,
 			   "failed to enable device after resume (%d)\n", rc);
@@ -6382,7 +6317,6 @@ EXPORT_SYMBOL_GPL(ata_std_ports);
 EXPORT_SYMBOL_GPL(ata_host_init);
 EXPORT_SYMBOL_GPL(ata_device_add);
 EXPORT_SYMBOL_GPL(ata_host_detach);
-EXPORT_SYMBOL_GPL(ata_host_remove);
 EXPORT_SYMBOL_GPL(ata_sg_init);
 EXPORT_SYMBOL_GPL(ata_sg_init_one);
 EXPORT_SYMBOL_GPL(ata_hsm_move);
@@ -6399,8 +6333,6 @@ EXPORT_SYMBOL_GPL(ata_check_status);
 EXPORT_SYMBOL_GPL(ata_altstatus);
 EXPORT_SYMBOL_GPL(ata_exec_command);
 EXPORT_SYMBOL_GPL(ata_port_start);
-EXPORT_SYMBOL_GPL(ata_port_stop);
-EXPORT_SYMBOL_GPL(ata_host_stop);
 EXPORT_SYMBOL_GPL(ata_interrupt);
 EXPORT_SYMBOL_GPL(ata_mmio_data_xfer);
 EXPORT_SYMBOL_GPL(ata_pio_data_xfer);
@@ -6461,7 +6393,6 @@ EXPORT_SYMBOL_GPL(ata_timing_merge);
 
 #ifdef CONFIG_PCI
 EXPORT_SYMBOL_GPL(pci_test_config_bits);
-EXPORT_SYMBOL_GPL(ata_pci_host_stop);
 EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
 EXPORT_SYMBOL_GPL(ata_pci_init_one);
 EXPORT_SYMBOL_GPL(ata_pci_remove_one);
include/linux/libata.h +0 -4
index f6a6bef..afeb48b 100644
@@ -724,7 +724,6 @@ extern int ata_device_add(const struct ata_probe_ent *ent);
 extern void ata_host_detach(struct ata_host *host);
 extern void ata_host_init(struct ata_host *, struct device *,
 			  unsigned long, const struct ata_port_operations *);
-extern void ata_host_remove(struct ata_host *host);
 extern int ata_scsi_detect(struct scsi_host_template *sht);
 extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
 extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *));
@@ -770,8 +769,6 @@ extern u8 ata_check_status(struct ata_port *ap);
 extern u8 ata_altstatus(struct ata_port *ap);
 extern void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf);
 extern int ata_port_start (struct ata_port *ap);
-extern void ata_port_stop (struct ata_port *ap);
-extern void ata_host_stop (struct ata_host *host);
 extern irqreturn_t ata_interrupt (int irq, void *dev_instance);
 extern void ata_mmio_data_xfer(struct ata_device *adev, unsigned char *buf,
 			       unsigned int buflen, int write_data);
@@ -858,7 +855,6 @@ struct pci_bits {
 	unsigned long		val;
 };
 
-extern void ata_pci_host_stop (struct ata_host *host);
 extern struct ata_probe_ent *
 ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int portmask);
 extern int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits);
Signature · 1 line
1.4.4.3
author
#20 lore raw

PATCH 12/13 devres: implement pcim_iomap_regions()

In reply to Tejun Heo

Implement pcim_iomap_regions(). This function takes mask of BARs to request and iomap. No BAR should have length of zero. BARs are iomapped using pcim_iomap_table().

---
 include/linux/io.h |    2 +
 lib/iomap.c        |   53 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+), 0 deletions(-)
include/linux/io.h +2 -0
index f5edf9c..45a9c94 100644
@@ -45,6 +45,8 @@ void __iomem * pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);
 void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr);
 void __iomem * const * pcim_iomap_table(struct pci_dev *pdev);
 
+int pcim_iomap_regions(struct pci_dev *pdev, u16 mask, const char *name);
+
 /**
  *	check_signature		-	find BIOS signatures
  *	@io_addr: mmio address to check
lib/iomap.c +53 -0

56 lines not on this page · raw message

Signature · 1 line
1.4.4.3
#21 lore raw
In reply to Tejun Heo

Tejun,

Please

1) Collapse patches 1-7 into a single patch, and

2) rediff against latest libata-dev.git#upstream

and I will apply straightaway.

Jeff