cpufreq: Don't skip frequency validation for has_target() drivers
[linux-2.6-block.git] / kernel / memremap.c
index 1490e63f69a955e3857e8b8e10bdaa62cc063a3d..6e1970719dc23dac7a164560883c14ab7b09c50e 100644 (file)
@@ -95,6 +95,7 @@ static void devm_memremap_pages_release(void *data)
        pgmap->kill(pgmap->ref);
        for_each_device_pfn(pfn, pgmap)
                put_page(pfn_to_page(pfn));
+       pgmap->cleanup(pgmap->ref);
 
        /* pages are dead and unused, undo the arch mapping */
        align_start = res->start & ~(SECTION_SIZE - 1);
@@ -133,8 +134,8 @@ static void devm_memremap_pages_release(void *data)
  * 2/ The altmap field may optionally be initialized, in which case altmap_valid
  *    must be set to true
  *
- * 3/ pgmap->ref must be 'live' on entry and will be killed at
- *    devm_memremap_pages_release() time, or if this routine fails.
+ * 3/ pgmap->ref must be 'live' on entry and will be killed and reaped
+ *    at devm_memremap_pages_release() time, or if this routine fails.
  *
  * 4/ res is expected to be a host memory range that could feasibly be
  *    treated as a "System RAM" range, i.e. not a device mmio range, but
@@ -156,8 +157,10 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
        pgprot_t pgprot = PAGE_KERNEL;
        int error, nid, is_ram;
 
-       if (!pgmap->ref || !pgmap->kill)
+       if (!pgmap->ref || !pgmap->kill || !pgmap->cleanup) {
+               WARN(1, "Missing reference count teardown definition\n");
                return ERR_PTR(-EINVAL);
+       }
 
        align_start = res->start & ~(SECTION_SIZE - 1);
        align_size = ALIGN(res->start + resource_size(res), SECTION_SIZE)
@@ -168,14 +171,16 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
        if (conflict_pgmap) {
                dev_WARN(dev, "Conflicting mapping in same section\n");
                put_dev_pagemap(conflict_pgmap);
-               return ERR_PTR(-ENOMEM);
+               error = -ENOMEM;
+               goto err_array;
        }
 
        conflict_pgmap = get_dev_pagemap(PHYS_PFN(align_end), NULL);
        if (conflict_pgmap) {
                dev_WARN(dev, "Conflicting mapping in same section\n");
                put_dev_pagemap(conflict_pgmap);
-               return ERR_PTR(-ENOMEM);
+               error = -ENOMEM;
+               goto err_array;
        }
 
        is_ram = region_intersects(align_start, align_size,
@@ -267,10 +272,18 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
        pgmap_array_delete(res);
  err_array:
        pgmap->kill(pgmap->ref);
+       pgmap->cleanup(pgmap->ref);
+
        return ERR_PTR(error);
 }
 EXPORT_SYMBOL_GPL(devm_memremap_pages);
 
+void devm_memunmap_pages(struct device *dev, struct dev_pagemap *pgmap)
+{
+       devm_release_action(dev, devm_memremap_pages_release, pgmap);
+}
+EXPORT_SYMBOL_GPL(devm_memunmap_pages);
+
 unsigned long vmem_altmap_offset(struct vmem_altmap *altmap)
 {
        /* number of pfns from base where pfn_to_page() is valid */