Skip to contentSkip to conversation

Managed device resources, take #3

PATCH New Tejun Heo 2 participants 9 messages Jan 20, 2007 – Feb 2, 2007

Subsystems (8)
lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

PATCH 1/1 Managed device resources, take #3

Hello,

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

* devres patches collapsed into one as Jeff requested

* updates for sata_inic162x added

* rebased to the current upstream

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 initial take[I].

This patchset is against

[U] libata-dev#upstream
[1] + sata_promise-kill-qc-nsect
[2] + lbiata-fix-compile-warning-caused-by-ignoring-__must_check results

The git tree contains both previous patches, but this patchset apply okay with either one of or both patches omitted.

Thanks.

--
tejun

[L] http://thread.gmane.org/gmane.linux.kernel/482455
[I] http://thread.gmane.org/gmane.linux.ide/14690
[U] 8ed22df2570dbb7df2bd161bb9381a6fd17de3d3
[1] http://article.gmane.org/gmane.linux.ide/15189
[2] http://article.gmane.org/gmane.linux.ide/15192

author
#1 lore raw

PATCH 1/7 devres: device resource management

Implement device resource management, 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).

This patch adds devres core including documentation and the following managed interfaces.

* alloc/free : devm_kzalloc(), devm_kzfree()
* IO region : devm_request_region(), devm_release_region()
* IRQ : devm_request_irq(), devm_free_irq()
* DMA : dmam_alloc_coherent(), dmam_free_coherent(),
dmam_declare_coherent_memory(), dmam_pool_create(), dmam_pool_destroy()
* PCI : pcim_enable_device(), pcim_pin_device(), pci_is_managed()
* iomap : devm_ioport_map(), devm_ioport_unmap(), devm_ioremap(),
devm_ioremap_nocache(), devm_iounmap(), pcim_iomap_table(), pcim_iomap(), pcim_iounmap()

---
 Documentation/driver-model/devres.txt |  268 ++++++++++++++
 drivers/base/Kconfig                  |   12 +
 drivers/base/Makefile                 |    1 +
 drivers/base/base.h                   |    1 +
 drivers/base/core.c                   |    2 +
 drivers/base/dd.c                     |    3 +
 drivers/base/devres.c                 |  644 +++++++++++++++++++++++++++++++++
 drivers/base/dma-mapping.c            |  218 +++++++++++
 drivers/base/dmapool.c                |   59 +++
 drivers/pci/pci.c                     |  127 +++++++-
 include/linux/device.h                |   38 ++
 include/linux/dma-mapping.h           |   29 ++-
 include/linux/dmapool.h               |    7 +
 include/linux/interrupt.h             |    6 +
 include/linux/io.h                    |   17 +
 include/linux/ioport.h                |   20 +
 include/linux/pci.h                   |    9 +
 kernel/irq/manage.c                   |   86 +++++
 kernel/resource.c                     |   62 ++++
 lib/Makefile                          |    3 +-
 lib/iomap.c                           |  246 +++++++++++++-
 21 files changed, 1853 insertions(+), 5 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
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 -0
index 7bbb9ee..e9eb738 100644
@@ -3,6 +3,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 \
+			   dma-mapping.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

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

drivers/pci/pci.c +126 -1

178 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*/
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
 
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
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
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 */
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
kernel/irq/manage.c +86 -0

89 lines not on this page · raw message

kernel/resource.c +62 -0

74 lines not on this page · raw message

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.4
#2 lore raw

Re: [PATCH 1/7] devres: device resource management

Quoted 1 line · Tejun Heo

Implement device resource management, in short, devres. A device

ACK patches 1-7. Applied patches 1-6 (with some remaining pata_platform damage) to #upstream.

Please examine the resulting damage, and resend patch #7, plus any missing bits

Jeff

author
#3 lore raw

PATCH 1/2 pata_platform: fix devres conversion

devres updates for pata_platform were dropped while merging devres patches due to merge conflict. This is the updated version.

---
 drivers/ata/pata_platform.c |   31 +++++++------------------------
 1 file changed, 7 insertions(+), 24 deletions(-)
