1 /*- 2 * Copyright (c) 2016 Netflix, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/ioccom.h> 28 29 #include <ctype.h> 30 #include <err.h> 31 #include <fcntl.h> 32 #include <stdbool.h> 33 #include <stddef.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <sysexits.h> 38 #include <unistd.h> 39 40 #include "nvmecontrol.h" 41 42 _Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY, 43 "nvme_power_state size wrong"); 44 45 #define POWER_NONE 0xffffffffu 46 47 static struct options { 48 bool list; 49 uint32_t power; 50 uint32_t workload; 51 const char *dev; 52 } opt = { 53 .list = false, 54 .power = POWER_NONE, 55 .workload = 0, 56 .dev = NULL, 57 }; 58 59 static void 60 power_list_one(int i, struct nvme_power_state *nps) 61 { 62 int mpower, apower, ipower; 63 uint8_t mps, nops, aps, apw; 64 65 mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) & 66 NVME_PWR_ST_MPS_MASK; 67 nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) & 68 NVME_PWR_ST_NOPS_MASK; 69 apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) & 70 NVME_PWR_ST_APW_MASK; 71 aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) & 72 NVME_PWR_ST_APS_MASK; 73 74 mpower = nps->mp; 75 if (mps == 0) 76 mpower *= 100; 77 ipower = nps->idlp; 78 if (nps->ips == 1) 79 ipower *= 100; 80 apower = nps->actp; 81 if (aps == 1) 82 apower *= 100; 83 printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n", 84 i, mpower / 10000, mpower % 10000, 85 nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000, 86 nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl, 87 nps->rwt, nps->rwl, ipower / 10000, ipower % 10000, 88 apower / 10000, apower % 10000, apw); 89 } 90 91 static void 92 power_list(struct nvme_controller_data *cdata) 93 { 94 int i; 95 96 printf("\nPower States Supported: %d\n\n", cdata->npss + 1); 97 printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workloadd\n"); 98 printf("-- -------- --------- --------- -- -- -- -- -------- -------- --\n"); 99 for (i = 0; i <= cdata->npss; i++) 100 power_list_one(i, &cdata->power_state[i]); 101 } 102 103 static void 104 power_set(int fd, int power_val, int workload, int perm) 105 { 106 struct nvme_pt_command pt; 107 uint32_t p; 108 109 p = perm ? (1u << 31) : 0; 110 memset(&pt, 0, sizeof(pt)); 111 pt.cmd.opc = NVME_OPC_SET_FEATURES; 112 pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT | p); 113 pt.cmd.cdw11 = htole32(power_val | (workload << 5)); 114 115 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 116 err(EX_IOERR, "set feature power mgmt request failed"); 117 118 if (nvme_completion_is_error(&pt.cpl)) 119 errx(EX_IOERR, "set feature power mgmt request returned error"); 120 } 121 122 static void 123 power_show(int fd) 124 { 125 struct nvme_pt_command pt; 126 127 memset(&pt, 0, sizeof(pt)); 128 pt.cmd.opc = NVME_OPC_GET_FEATURES; 129 pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT); 130 131 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 132 err(EX_IOERR, "set feature power mgmt request failed"); 133 134 if (nvme_completion_is_error(&pt.cpl)) 135 errx(EX_IOERR, "set feature power mgmt request returned error"); 136 137 printf("Current Power State is %d\n", pt.cpl.cdw0 & 0x1F); 138 printf("Current Workload Hint is %d\n", pt.cpl.cdw0 >> 5); 139 } 140 141 static void 142 power(const struct cmd *f, int argc, char *argv[]) 143 { 144 struct nvme_controller_data cdata; 145 int fd; 146 char *path; 147 uint32_t nsid; 148 149 if (arg_parse(argc, argv, f)) 150 return; 151 152 if (opt.list && opt.power != POWER_NONE) { 153 fprintf(stderr, "Can't set power and list power states\n"); 154 arg_help(argc, argv, f); 155 } 156 157 open_dev(opt.dev, &fd, 1, 1); 158 get_nsid(fd, &path, &nsid); 159 if (nsid != 0) { 160 close(fd); 161 open_dev(path, &fd, 1, 1); 162 } 163 free(path); 164 165 if (opt.list) { 166 if (read_controller_data(fd, &cdata)) 167 errx(EX_IOERR, "Identify request failed"); 168 power_list(&cdata); 169 goto out; 170 } 171 172 if (opt.power != POWER_NONE) { 173 power_set(fd, opt.power, opt.workload, 0); 174 goto out; 175 } 176 power_show(fd); 177 178 out: 179 close(fd); 180 exit(0); 181 } 182 183 static const struct opts power_opts[] = { 184 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 185 OPT("list", 'l', arg_none, opt, list, 186 "List the valid power states"), 187 OPT("power", 'p', arg_uint32, opt, power, 188 "Set the power state"), 189 OPT("workload", 'w', arg_uint32, opt, workload, 190 "Set the workload hint"), 191 { NULL, 0, arg_none, NULL, NULL } 192 }; 193 #undef OPT 194 195 static const struct args power_args[] = { 196 { arg_string, &opt.dev, "controller-id|namespace-id" }, 197 { arg_none, NULL, NULL }, 198 }; 199 200 static struct cmd power_cmd = { 201 .name = "power", 202 .fn = power, 203 .descr = "Manage power states for the drive", 204 .ctx_size = sizeof(opt), 205 .opts = power_opts, 206 .args = power_args, 207 }; 208 209 CMD_COMMAND(power_cmd); 210