tools/testing/nvdimm: Emulate firmware activation commands
Augment the existing firmware update emulation to track activations and validate proper update vs activate sequencing. The DIMM firmware activate capability has a concept of a maximum amount of time platform firmware will quiesce the system relative to how many DIMMs are being activated in parallel. Simulate that DIMM activation happens serially, 1 second per-DIMM, and limit the max at 3 seconds. The nfit_test0 bus emulates 5 DIMMs so it will take 2 activations to update all DIMMs. Cc: Vishal Verma <vishal.l.verma@intel.com> Cc: Dave Jiang <dave.jiang@intel.com> Cc: Ira Weiny <ira.weiny@intel.com> Reported-by: Andy Shevchenko <andriy.shevchenko@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
This commit is contained in:
parent
abfd4d9c82
commit
916566ae78
|
@ -132,6 +132,9 @@ struct nd_intel_fw_activate_dimminfo {
|
|||
u8 reserved[7];
|
||||
} __packed;
|
||||
|
||||
#define ND_INTEL_DIMM_FWA_ARM 1
|
||||
#define ND_INTEL_DIMM_FWA_DISARM 0
|
||||
|
||||
struct nd_intel_fw_activate_arm {
|
||||
u8 activate_arm;
|
||||
u32 status;
|
||||
|
@ -160,6 +163,8 @@ struct nd_intel_bus_fw_activate_businfo {
|
|||
#define ND_INTEL_BUS_FWA_STATUS_NOIDLE (6 | 5 << 16)
|
||||
#define ND_INTEL_BUS_FWA_STATUS_ABORT (6 | 6 << 16)
|
||||
|
||||
#define ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE (0)
|
||||
#define ND_INTEL_BUS_FWA_IODEV_OS_IDLE (1)
|
||||
struct nd_intel_bus_fw_activate {
|
||||
u8 iodev_state;
|
||||
u32 status;
|
||||
|
|
|
@ -173,6 +173,9 @@ struct nfit_test_fw {
|
|||
u64 version;
|
||||
u32 size_received;
|
||||
u64 end_time;
|
||||
bool armed;
|
||||
bool missed_activate;
|
||||
unsigned long last_activate;
|
||||
};
|
||||
|
||||
struct nfit_test {
|
||||
|
@ -345,7 +348,7 @@ static int nd_intel_test_finish_fw(struct nfit_test *t,
|
|||
__func__, t, nd_cmd, buf_len, idx);
|
||||
|
||||
if (fw->state == FW_STATE_UPDATED) {
|
||||
/* update already done, need cold boot */
|
||||
/* update already done, need activation */
|
||||
nd_cmd->status = 0x20007;
|
||||
return 0;
|
||||
}
|
||||
|
@ -430,6 +433,7 @@ static int nd_intel_test_finish_query(struct nfit_test *t,
|
|||
}
|
||||
dev_dbg(dev, "%s: transition out verify\n", __func__);
|
||||
fw->state = FW_STATE_UPDATED;
|
||||
fw->missed_activate = false;
|
||||
/* fall through */
|
||||
case FW_STATE_UPDATED:
|
||||
nd_cmd->status = 0;
|
||||
|
@ -1178,6 +1182,134 @@ static int nd_intel_test_cmd_master_secure_erase(struct nfit_test *t,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long last_activate;
|
||||
|
||||
static int nvdimm_bus_intel_fw_activate_businfo(struct nfit_test *t,
|
||||
struct nd_intel_bus_fw_activate_businfo *nd_cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
int i, armed = 0;
|
||||
int state;
|
||||
u64 tmo;
|
||||
|
||||
for (i = 0; i < NUM_DCR; i++) {
|
||||
struct nfit_test_fw *fw = &t->fw[i];
|
||||
|
||||
if (fw->armed)
|
||||
armed++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emulate 3 second activation max, and 1 second incremental
|
||||
* quiesce time per dimm requiring multiple activates to get all
|
||||
* DIMMs updated.
|
||||
*/
|
||||
if (armed)
|
||||
state = ND_INTEL_FWA_ARMED;
|
||||
else if (!last_activate || time_after(jiffies, last_activate + 3 * HZ))
|
||||
state = ND_INTEL_FWA_IDLE;
|
||||
else
|
||||
state = ND_INTEL_FWA_BUSY;
|
||||
|
||||
tmo = armed * USEC_PER_SEC;
|
||||
*nd_cmd = (struct nd_intel_bus_fw_activate_businfo) {
|
||||
.capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE
|
||||
| ND_INTEL_BUS_FWA_CAP_OSQUIESCE
|
||||
| ND_INTEL_BUS_FWA_CAP_RESET,
|
||||
.state = state,
|
||||
.activate_tmo = tmo,
|
||||
.cpu_quiesce_tmo = tmo,
|
||||
.io_quiesce_tmo = tmo,
|
||||
.max_quiesce_tmo = 3 * USEC_PER_SEC,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvdimm_bus_intel_fw_activate(struct nfit_test *t,
|
||||
struct nd_intel_bus_fw_activate *nd_cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
struct nd_intel_bus_fw_activate_businfo info;
|
||||
u32 status = 0;
|
||||
int i;
|
||||
|
||||
nvdimm_bus_intel_fw_activate_businfo(t, &info, sizeof(info));
|
||||
if (info.state == ND_INTEL_FWA_BUSY)
|
||||
status = ND_INTEL_BUS_FWA_STATUS_BUSY;
|
||||
else if (info.activate_tmo > info.max_quiesce_tmo)
|
||||
status = ND_INTEL_BUS_FWA_STATUS_TMO;
|
||||
else if (info.state == ND_INTEL_FWA_IDLE)
|
||||
status = ND_INTEL_BUS_FWA_STATUS_NOARM;
|
||||
|
||||
dev_dbg(&t->pdev.dev, "status: %d\n", status);
|
||||
nd_cmd->status = status;
|
||||
if (status && status != ND_INTEL_BUS_FWA_STATUS_TMO)
|
||||
return 0;
|
||||
|
||||
last_activate = jiffies;
|
||||
for (i = 0; i < NUM_DCR; i++) {
|
||||
struct nfit_test_fw *fw = &t->fw[i];
|
||||
|
||||
if (!fw->armed)
|
||||
continue;
|
||||
if (fw->state != FW_STATE_UPDATED)
|
||||
fw->missed_activate = true;
|
||||
else
|
||||
fw->state = FW_STATE_NEW;
|
||||
fw->armed = false;
|
||||
fw->last_activate = last_activate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nd_intel_test_cmd_fw_activate_dimminfo(struct nfit_test *t,
|
||||
struct nd_intel_fw_activate_dimminfo *nd_cmd,
|
||||
unsigned int buf_len, int dimm)
|
||||
{
|
||||
struct nd_intel_bus_fw_activate_businfo info;
|
||||
struct nfit_test_fw *fw = &t->fw[dimm];
|
||||
u32 result, state;
|
||||
|
||||
nvdimm_bus_intel_fw_activate_businfo(t, &info, sizeof(info));
|
||||
|
||||
if (info.state == ND_INTEL_FWA_BUSY)
|
||||
state = ND_INTEL_FWA_BUSY;
|
||||
else if (info.state == ND_INTEL_FWA_IDLE)
|
||||
state = ND_INTEL_FWA_IDLE;
|
||||
else if (fw->armed)
|
||||
state = ND_INTEL_FWA_ARMED;
|
||||
else
|
||||
state = ND_INTEL_FWA_IDLE;
|
||||
|
||||
result = ND_INTEL_DIMM_FWA_NONE;
|
||||
if (last_activate && fw->last_activate == last_activate &&
|
||||
state == ND_INTEL_FWA_IDLE) {
|
||||
if (fw->missed_activate)
|
||||
result = ND_INTEL_DIMM_FWA_NOTSTAGED;
|
||||
else
|
||||
result = ND_INTEL_DIMM_FWA_SUCCESS;
|
||||
}
|
||||
|
||||
*nd_cmd = (struct nd_intel_fw_activate_dimminfo) {
|
||||
.result = result,
|
||||
.state = state,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nd_intel_test_cmd_fw_activate_arm(struct nfit_test *t,
|
||||
struct nd_intel_fw_activate_arm *nd_cmd,
|
||||
unsigned int buf_len, int dimm)
|
||||
{
|
||||
struct nfit_test_fw *fw = &t->fw[dimm];
|
||||
|
||||
fw->armed = nd_cmd->activate_arm == ND_INTEL_DIMM_FWA_ARM;
|
||||
nd_cmd->status = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dimm(struct nfit_mem *nfit_mem, unsigned int func)
|
||||
{
|
||||
|
@ -1296,6 +1428,14 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
rc = nd_intel_test_cmd_master_secure_erase(t,
|
||||
buf, buf_len, i);
|
||||
break;
|
||||
case NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO:
|
||||
rc = nd_intel_test_cmd_fw_activate_dimminfo(
|
||||
t, buf, buf_len, i);
|
||||
break;
|
||||
case NVDIMM_INTEL_FW_ACTIVATE_ARM:
|
||||
rc = nd_intel_test_cmd_fw_activate_arm(
|
||||
t, buf, buf_len, i);
|
||||
break;
|
||||
case ND_INTEL_ENABLE_LSS_STATUS:
|
||||
rc = nd_intel_test_cmd_set_lss_status(t,
|
||||
buf, buf_len);
|
||||
|
@ -1380,9 +1520,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
if (!nd_desc)
|
||||
return -ENOTTY;
|
||||
|
||||
if (cmd == ND_CMD_CALL) {
|
||||
if (cmd == ND_CMD_CALL && call_pkg->nd_family
|
||||
== NVDIMM_BUS_FAMILY_NFIT) {
|
||||
func = call_pkg->nd_command;
|
||||
|
||||
buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
|
||||
buf = (void *) call_pkg->nd_payload;
|
||||
|
||||
|
@ -1406,7 +1546,26 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
} else if (cmd == ND_CMD_CALL && call_pkg->nd_family
|
||||
== NVDIMM_BUS_FAMILY_INTEL) {
|
||||
func = call_pkg->nd_command;
|
||||
buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
|
||||
buf = (void *) call_pkg->nd_payload;
|
||||
|
||||
switch (func) {
|
||||
case NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO:
|
||||
rc = nvdimm_bus_intel_fw_activate_businfo(t,
|
||||
buf, buf_len);
|
||||
return rc;
|
||||
case NVDIMM_BUS_INTEL_FW_ACTIVATE:
|
||||
rc = nvdimm_bus_intel_fw_activate(t, buf,
|
||||
buf_len);
|
||||
return rc;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
} else if (cmd == ND_CMD_CALL)
|
||||
return -ENOTTY;
|
||||
|
||||
if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask))
|
||||
return -ENOTTY;
|
||||
|
@ -1832,6 +1991,7 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|||
struct acpi_nfit_flush_address *flush;
|
||||
struct acpi_nfit_capabilities *pcap;
|
||||
unsigned int offset = 0, i;
|
||||
unsigned long *acpi_mask;
|
||||
|
||||
/*
|
||||
* spa0 (interleave first half of dimm0 and dimm1, note storage
|
||||
|
@ -2558,6 +2718,12 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|||
&acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(NVDIMM_INTEL_MASTER_SECURE_ERASE,
|
||||
&acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(NVDIMM_INTEL_FW_ACTIVATE_ARM, &acpi_desc->dimm_cmd_force_en);
|
||||
|
||||
acpi_mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
|
||||
set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, acpi_mask);
|
||||
set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE, acpi_mask);
|
||||
}
|
||||
|
||||
static void nfit_test1_setup(struct nfit_test *t)
|
||||
|
@ -2733,6 +2899,7 @@ static int nfit_ctl_test(struct device *dev)
|
|||
struct nd_cmd_clear_error clear_err;
|
||||
struct nd_cmd_ars_status ars_stat;
|
||||
struct nd_cmd_ars_cap ars_cap;
|
||||
struct nd_intel_bus_fw_activate_businfo fwa_info;
|
||||
char buf[sizeof(struct nd_cmd_ars_status)
|
||||
+ sizeof(struct nd_ars_record)];
|
||||
};
|
||||
|
@ -2761,11 +2928,15 @@ static int nfit_ctl_test(struct device *dev)
|
|||
.module = THIS_MODULE,
|
||||
.provider_name = "ACPI.NFIT",
|
||||
.ndctl = acpi_nfit_ctl,
|
||||
.bus_family_mask = 1UL << NVDIMM_BUS_FAMILY_NFIT
|
||||
| 1UL << NVDIMM_BUS_FAMILY_INTEL,
|
||||
},
|
||||
.bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA
|
||||
| 1UL << NFIT_CMD_ARS_INJECT_SET
|
||||
| 1UL << NFIT_CMD_ARS_INJECT_CLEAR
|
||||
| 1UL << NFIT_CMD_ARS_INJECT_GET,
|
||||
.family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL] =
|
||||
NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK,
|
||||
.dev = &adev->dev,
|
||||
};
|
||||
|
||||
|
@ -2932,6 +3103,36 @@ static int nfit_ctl_test(struct device *dev)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* test firmware activate bus info */
|
||||
cmd_size = sizeof(cmd.fwa_info);
|
||||
cmd = (struct nfit_ctl_test_cmd) {
|
||||
.pkg = {
|
||||
.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,
|
||||
.nd_family = NVDIMM_BUS_FAMILY_INTEL,
|
||||
.nd_size_out = cmd_size,
|
||||
.nd_fw_size = cmd_size,
|
||||
},
|
||||
.fwa_info = {
|
||||
.state = ND_INTEL_FWA_IDLE,
|
||||
.capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE
|
||||
| ND_INTEL_BUS_FWA_CAP_OSQUIESCE,
|
||||
.activate_tmo = 1,
|
||||
.cpu_quiesce_tmo = 1,
|
||||
.io_quiesce_tmo = 1,
|
||||
.max_quiesce_tmo = 1,
|
||||
},
|
||||
};
|
||||
rc = setup_result(cmd.buf, cmd_size);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CALL,
|
||||
&cmd, sizeof(cmd.pkg) + cmd_size, &cmd_rc);
|
||||
if (rc < 0 || cmd_rc) {
|
||||
dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
|
||||
__func__, __LINE__, rc, cmd_rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue