From d636ad2ee8fad6fa0926db2ce7f3217f9ea4f023 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 30 Jan 2016 06:18:37 +0000 Subject: [PATCH] Implement power command to list all power modes, find out the power mode we're in and to set the power mode. --- sbin/nvmecontrol/Makefile | 2 +- sbin/nvmecontrol/nvmecontrol.8 | 17 +++ sbin/nvmecontrol/nvmecontrol.c | 1 + sbin/nvmecontrol/nvmecontrol.h | 4 + sbin/nvmecontrol/power.c | 185 +++++++++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 sbin/nvmecontrol/power.c diff --git a/sbin/nvmecontrol/Makefile b/sbin/nvmecontrol/Makefile index ea60da3c2941..e8a23719a2a7 100644 --- a/sbin/nvmecontrol/Makefile +++ b/sbin/nvmecontrol/Makefile @@ -2,7 +2,7 @@ PROG= nvmecontrol SRCS= nvmecontrol.c devlist.c firmware.c identify.c logpage.c \ - perftest.c reset.c nvme_util.c + perftest.c reset.c nvme_util.c power.c MAN= nvmecontrol.8 .PATH: ${.CURDIR}/../../sys/dev/nvme diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8 index 3b4b5c23aef3..ae8132ec5cbc 100644 --- a/sbin/nvmecontrol/nvmecontrol.8 +++ b/sbin/nvmecontrol/nvmecontrol.8 @@ -70,6 +70,11 @@ .Op Fl f Ar path_to_firmware .Op Fl a .Aq device id +.Nm +.Ic power +.Op Fl l +.Op Fl p power_state +.Op fl w workload_hint .Sh DESCRIPTION NVM Express (NVMe) is a storage protocol standard, for SSDs and other high-speed storage devices over PCI Express. @@ -120,6 +125,18 @@ Activate the firmware in slot 4 of the nvme0 controller on the next reset. .Pp Download the firmware image contained in "/tmp/nvme_firmware" to slot 7 of the nvme0 controller and activate it on the next reset. +.Pp +.Dl nvmecontrol power -l nvme0 +.Pp +List all the current power modes. +.Pp +.Dl nvmecontrol power -p 3 nvme0 +.Pp +Set the current power mode. +.Pp +.Dl nvmecontrol power nvme0 +.Pp +Get the current power mode. .Sh AUTHORS .An -nosplit .Nm diff --git a/sbin/nvmecontrol/nvmecontrol.c b/sbin/nvmecontrol/nvmecontrol.c index 4dee1909280a..cd7c19d0165d 100644 --- a/sbin/nvmecontrol/nvmecontrol.c +++ b/sbin/nvmecontrol/nvmecontrol.c @@ -58,6 +58,7 @@ static struct nvme_function { {"reset", reset, RESET_USAGE}, {"logpage", logpage, LOGPAGE_USAGE}, {"firmware", firmware, FIRMWARE_USAGE}, + {"power", power, POWER_USAGE}, {NULL, NULL, NULL}, }; diff --git a/sbin/nvmecontrol/nvmecontrol.h b/sbin/nvmecontrol/nvmecontrol.h index 8401dd7ccda3..b3cecd26dcea 100644 --- a/sbin/nvmecontrol/nvmecontrol.h +++ b/sbin/nvmecontrol/nvmecontrol.h @@ -55,12 +55,16 @@ #define FIRMWARE_USAGE \ " nvmecontrol firmware [-s slot] [-f path_to_firmware] [-a] \n" +#define POWER_USAGE \ +" nvmecontrol power [-l] [-p new-state [-w workload-hint]] \n" + void devlist(int argc, char *argv[]); void identify(int argc, char *argv[]); void perftest(int argc, char *argv[]); void reset(int argc, char *argv[]); void logpage(int argc, char *argv[]); void firmware(int argc, char *argv[]); +void power(int argc, char *argv[]); int open_dev(const char *str, int *fd, int show_error, int exit_on_error); void parse_ns_str(const char *ns_str, char *ctrlr_str, int *nsid); diff --git a/sbin/nvmecontrol/power.c b/sbin/nvmecontrol/power.c new file mode 100644 index 000000000000..e681ccd14dcc --- /dev/null +++ b/sbin/nvmecontrol/power.c @@ -0,0 +1,185 @@ +/*- + * Copyright (c) 2016 Netflix, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +_Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY, + "nvme_power_state size wrong"); + +static void +power_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, POWER_USAGE); + exit(1); +} + +static void +power_list_one(int i, struct nvme_power_state *nps) +{ + int mpower, apower, ipower; + + mpower = nps->mp; + if (nps->mps == 0) + mpower *= 100; + ipower = nps->idlp; + if (nps->ips == 1) + ipower *= 100; + apower = nps->actp; + if (nps->aps == 1) + apower *= 100; + printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n", + i, mpower / 10000, mpower % 10000, + nps->nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000, + nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl, + nps->rwt, nps->rwl, ipower / 10000, ipower % 10000, + apower / 10000, apower % 10000, nps->apw); +} + +static void +power_list(struct nvme_controller_data *cdata) +{ + int i; + + printf("\nPower States Supported: %d\n\n", cdata->npss + 1); + printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workloadd\n"); + printf("-- -------- --------- --------- -- -- -- -- -------- -------- --\n"); + for (i = 0; i <= cdata->npss; i++) + power_list_one(i, &cdata->power_state[i]); +} + +static void +power_set(int fd, int power, int workload, int perm) +{ + struct nvme_pt_command pt; + uint32_t p; + + p = perm ? (1u << 31) : 0; + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_SET_FEATURES; + pt.cmd.cdw10 = NVME_FEAT_POWER_MANAGEMENT | p; + pt.cmd.cdw11 = power | (workload << 5); + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(1, "set feature power mgmt request failed"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "set feature power mgmt request returned error"); +} + +static void +power_show(int fd) +{ + struct nvme_pt_command pt; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_GET_FEATURES; + pt.cmd.cdw10 = NVME_FEAT_POWER_MANAGEMENT; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(1, "set feature power mgmt request failed"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "set feature power mgmt request returned error"); + + printf("Current Power Mode is %d\n", pt.cpl.cdw0); +} + +void +power(int argc, char *argv[]) +{ + struct nvme_controller_data cdata; + int ch, listflag = 0, powerflag = 0, power = 0, fd; + int workload = 0; + char *end; + + while ((ch = getopt(argc, argv, "lp:w:")) != -1) { + switch ((char)ch) { + case 'l': + listflag = 1; + break; + case 'p': + powerflag = 1; + power = strtol(optarg, &end, 0); + if (*end != '\0') { + fprintf(stderr, "Invalid power state number: %s\n", optarg); + power_usage(); + } + break; + case 'w': + workload = strtol(optarg, &end, 0); + if (*end != '\0') { + fprintf(stderr, "Invalid workload hint: %s\n", optarg); + power_usage(); + } + break; + default: + power_usage(); + } + } + + /* Check that a controller was specified. */ + if (optind >= argc) + power_usage(); + + if (listflag && powerflag) { + fprintf(stderr, "Can't set power and list power states\n"); + power_usage(); + } + + open_dev(argv[optind], &fd, 1, 1); + read_controller_data(fd, &cdata); + + if (listflag) { + power_list(&cdata); + goto out; + } + + if (powerflag) { + power_set(fd, power, workload, 0); + goto out; + } + power_show(fd); + +out: + close(fd); + exit(0); +}