1/*-
2 * Copyright (c) 2016 Netflix, Inc
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: releng/11.0/sbin/nvmecontrol/power.c 296672 2016-03-11 17:25:18Z dim $");
29
30#include <sys/param.h>
31#include <sys/ioccom.h>
32
33#include <ctype.h>
34#include <err.h>
35#include <fcntl.h>
36#include <stddef.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include "nvmecontrol.h"
43
44_Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY,
45	       "nvme_power_state size wrong");
46
47static void
48power_usage(void)
49{
50	fprintf(stderr, "usage:\n");
51	fprintf(stderr, POWER_USAGE);
52	exit(1);
53}
54
55static void
56power_list_one(int i, struct nvme_power_state *nps)
57{
58	int mpower, apower, ipower;
59
60	mpower = nps->mp;
61	if (nps->mps == 0)
62		mpower *= 100;
63	ipower = nps->idlp;
64	if (nps->ips == 1)
65		ipower *= 100;
66	apower = nps->actp;
67	if (nps->aps == 1)
68		apower *= 100;
69	printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n",
70	       i, mpower / 10000, mpower % 10000,
71	       nps->nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000,
72	       nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl,
73	       nps->rwt, nps->rwl, ipower / 10000, ipower % 10000,
74	       apower / 10000, apower % 10000, nps->apw);
75}
76
77static void
78power_list(struct nvme_controller_data *cdata)
79{
80	int i;
81
82	printf("\nPower States Supported: %d\n\n", cdata->npss + 1);
83	printf(" #   Max pwr  Enter Lat  Exit Lat RT RL WT WL Idle Pwr  Act Pwr Workloadd\n");
84	printf("--  --------  --------- --------- -- -- -- -- -------- -------- --\n");
85	for (i = 0; i <= cdata->npss; i++)
86		power_list_one(i, &cdata->power_state[i]);
87}
88
89static void
90power_set(int fd, int power_val, int workload, int perm)
91{
92	struct nvme_pt_command	pt;
93	uint32_t p;
94
95	p = perm ? (1u << 31) : 0;
96	memset(&pt, 0, sizeof(pt));
97	pt.cmd.opc = NVME_OPC_SET_FEATURES;
98	pt.cmd.cdw10 = NVME_FEAT_POWER_MANAGEMENT | p;
99	pt.cmd.cdw11 = power_val | (workload << 5);
100
101	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
102		err(1, "set feature power mgmt request failed");
103
104	if (nvme_completion_is_error(&pt.cpl))
105		errx(1, "set feature power mgmt request returned error");
106}
107
108static void
109power_show(int fd)
110{
111	struct nvme_pt_command	pt;
112
113	memset(&pt, 0, sizeof(pt));
114	pt.cmd.opc = NVME_OPC_GET_FEATURES;
115	pt.cmd.cdw10 = NVME_FEAT_POWER_MANAGEMENT;
116
117	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
118		err(1, "set feature power mgmt request failed");
119
120	if (nvme_completion_is_error(&pt.cpl))
121		errx(1, "set feature power mgmt request returned error");
122
123	printf("Current Power Mode is %d\n", pt.cpl.cdw0);
124}
125
126void
127power(int argc, char *argv[])
128{
129	struct nvme_controller_data	cdata;
130	int				ch, listflag = 0, powerflag = 0, power_val = 0, fd;
131	int				workload = 0;
132	char				*end;
133
134	while ((ch = getopt(argc, argv, "lp:w:")) != -1) {
135		switch ((char)ch) {
136		case 'l':
137			listflag = 1;
138			break;
139		case 'p':
140			powerflag = 1;
141			power_val = strtol(optarg, &end, 0);
142			if (*end != '\0') {
143				fprintf(stderr, "Invalid power state number: %s\n", optarg);
144				power_usage();
145			}
146			break;
147		case 'w':
148			workload = strtol(optarg, &end, 0);
149			if (*end != '\0') {
150				fprintf(stderr, "Invalid workload hint: %s\n", optarg);
151				power_usage();
152			}
153			break;
154		default:
155			power_usage();
156		}
157	}
158
159	/* Check that a controller was specified. */
160	if (optind >= argc)
161		power_usage();
162
163	if (listflag && powerflag) {
164		fprintf(stderr, "Can't set power and list power states\n");
165		power_usage();
166	}
167
168	open_dev(argv[optind], &fd, 1, 1);
169	read_controller_data(fd, &cdata);
170
171	if (listflag) {
172		power_list(&cdata);
173		goto out;
174	}
175
176	if (powerflag) {
177		power_set(fd, power_val, workload, 0);
178		goto out;
179	}
180	power_show(fd);
181
182out:
183	close(fd);
184	exit(0);
185}
186