]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
libnvdimm: Use max contiguous area for namespace size
authorKeith Busch <keith.busch@intel.com>
Tue, 24 Jul 2018 21:07:57 +0000 (15:07 -0600)
committerDave Jiang <dave.jiang@intel.com>
Wed, 25 Jul 2018 21:11:09 +0000 (14:11 -0700)
This patch will find the max contiguous area to determine the largest
pmem namespace size that can be created. If the requested size exceeds
the largest available, ENOSPC error will be returned.

This fixes the allocation underrun error and wrong error return code
that have otherwise been observed as the following kernel warning:

  WARNING: CPU: <CPU> PID: <PID> at drivers/nvdimm/namespace_devs.c:913 size_store

Fixes: a1f3e4d6a0c3 ("libnvdimm, region: update nd_region_available_dpa() for multi-pmem support")
Cc: <stable@vger.kernel.org>
Signed-off-by: Keith Busch <keith.busch@intel.com>
Reviewed-by: Vishal Verma <vishal.l.verma@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
drivers/nvdimm/dimm_devs.c
drivers/nvdimm/namespace_devs.c
drivers/nvdimm/nd-core.h
drivers/nvdimm/region_devs.c

index 8d348b22ba453a58938d9fa921dd890109a4c7de..863cabc352159c54e3beb6659519418aadc3c0bc 100644 (file)
@@ -536,6 +536,37 @@ resource_size_t nd_blk_available_dpa(struct nd_region *nd_region)
        return info.available;
 }
 
+/**
+ * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max
+ *                        contiguous unallocated dpa range.
+ * @nd_region: constrain available space check to this reference region
+ * @nd_mapping: container of dpa-resource-root + labels
+ */
+resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region,
+                                          struct nd_mapping *nd_mapping)
+{
+       struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
+       struct nvdimm_bus *nvdimm_bus;
+       resource_size_t max = 0;
+       struct resource *res;
+
+       /* if a dimm is disabled the available capacity is zero */
+       if (!ndd)
+               return 0;
+
+       nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
+       if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm))
+               return 0;
+       for_each_dpa_resource(ndd, res) {
+               if (strcmp(res->name, "pmem-reserve") != 0)
+                       continue;
+               if (resource_size(res) > max)
+                       max = resource_size(res);
+       }
+       release_free_pmem(nvdimm_bus, nd_mapping);
+       return max;
+}
+
 /**
  * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
  * @nd_mapping: container of dpa-resource-root + labels
index cb322f2bc60525d1795b63ad2e89da9353bbcf1e..4a4266250c28cfde56b08a2aa7ce04a27d6e6527 100644 (file)
@@ -799,7 +799,7 @@ static int merge_dpa(struct nd_region *nd_region,
        return 0;
 }
 
-static int __reserve_free_pmem(struct device *dev, void *data)
+int __reserve_free_pmem(struct device *dev, void *data)
 {
        struct nvdimm *nvdimm = data;
        struct nd_region *nd_region;
@@ -836,7 +836,7 @@ static int __reserve_free_pmem(struct device *dev, void *data)
        return 0;
 }
 
-static void release_free_pmem(struct nvdimm_bus *nvdimm_bus,
+void release_free_pmem(struct nvdimm_bus *nvdimm_bus,
                struct nd_mapping *nd_mapping)
 {
        struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
@@ -1032,7 +1032,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val)
 
                allocated += nvdimm_allocated_dpa(ndd, &label_id);
        }
-       available = nd_region_available_dpa(nd_region);
+       available = nd_region_allocatable_dpa(nd_region);
 
        if (val > available + allocated)
                return -ENOSPC;
index 79274ead54fb0cbe3f7034f095a7b1d3b925756a..ac68072fb8cd683c8917c9f061f127315cabb2ef 100644 (file)
@@ -100,6 +100,14 @@ struct nd_region;
 struct nvdimm_drvdata;
 struct nd_mapping;
 void nd_mapping_free_labels(struct nd_mapping *nd_mapping);
+
+int __reserve_free_pmem(struct device *dev, void *data);
+void release_free_pmem(struct nvdimm_bus *nvdimm_bus,
+                      struct nd_mapping *nd_mapping);
+
+resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region,
+                                          struct nd_mapping *nd_mapping);
+resource_size_t nd_region_allocatable_dpa(struct nd_region *nd_region);
 resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
                struct nd_mapping *nd_mapping, resource_size_t *overlap);
 resource_size_t nd_blk_available_dpa(struct nd_region *nd_region);
index ec3543b83330f25e040894599c44b3898d6bd66b..c30d5af02cc26743ae13bab51e8af8923ca8b62f 100644 (file)
@@ -389,6 +389,30 @@ resource_size_t nd_region_available_dpa(struct nd_region *nd_region)
        return available;
 }
 
+resource_size_t nd_region_allocatable_dpa(struct nd_region *nd_region)
+{
+       resource_size_t available = 0;
+       int i;
+
+       if (is_memory(&nd_region->dev))
+               available = PHYS_ADDR_MAX;
+
+       WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
+       for (i = 0; i < nd_region->ndr_mappings; i++) {
+               struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+
+               if (is_memory(&nd_region->dev))
+                       available = min(available,
+                                       nd_pmem_max_contiguous_dpa(nd_region,
+                                                                  nd_mapping));
+               else if (is_nd_blk(&nd_region->dev))
+                       available += nd_blk_available_dpa(nd_region);
+       }
+       if (is_memory(&nd_region->dev))
+               return available * nd_region->ndr_mappings;
+       return available;
+}
+
 static ssize_t available_size_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {