static int acpi_nfit_register_dimms()

in nfit/core.c [2062:2186]


static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
{
	struct nfit_mem *nfit_mem;
	int dimm_count = 0, rc;
	struct nvdimm *nvdimm;

	list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
		struct acpi_nfit_flush_address *flush;
		unsigned long flags = 0, cmd_mask;
		struct nfit_memdev *nfit_memdev;
		u32 device_handle;
		u16 mem_flags;

		device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
		nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle);
		if (nvdimm) {
			dimm_count++;
			continue;
		}

		if (nfit_mem->bdw && nfit_mem->memdev_pmem) {
			set_bit(NDD_ALIASING, &flags);
			set_bit(NDD_LABELING, &flags);
		}

		/* collate flags across all memdevs for this dimm */
		list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
			struct acpi_nfit_memory_map *dimm_memdev;

			dimm_memdev = __to_nfit_memdev(nfit_mem);
			if (dimm_memdev->device_handle
					!= nfit_memdev->memdev->device_handle)
				continue;
			dimm_memdev->flags |= nfit_memdev->memdev->flags;
		}

		mem_flags = __to_nfit_memdev(nfit_mem)->flags;
		if (mem_flags & ACPI_NFIT_MEM_NOT_ARMED)
			set_bit(NDD_UNARMED, &flags);

		rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle);
		if (rc)
			continue;

		/*
		 * TODO: provide translation for non-NVDIMM_FAMILY_INTEL
		 * devices (i.e. from nd_cmd to acpi_dsm) to standardize the
		 * userspace interface.
		 */
		cmd_mask = 1UL << ND_CMD_CALL;
		if (nfit_mem->family == NVDIMM_FAMILY_INTEL) {
			/*
			 * These commands have a 1:1 correspondence
			 * between DSM payload and libnvdimm ioctl
			 * payload format.
			 */
			cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK;
		}

		/* Quirk to ignore LOCAL for labels on HYPERV DIMMs */
		if (nfit_mem->family == NVDIMM_FAMILY_HYPERV)
			set_bit(NDD_NOBLK, &flags);

		if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
			set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
			set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
		}
		if (test_bit(NFIT_MEM_LSW, &nfit_mem->flags))
			set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);

		flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush
			: NULL;
		nvdimm = __nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
				acpi_nfit_dimm_attribute_groups,
				flags, cmd_mask, flush ? flush->hint_count : 0,
				nfit_mem->flush_wpq, &nfit_mem->id[0],
				acpi_nfit_get_security_ops(nfit_mem->family),
				acpi_nfit_get_fw_ops(nfit_mem));
		if (!nvdimm)
			return -ENOMEM;

		nfit_mem->nvdimm = nvdimm;
		dimm_count++;

		if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0)
			continue;

		dev_err(acpi_desc->dev, "Error found in NVDIMM %s flags:%s%s%s%s%s\n",
				nvdimm_name(nvdimm),
		  mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "",
		  mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"",
		  mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? " flush_fail" : "",
		  mem_flags & ACPI_NFIT_MEM_NOT_ARMED ? " not_armed" : "",
		  mem_flags & ACPI_NFIT_MEM_MAP_FAILED ? " map_fail" : "");

	}

	rc = nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
	if (rc)
		return rc;

	/*
	 * Now that dimms are successfully registered, and async registration
	 * is flushed, attempt to enable event notification.
	 */
	list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
		struct kernfs_node *nfit_kernfs;

		nvdimm = nfit_mem->nvdimm;
		if (!nvdimm)
			continue;

		nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
		if (nfit_kernfs)
			nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
					"flags");
		sysfs_put(nfit_kernfs);
		if (!nfit_mem->flags_attr)
			dev_warn(acpi_desc->dev, "%s: notifications disabled\n",
					nvdimm_name(nvdimm));
	}

	return devm_add_action_or_reset(acpi_desc->dev, shutdown_dimm_notify,
			acpi_desc);
}