in host/core.c [2332:2435]
static int nvme_configure_apst(struct nvme_ctrl *ctrl)
{
struct nvme_feat_auto_pst *table;
unsigned apste = 0;
u64 max_lat_us = 0;
__le64 target = 0;
int max_ps = -1;
int state;
int ret;
unsigned last_lt_index = UINT_MAX;
/*
* If APST isn't supported or if we haven't been initialized yet,
* then don't do anything.
*/
if (!ctrl->apsta)
return 0;
if (ctrl->npss > 31) {
dev_warn(ctrl->device, "NPSS is invalid; not using APST\n");
return 0;
}
table = kzalloc(sizeof(*table), GFP_KERNEL);
if (!table)
return 0;
if (!ctrl->apst_enabled || ctrl->ps_max_latency_us == 0) {
/* Turn off APST. */
dev_dbg(ctrl->device, "APST disabled\n");
goto done;
}
/*
* Walk through all states from lowest- to highest-power.
* According to the spec, lower-numbered states use more power. NPSS,
* despite the name, is the index of the lowest-power state, not the
* number of states.
*/
for (state = (int)ctrl->npss; state >= 0; state--) {
u64 total_latency_us, exit_latency_us, transition_ms;
if (target)
table->entries[state] = target;
/*
* Don't allow transitions to the deepest state if it's quirked
* off.
*/
if (state == ctrl->npss &&
(ctrl->quirks & NVME_QUIRK_NO_DEEPEST_PS))
continue;
/*
* Is this state a useful non-operational state for higher-power
* states to autonomously transition to?
*/
if (!(ctrl->psd[state].flags & NVME_PS_FLAGS_NON_OP_STATE))
continue;
exit_latency_us = (u64)le32_to_cpu(ctrl->psd[state].exit_lat);
if (exit_latency_us > ctrl->ps_max_latency_us)
continue;
total_latency_us = exit_latency_us +
le32_to_cpu(ctrl->psd[state].entry_lat);
/*
* This state is good. It can be used as the APST idle target
* for higher power states.
*/
if (apst_primary_timeout_ms && apst_primary_latency_tol_us) {
if (!nvme_apst_get_transition_time(total_latency_us,
&transition_ms, &last_lt_index))
continue;
} else {
transition_ms = total_latency_us + 19;
do_div(transition_ms, 20);
if (transition_ms > (1 << 24) - 1)
transition_ms = (1 << 24) - 1;
}
target = cpu_to_le64((state << 3) | (transition_ms << 8));
if (max_ps == -1)
max_ps = state;
if (total_latency_us > max_lat_us)
max_lat_us = total_latency_us;
}
if (max_ps == -1)
dev_dbg(ctrl->device, "APST enabled but no non-operational states are available\n");
else
dev_dbg(ctrl->device, "APST enabled: max PS = %d, max round-trip latency = %lluus, table = %*phN\n",
max_ps, max_lat_us, (int)sizeof(*table), table);
apste = 1;
done:
ret = nvme_set_features(ctrl, NVME_FEAT_AUTO_PST, apste,
table, sizeof(*table), NULL);
if (ret)
dev_err(ctrl->device, "failed to set APST feature (%d)\n", ret);
kfree(table);
return ret;
}