// SPDX-License-Identifier: GPL-2.0-only/* * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. */#include <linux/kstrtox.h>#include <linux/module.h>#include <linux/device.h>#include <linux/sort.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/nd.h>#include "nd-core.h"#include "pmem.h"#include "pfn.h"#include "nd.h" static void namespace_io_release(struct device *dev){ struct nd_namespace_io *nsio = to_nd_namespace_io(dev); kfree(nsio);} static void namespace_pmem_release(struct device *dev){ struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); struct nd_region *nd_region = to_nd_region(dev->parent); if (nspm->id >= 0) ida_free(&nd_region->ns_ida, nspm->id); kfree(nspm->alt_name); kfree(nspm->uuid); kfree(nspm);} static bool is_namespace_pmem(const struct device *dev);static bool is_namespace_io(const struct device *dev); static int is_uuid_busy(struct device *dev, void *data){ uuid_t *uuid1 = data, *uuid2 = NULL; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); uuid2 = nspm->uuid; } else if (is_nd_btt(dev)) { struct nd_btt *nd_btt = to_nd_btt(dev); uuid2 = nd_btt->uuid; } else if (is_nd_pfn(dev)) { struct nd_pfn *nd_pfn = to_nd_pfn(dev); uuid2 = nd_pfn->uuid; } if (uuid2 && uuid_equal(uuid1, uuid2)) return -EBUSY; return 0;} static int is_namespace_uuid_busy(struct device *dev, void *data){ if (is_nd_region(dev)) return device_for_each_child(dev, data, is_uuid_busy); return 0;} /** * nd_is_uuid_unique - verify that no other namespace has @uuid * @dev: any device on a nvdimm_bus * @uuid: uuid to check * * Returns: %true if the uuid is unique, %false if not */bool nd_is_uuid_unique(struct device *dev, uuid_t *uuid){ struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); if (!nvdimm_bus) return false; WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev)); if (device_for_each_child(&nvdimm_bus->dev, uuid, is_namespace_uuid_busy) != 0) return false; return true;} bool pmem_should_map_pages(struct device *dev){ struct nd_region *nd_region = to_nd_region(dev->parent); struct nd_namespace_common *ndns = to_ndns(dev); struct nd_namespace_io *nsio; if (!IS_ENABLED(CONFIG_ZONE_DEVICE)) return false; if (!test_bit(ND_REGION_PAGEMAP, &nd_region->flags)) return false; if (is_nd_pfn(dev) || is_nd_btt(dev)) return false; if (ndns->force_raw) return false; nsio = to_nd_namespace_io(dev); if (region_intersects(nsio->res.start, resource_size(&nsio->res), IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_MIXED) return false; return ARCH_MEMREMAP_PMEM == MEMREMAP_WB;}EXPORT_SYMBOL(pmem_should_map_pages); unsigned int pmem_sector_size(struct nd_namespace_common *ndns){ if (is_namespace_pmem(&ndns->dev)) { struct nd_namespace_pmem *nspm; nspm = to_nd_namespace_pmem(&ndns->dev); if (nspm->lbasize == 0 || nspm->lbasize == 512) /* default */; else if (nspm->lbasize == 4096) return 4096; else dev_WARN(&ndns->dev, "unsupported sector size: %ld\n", nspm->lbasize); } /* * There is no namespace label (is_namespace_io()), or the label * indicates the default sector size. */ return 512;}EXPORT_SYMBOL(pmem_sector_size); const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, char *name){ struct nd_region *nd_region = to_nd_region(ndns->dev.parent); const char *suffix = NULL; if (ndns->claim && is_nd_btt(ndns->claim)) suffix = "s"; if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev)) { int nsidx = 0; if (is_namespace_pmem(&ndns->dev)) { struct nd_namespace_pmem *nspm; nspm = to_nd_namespace_pmem(&ndns->dev); nsidx = nspm->id; } if (nsidx) sprintf(name, "pmem%d.%d%s", nd_region->id, nsidx, suffix ? suffix : ""); else sprintf(name, "pmem%d%s", nd_region->id, suffix ? suffix : ""); } else { return NULL; } return name;}EXPORT_SYMBOL(nvdimm_namespace_disk_name); const uuid_t *nd_dev_to_uuid(struct device *dev){ if (dev && is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); return nspm->uuid; } return &uuid_null;}EXPORT_SYMBOL(nd_dev_to_uuid); static ssize_t nstype_show(struct device *dev, struct device_attribute *attr, char *buf){ struct nd_region *nd_region = to_nd_region(dev->parent); return sprintf(buf, "%d\n", nd_region_to_nstype(nd_region));}static DEVICE_ATTR_RO(nstype); static ssize_t __alt_name_store(struct device *dev, const char *buf, const size_t len){ char *input, *pos, *alt_name, **ns_altname; ssize_t rc; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ns_altname = &nspm->alt_name; } else return -ENXIO; if (dev->driver || to_ndns(dev)->claim) return -EBUSY; input = kstrndup(buf, len, GFP_KERNEL); if (!input) return -ENOMEM; pos = strim(input); if (strlen(pos) + 1 > NSLABEL_NAME_LEN) { rc = -EINVAL; goto out; } alt_name = kzalloc(NSLABEL_NAME_LEN, GFP_KERNEL); if (!alt_name) { rc = -ENOMEM; goto out; } kfree(*ns_altname); *ns_altname = alt_name; sprintf(*ns_altname, "%s", pos); rc = len; out: kfree(input); return rc;} static int nd_namespace_label_update(struct nd_region *nd_region, struct device *dev){ dev_WARN_ONCE(dev, dev->driver || to_ndns(dev)->claim, "namespace must be idle during label update\n"); if (dev->driver || to_ndns(dev)->claim) return 0; /* * Only allow label writes that will result in a valid namespace * or deletion of an existing namespace. */ if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); resource_size_t size = resource_size(&nspm->nsio.res); if (size == 0 && nspm->uuid) /* delete allocation */; else if (!nspm->uuid) return 0; return nd_pmem_namespace_label_update(nd_region, nspm, size); } else return -ENXIO;} static ssize_t alt_name_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ struct nd_region *nd_region = to_nd_region(dev->parent); ssize_t rc; guard(device)(dev); guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); rc = __alt_name_store(dev, buf, len); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc); return rc < 0 ? rc : len;} static ssize_t alt_name_show(struct device *dev, struct device_attribute *attr, char *buf){ char *ns_altname; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ns_altname = nspm->alt_name; } else return -ENXIO; return sprintf(buf, "%s\n", ns_altname ? ns_altname : "");}static DEVICE_ATTR_RW(alt_name); static int scan_free(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_label_id *label_id, resource_size_t n){ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); int rc = 0; while (n) { struct resource *res, *last; last = NULL; for_each_dpa_resource(ndd, res) if (strcmp(res->name, label_id->id) == 0) last = res; res = last; if (!res) return 0; if (n >= resource_size(res)) { n -= resource_size(res); nd_dbg_dpa(nd_region, ndd, res, "delete %d\n", rc); nvdimm_free_dpa(ndd, res); /* retry with last resource deleted */ continue; } rc = adjust_resource(res, res->start, resource_size(res) - n); if (rc == 0) res->flags |= DPA_RESOURCE_ADJUSTED; nd_dbg_dpa(nd_region, ndd, res, "shrink %d\n", rc); break; } return rc;} /** * shrink_dpa_allocation - for each dimm in region free n bytes for label_id * @nd_region: the set of dimms to reclaim @n bytes from * @label_id: unique identifier for the namespace consuming this dpa range * @n: number of bytes per-dimm to release * * Assumes resources are ordered. Starting from the end try to * adjust_resource() the allocation to @n, but if @n is larger than the * allocation delete it and find the 'new' last allocation in the label * set. * * Returns: %0 on success on -errno on error */static int shrink_dpa_allocation(struct nd_region *nd_region, struct nd_label_id *label_id, resource_size_t n){ int i; for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; int rc; rc = scan_free(nd_region, nd_mapping, label_id, n); if (rc) return rc; } return 0;} static resource_size_t init_dpa_allocation(struct nd_label_id *label_id, struct nd_region *nd_region, struct nd_mapping *nd_mapping, resource_size_t n){ struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct resource *res; int rc = 0; /* first resource allocation for this label-id or dimm */ res = nvdimm_allocate_dpa(ndd, label_id, nd_mapping->start, n); if (!res) rc = -EBUSY; nd_dbg_dpa(nd_region, ndd, res, "init %d\n", rc); return rc ? n : 0;} /** * space_valid() - validate free dpa space against constraints * @nd_region: hosting region of the free space * @ndd: dimm device data for debug * @label_id: namespace id to allocate space * @prev: potential allocation that precedes free space * @next: allocation that follows the given free space range * @exist: first allocation with same id in the mapping * @n: range that must satisfied for pmem allocations * @valid: free space range to validate * * BLK-space is valid as long as it does not precede a PMEM * allocation in a given region. PMEM-space must be contiguous * and adjacent to an existing allocation (if one * exists). If reserving PMEM any space is valid. */static void space_valid(struct nd_region *nd_region, struct nvdimm_drvdata *ndd, struct nd_label_id *label_id, struct resource *prev, struct resource *next, struct resource *exist, resource_size_t n, struct resource *valid){ bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0; unsigned long align; align = nd_region->align / nd_region->ndr_mappings; valid->start = ALIGN(valid->start, align); valid->end = ALIGN_DOWN(valid->end + 1, align) - 1; if (valid->start >= valid->end) goto invalid; if (is_reserve) return; /* allocation needs to be contiguous, so this is all or nothing */ if (resource_size(valid) < n) goto invalid; /* we've got all the space we need and no existing allocation */ if (!exist) return; /* allocation needs to be contiguous with the existing namespace */ if (valid->start == exist->end + 1 || valid->end == exist->start - 1) return; invalid: /* truncate @valid size to 0 */ valid->end = valid->start - 1;} enum alloc_loc { ALLOC_ERR = 0, ALLOC_BEFORE, ALLOC_MID, ALLOC_AFTER,}; static resource_size_t scan_allocate(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_label_id *label_id, resource_size_t n){ resource_size_t mapping_end = nd_mapping->start + nd_mapping->size - 1; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct resource *res, *exist = NULL, valid; const resource_size_t to_allocate = n; int first; for_each_dpa_resource(ndd, res) if (strcmp(label_id->id, res->name) == 0) exist = res; valid.start = nd_mapping->start; valid.end = mapping_end; valid.name = "free space"; retry: first = 0; for_each_dpa_resource(ndd, res) { struct resource *next = res->sibling, *new_res = NULL; resource_size_t allocate, available = 0; enum alloc_loc loc = ALLOC_ERR; const char *action; int rc = 0; /* ignore resources outside this nd_mapping */ if (res->start > mapping_end) continue; if (res->end < nd_mapping->start) continue; /* space at the beginning of the mapping */ if (!first++ && res->start > nd_mapping->start) { valid.start = nd_mapping->start; valid.end = res->start - 1; space_valid(nd_region, ndd, label_id, NULL, next, exist, to_allocate, &valid); available = resource_size(&valid); if (available) loc = ALLOC_BEFORE; } /* space between allocations */ if (!loc && next) { valid.start = res->start + resource_size(res); valid.end = min(mapping_end, next->start - 1); space_valid(nd_region, ndd, label_id, res, next, exist, to_allocate, &valid); available = resource_size(&valid); if (available) loc = ALLOC_MID; } /* space at the end of the mapping */ if (!loc && !next) { valid.start = res->start + resource_size(res); valid.end = mapping_end; space_valid(nd_region, ndd, label_id, res, next, exist, to_allocate, &valid); available = resource_size(&valid); if (available) loc = ALLOC_AFTER; } if (!loc || !available) continue;Lines 1–500 of 2231. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. */ #include <linux/kstrtox.h> #include <linux/module.h> #include <linux/device.h> #include <linux/sort.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/nd.h> #include "nd-core.h" #include "pmem.h" #include "pfn.h" #include "nd.h" static void namespace_io_release(struct device *dev) { struct nd_namespace_io *nsio = to_nd_namespace_io(dev); kfree(nsio); } static void namespace_pmem_release(struct device *dev) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); struct nd_region *nd_region = to_nd_region(dev->parent); if (nspm->id >= 0) ida_free(&nd_region->ns_ida, nspm->id); kfree(nspm->alt_name); kfree(nspm->uuid); kfree(nspm); } static bool is_namespace_pmem(const struct device *dev); static bool is_namespace_io(const struct device *dev); static int is_uuid_busy(struct device *dev, void *data) { uuid_t *uuid1 = data, *uuid2 = NULL; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); uuid2 = nspm->uuid; } else if (is_nd_btt(dev)) { struct nd_btt *nd_btt = to_nd_btt(dev); uuid2 = nd_btt->uuid; } else if (is_nd_pfn(dev)) { struct nd_pfn *nd_pfn = to_nd_pfn(dev); uuid2 = nd_pfn->uuid; } if (uuid2 && uuid_equal(uuid1, uuid2)) return -EBUSY; return 0; } static int is_namespace_uuid_busy(struct device *dev, void *data) { if (is_nd_region(dev)) return device_for_each_child(dev, data, is_uuid_busy); return 0; } /** * nd_is_uuid_unique - verify that no other namespace has @uuid * @dev: any device on a nvdimm_bus * @uuid: uuid to check * * Returns: %true if the uuid is unique, %false if not */ bool nd_is_uuid_unique(struct device *dev, uuid_t *uuid) { struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); if (!nvdimm_bus) return false; WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev)); if (device_for_each_child(&nvdimm_bus->dev, uuid, is_namespace_uuid_busy) != 0) return false; return true; } bool pmem_should_map_pages(struct device *dev) { struct nd_region *nd_region = to_nd_region(dev->parent); struct nd_namespace_common *ndns = to_ndns(dev); struct nd_namespace_io *nsio; if (!IS_ENABLED(CONFIG_ZONE_DEVICE)) return false; if (!test_bit(ND_REGION_PAGEMAP, &nd_region->flags)) return false; if (is_nd_pfn(dev) || is_nd_btt(dev)) return false; if (ndns->force_raw) return false; nsio = to_nd_namespace_io(dev); if (region_intersects(nsio->res.start, resource_size(&nsio->res), IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_MIXED) return false; return ARCH_MEMREMAP_PMEM == MEMREMAP_WB; } EXPORT_SYMBOL(pmem_should_map_pages); unsigned int pmem_sector_size(struct nd_namespace_common *ndns) { if (is_namespace_pmem(&ndns->dev)) { struct nd_namespace_pmem *nspm; nspm = to_nd_namespace_pmem(&ndns->dev); if (nspm->lbasize == 0 || nspm->lbasize == 512) /* default */; else if (nspm->lbasize == 4096) return 4096; else dev_WARN(&ndns->dev, "unsupported sector size: %ld\n", nspm->lbasize); } /* * There is no namespace label (is_namespace_io()), or the label * indicates the default sector size. */ return 512; } EXPORT_SYMBOL(pmem_sector_size); const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, char *name) { struct nd_region *nd_region = to_nd_region(ndns->dev.parent); const char *suffix = NULL; if (ndns->claim && is_nd_btt(ndns->claim)) suffix = "s"; if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev)) { int nsidx = 0; if (is_namespace_pmem(&ndns->dev)) { struct nd_namespace_pmem *nspm; nspm = to_nd_namespace_pmem(&ndns->dev); nsidx = nspm->id; } if (nsidx) sprintf(name, "pmem%d.%d%s", nd_region->id, nsidx, suffix ? suffix : ""); else sprintf(name, "pmem%d%s", nd_region->id, suffix ? suffix : ""); } else { return NULL; } return name; } EXPORT_SYMBOL(nvdimm_namespace_disk_name); const uuid_t *nd_dev_to_uuid(struct device *dev) { if (dev && is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); return nspm->uuid; } return &uuid_null; } EXPORT_SYMBOL(nd_dev_to_uuid); static ssize_t nstype_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region *nd_region = to_nd_region(dev->parent); return sprintf(buf, "%d\n", nd_region_to_nstype(nd_region)); } static DEVICE_ATTR_RO(nstype); static ssize_t __alt_name_store(struct device *dev, const char *buf, const size_t len) { char *input, *pos, *alt_name, **ns_altname; ssize_t rc; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ns_altname = &nspm->alt_name; } else return -ENXIO; if (dev->driver || to_ndns(dev)->claim) return -EBUSY; input = kstrndup(buf, len, GFP_KERNEL); if (!input) return -ENOMEM; pos = strim(input); if (strlen(pos) + 1 > NSLABEL_NAME_LEN) { rc = -EINVAL; goto out; } alt_name = kzalloc(NSLABEL_NAME_LEN, GFP_KERNEL); if (!alt_name) { rc = -ENOMEM; goto out; } kfree(*ns_altname); *ns_altname = alt_name; sprintf(*ns_altname, "%s", pos); rc = len; out: kfree(input); return rc; } static int nd_namespace_label_update(struct nd_region *nd_region, struct device *dev) { dev_WARN_ONCE(dev, dev->driver || to_ndns(dev)->claim, "namespace must be idle during label update\n"); if (dev->driver || to_ndns(dev)->claim) return 0; /* * Only allow label writes that will result in a valid namespace * or deletion of an existing namespace. */ if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); resource_size_t size = resource_size(&nspm->nsio.res); if (size == 0 && nspm->uuid) /* delete allocation */; else if (!nspm->uuid) return 0; return nd_pmem_namespace_label_update(nd_region, nspm, size); } else return -ENXIO; } static ssize_t alt_name_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct nd_region *nd_region = to_nd_region(dev->parent); ssize_t rc; guard(device)(dev); guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); rc = __alt_name_store(dev, buf, len); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc); return rc < 0 ? rc : len; } static ssize_t alt_name_show(struct device *dev, struct device_attribute *attr, char *buf) { char *ns_altname; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ns_altname = nspm->alt_name; } else return -ENXIO; return sprintf(buf, "%s\n", ns_altname ? ns_altname : ""); } static DEVICE_ATTR_RW(alt_name); static int scan_free(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_label_id *label_id, resource_size_t n) { struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); int rc = 0; while (n) { struct resource *res, *last; last = NULL; for_each_dpa_resource(ndd, res) if (strcmp(res->name, label_id->id) == 0) last = res; res = last; if (!res) return 0; if (n >= resource_size(res)) { n -= resource_size(res); nd_dbg_dpa(nd_region, ndd, res, "delete %d\n", rc); nvdimm_free_dpa(ndd, res); /* retry with last resource deleted */ continue; } rc = adjust_resource(res, res->start, resource_size(res) - n); if (rc == 0) res->flags |= DPA_RESOURCE_ADJUSTED; nd_dbg_dpa(nd_region, ndd, res, "shrink %d\n", rc); break; } return rc; } /** * shrink_dpa_allocation - for each dimm in region free n bytes for label_id * @nd_region: the set of dimms to reclaim @n bytes from * @label_id: unique identifier for the namespace consuming this dpa range * @n: number of bytes per-dimm to release * * Assumes resources are ordered. Starting from the end try to * adjust_resource() the allocation to @n, but if @n is larger than the * allocation delete it and find the 'new' last allocation in the label * set. * * Returns: %0 on success on -errno on error */ static int shrink_dpa_allocation(struct nd_region *nd_region, struct nd_label_id *label_id, resource_size_t n) { int i; for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; int rc; rc = scan_free(nd_region, nd_mapping, label_id, n); if (rc) return rc; } return 0; } static resource_size_t init_dpa_allocation(struct nd_label_id *label_id, struct nd_region *nd_region, struct nd_mapping *nd_mapping, resource_size_t n) { struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct resource *res; int rc = 0; /* first resource allocation for this label-id or dimm */ res = nvdimm_allocate_dpa(ndd, label_id, nd_mapping->start, n); if (!res) rc = -EBUSY; nd_dbg_dpa(nd_region, ndd, res, "init %d\n", rc); return rc ? n : 0; } /** * space_valid() - validate free dpa space against constraints * @nd_region: hosting region of the free space * @ndd: dimm device data for debug * @label_id: namespace id to allocate space * @prev: potential allocation that precedes free space * @next: allocation that follows the given free space range * @exist: first allocation with same id in the mapping * @n: range that must satisfied for pmem allocations * @valid: free space range to validate * * BLK-space is valid as long as it does not precede a PMEM * allocation in a given region. PMEM-space must be contiguous * and adjacent to an existing allocation (if one * exists). If reserving PMEM any space is valid. */ static void space_valid(struct nd_region *nd_region, struct nvdimm_drvdata *ndd, struct nd_label_id *label_id, struct resource *prev, struct resource *next, struct resource *exist, resource_size_t n, struct resource *valid) { bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0; unsigned long align; align = nd_region->align / nd_region->ndr_mappings; valid->start = ALIGN(valid->start, align); valid->end = ALIGN_DOWN(valid->end + 1, align) - 1; if (valid->start >= valid->end) goto invalid; if (is_reserve) return; /* allocation needs to be contiguous, so this is all or nothing */ if (resource_size(valid) < n) goto invalid; /* we've got all the space we need and no existing allocation */ if (!exist) return; /* allocation needs to be contiguous with the existing namespace */ if (valid->start == exist->end + 1 || valid->end == exist->start - 1) return; invalid: /* truncate @valid size to 0 */ valid->end = valid->start - 1; } enum alloc_loc { ALLOC_ERR = 0, ALLOC_BEFORE, ALLOC_MID, ALLOC_AFTER, }; static resource_size_t scan_allocate(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_label_id *label_id, resource_size_t n) { resource_size_t mapping_end = nd_mapping->start + nd_mapping->size - 1; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct resource *res, *exist = NULL, valid; const resource_size_t to_allocate = n; int first; for_each_dpa_resource(ndd, res) if (strcmp(label_id->id, res->name) == 0) exist = res; valid.start = nd_mapping->start; valid.end = mapping_end; valid.name = "free space"; retry: first = 0; for_each_dpa_resource(ndd, res) { struct resource *next = res->sibling, *new_res = NULL; resource_size_t allocate, available = 0; enum alloc_loc loc = ALLOC_ERR; const char *action; int rc = 0; /* ignore resources outside this nd_mapping */ if (res->start > mapping_end) continue; if (res->end < nd_mapping->start) continue; /* space at the beginning of the mapping */ if (!first++ && res->start > nd_mapping->start) { valid.start = nd_mapping->start; valid.end = res->start - 1; space_valid(nd_region, ndd, label_id, NULL, next, exist, to_allocate, &valid); available = resource_size(&valid); if (available) loc = ALLOC_BEFORE; } /* space between allocations */ if (!loc && next) { valid.start = res->start + resource_size(res); valid.end = min(mapping_end, next->start - 1); space_valid(nd_region, ndd, label_id, res, next, exist, to_allocate, &valid); available = resource_size(&valid); if (available) loc = ALLOC_MID; } /* space at the end of the mapping */ if (!loc && !next) { valid.start = res->start + resource_size(res); valid.end = mapping_end; space_valid(nd_region, ndd, label_id, res, next, exist, to_allocate, &valid); available = resource_size(&valid); if (available) loc = ALLOC_AFTER; } if (!loc || !available) continue;