drivers/ata/pata_platform.c +7 -24
Index: work/drivers/ata/pata_platform.c
===================================================================
@@ -47,23 +47,6 @@ static int pata_platform_set_mode(struct
 	return 0;
 }
 
-static void pata_platform_host_stop(struct ata_host *host)
-{
-	int i;
-
-	/*
-	 * Unmap the bases for MMIO
-	 */
-	for (i = 0; i < host->n_ports; i++) {
-		struct ata_port *ap = host->ports[i];
-
-		if (ap->flags & ATA_FLAG_MMIO) {
-			iounmap((void __iomem *)ap->ioaddr.ctl_addr);
-			iounmap((void __iomem *)ap->ioaddr.cmd_addr);
-		}
-	}
-}
-
 static struct scsi_host_template pata_platform_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -106,8 +89,6 @@ static struct ata_port_operations pata_p
 	.irq_clear		= ata_bmdma_irq_clear,
 
 	.port_start		= ata_port_start,
-	.port_stop		= ata_port_stop,
-	.host_stop		= pata_platform_host_stop
 };
 
 static void pata_platform_setup_port(struct ata_ioports *ioaddr,
@@ -209,15 +190,17 @@ static int __devinit pata_platform_probe
 	if (mmio) {
 		ae.port_flags |= ATA_FLAG_MMIO;
 
-		ae.port[0].cmd_addr = (unsigned long)ioremap(io_res->start,
-				io_res->end - io_res->start + 1);
+		ae.port[0].cmd_addr = (unsigned long)
+			devm_ioremap(&pdev->dev, io_res->start,
+				     io_res->end - io_res->start + 1);
 		if (unlikely(!ae.port[0].cmd_addr)) {
 			dev_err(&pdev->dev, "failed to remap IO base\n");
 			return -ENXIO;
 		}
 
-		ae.port[0].ctl_addr = (unsigned long)ioremap(ctl_res->start,
-				ctl_res->end - ctl_res->start + 1);
+		ae.port[0].ctl_addr = (unsigned long)
+			devm_ioremap(&pdev->dev, ctl_res->start,
+				     ctl_res->end - ctl_res->start + 1);
 		if (unlikely(!ae.port[0].ctl_addr)) {
 			dev_err(&pdev->dev, "failed to remap CTL base\n");
 			ret = -ENXIO;
@@ -261,7 +244,7 @@ static int __devexit pata_platform_remov
 	struct device *dev = &pdev->dev;
 	struct ata_host *host = dev_get_drvdata(dev);
 
-	ata_host_remove(host);
+	ata_host_detach(host);
 	dev_set_drvdata(dev, NULL);
 
 	return 0;
#4 lore raw

Re: [PATCH 1/2] pata_platform: fix devres conversion

Quoted 4 lines · Tejun Heo

devres updates for pata_platform were dropped while merging devres patches due to merge conflict. This is the updated version.

applied 1-2

author
#5 lore raw

PATCH 2/7 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 8315ee6..20a943f 100644
@@ -6002,6 +6002,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
  *
@@ -6016,8 +6033,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)
@@ -6392,7 +6408,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 09c110a..8bad682 100644
@@ -718,7 +718,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.4
author
#6 lore raw

PATCH 3/7 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 adc5eda..a2270c3 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 8bad682..c24c170 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>
@@ -725,7 +725,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 *);
@@ -1222,14 +1221,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.4
author
#7 lore raw

PATCH 5/7 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 a9a4c67..34e7f18 100644
@@ -5516,31 +5516,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
  *
@@ -5879,7 +5854,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");
@@ -6034,22 +6009,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)
 {
@@ -6109,26 +6068,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).
@@ -6138,14 +6084,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 */
@@ -6199,11 +6138,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);
@@ -6383,7 +6318,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);
@@ -6400,8 +6334,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);
@@ -6462,7 +6394,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 c24c170..8e93779 100644
@@ -721,7 +721,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 *));
@@ -767,8 +766,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);
@@ -855,7 +852,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.4
author
#8 lore raw

PATCH 6/7 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.4