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