xref: /freebsd/sys/dev/nvme/nvme_sysctl.c (revision 56e53cb8ef000c3ef72337a4095987a932cdedef)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2012-2016 Intel Corporation
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "opt_nvme.h"
33 
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/sysctl.h>
37 
38 #include "nvme_private.h"
39 
40 #ifndef NVME_USE_NVD
41 #define NVME_USE_NVD 1
42 #endif
43 
44 int nvme_use_nvd = NVME_USE_NVD;
45 
46 SYSCTL_NODE(_hw, OID_AUTO, nvme, CTLFLAG_RD, 0, "NVMe sysctl tunables");
47 SYSCTL_INT(_hw_nvme, OID_AUTO, use_nvd, CTLFLAG_RDTUN,
48     &nvme_use_nvd, 1, "1 = Create NVD devices, 0 = Create NDA devices");
49 
50 /*
51  * CTLTYPE_S64 and sysctl_handle_64 were added in r217616.  Define these
52  *  explicitly here for older kernels that don't include the r217616
53  *  changeset.
54  */
55 #ifndef CTLTYPE_S64
56 #define CTLTYPE_S64		CTLTYPE_QUAD
57 #define sysctl_handle_64	sysctl_handle_quad
58 #endif
59 
60 static void
61 nvme_dump_queue(struct nvme_qpair *qpair)
62 {
63 	struct nvme_completion *cpl;
64 	struct nvme_command *cmd;
65 	int i;
66 
67 	printf("id:%04Xh phase:%d\n", qpair->id, qpair->phase);
68 
69 	printf("Completion queue:\n");
70 	for (i = 0; i < qpair->num_entries; i++) {
71 		cpl = &qpair->cpl[i];
72 		printf("%05d: ", i);
73 		nvme_dump_completion(cpl);
74 	}
75 
76 	printf("Submission queue:\n");
77 	for (i = 0; i < qpair->num_entries; i++) {
78 		cmd = &qpair->cmd[i];
79 		printf("%05d: ", i);
80 		nvme_dump_command(cmd);
81 	}
82 }
83 
84 
85 static int
86 nvme_sysctl_dump_debug(SYSCTL_HANDLER_ARGS)
87 {
88 	struct nvme_qpair 	*qpair = arg1;
89 	uint32_t		val = 0;
90 
91 	int error = sysctl_handle_int(oidp, &val, 0, req);
92 
93 	if (error)
94 		return (error);
95 
96 	if (val != 0)
97 		nvme_dump_queue(qpair);
98 
99 	return (0);
100 }
101 
102 static int
103 nvme_sysctl_int_coal_time(SYSCTL_HANDLER_ARGS)
104 {
105 	struct nvme_controller *ctrlr = arg1;
106 	uint32_t oldval = ctrlr->int_coal_time;
107 	int error = sysctl_handle_int(oidp, &ctrlr->int_coal_time, 0,
108 	    req);
109 
110 	if (error)
111 		return (error);
112 
113 	if (oldval != ctrlr->int_coal_time)
114 		nvme_ctrlr_cmd_set_interrupt_coalescing(ctrlr,
115 		    ctrlr->int_coal_time, ctrlr->int_coal_threshold, NULL,
116 		    NULL);
117 
118 	return (0);
119 }
120 
121 static int
122 nvme_sysctl_int_coal_threshold(SYSCTL_HANDLER_ARGS)
123 {
124 	struct nvme_controller *ctrlr = arg1;
125 	uint32_t oldval = ctrlr->int_coal_threshold;
126 	int error = sysctl_handle_int(oidp, &ctrlr->int_coal_threshold, 0,
127 	    req);
128 
129 	if (error)
130 		return (error);
131 
132 	if (oldval != ctrlr->int_coal_threshold)
133 		nvme_ctrlr_cmd_set_interrupt_coalescing(ctrlr,
134 		    ctrlr->int_coal_time, ctrlr->int_coal_threshold, NULL,
135 		    NULL);
136 
137 	return (0);
138 }
139 
140 static int
141 nvme_sysctl_timeout_period(SYSCTL_HANDLER_ARGS)
142 {
143 	struct nvme_controller *ctrlr = arg1;
144 	uint32_t oldval = ctrlr->timeout_period;
145 	int error = sysctl_handle_int(oidp, &ctrlr->timeout_period, 0, req);
146 
147 	if (error)
148 		return (error);
149 
150 	if (ctrlr->timeout_period > NVME_MAX_TIMEOUT_PERIOD ||
151 	    ctrlr->timeout_period < NVME_MIN_TIMEOUT_PERIOD) {
152 		ctrlr->timeout_period = oldval;
153 		return (EINVAL);
154 	}
155 
156 	return (0);
157 }
158 
159 static void
160 nvme_qpair_reset_stats(struct nvme_qpair *qpair)
161 {
162 
163 	qpair->num_cmds = 0;
164 	qpair->num_intr_handler_calls = 0;
165 }
166 
167 static int
168 nvme_sysctl_num_cmds(SYSCTL_HANDLER_ARGS)
169 {
170 	struct nvme_controller 	*ctrlr = arg1;
171 	int64_t			num_cmds = 0;
172 	int			i;
173 
174 	num_cmds = ctrlr->adminq.num_cmds;
175 
176 	for (i = 0; i < ctrlr->num_io_queues; i++)
177 		num_cmds += ctrlr->ioq[i].num_cmds;
178 
179 	return (sysctl_handle_64(oidp, &num_cmds, 0, req));
180 }
181 
182 static int
183 nvme_sysctl_num_intr_handler_calls(SYSCTL_HANDLER_ARGS)
184 {
185 	struct nvme_controller 	*ctrlr = arg1;
186 	int64_t			num_intr_handler_calls = 0;
187 	int			i;
188 
189 	num_intr_handler_calls = ctrlr->adminq.num_intr_handler_calls;
190 
191 	for (i = 0; i < ctrlr->num_io_queues; i++)
192 		num_intr_handler_calls += ctrlr->ioq[i].num_intr_handler_calls;
193 
194 	return (sysctl_handle_64(oidp, &num_intr_handler_calls, 0, req));
195 }
196 
197 static int
198 nvme_sysctl_reset_stats(SYSCTL_HANDLER_ARGS)
199 {
200 	struct nvme_controller 	*ctrlr = arg1;
201 	uint32_t		i, val = 0;
202 
203 	int error = sysctl_handle_int(oidp, &val, 0, req);
204 
205 	if (error)
206 		return (error);
207 
208 	if (val != 0) {
209 		nvme_qpair_reset_stats(&ctrlr->adminq);
210 
211 		for (i = 0; i < ctrlr->num_io_queues; i++)
212 			nvme_qpair_reset_stats(&ctrlr->ioq[i]);
213 	}
214 
215 	return (0);
216 }
217 
218 
219 static void
220 nvme_sysctl_initialize_queue(struct nvme_qpair *qpair,
221     struct sysctl_ctx_list *ctrlr_ctx, struct sysctl_oid *que_tree)
222 {
223 	struct sysctl_oid_list	*que_list = SYSCTL_CHILDREN(que_tree);
224 
225 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "num_entries",
226 	    CTLFLAG_RD, &qpair->num_entries, 0,
227 	    "Number of entries in hardware queue");
228 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "num_trackers",
229 	    CTLFLAG_RD, &qpair->num_trackers, 0,
230 	    "Number of trackers pre-allocated for this queue pair");
231 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "sq_head",
232 	    CTLFLAG_RD, &qpair->sq_head, 0,
233 	    "Current head of submission queue (as observed by driver)");
234 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "sq_tail",
235 	    CTLFLAG_RD, &qpair->sq_tail, 0,
236 	    "Current tail of submission queue (as observed by driver)");
237 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "cq_head",
238 	    CTLFLAG_RD, &qpair->cq_head, 0,
239 	    "Current head of completion queue (as observed by driver)");
240 
241 	SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_cmds",
242 	    CTLFLAG_RD, &qpair->num_cmds, "Number of commands submitted");
243 	SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_intr_handler_calls",
244 	    CTLFLAG_RD, &qpair->num_intr_handler_calls,
245 	    "Number of times interrupt handler was invoked (will typically be "
246 	    "less than number of actual interrupts generated due to "
247 	    "coalescing)");
248 
249 	SYSCTL_ADD_PROC(ctrlr_ctx, que_list, OID_AUTO,
250 	    "dump_debug", CTLTYPE_UINT | CTLFLAG_RW, qpair, 0,
251 	    nvme_sysctl_dump_debug, "IU", "Dump debug data");
252 }
253 
254 void
255 nvme_sysctl_initialize_ctrlr(struct nvme_controller *ctrlr)
256 {
257 	struct sysctl_ctx_list	*ctrlr_ctx;
258 	struct sysctl_oid	*ctrlr_tree, *que_tree;
259 	struct sysctl_oid_list	*ctrlr_list;
260 #define QUEUE_NAME_LENGTH	16
261 	char			queue_name[QUEUE_NAME_LENGTH];
262 	int			i;
263 
264 	ctrlr_ctx = device_get_sysctl_ctx(ctrlr->dev);
265 	ctrlr_tree = device_get_sysctl_tree(ctrlr->dev);
266 	ctrlr_list = SYSCTL_CHILDREN(ctrlr_tree);
267 
268 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_cpus_per_ioq",
269 	    CTLFLAG_RD, &ctrlr->num_cpus_per_ioq, 0,
270 	    "Number of CPUs assigned per I/O queue pair");
271 
272 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
273 	    "int_coal_time", CTLTYPE_UINT | CTLFLAG_RW, ctrlr, 0,
274 	    nvme_sysctl_int_coal_time, "IU",
275 	    "Interrupt coalescing timeout (in microseconds)");
276 
277 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
278 	    "int_coal_threshold", CTLTYPE_UINT | CTLFLAG_RW, ctrlr, 0,
279 	    nvme_sysctl_int_coal_threshold, "IU",
280 	    "Interrupt coalescing threshold");
281 
282 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
283 	    "timeout_period", CTLTYPE_UINT | CTLFLAG_RW, ctrlr, 0,
284 	    nvme_sysctl_timeout_period, "IU",
285 	    "Timeout period (in seconds)");
286 
287 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
288 	    "num_cmds", CTLTYPE_S64 | CTLFLAG_RD,
289 	    ctrlr, 0, nvme_sysctl_num_cmds, "IU",
290 	    "Number of commands submitted");
291 
292 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
293 	    "num_intr_handler_calls", CTLTYPE_S64 | CTLFLAG_RD,
294 	    ctrlr, 0, nvme_sysctl_num_intr_handler_calls, "IU",
295 	    "Number of times interrupt handler was invoked (will "
296 	    "typically be less than number of actual interrupts "
297 	    "generated due to coalescing)");
298 
299 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
300 	    "reset_stats", CTLTYPE_UINT | CTLFLAG_RW, ctrlr, 0,
301 	    nvme_sysctl_reset_stats, "IU", "Reset statistics to zero");
302 
303 	que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO, "adminq",
304 	    CTLFLAG_RD, NULL, "Admin Queue");
305 
306 	nvme_sysctl_initialize_queue(&ctrlr->adminq, ctrlr_ctx, que_tree);
307 
308 	for (i = 0; i < ctrlr->num_io_queues; i++) {
309 		snprintf(queue_name, QUEUE_NAME_LENGTH, "ioq%d", i);
310 		que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO,
311 		    queue_name, CTLFLAG_RD, NULL, "IO Queue");
312 		nvme_sysctl_initialize_queue(&ctrlr->ioq[i], ctrlr_ctx,
313 		    que_tree);
314 	}
315 }
316