Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108408723
D47381.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D47381.diff
View Options
diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8
--- a/sbin/nvmecontrol/nvmecontrol.8
+++ b/sbin/nvmecontrol/nvmecontrol.8
@@ -172,7 +172,10 @@
.Aq Ar device-id
.Nm
.Ic power
+.Op Fl a Ar apst_state
+.Op Fl d Ar apst_data
.Op Fl l
+.Op Fl m Ar apst_limit
.Op Fl p Ar power_state
.Op Fl w Ar workload_hint
.Nm
@@ -481,8 +484,24 @@
.Ss power
Manage the power modes of the NVMe controller.
.Bl -tag -width 6n
+.It Fl a Ar state
+Enable (1) or disable (0) Autonomous Power State Transition (APST).
+When enabled, the controller will autonomously switch power states
+according to configuration data.
+.It Fl d Ar target:idle[,target:idle ...]
+Set the APST data.
+Each target:idle pair corresponds to a specific available power
+state starting from the lowest, and defines the target state to
+transition to, as well as the idle time in milliseconds to wait
+before that transition.
+Setting both parameters to zero disables switching from that state.
.It Fl l
List all supported power modes.
+.It Fl m Ar limit
+Configure APST to use all available non-operational power states
+with total latency below the specified
+.Ar limit
+(in milliseconds).
.It Fl p Ar mode
Set the power mode to
.Ar mode .
diff --git a/sbin/nvmecontrol/power.c b/sbin/nvmecontrol/power.c
--- a/sbin/nvmecontrol/power.c
+++ b/sbin/nvmecontrol/power.c
@@ -27,6 +27,7 @@
#include <sys/param.h>
#include <sys/ioccom.h>
+#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
@@ -43,17 +44,23 @@
_Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY,
"nvme_power_state size wrong");
-#define POWER_NONE 0xffffffffu
+#define NONE 0xffffffffu
static struct options {
bool list;
+ uint32_t apst;
+ uint32_t apst_limit;
uint32_t power;
uint32_t workload;
+ const char *apst_data;
const char *dev;
} opt = {
.list = false,
- .power = POWER_NONE,
+ .apst = NONE,
+ .apst_limit = NONE,
+ .power = NONE,
.workload = 0,
+ .apst_data = NULL,
.dev = NULL,
};
@@ -120,10 +127,131 @@
errx(EX_IOERR, "set feature power mgmt request returned error");
}
+enum feat_opc { GET, SET };
+
+static int
+power_apst_cmd(int fd, enum feat_opc opc, bool enable, uint64_t *data,
+ int size)
+{
+ struct nvme_pt_command pt;
+
+ memset(&pt, 0, sizeof(pt));
+ pt.cmd.opc = (opc == SET) ?
+ NVME_OPC_SET_FEATURES : NVME_OPC_GET_FEATURES;
+ pt.cmd.cdw10 = htole32(NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION);
+ pt.cmd.cdw11 = htole32(enable);
+ pt.buf = data;
+ pt.len = size;
+ pt.is_read = (opc == GET) ? 1 : 0;
+
+ if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) == -1)
+ err(EX_IOERR, "APST %s command failed",
+ (opc == SET) ? "set" : "get");
+
+ if (nvme_completion_is_error(&pt.cpl))
+ errx(EX_IOERR, "APST %s command returned error",
+ (opc == SET) ? "set" : "get");
+
+ return (pt.cpl.cdw0);
+}
+
static void
-power_show(int fd)
+power_apst_data_generate(struct nvme_controller_data *cdata,
+ uint64_t *data, int num, int limit)
+{
+ int i, itpt, latency;
+
+ assert(cdata->npss < num);
+
+ for (i = cdata->npss; i > 0; --i) {
+ if (!NVMEV(NVME_PWR_ST_NOPS,
+ cdata->power_state[i].mps_nops)) {
+ data[i - 1] = data[i];
+ continue;
+ }
+
+ latency = (cdata->power_state[i].enlat +
+ cdata->power_state[i].exlat) / 1000;
+ if (latency > limit)
+ continue;
+
+ /* Wait 50x the latency before each transition. */
+ itpt = MIN(latency * 50, (1 << 24) - 1);
+ data[i - 1] = htole64(itpt << 8 | i << 3);
+ }
+}
+
+static void
+power_apst_data_parse(uint64_t *data, int num, const char *dstr)
+{
+ int i, itps, itpt;
+ char *str, *token;
+
+ str = strdup(dstr);
+
+ for (i = 0; (token = strsep(&str, " ,")) != NULL && i < num; ++i) {
+ if (sscanf(token, "%i:%i", &itps, &itpt) != 2)
+ errx(EX_USAGE, "cannot parse provided configuration");
+
+ if (itps < 0 || itps >= 1 << 5)
+ errx(EX_USAGE, "invalid ITPS=%d (must be 0..%d)",
+ itps, 1 << 5);
+ if (itpt < 0 || itpt >= 1 << 24)
+ errx(EX_USAGE, "invalid ITPT=%d (must be 0..%d)",
+ itpt, 1 << 24);
+
+ data[i] = htole64(itpt << 8 | itps << 3);
+ }
+}
+
+static void
+power_apst_show(uint64_t *data, int num, bool enabled)
+{
+ int entry, i;
+
+ while (num > 0 && data[num - 1] == 0)
+ --num;
+
+ printf("APST %s\n", enabled ? "enabled" : "disabled");
+ printf("\n # ITPS ITPT Hex\n");
+ printf("-- ---- ------ --------\n");
+ for (i = 0; i < num || i == 0; ++i) {
+ entry = letoh(data[i]);
+ printf("%2d: %4d %4dms %#8x\n",
+ i, (entry & 0xF8) >> 3, entry >> 8, entry);
+ }
+}
+
+static void
+power_apst(int fd, struct nvme_controller_data *cdata,
+ uint32_t enable, const char *dstr, uint32_t limit)
+{
+ uint64_t data[32];
+
+ if (cdata->apsta == 0)
+ errx(EX_UNAVAILABLE, "Not supported by the controller");
+
+ if (enable == NONE)
+ enable = power_apst_cmd(fd, GET, 0, NULL, 0);
+
+ memset(&data, 0, sizeof(data));
+
+ if (dstr != NULL)
+ power_apst_data_parse(data, nitems(data), dstr);
+ else if (limit != NONE)
+ power_apst_data_generate(cdata, data, nitems(data), limit);
+ else
+ power_apst_cmd(fd, GET, 0, data, sizeof(data));
+
+ power_apst_cmd(fd, SET, enable, data, sizeof(data));
+}
+
+static void
+power_show(int fd, struct nvme_controller_data *cdata)
{
struct nvme_pt_command pt;
+ uint64_t data[32];
+ int status;
memset(&pt, 0, sizeof(pt));
pt.cmd.opc = NVME_OPC_GET_FEATURES;
@@ -137,6 +265,11 @@
printf("Current Power State is %d\n", pt.cpl.cdw0 & 0x1F);
printf("Current Workload Hint is %d\n", pt.cpl.cdw0 >> 5);
+
+ if (cdata->apsta != 0) {
+ status = power_apst_cmd(fd, GET, 0, data, sizeof(data));
+ power_apst_show(data, nitems(data), status);
+ }
}
static void
@@ -150,7 +283,7 @@
if (arg_parse(argc, argv, f))
return;
- if (opt.list && opt.power != POWER_NONE) {
+ if (opt.list && opt.power != NONE) {
fprintf(stderr, "Can't set power and list power states\n");
arg_help(argc, argv, f);
}
@@ -163,18 +296,27 @@
}
free(path);
+ if (opt.power != NONE) {
+ power_set(fd, opt.power, opt.workload, 0);
+ goto out;
+ }
+
+ if (read_controller_data(fd, &cdata))
+ errx(EX_IOERR, "Identify request failed");
+
if (opt.list) {
- if (read_controller_data(fd, &cdata))
- errx(EX_IOERR, "Identify request failed");
power_list(&cdata);
goto out;
}
- if (opt.power != POWER_NONE) {
- power_set(fd, opt.power, opt.workload, 0);
+ if (opt.apst != NONE || opt.apst_limit != NONE ||
+ opt.apst_data != NULL) {
+ power_apst(fd, &cdata, opt.apst,
+ opt.apst_data, opt.apst_limit);
goto out;
}
- power_show(fd);
+
+ power_show(fd, &cdata);
out:
close(fd);
@@ -183,8 +325,14 @@
static const struct opts power_opts[] = {
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
+ OPT("apst", 'a', arg_uint32, opt, apst,
+ "Enable or disable APST"),
+ OPT("data", 'd', arg_string, opt, apst_data,
+ "Set the APST configuration"),
OPT("list", 'l', arg_none, opt, list,
"List the valid power states"),
+ OPT("limit", 'm', arg_uint32, opt, apst_limit,
+ "Set the APST latency limit"),
OPT("power", 'p', arg_uint32, opt, power,
"Set the power state"),
OPT("workload", 'w', arg_uint32, opt, workload,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jan 25, 1:27 PM (17 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16145154
Default Alt Text
D47381.diff (7 KB)
Attached To
Mode
D47381: nvmecontrol: Add APST feature support
Attached
Detach File
Event Timeline
Log In to Comment