nvme_sysctl.c revision 330897
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: stable/11/sys/dev/nvme/nvme_sysctl.c 330897 2018-03-14 03:19:51Z eadler $");
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
44int nvme_use_nvd = NVME_USE_NVD;
45
46SYSCTL_NODE(_hw, OID_AUTO, nvme, CTLFLAG_RD, 0, "NVMe sysctl tunables");
47SYSCTL_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
60static void
61nvme_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
85static int
86nvme_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
102static int
103nvme_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
121static int
122nvme_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
140static int
141nvme_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
159static void
160nvme_qpair_reset_stats(struct nvme_qpair *qpair)
161{
162
163	qpair->num_cmds = 0;
164	qpair->num_intr_handler_calls = 0;
165}
166
167static int
168nvme_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
182static int
183nvme_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
197static int
198nvme_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
219static void
220nvme_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
254void
255nvme_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