Merge branch 'for-5.1/nfit/ars' into libnvdimm-for-next
Merge several updates to the ARS implementation. Highlights include: * Support retrieval of short-ARS results if the ARS state is "requires continuation", and even if the "no_init_ars" module parameter is specified. * Allow busy-polling of the kernel ARS state by allowing root to reset the exponential back-off timer. * Filter potentially stale ARS results by tracking query-ARS relative to the previous start-ARS.
This commit is contained in:
@@ -1323,19 +1323,30 @@ static ssize_t scrub_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvdimm_bus_descriptor *nd_desc;
|
||||
struct acpi_nfit_desc *acpi_desc;
|
||||
ssize_t rc = -ENXIO;
|
||||
bool busy;
|
||||
|
||||
device_lock(dev);
|
||||
nd_desc = dev_get_drvdata(dev);
|
||||
if (nd_desc) {
|
||||
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
|
||||
|
||||
mutex_lock(&acpi_desc->init_mutex);
|
||||
rc = sprintf(buf, "%d%s", acpi_desc->scrub_count,
|
||||
acpi_desc->scrub_busy
|
||||
&& !acpi_desc->cancel ? "+\n" : "\n");
|
||||
mutex_unlock(&acpi_desc->init_mutex);
|
||||
if (!nd_desc) {
|
||||
device_unlock(dev);
|
||||
return rc;
|
||||
}
|
||||
acpi_desc = to_acpi_desc(nd_desc);
|
||||
|
||||
mutex_lock(&acpi_desc->init_mutex);
|
||||
busy = test_bit(ARS_BUSY, &acpi_desc->scrub_flags)
|
||||
&& !test_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
|
||||
rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, busy ? "+\n" : "\n");
|
||||
/* Allow an admin to poll the busy state at a higher rate */
|
||||
if (busy && capable(CAP_SYS_RAWIO) && !test_and_set_bit(ARS_POLL,
|
||||
&acpi_desc->scrub_flags)) {
|
||||
acpi_desc->scrub_tmo = 1;
|
||||
mod_delayed_work(nfit_wq, &acpi_desc->dwork, HZ);
|
||||
}
|
||||
|
||||
mutex_unlock(&acpi_desc->init_mutex);
|
||||
device_unlock(dev);
|
||||
return rc;
|
||||
}
|
||||
@@ -2681,7 +2692,10 @@ static int ars_start(struct acpi_nfit_desc *acpi_desc,
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return cmd_rc;
|
||||
if (cmd_rc < 0)
|
||||
return cmd_rc;
|
||||
set_bit(ARS_VALID, &acpi_desc->scrub_flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ars_continue(struct acpi_nfit_desc *acpi_desc)
|
||||
@@ -2691,11 +2705,11 @@ static int ars_continue(struct acpi_nfit_desc *acpi_desc)
|
||||
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
|
||||
struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
|
||||
|
||||
memset(&ars_start, 0, sizeof(ars_start));
|
||||
ars_start.address = ars_status->restart_address;
|
||||
ars_start.length = ars_status->restart_length;
|
||||
ars_start.type = ars_status->type;
|
||||
ars_start.flags = acpi_desc->ars_start_flags;
|
||||
ars_start = (struct nd_cmd_ars_start) {
|
||||
.address = ars_status->restart_address,
|
||||
.length = ars_status->restart_length,
|
||||
.type = ars_status->type,
|
||||
};
|
||||
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start,
|
||||
sizeof(ars_start), &cmd_rc);
|
||||
if (rc < 0)
|
||||
@@ -2774,6 +2788,17 @@ static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc)
|
||||
*/
|
||||
if (ars_status->out_length < 44)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Ignore potentially stale results that are only refreshed
|
||||
* after a start-ARS event.
|
||||
*/
|
||||
if (!test_and_clear_bit(ARS_VALID, &acpi_desc->scrub_flags)) {
|
||||
dev_dbg(acpi_desc->dev, "skip %d stale records\n",
|
||||
ars_status->num_records);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ars_status->num_records; i++) {
|
||||
/* only process full records */
|
||||
if (ars_status->out_length
|
||||
@@ -3044,14 +3069,16 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc,
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (no_init_ars || test_bit(ARS_FAILED, &nfit_spa->ars_state))
|
||||
if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
|
||||
return acpi_nfit_register_region(acpi_desc, nfit_spa);
|
||||
|
||||
set_bit(ARS_REQ_SHORT, &nfit_spa->ars_state);
|
||||
set_bit(ARS_REQ_LONG, &nfit_spa->ars_state);
|
||||
if (!no_init_ars)
|
||||
set_bit(ARS_REQ_LONG, &nfit_spa->ars_state);
|
||||
|
||||
switch (acpi_nfit_query_poison(acpi_desc)) {
|
||||
case 0:
|
||||
case -ENOSPC:
|
||||
case -EAGAIN:
|
||||
rc = ars_start(acpi_desc, nfit_spa, ARS_REQ_SHORT);
|
||||
/* shouldn't happen, try again later */
|
||||
@@ -3076,7 +3103,6 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc,
|
||||
break;
|
||||
case -EBUSY:
|
||||
case -ENOMEM:
|
||||
case -ENOSPC:
|
||||
/*
|
||||
* BIOS was using ARS, wait for it to complete (or
|
||||
* resources to become available) and then perform our
|
||||
@@ -3111,7 +3137,7 @@ static unsigned int __acpi_nfit_scrub(struct acpi_nfit_desc *acpi_desc,
|
||||
|
||||
lockdep_assert_held(&acpi_desc->init_mutex);
|
||||
|
||||
if (acpi_desc->cancel)
|
||||
if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags))
|
||||
return 0;
|
||||
|
||||
if (query_rc == -EBUSY) {
|
||||
@@ -3185,7 +3211,7 @@ static void __sched_ars(struct acpi_nfit_desc *acpi_desc, unsigned int tmo)
|
||||
{
|
||||
lockdep_assert_held(&acpi_desc->init_mutex);
|
||||
|
||||
acpi_desc->scrub_busy = 1;
|
||||
set_bit(ARS_BUSY, &acpi_desc->scrub_flags);
|
||||
/* note this should only be set from within the workqueue */
|
||||
if (tmo)
|
||||
acpi_desc->scrub_tmo = tmo;
|
||||
@@ -3201,7 +3227,7 @@ static void notify_ars_done(struct acpi_nfit_desc *acpi_desc)
|
||||
{
|
||||
lockdep_assert_held(&acpi_desc->init_mutex);
|
||||
|
||||
acpi_desc->scrub_busy = 0;
|
||||
clear_bit(ARS_BUSY, &acpi_desc->scrub_flags);
|
||||
acpi_desc->scrub_count++;
|
||||
if (acpi_desc->scrub_count_state)
|
||||
sysfs_notify_dirent(acpi_desc->scrub_count_state);
|
||||
@@ -3222,6 +3248,7 @@ static void acpi_nfit_scrub(struct work_struct *work)
|
||||
else
|
||||
notify_ars_done(acpi_desc);
|
||||
memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
|
||||
clear_bit(ARS_POLL, &acpi_desc->scrub_flags);
|
||||
mutex_unlock(&acpi_desc->init_mutex);
|
||||
}
|
||||
|
||||
@@ -3256,6 +3283,7 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
|
||||
struct nfit_spa *nfit_spa;
|
||||
int rc;
|
||||
|
||||
set_bit(ARS_VALID, &acpi_desc->scrub_flags);
|
||||
list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
|
||||
switch (nfit_spa_type(nfit_spa->spa)) {
|
||||
case NFIT_SPA_VOLATILE:
|
||||
@@ -3490,7 +3518,7 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc,
|
||||
struct nfit_spa *nfit_spa;
|
||||
|
||||
mutex_lock(&acpi_desc->init_mutex);
|
||||
if (acpi_desc->cancel) {
|
||||
if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags)) {
|
||||
mutex_unlock(&acpi_desc->init_mutex);
|
||||
return 0;
|
||||
}
|
||||
@@ -3569,7 +3597,7 @@ void acpi_nfit_shutdown(void *data)
|
||||
mutex_unlock(&acpi_desc_lock);
|
||||
|
||||
mutex_lock(&acpi_desc->init_mutex);
|
||||
acpi_desc->cancel = 1;
|
||||
set_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
|
||||
cancel_delayed_work_sync(&acpi_desc->dwork);
|
||||
mutex_unlock(&acpi_desc->init_mutex);
|
||||
|
||||
|
@@ -214,6 +214,13 @@ struct nfit_mem {
|
||||
int family;
|
||||
};
|
||||
|
||||
enum scrub_flags {
|
||||
ARS_BUSY,
|
||||
ARS_CANCEL,
|
||||
ARS_VALID,
|
||||
ARS_POLL,
|
||||
};
|
||||
|
||||
struct acpi_nfit_desc {
|
||||
struct nvdimm_bus_descriptor nd_desc;
|
||||
struct acpi_table_header acpi_header;
|
||||
@@ -227,7 +234,6 @@ struct acpi_nfit_desc {
|
||||
struct list_head idts;
|
||||
struct nvdimm_bus *nvdimm_bus;
|
||||
struct device *dev;
|
||||
u8 ars_start_flags;
|
||||
struct nd_cmd_ars_status *ars_status;
|
||||
struct nfit_spa *scrub_spa;
|
||||
struct delayed_work dwork;
|
||||
@@ -236,8 +242,7 @@ struct acpi_nfit_desc {
|
||||
unsigned int max_ars;
|
||||
unsigned int scrub_count;
|
||||
unsigned int scrub_mode;
|
||||
unsigned int scrub_busy:1;
|
||||
unsigned int cancel:1;
|
||||
unsigned long scrub_flags;
|
||||
unsigned long dimm_cmd_force_en;
|
||||
unsigned long bus_cmd_force_en;
|
||||
unsigned long bus_nfit_cmd_force_en;
|
||||
|
Reference in New Issue
Block a user