Message ID | 160272252925.3136502.17220638073995895400.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive) |
---|---|
State | Accepted |
Commit | a455aa72f7c46b881721668b3ee810713adc7a5b |
Headers | show |
Series | device-dax subdivision v5 to v6 fixups | expand |
On 15.10.20 02:42, Dan Williams wrote: > The conversion to request_mem_region() is broken because it assumes that > the range is marked busy prior to release. However, due to the way that > the kmem driver manipulates the IORESOURCE_BUSY flag (clears it to > let {add,remove}_memory() handle busy) it requires a manual > release_resource() to perform cleanup. > > Given that the actual 'struct resource *' needs to be recalled, not just > the range, add that tracking to the kmem driver-data. > > Reported-by: David Hildenbrand <david@redhat.com> > Fixes: 0513bd5bb114 ("device-dax/kmem: replace release_resource() with release_mem_region()") > Cc: Vishal Verma <vishal.l.verma@intel.com> > Cc: Dave Hansen <dave.hansen@linux.intel.com> > Cc: Pavel Tatashin <pasha.tatashin@soleen.com> > Cc: Brice Goglin <Brice.Goglin@inria.fr> > Cc: Dave Jiang <dave.jiang@intel.com> > Cc: Ira Weiny <ira.weiny@intel.com> > Cc: Jia He <justin.he@arm.com> > Cc: Joao Martins <joao.m.martins@oracle.com> > Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com> > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > --- > drivers/dax/kmem.c | 48 ++++++++++++++++++++++++++++++++++-------------- > 1 file changed, 34 insertions(+), 14 deletions(-) > > diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c > index 6c933f2b604e..af04b6d1d263 100644 > --- a/drivers/dax/kmem.c > +++ b/drivers/dax/kmem.c > @@ -35,11 +35,17 @@ static int dax_kmem_range(struct dev_dax *dev_dax, int i, struct range *r) > return 0; > } > > +struct dax_kmem_data { > + const char *res_name; > + struct resource *res[]; > +}; > + > static int dev_dax_kmem_probe(struct dev_dax *dev_dax) > { > struct device *dev = &dev_dax->dev; > + struct dax_kmem_data *data; > + int rc = -ENOMEM; > int i, mapped = 0; > - char *res_name; > int numa_node; > > /* > @@ -55,14 +61,17 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) > return -EINVAL; > } > > - res_name = kstrdup(dev_name(dev), GFP_KERNEL); > - if (!res_name) > + data = kzalloc(sizeof(*data) + sizeof(struct resource *) * dev_dax->nr_range, GFP_KERNEL); > + if (!data) > return -ENOMEM; > > + data->res_name = kstrdup(dev_name(dev), GFP_KERNEL); > + if (!data->res_name) > + goto err_res_name; > + > for (i = 0; i < dev_dax->nr_range; i++) { > struct resource *res; > struct range range; > - int rc; > > rc = dax_kmem_range(dev_dax, i, &range); > if (rc) { > @@ -72,7 +81,7 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) > } > > /* Region is permanently reserved if hotremove fails. */ > - res = request_mem_region(range.start, range_len(&range), res_name); > + res = request_mem_region(range.start, range_len(&range), data->res_name); > if (!res) { > dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve region\n", > i, range.start, range.end); > @@ -82,9 +91,10 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) > */ > if (mapped) > continue; > - kfree(res_name); > - return -EBUSY; > + rc = -EBUSY; > + goto err_request_mem; > } > + data->res[i] = res; > > /* > * Set flags appropriate for System RAM. Leave ..._BUSY clear > @@ -104,18 +114,25 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) > if (rc) { > dev_warn(dev, "mapping%d: %#llx-%#llx memory add failed\n", > i, range.start, range.end); > - release_mem_region(range.start, range_len(&range)); > + release_resource(res); > + kfree(res); > + data->res[i] = NULL; > if (mapped) > continue; > - kfree(res_name); > - return rc; > + goto err_request_mem; > } > mapped++; > } > > - dev_set_drvdata(dev, res_name); > + dev_set_drvdata(dev, data); > > return 0; > + > +err_request_mem: > + kfree(data->res_name); > +err_res_name: > + kfree(data); > + return rc; > } > > #ifdef CONFIG_MEMORY_HOTREMOVE > @@ -123,7 +140,7 @@ static int dev_dax_kmem_remove(struct dev_dax *dev_dax) > { > int i, success = 0; > struct device *dev = &dev_dax->dev; > - const char *res_name = dev_get_drvdata(dev); > + struct dax_kmem_data *data = dev_get_drvdata(dev); > > /* > * We have one shot for removing memory, if some memory blocks were not > @@ -142,7 +159,9 @@ static int dev_dax_kmem_remove(struct dev_dax *dev_dax) > rc = remove_memory(dev_dax->target_node, range.start, > range_len(&range)); > if (rc == 0) { > - release_mem_region(range.start, range_len(&range)); > + release_resource(data->res[i]); > + kfree(data->res[i]); > + data->res[i] = NULL; > success++; > continue; > } > @@ -153,7 +172,8 @@ static int dev_dax_kmem_remove(struct dev_dax *dev_dax) > } > > if (success >= dev_dax->nr_range) { > - kfree(res_name); > + kfree(data->res_name); > + kfree(data); > dev_set_drvdata(dev, NULL); > } > > Looks sane to me Reviewed-by: David Hildenbrand <david@redhat.com>
diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c index 6c933f2b604e..af04b6d1d263 100644 --- a/drivers/dax/kmem.c +++ b/drivers/dax/kmem.c @@ -35,11 +35,17 @@ static int dax_kmem_range(struct dev_dax *dev_dax, int i, struct range *r) return 0; } +struct dax_kmem_data { + const char *res_name; + struct resource *res[]; +}; + static int dev_dax_kmem_probe(struct dev_dax *dev_dax) { struct device *dev = &dev_dax->dev; + struct dax_kmem_data *data; + int rc = -ENOMEM; int i, mapped = 0; - char *res_name; int numa_node; /* @@ -55,14 +61,17 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) return -EINVAL; } - res_name = kstrdup(dev_name(dev), GFP_KERNEL); - if (!res_name) + data = kzalloc(sizeof(*data) + sizeof(struct resource *) * dev_dax->nr_range, GFP_KERNEL); + if (!data) return -ENOMEM; + data->res_name = kstrdup(dev_name(dev), GFP_KERNEL); + if (!data->res_name) + goto err_res_name; + for (i = 0; i < dev_dax->nr_range; i++) { struct resource *res; struct range range; - int rc; rc = dax_kmem_range(dev_dax, i, &range); if (rc) { @@ -72,7 +81,7 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) } /* Region is permanently reserved if hotremove fails. */ - res = request_mem_region(range.start, range_len(&range), res_name); + res = request_mem_region(range.start, range_len(&range), data->res_name); if (!res) { dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve region\n", i, range.start, range.end); @@ -82,9 +91,10 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) */ if (mapped) continue; - kfree(res_name); - return -EBUSY; + rc = -EBUSY; + goto err_request_mem; } + data->res[i] = res; /* * Set flags appropriate for System RAM. Leave ..._BUSY clear @@ -104,18 +114,25 @@ static int dev_dax_kmem_probe(struct dev_dax *dev_dax) if (rc) { dev_warn(dev, "mapping%d: %#llx-%#llx memory add failed\n", i, range.start, range.end); - release_mem_region(range.start, range_len(&range)); + release_resource(res); + kfree(res); + data->res[i] = NULL; if (mapped) continue; - kfree(res_name); - return rc; + goto err_request_mem; } mapped++; } - dev_set_drvdata(dev, res_name); + dev_set_drvdata(dev, data); return 0; + +err_request_mem: + kfree(data->res_name); +err_res_name: + kfree(data); + return rc; } #ifdef CONFIG_MEMORY_HOTREMOVE @@ -123,7 +140,7 @@ static int dev_dax_kmem_remove(struct dev_dax *dev_dax) { int i, success = 0; struct device *dev = &dev_dax->dev; - const char *res_name = dev_get_drvdata(dev); + struct dax_kmem_data *data = dev_get_drvdata(dev); /* * We have one shot for removing memory, if some memory blocks were not @@ -142,7 +159,9 @@ static int dev_dax_kmem_remove(struct dev_dax *dev_dax) rc = remove_memory(dev_dax->target_node, range.start, range_len(&range)); if (rc == 0) { - release_mem_region(range.start, range_len(&range)); + release_resource(data->res[i]); + kfree(data->res[i]); + data->res[i] = NULL; success++; continue; } @@ -153,7 +172,8 @@ static int dev_dax_kmem_remove(struct dev_dax *dev_dax) } if (success >= dev_dax->nr_range) { - kfree(res_name); + kfree(data->res_name); + kfree(data); dev_set_drvdata(dev, NULL); }
The conversion to request_mem_region() is broken because it assumes that the range is marked busy prior to release. However, due to the way that the kmem driver manipulates the IORESOURCE_BUSY flag (clears it to let {add,remove}_memory() handle busy) it requires a manual release_resource() to perform cleanup. Given that the actual 'struct resource *' needs to be recalled, not just the range, add that tracking to the kmem driver-data. Reported-by: David Hildenbrand <david@redhat.com> Fixes: 0513bd5bb114 ("device-dax/kmem: replace release_resource() with release_mem_region()") Cc: Vishal Verma <vishal.l.verma@intel.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: Pavel Tatashin <pasha.tatashin@soleen.com> Cc: Brice Goglin <Brice.Goglin@inria.fr> Cc: Dave Jiang <dave.jiang@intel.com> Cc: Ira Weiny <ira.weiny@intel.com> Cc: Jia He <justin.he@arm.com> Cc: Joao Martins <joao.m.martins@oracle.com> Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- drivers/dax/kmem.c | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-)