static int acpi_nfit_register_region()

in nfit/core.c [2975:3092]


static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
		struct nfit_spa *nfit_spa)
{
	static struct nd_mapping_desc mappings[ND_MAX_MAPPINGS];
	struct acpi_nfit_system_address *spa = nfit_spa->spa;
	struct nd_blk_region_desc ndbr_desc;
	struct nd_region_desc *ndr_desc;
	struct nfit_memdev *nfit_memdev;
	struct nvdimm_bus *nvdimm_bus;
	struct resource res;
	int count = 0, rc;

	if (nfit_spa->nd_region)
		return 0;

	if (spa->range_index == 0 && !nfit_spa_is_virtual(spa)) {
		dev_dbg(acpi_desc->dev, "detected invalid spa index\n");
		return 0;
	}

	memset(&res, 0, sizeof(res));
	memset(&mappings, 0, sizeof(mappings));
	memset(&ndbr_desc, 0, sizeof(ndbr_desc));
	res.start = spa->address;
	res.end = res.start + spa->length - 1;
	ndr_desc = &ndbr_desc.ndr_desc;
	ndr_desc->res = &res;
	ndr_desc->provider_data = nfit_spa;
	ndr_desc->attr_groups = acpi_nfit_region_attribute_groups;
	if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) {
		ndr_desc->numa_node = pxm_to_online_node(spa->proximity_domain);
		ndr_desc->target_node = pxm_to_node(spa->proximity_domain);
	} else {
		ndr_desc->numa_node = NUMA_NO_NODE;
		ndr_desc->target_node = NUMA_NO_NODE;
	}

	/* Fallback to address based numa information if node lookup failed */
	if (ndr_desc->numa_node == NUMA_NO_NODE) {
		ndr_desc->numa_node = memory_add_physaddr_to_nid(spa->address);
		dev_info(acpi_desc->dev, "changing numa node from %d to %d for nfit region [%pa-%pa]",
			NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end);
	}
	if (ndr_desc->target_node == NUMA_NO_NODE) {
		ndr_desc->target_node = phys_to_target_node(spa->address);
		dev_info(acpi_desc->dev, "changing target node from %d to %d for nfit region [%pa-%pa]",
			NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end);
	}

	/*
	 * Persistence domain bits are hierarchical, if
	 * ACPI_NFIT_CAPABILITY_CACHE_FLUSH is set then
	 * ACPI_NFIT_CAPABILITY_MEM_FLUSH is implied.
	 */
	if (acpi_desc->platform_cap & ACPI_NFIT_CAPABILITY_CACHE_FLUSH)
		set_bit(ND_REGION_PERSIST_CACHE, &ndr_desc->flags);
	else if (acpi_desc->platform_cap & ACPI_NFIT_CAPABILITY_MEM_FLUSH)
		set_bit(ND_REGION_PERSIST_MEMCTRL, &ndr_desc->flags);

	list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
		struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev;
		struct nd_mapping_desc *mapping;

		/* range index 0 == unmapped in SPA or invalid-SPA */
		if (memdev->range_index == 0 || spa->range_index == 0)
			continue;
		if (memdev->range_index != spa->range_index)
			continue;
		if (count >= ND_MAX_MAPPINGS) {
			dev_err(acpi_desc->dev, "spa%d exceeds max mappings %d\n",
					spa->range_index, ND_MAX_MAPPINGS);
			return -ENXIO;
		}
		mapping = &mappings[count++];
		rc = acpi_nfit_init_mapping(acpi_desc, mapping, ndr_desc,
				memdev, nfit_spa);
		if (rc)
			goto out;
	}

	ndr_desc->mapping = mappings;
	ndr_desc->num_mappings = count;
	rc = acpi_nfit_init_interleave_set(acpi_desc, ndr_desc, spa);
	if (rc)
		goto out;

	nvdimm_bus = acpi_desc->nvdimm_bus;
	if (nfit_spa_type(spa) == NFIT_SPA_PM) {
		rc = acpi_nfit_insert_resource(acpi_desc, ndr_desc);
		if (rc) {
			dev_warn(acpi_desc->dev,
				"failed to insert pmem resource to iomem: %d\n",
				rc);
			goto out;
		}

		nfit_spa->nd_region = nvdimm_pmem_region_create(nvdimm_bus,
				ndr_desc);
		if (!nfit_spa->nd_region)
			rc = -ENOMEM;
	} else if (nfit_spa_is_volatile(spa)) {
		nfit_spa->nd_region = nvdimm_volatile_region_create(nvdimm_bus,
				ndr_desc);
		if (!nfit_spa->nd_region)
			rc = -ENOMEM;
	} else if (nfit_spa_is_virtual(spa)) {
		nfit_spa->nd_region = nvdimm_pmem_region_create(nvdimm_bus,
				ndr_desc);
		if (!nfit_spa->nd_region)
			rc = -ENOMEM;
	}

 out:
	if (rc)
		dev_err(acpi_desc->dev, "failed to register spa range %d\n",
				nfit_spa->spa->range_index);
	return rc;
}