xref: /freebsd/sys/dev/ufshci/ufshci_sysctl.c (revision 7d748c594ef0ddc45b3b8d0c4d368150e975724e)
1 /*-
2  * Copyright (c) 2025, Samsung Electronics Co., Ltd.
3  * Written by Jaeyoon Choi
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 
8 #include <sys/param.h>
9 #include <sys/systm.h>
10 #include <sys/bus.h>
11 #include <sys/sysctl.h>
12 
13 #include "ufshci_private.h"
14 
15 static int
ufshci_sysctl_timeout_period(SYSCTL_HANDLER_ARGS)16 ufshci_sysctl_timeout_period(SYSCTL_HANDLER_ARGS)
17 {
18 	uint32_t *ptr = arg1;
19 	uint32_t newval = *ptr;
20 	int error = sysctl_handle_int(oidp, &newval, 0, req);
21 
22 	if (error || (req->newptr == NULL))
23 		return (error);
24 
25 	if (newval > UFSHCI_MAX_TIMEOUT_PERIOD ||
26 	    newval < UFSHCI_MIN_TIMEOUT_PERIOD) {
27 		return (EINVAL);
28 	} else {
29 		*ptr = newval;
30 	}
31 
32 	return (0);
33 }
34 
35 static int
ufshci_sysctl_num_cmds(SYSCTL_HANDLER_ARGS)36 ufshci_sysctl_num_cmds(SYSCTL_HANDLER_ARGS)
37 {
38 	struct ufshci_controller *ctrlr = arg1;
39 	int64_t num_cmds = 0;
40 	int i;
41 
42 	num_cmds = ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_cmds;
43 
44 	if (ctrlr->transfer_req_queue.hwq != NULL) {
45 		for (i = 0; i < ctrlr->num_io_queues; i++)
46 			num_cmds += ctrlr->transfer_req_queue.hwq[i].num_cmds;
47 	}
48 
49 	return (sysctl_handle_64(oidp, &num_cmds, 0, req));
50 }
51 
52 static int
ufshci_sysctl_num_intr_handler_calls(SYSCTL_HANDLER_ARGS)53 ufshci_sysctl_num_intr_handler_calls(SYSCTL_HANDLER_ARGS)
54 {
55 	struct ufshci_controller *ctrlr = arg1;
56 	int64_t num_intr_handler_calls = 0;
57 	int i;
58 
59 	num_intr_handler_calls =
60 	    ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_intr_handler_calls;
61 
62 	if (ctrlr->transfer_req_queue.hwq != NULL) {
63 		for (i = 0; i < ctrlr->num_io_queues; i++)
64 			num_intr_handler_calls += ctrlr->transfer_req_queue
65 						      .hwq[i]
66 						      .num_intr_handler_calls;
67 	}
68 
69 	return (sysctl_handle_64(oidp, &num_intr_handler_calls, 0, req));
70 }
71 
72 static int
ufshci_sysctl_num_retries(SYSCTL_HANDLER_ARGS)73 ufshci_sysctl_num_retries(SYSCTL_HANDLER_ARGS)
74 {
75 	struct ufshci_controller *ctrlr = arg1;
76 	int64_t num_retries = 0;
77 	int i;
78 
79 	num_retries = ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_retries;
80 
81 	if (ctrlr->transfer_req_queue.hwq != NULL) {
82 		for (i = 0; i < ctrlr->num_io_queues; i++)
83 			num_retries +=
84 			    ctrlr->transfer_req_queue.hwq[i].num_retries;
85 	}
86 
87 	return (sysctl_handle_64(oidp, &num_retries, 0, req));
88 }
89 
90 static int
ufshci_sysctl_num_failures(SYSCTL_HANDLER_ARGS)91 ufshci_sysctl_num_failures(SYSCTL_HANDLER_ARGS)
92 {
93 	struct ufshci_controller *ctrlr = arg1;
94 	int64_t num_failures = 0;
95 	int i;
96 
97 	num_failures =
98 	    ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q].num_failures;
99 
100 	if (ctrlr->transfer_req_queue.hwq != NULL) {
101 		for (i = 0; i < ctrlr->num_io_queues; i++)
102 			num_failures +=
103 			    ctrlr->transfer_req_queue.hwq[i].num_failures;
104 	}
105 
106 	return (sysctl_handle_64(oidp, &num_failures, 0, req));
107 }
108 
109 static void
ufshci_sysctl_initialize_queue(struct ufshci_hw_queue * hwq,struct sysctl_ctx_list * ctrlr_ctx,struct sysctl_oid * que_tree)110 ufshci_sysctl_initialize_queue(struct ufshci_hw_queue *hwq,
111     struct sysctl_ctx_list *ctrlr_ctx, struct sysctl_oid *que_tree)
112 {
113 	struct sysctl_oid_list *que_list = SYSCTL_CHILDREN(que_tree);
114 
115 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "num_entries",
116 	    CTLFLAG_RD, &hwq->num_entries, 0,
117 	    "Number of entries in hardware queue");
118 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "num_trackers",
119 	    CTLFLAG_RD, &hwq->num_trackers, 0,
120 	    "Number of trackers pre-allocated for this queue pair");
121 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "sq_head", CTLFLAG_RD,
122 	    &hwq->sq_head, 0,
123 	    "Current head of submission queue (as observed by driver)");
124 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "sq_tail", CTLFLAG_RD,
125 	    &hwq->sq_tail, 0,
126 	    "Current tail of submission queue (as observed by driver)");
127 	SYSCTL_ADD_UINT(ctrlr_ctx, que_list, OID_AUTO, "cq_head", CTLFLAG_RD,
128 	    &hwq->cq_head, 0,
129 	    "Current head of completion queue (as observed by driver)");
130 
131 	SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_cmds", CTLFLAG_RD,
132 	    &hwq->num_cmds, "Number of commands submitted");
133 	SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_intr_handler_calls",
134 	    CTLFLAG_RD, &hwq->num_intr_handler_calls,
135 	    "Number of times interrupt handler was invoked (will typically be "
136 	    "less than number of actual interrupts generated due to "
137 	    "interrupt aggregation)");
138 	SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_retries",
139 	    CTLFLAG_RD, &hwq->num_retries, "Number of commands retried");
140 	SYSCTL_ADD_QUAD(ctrlr_ctx, que_list, OID_AUTO, "num_failures",
141 	    CTLFLAG_RD, &hwq->num_failures,
142 	    "Number of commands ending in failure after all retries");
143 
144 	/* TODO: Implement num_ignored */
145 	/* TODO: Implement recovery state */
146 	/* TODO: Implement dump debug */
147 }
148 
149 void
ufshci_sysctl_initialize_ctrlr(struct ufshci_controller * ctrlr)150 ufshci_sysctl_initialize_ctrlr(struct ufshci_controller *ctrlr)
151 {
152 	struct sysctl_ctx_list *ctrlr_ctx;
153 	struct sysctl_oid *ctrlr_tree, *que_tree, *ioq_tree;
154 	struct sysctl_oid_list *ctrlr_list, *ioq_list;
155 	struct ufshci_device *dev = &ctrlr->ufs_dev;
156 #define QUEUE_NAME_LENGTH 16
157 	char queue_name[QUEUE_NAME_LENGTH];
158 	int i;
159 
160 	ctrlr_ctx = device_get_sysctl_ctx(ctrlr->dev);
161 	ctrlr_tree = device_get_sysctl_tree(ctrlr->dev);
162 	ctrlr_list = SYSCTL_CHILDREN(ctrlr_tree);
163 
164 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "major_version",
165 	    CTLFLAG_RD, &ctrlr->major_version, 0, "UFS spec major version");
166 
167 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "minor_version",
168 	    CTLFLAG_RD, &ctrlr->minor_version, 0, "UFS spec minor version");
169 
170 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "io_queue_mode",
171 	    CTLFLAG_RD, &ctrlr->transfer_req_queue.queue_mode, 0,
172 	    "Active host-side queuing scheme "
173 	    "(Single-Doorbell or Multi-Circular-Queue)");
174 
175 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_io_queues",
176 	    CTLFLAG_RD, &ctrlr->num_io_queues, 0, "Number of I/O queue pairs");
177 
178 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "cap", CTLFLAG_RD,
179 	    &ctrlr->cap, 0, "Number of I/O queue pairs");
180 
181 	SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_enabled",
182 	    CTLFLAG_RD, &dev->is_wb_enabled, 0, "WriteBooster enable/disable");
183 
184 	SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_flush_enabled",
185 	    CTLFLAG_RD, &dev->is_wb_flush_enabled, 0,
186 	    "WriteBooster flush enable/disable");
187 
188 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_buffer_type",
189 	    CTLFLAG_RD, &dev->wb_buffer_type, 0, "WriteBooster type");
190 
191 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "wb_buffer_size_mb",
192 	    CTLFLAG_RD, &dev->wb_buffer_size_mb, 0,
193 	    "WriteBooster buffer size in MB");
194 
195 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO,
196 	    "wb_user_space_config_option", CTLFLAG_RD,
197 	    &dev->wb_user_space_config_option, 0,
198 	    "WriteBooster preserve user space mode");
199 
200 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "timeout_period",
201 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, &ctrlr->timeout_period,
202 	    0, ufshci_sysctl_timeout_period, "IU",
203 	    "Timeout period for I/O queues (in seconds)");
204 
205 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_cmds",
206 	    CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
207 	    ufshci_sysctl_num_cmds, "IU", "Number of commands submitted");
208 
209 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
210 	    "num_intr_handler_calls", CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
211 	    ctrlr, 0, ufshci_sysctl_num_intr_handler_calls, "IU",
212 	    "Number of times interrupt handler was invoked (will "
213 	    "typically be less than number of actual interrupts "
214 	    "generated due to coalescing)");
215 
216 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_retries",
217 	    CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
218 	    ufshci_sysctl_num_retries, "IU", "Number of commands retried");
219 
220 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_failures",
221 	    CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
222 	    ufshci_sysctl_num_failures, "IU",
223 	    "Number of commands ending in failure after all retries");
224 
225 	que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO, "utmrq",
226 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
227 	    "UTP Task Management Request Queue");
228 
229 	ufshci_sysctl_initialize_queue(
230 	    &ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q], ctrlr_ctx, que_tree);
231 
232 	/*
233 	 * Make sure that we've constructed the I/O queues before setting up the
234 	 * sysctls. Failed controllers won't allocate it, but we want the rest
235 	 * of the sysctls to diagnose things.
236 	 */
237 	if (ctrlr->transfer_req_queue.hwq != NULL) {
238 		ioq_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO,
239 		    "ioq", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
240 		    "UTP Transfer Request Queue (I/O Queue)");
241 		ioq_list = SYSCTL_CHILDREN(ioq_tree);
242 
243 		for (i = 0; i < ctrlr->num_io_queues; i++) {
244 			snprintf(queue_name, QUEUE_NAME_LENGTH, "%d", i);
245 			que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ioq_list,
246 			    OID_AUTO, queue_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
247 			    NULL, "IO Queue");
248 			ufshci_sysctl_initialize_queue(
249 			    &ctrlr->transfer_req_queue.hwq[i], ctrlr_ctx,
250 			    que_tree);
251 		}
252 	}
253 }
254