static int scan_free(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_label_id *label_id, resource_size_t n) { bool is_blk = strncmp(label_id->id, "blk", 3) == 0; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); int rc = 0; while (n) { struct resource *res, *last; resource_size_t new_start; 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; } /* * Keep BLK allocations relegated to high DPA as much as * possible */ if (is_blk) new_start = res->start + n; else new_start = res->start; rc = adjust_resource(res, new_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; }
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) { bool is_blk = strncmp(label_id->id, "blk", 3) == 0; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); resource_size_t first_dpa; struct resource *res; int rc = 0; /* allocate blk from highest dpa first */ if (is_blk) first_dpa = nd_mapping->start + nd_mapping->size - n; else first_dpa = nd_mapping->start; /* first resource allocation for this label-id or dimm */ res = nvdimm_allocate_dpa(ndd, label_id, first_dpa, n); if (!res) rc = -EBUSY; nd_dbg_dpa(nd_region, ndd, res, "init %d\n", rc); return rc ? n : 0; }
/** * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa * @nd_mapping: container of dpa-resource-root + labels * @nd_region: constrain available space check to this reference region * @overlap: calculate available space assuming this level of overlap * * Validate that a PMEM label, if present, aligns with the start of an * interleave set and truncate the available size at the lowest BLK * overlap point. * * The expectation is that this routine is called multiple times as it * probes for the largest BLK encroachment for any single member DIMM of * the interleave set. Once that value is determined the PMEM-limit for * the set can be established. */ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, struct nd_mapping *nd_mapping, resource_size_t *overlap) { resource_size_t map_start, map_end, busy = 0, available, blk_start; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct resource *res; const char *reason; if (!ndd) return 0; map_start = nd_mapping->start; map_end = map_start + nd_mapping->size - 1; blk_start = max(map_start, map_end + 1 - *overlap); for_each_dpa_resource(ndd, res) if (res->start >= map_start && res->start < map_end) { if (strncmp(res->name, "blk", 3) == 0) blk_start = min(blk_start, res->start); else if (res->start != map_start) { reason = "misaligned to iset"; goto err; } else { if (busy) { reason = "duplicate overlapping PMEM reservations?"; goto err; } busy += resource_size(res); continue; } } else if (res->end >= map_start && res->end <= map_end) { if (strncmp(res->name, "blk", 3) == 0) { /* * If a BLK allocation overlaps the start of * PMEM the entire interleave set may now only * be used for BLK. */ blk_start = map_start; } else { reason = "misaligned to iset"; goto err; } } else if (map_start > res->start && map_start < res->end) { /* total eclipse of the mapping */ busy += nd_mapping->size; blk_start = map_start; } *overlap = map_end + 1 - blk_start; available = blk_start - map_start; if (busy < available) return available - busy; return 0; err: /* * Something is wrong, PMEM must align with the start of the * interleave set, and there can only be one allocation per set. */ nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); return 0; }
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; bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0; bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); const resource_size_t to_allocate = n; struct resource *res; int first; retry: first = 0; for_each_dpa_resource(ndd, res) { resource_size_t allocate, available = 0, free_start, free_end; struct resource *next = res->sibling, *new_res = NULL; 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) { free_start = nd_mapping->start; available = res->start - free_start; if (space_valid(is_pmem, is_reserve, label_id, NULL)) loc = ALLOC_BEFORE; } /* space between allocations */ if (!loc && next) { free_start = res->start + resource_size(res); free_end = min(mapping_end, next->start - 1); if (space_valid(is_pmem, is_reserve, label_id, res) && free_start < free_end) { available = free_end + 1 - free_start; loc = ALLOC_MID; } } /* space at the end of the mapping */ if (!loc && !next) { free_start = res->start + resource_size(res); free_end = mapping_end; if (space_valid(is_pmem, is_reserve, label_id, res) && free_start < free_end) { available = free_end + 1 - free_start; loc = ALLOC_AFTER; } } if (!loc || !available) continue; allocate = min(available, n); switch (loc) { case ALLOC_BEFORE: if (strcmp(res->name, label_id->id) == 0) { /* adjust current resource up */ if (is_pmem && !is_reserve) return n; rc = adjust_resource(res, res->start - allocate, resource_size(res) + allocate); action = "cur grow up"; } else action = "allocate"; break; case ALLOC_MID: if (strcmp(next->name, label_id->id) == 0) { /* adjust next resource up */ if (is_pmem && !is_reserve) return n; rc = adjust_resource(next, next->start - allocate, resource_size(next) + allocate); new_res = next; action = "next grow up"; } else if (strcmp(res->name, label_id->id) == 0) { action = "grow down"; } else action = "allocate"; break; case ALLOC_AFTER: if (strcmp(res->name, label_id->id) == 0) action = "grow down"; else action = "allocate"; break; default: return n; } if (strcmp(action, "allocate") == 0) { /* BLK allocate bottom up */ if (!is_pmem) free_start += available - allocate; else if (!is_reserve && free_start != nd_mapping->start) return n; new_res = nvdimm_allocate_dpa(ndd, label_id, free_start, allocate); if (!new_res) rc = -EBUSY; } else if (strcmp(action, "grow down") == 0) { /* adjust current resource down */ rc = adjust_resource(res, res->start, resource_size(res) + allocate); if (rc == 0) res->flags |= DPA_RESOURCE_ADJUSTED; } if (!new_res) new_res = res; nd_dbg_dpa(nd_region, ndd, new_res, "%s(%d) %d\n", action, loc, rc); if (rc) return n; n -= allocate; if (n) { /* * Retry scan with newly inserted resources. * For example, if we did an ALLOC_BEFORE * insertion there may also have been space * available for an ALLOC_AFTER insertion, so we * need to check this same resource again */ goto retry; } else return 0; }