acpi: nfit: Add support for hot-add
Add a .notify callback to the acpi_nfit_driver that gets called on a hotplug event. From this, evaluate the _FIT ACPI method which returns the updated NFIT with handles for the hot-plugged NVDIMM. Iterate over the new NFIT, and add any new tables found, and register/enable the corresponding regions. In the nfit test framework, after normal initialization, update the NFIT with a new hot-plugged NVDIMM, and directly call into the driver to update its view of the available regions. Cc: Dan Williams <dan.j.williams@intel.com> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Cc: Toshi Kani <toshi.kani@hpe.com> Cc: Elliott, Robert <elliott@hpe.com> Cc: Jeff Moyer <jmoyer@redhat.com> Cc: <linux-acpi@vger.kernel.org> Cc: <linux-nvdimm@lists.01.org> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
这个提交包含在:
@@ -17,8 +17,10 @@
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ndctl.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <nfit.h>
|
||||
#include <nd.h>
|
||||
@@ -44,6 +46,15 @@
|
||||
* +------+ | blk5.0 | pm1.0 | 3 region5
|
||||
* +-------------------------+----------+-+-------+
|
||||
*
|
||||
* +--+---+
|
||||
* | cpu1 |
|
||||
* +--+---+ (Hotplug DIMM)
|
||||
* | +----------------------------------------------+
|
||||
* +--+---+ | blk6.0/pm7.0 | 4 region6/7
|
||||
* | imc0 +--+----------------------------------------------+
|
||||
* +------+
|
||||
*
|
||||
*
|
||||
* *) In this layout we have four dimms and two memory controllers in one
|
||||
* socket. Each unique interface (BLK or PMEM) to DPA space
|
||||
* is identified by a region device with a dynamically assigned id.
|
||||
@@ -85,8 +96,8 @@
|
||||
* reference an NVDIMM.
|
||||
*/
|
||||
enum {
|
||||
NUM_PM = 2,
|
||||
NUM_DCR = 4,
|
||||
NUM_PM = 3,
|
||||
NUM_DCR = 5,
|
||||
NUM_BDW = NUM_DCR,
|
||||
NUM_SPA = NUM_PM + NUM_DCR + NUM_BDW,
|
||||
NUM_MEM = NUM_DCR + NUM_BDW + 2 /* spa0 iset */ + 4 /* spa1 iset */,
|
||||
@@ -115,6 +126,7 @@ static u32 handle[NUM_DCR] = {
|
||||
[1] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 1),
|
||||
[2] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 0),
|
||||
[3] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 1),
|
||||
[4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0),
|
||||
};
|
||||
|
||||
struct nfit_test {
|
||||
@@ -138,6 +150,7 @@ struct nfit_test {
|
||||
dma_addr_t *dcr_dma;
|
||||
int (*alloc)(struct nfit_test *t);
|
||||
void (*setup)(struct nfit_test *t);
|
||||
int setup_hotplug;
|
||||
};
|
||||
|
||||
static struct nfit_test *to_nfit_test(struct device *dev)
|
||||
@@ -428,6 +441,10 @@ static int nfit_test0_alloc(struct nfit_test *t)
|
||||
if (!t->spa_set[1])
|
||||
return -ENOMEM;
|
||||
|
||||
t->spa_set[2] = test_alloc_coherent(t, SPA0_SIZE, &t->spa_set_dma[2]);
|
||||
if (!t->spa_set[2])
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < NUM_DCR; i++) {
|
||||
t->dimm[i] = test_alloc(t, DIMM_SIZE, &t->dimm_dma[i]);
|
||||
if (!t->dimm[i])
|
||||
@@ -950,6 +967,126 @@ static void nfit_test0_setup(struct nfit_test *t)
|
||||
flush->hint_count = 1;
|
||||
flush->hint_address[0] = t->flush_dma[3];
|
||||
|
||||
if (t->setup_hotplug) {
|
||||
offset = offset + sizeof(struct acpi_nfit_flush_address) * 4;
|
||||
/* dcr-descriptor4 */
|
||||
dcr = nfit_buf + offset;
|
||||
dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
|
||||
dcr->header.length = sizeof(struct acpi_nfit_control_region);
|
||||
dcr->region_index = 4+1;
|
||||
dcr->vendor_id = 0xabcd;
|
||||
dcr->device_id = 0;
|
||||
dcr->revision_id = 1;
|
||||
dcr->serial_number = ~handle[4];
|
||||
dcr->windows = 1;
|
||||
dcr->window_size = DCR_SIZE;
|
||||
dcr->command_offset = 0;
|
||||
dcr->command_size = 8;
|
||||
dcr->status_offset = 8;
|
||||
dcr->status_size = 4;
|
||||
|
||||
offset = offset + sizeof(struct acpi_nfit_control_region);
|
||||
/* bdw4 (spa/dcr4, dimm4) */
|
||||
bdw = nfit_buf + offset;
|
||||
bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION;
|
||||
bdw->header.length = sizeof(struct acpi_nfit_data_region);
|
||||
bdw->region_index = 4+1;
|
||||
bdw->windows = 1;
|
||||
bdw->offset = 0;
|
||||
bdw->size = BDW_SIZE;
|
||||
bdw->capacity = DIMM_SIZE;
|
||||
bdw->start_address = 0;
|
||||
|
||||
offset = offset + sizeof(struct acpi_nfit_data_region);
|
||||
/* spa10 (dcr4) dimm4 */
|
||||
spa = nfit_buf + offset;
|
||||
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
|
||||
spa->header.length = sizeof(*spa);
|
||||
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
|
||||
spa->range_index = 10+1;
|
||||
spa->address = t->dcr_dma[4];
|
||||
spa->length = DCR_SIZE;
|
||||
|
||||
/*
|
||||
* spa11 (single-dimm interleave for hotplug, note storage
|
||||
* does not actually alias the related block-data-window
|
||||
* regions)
|
||||
*/
|
||||
spa = nfit_buf + offset + sizeof(*spa);
|
||||
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
|
||||
spa->header.length = sizeof(*spa);
|
||||
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
|
||||
spa->range_index = 11+1;
|
||||
spa->address = t->spa_set_dma[2];
|
||||
spa->length = SPA0_SIZE;
|
||||
|
||||
/* spa12 (bdw for dcr4) dimm4 */
|
||||
spa = nfit_buf + offset + sizeof(*spa) * 2;
|
||||
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
|
||||
spa->header.length = sizeof(*spa);
|
||||
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
|
||||
spa->range_index = 12+1;
|
||||
spa->address = t->dimm_dma[4];
|
||||
spa->length = DIMM_SIZE;
|
||||
|
||||
offset = offset + sizeof(*spa) * 3;
|
||||
/* mem-region14 (spa/dcr4, dimm4) */
|
||||
memdev = nfit_buf + offset;
|
||||
memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
|
||||
memdev->header.length = sizeof(*memdev);
|
||||
memdev->device_handle = handle[4];
|
||||
memdev->physical_id = 4;
|
||||
memdev->region_id = 0;
|
||||
memdev->range_index = 10+1;
|
||||
memdev->region_index = 4+1;
|
||||
memdev->region_size = 0;
|
||||
memdev->region_offset = 0;
|
||||
memdev->address = 0;
|
||||
memdev->interleave_index = 0;
|
||||
memdev->interleave_ways = 1;
|
||||
|
||||
/* mem-region15 (spa0, dimm4) */
|
||||
memdev = nfit_buf + offset +
|
||||
sizeof(struct acpi_nfit_memory_map);
|
||||
memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
|
||||
memdev->header.length = sizeof(*memdev);
|
||||
memdev->device_handle = handle[4];
|
||||
memdev->physical_id = 4;
|
||||
memdev->region_id = 0;
|
||||
memdev->range_index = 11+1;
|
||||
memdev->region_index = 4+1;
|
||||
memdev->region_size = SPA0_SIZE;
|
||||
memdev->region_offset = t->spa_set_dma[2];
|
||||
memdev->address = 0;
|
||||
memdev->interleave_index = 0;
|
||||
memdev->interleave_ways = 1;
|
||||
|
||||
/* mem-region16 (spa/dcr4, dimm4) */
|
||||
memdev = nfit_buf + offset +
|
||||
sizeof(struct acpi_nfit_memory_map) * 2;
|
||||
memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
|
||||
memdev->header.length = sizeof(*memdev);
|
||||
memdev->device_handle = handle[4];
|
||||
memdev->physical_id = 4;
|
||||
memdev->region_id = 0;
|
||||
memdev->range_index = 12+1;
|
||||
memdev->region_index = 4+1;
|
||||
memdev->region_size = 0;
|
||||
memdev->region_offset = 0;
|
||||
memdev->address = 0;
|
||||
memdev->interleave_index = 0;
|
||||
memdev->interleave_ways = 1;
|
||||
|
||||
offset = offset + sizeof(struct acpi_nfit_memory_map) * 3;
|
||||
/* flush3 (dimm4) */
|
||||
flush = nfit_buf + offset;
|
||||
flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS;
|
||||
flush->header.length = sizeof(struct acpi_nfit_flush_address);
|
||||
flush->device_handle = handle[4];
|
||||
flush->hint_count = 1;
|
||||
flush->hint_address[0] = t->flush_dma[4];
|
||||
}
|
||||
|
||||
acpi_desc = &t->acpi_desc;
|
||||
set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en);
|
||||
set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
|
||||
@@ -1108,6 +1245,29 @@ static int nfit_test_probe(struct platform_device *pdev)
|
||||
if (!acpi_desc->nvdimm_bus)
|
||||
return -ENXIO;
|
||||
|
||||
INIT_LIST_HEAD(&acpi_desc->spa_maps);
|
||||
INIT_LIST_HEAD(&acpi_desc->spas);
|
||||
INIT_LIST_HEAD(&acpi_desc->dcrs);
|
||||
INIT_LIST_HEAD(&acpi_desc->bdws);
|
||||
INIT_LIST_HEAD(&acpi_desc->idts);
|
||||
INIT_LIST_HEAD(&acpi_desc->flushes);
|
||||
INIT_LIST_HEAD(&acpi_desc->memdevs);
|
||||
INIT_LIST_HEAD(&acpi_desc->dimms);
|
||||
mutex_init(&acpi_desc->spa_map_mutex);
|
||||
mutex_init(&acpi_desc->init_mutex);
|
||||
|
||||
rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_size);
|
||||
if (rc) {
|
||||
nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (nfit_test->setup != nfit_test0_setup)
|
||||
return 0;
|
||||
|
||||
nfit_test->setup_hotplug = 1;
|
||||
nfit_test->setup(nfit_test);
|
||||
|
||||
rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_size);
|
||||
if (rc) {
|
||||
nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
|
||||
|
在新工单中引用
屏蔽一个用户