xref: /freebsd/sbin/nvmecontrol/power.c (revision 52467047aa7dd6d21f6303b28e4278cab2d8fe48)
1038659e7SWarner Losh /*-
2*52467047SWarner Losh  * Copyright (c) 2016 Netflix, Inc.
3038659e7SWarner Losh  *
4038659e7SWarner Losh  * Redistribution and use in source and binary forms, with or without
5038659e7SWarner Losh  * modification, are permitted provided that the following conditions
6038659e7SWarner Losh  * are met:
7038659e7SWarner Losh  * 1. Redistributions of source code must retain the above copyright
8038659e7SWarner Losh  *    notice, this list of conditions and the following disclaimer.
9038659e7SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
10038659e7SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
11038659e7SWarner Losh  *    documentation and/or other materials provided with the distribution.
12038659e7SWarner Losh  *
13038659e7SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14038659e7SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15038659e7SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16038659e7SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17038659e7SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18038659e7SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19038659e7SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20038659e7SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21038659e7SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22038659e7SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23038659e7SWarner Losh  * SUCH DAMAGE.
24038659e7SWarner Losh  */
25038659e7SWarner Losh 
26038659e7SWarner Losh #include <sys/cdefs.h>
27038659e7SWarner Losh __FBSDID("$FreeBSD$");
28038659e7SWarner Losh 
29038659e7SWarner Losh #include <sys/param.h>
30038659e7SWarner Losh #include <sys/ioccom.h>
31038659e7SWarner Losh 
32038659e7SWarner Losh #include <ctype.h>
33038659e7SWarner Losh #include <err.h>
34038659e7SWarner Losh #include <fcntl.h>
35038659e7SWarner Losh #include <stddef.h>
36038659e7SWarner Losh #include <stdio.h>
37038659e7SWarner Losh #include <stdlib.h>
38038659e7SWarner Losh #include <string.h>
39038659e7SWarner Losh #include <unistd.h>
40038659e7SWarner Losh 
41038659e7SWarner Losh #include "nvmecontrol.h"
42038659e7SWarner Losh 
43038659e7SWarner Losh _Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY,
44038659e7SWarner Losh 	       "nvme_power_state size wrong");
45038659e7SWarner Losh 
46a13a291aSWarner Losh #define POWER_USAGE							       \
47d4fdb249SWarner Losh 	"power [-l] [-p new-state [-w workload-hint]] <controller id>\n"
48a13a291aSWarner Losh 
49038659e7SWarner Losh static void
50038659e7SWarner Losh power_list_one(int i, struct nvme_power_state *nps)
51038659e7SWarner Losh {
52038659e7SWarner Losh 	int mpower, apower, ipower;
530d787e9bSWojciech Macek 	uint8_t mps, nops, aps, apw;
540d787e9bSWojciech Macek 
550d787e9bSWojciech Macek 	mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) &
560d787e9bSWojciech Macek 		NVME_PWR_ST_MPS_MASK;
570d787e9bSWojciech Macek 	nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) &
580d787e9bSWojciech Macek 		NVME_PWR_ST_NOPS_MASK;
590d787e9bSWojciech Macek 	apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) &
600d787e9bSWojciech Macek 		NVME_PWR_ST_APW_MASK;
610d787e9bSWojciech Macek 	aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) &
620d787e9bSWojciech Macek 		NVME_PWR_ST_APS_MASK;
63038659e7SWarner Losh 
64038659e7SWarner Losh 	mpower = nps->mp;
650d787e9bSWojciech Macek 	if (mps == 0)
66038659e7SWarner Losh 		mpower *= 100;
67038659e7SWarner Losh 	ipower = nps->idlp;
68038659e7SWarner Losh 	if (nps->ips == 1)
69038659e7SWarner Losh 		ipower *= 100;
70038659e7SWarner Losh 	apower = nps->actp;
710d787e9bSWojciech Macek 	if (aps == 1)
72038659e7SWarner Losh 		apower *= 100;
73038659e7SWarner Losh 	printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n",
74038659e7SWarner Losh 	       i, mpower / 10000, mpower % 10000,
750d787e9bSWojciech Macek 	       nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000,
76038659e7SWarner Losh 	       nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl,
77038659e7SWarner Losh 	       nps->rwt, nps->rwl, ipower / 10000, ipower % 10000,
780d787e9bSWojciech Macek 	       apower / 10000, apower % 10000, apw);
79038659e7SWarner Losh }
80038659e7SWarner Losh 
81038659e7SWarner Losh static void
82038659e7SWarner Losh power_list(struct nvme_controller_data *cdata)
83038659e7SWarner Losh {
84038659e7SWarner Losh 	int i;
85038659e7SWarner Losh 
86038659e7SWarner Losh 	printf("\nPower States Supported: %d\n\n", cdata->npss + 1);
87038659e7SWarner Losh 	printf(" #   Max pwr  Enter Lat  Exit Lat RT RL WT WL Idle Pwr  Act Pwr Workloadd\n");
88038659e7SWarner Losh 	printf("--  --------  --------- --------- -- -- -- -- -------- -------- --\n");
89038659e7SWarner Losh 	for (i = 0; i <= cdata->npss; i++)
90038659e7SWarner Losh 		power_list_one(i, &cdata->power_state[i]);
91038659e7SWarner Losh }
92038659e7SWarner Losh 
93038659e7SWarner Losh static void
94b130d07fSDimitry Andric power_set(int fd, int power_val, int workload, int perm)
95038659e7SWarner Losh {
96038659e7SWarner Losh 	struct nvme_pt_command	pt;
97038659e7SWarner Losh 	uint32_t p;
98038659e7SWarner Losh 
99038659e7SWarner Losh 	p = perm ? (1u << 31) : 0;
100038659e7SWarner Losh 	memset(&pt, 0, sizeof(pt));
1019544e6dcSChuck Tuffli 	pt.cmd.opc = NVME_OPC_SET_FEATURES;
1020d787e9bSWojciech Macek 	pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT | p);
1030d787e9bSWojciech Macek 	pt.cmd.cdw11 = htole32(power_val | (workload << 5));
104038659e7SWarner Losh 
105038659e7SWarner Losh 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
106038659e7SWarner Losh 		err(1, "set feature power mgmt request failed");
107038659e7SWarner Losh 
108038659e7SWarner Losh 	if (nvme_completion_is_error(&pt.cpl))
109038659e7SWarner Losh 		errx(1, "set feature power mgmt request returned error");
110038659e7SWarner Losh }
111038659e7SWarner Losh 
112038659e7SWarner Losh static void
113038659e7SWarner Losh power_show(int fd)
114038659e7SWarner Losh {
115038659e7SWarner Losh 	struct nvme_pt_command	pt;
116038659e7SWarner Losh 
117038659e7SWarner Losh 	memset(&pt, 0, sizeof(pt));
1189544e6dcSChuck Tuffli 	pt.cmd.opc = NVME_OPC_GET_FEATURES;
1190d787e9bSWojciech Macek 	pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT);
120038659e7SWarner Losh 
121038659e7SWarner Losh 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
122038659e7SWarner Losh 		err(1, "set feature power mgmt request failed");
123038659e7SWarner Losh 
124038659e7SWarner Losh 	if (nvme_completion_is_error(&pt.cpl))
125038659e7SWarner Losh 		errx(1, "set feature power mgmt request returned error");
126038659e7SWarner Losh 
127038659e7SWarner Losh 	printf("Current Power Mode is %d\n", pt.cpl.cdw0);
128038659e7SWarner Losh }
129038659e7SWarner Losh 
130a13a291aSWarner Losh static void
1310d095c23SWarner Losh power(const struct nvme_function *nf, int argc, char *argv[])
132038659e7SWarner Losh {
133038659e7SWarner Losh 	struct nvme_controller_data	cdata;
134b130d07fSDimitry Andric 	int				ch, listflag = 0, powerflag = 0, power_val = 0, fd;
135038659e7SWarner Losh 	int				workload = 0;
136038659e7SWarner Losh 	char				*end;
137038659e7SWarner Losh 
138038659e7SWarner Losh 	while ((ch = getopt(argc, argv, "lp:w:")) != -1) {
139038659e7SWarner Losh 		switch ((char)ch) {
140038659e7SWarner Losh 		case 'l':
141038659e7SWarner Losh 			listflag = 1;
142038659e7SWarner Losh 			break;
143038659e7SWarner Losh 		case 'p':
144038659e7SWarner Losh 			powerflag = 1;
145b130d07fSDimitry Andric 			power_val = strtol(optarg, &end, 0);
146038659e7SWarner Losh 			if (*end != '\0') {
147038659e7SWarner Losh 				fprintf(stderr, "Invalid power state number: %s\n", optarg);
1487d923c13SWarner Losh 				usage(nf);
149038659e7SWarner Losh 			}
150038659e7SWarner Losh 			break;
151038659e7SWarner Losh 		case 'w':
152038659e7SWarner Losh 			workload = strtol(optarg, &end, 0);
153038659e7SWarner Losh 			if (*end != '\0') {
154038659e7SWarner Losh 				fprintf(stderr, "Invalid workload hint: %s\n", optarg);
1557d923c13SWarner Losh 				usage(nf);
156038659e7SWarner Losh 			}
157038659e7SWarner Losh 			break;
158038659e7SWarner Losh 		default:
1597d923c13SWarner Losh 			usage(nf);
160038659e7SWarner Losh 		}
161038659e7SWarner Losh 	}
162038659e7SWarner Losh 
163038659e7SWarner Losh 	/* Check that a controller was specified. */
164038659e7SWarner Losh 	if (optind >= argc)
1657d923c13SWarner Losh 		usage(nf);
166038659e7SWarner Losh 
167038659e7SWarner Losh 	if (listflag && powerflag) {
168038659e7SWarner Losh 		fprintf(stderr, "Can't set power and list power states\n");
1697d923c13SWarner Losh 		usage(nf);
170038659e7SWarner Losh 	}
171038659e7SWarner Losh 
172038659e7SWarner Losh 	open_dev(argv[optind], &fd, 1, 1);
173038659e7SWarner Losh 	read_controller_data(fd, &cdata);
174038659e7SWarner Losh 
175038659e7SWarner Losh 	if (listflag) {
176038659e7SWarner Losh 		power_list(&cdata);
177038659e7SWarner Losh 		goto out;
178038659e7SWarner Losh 	}
179038659e7SWarner Losh 
180038659e7SWarner Losh 	if (powerflag) {
181b130d07fSDimitry Andric 		power_set(fd, power_val, workload, 0);
182038659e7SWarner Losh 		goto out;
183038659e7SWarner Losh 	}
184038659e7SWarner Losh 	power_show(fd);
185038659e7SWarner Losh 
186038659e7SWarner Losh out:
187038659e7SWarner Losh 	close(fd);
188038659e7SWarner Losh 	exit(0);
189038659e7SWarner Losh }
190a13a291aSWarner Losh 
191a13a291aSWarner Losh NVME_COMMAND(top, power, power, POWER_USAGE);
192