device-dax: add dis-contiguous resource support
[linux-2.6-microblaze.git] / drivers / dax / device.c
index 5f80861..bf38971 100644 (file)
@@ -55,15 +55,22 @@ static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma,
 __weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff,
                unsigned long size)
 {
-       struct range *range = &dev_dax->range;
-       phys_addr_t phys;
-
-       phys = pgoff * PAGE_SIZE + range->start;
-       if (phys >= range->start && phys <= range->end) {
+       int i;
+
+       for (i = 0; i < dev_dax->nr_range; i++) {
+               struct dev_dax_range *dax_range = &dev_dax->ranges[i];
+               struct range *range = &dax_range->range;
+               unsigned long long pgoff_end;
+               phys_addr_t phys;
+
+               pgoff_end = dax_range->pgoff + PHYS_PFN(range_len(range)) - 1;
+               if (pgoff < dax_range->pgoff || pgoff > pgoff_end)
+                       continue;
+               phys = PFN_PHYS(pgoff - dax_range->pgoff) + range->start;
                if (phys + size - 1 <= range->end)
                        return phys;
+               break;
        }
-
        return -1;
 }
 
@@ -395,30 +402,40 @@ static void dev_dax_kill(void *dev_dax)
 int dev_dax_probe(struct dev_dax *dev_dax)
 {
        struct dax_device *dax_dev = dev_dax->dax_dev;
-       struct range *range = &dev_dax->range;
        struct device *dev = &dev_dax->dev;
        struct dev_pagemap *pgmap;
        struct inode *inode;
        struct cdev *cdev;
        void *addr;
-       int rc;
-
-       /* 1:1 map region resource range to device-dax instance range */
-       if (!devm_request_mem_region(dev, range->start, range_len(range),
-                               dev_name(dev))) {
-               dev_warn(dev, "could not reserve range: %#llx - %#llx\n",
-                               range->start, range->end);
-               return -EBUSY;
-       }
+       int rc, i;
 
        pgmap = dev_dax->pgmap;
+       if (dev_WARN_ONCE(dev, pgmap && dev_dax->nr_range > 1,
+                       "static pgmap / multi-range device conflict\n"))
+               return -EINVAL;
+
        if (!pgmap) {
-               pgmap = devm_kzalloc(dev, sizeof(*pgmap), GFP_KERNEL);
+               pgmap = devm_kzalloc(dev, sizeof(*pgmap) + sizeof(struct range)
+                               * (dev_dax->nr_range - 1), GFP_KERNEL);
                if (!pgmap)
                        return -ENOMEM;
-               pgmap->range = *range;
-               pgmap->nr_range = 1;
+               pgmap->nr_range = dev_dax->nr_range;
+       }
+
+       for (i = 0; i < dev_dax->nr_range; i++) {
+               struct range *range = &dev_dax->ranges[i].range;
+
+               if (!devm_request_mem_region(dev, range->start,
+                                       range_len(range), dev_name(dev))) {
+                       dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve range\n",
+                                       i, range->start, range->end);
+                       return -EBUSY;
+               }
+               /* don't update the range for static pgmap */
+               if (!dev_dax->pgmap)
+                       pgmap->ranges[i] = *range;
        }
+
        pgmap->type = MEMORY_DEVICE_GENERIC;
        addr = devm_memremap_pages(dev, pgmap);
        if (IS_ERR(addr))