xref: /freebsd/sys/dev/ufshci/ufshci_sysctl.c (revision 4757b351ea9d59d71d4a38b82506d2d16fcd560d)
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
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
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
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
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
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
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
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 #define QUEUE_NAME_LENGTH 16
156 	char queue_name[QUEUE_NAME_LENGTH];
157 	int i;
158 
159 	ctrlr_ctx = device_get_sysctl_ctx(ctrlr->dev);
160 	ctrlr_tree = device_get_sysctl_tree(ctrlr->dev);
161 	ctrlr_list = SYSCTL_CHILDREN(ctrlr_tree);
162 
163 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "major_version",
164 	    CTLFLAG_RD, &ctrlr->major_version, 0, "UFS spec major version");
165 
166 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "minor_version",
167 	    CTLFLAG_RD, &ctrlr->minor_version, 0, "UFS spec minor version");
168 
169 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "io_queue_mode",
170 	    CTLFLAG_RD, &ctrlr->transfer_req_queue.queue_mode, 0,
171 	    "Active host-side queuing scheme "
172 	    "(Single-Doorbell or Multi-Circular-Queue)");
173 
174 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_io_queues",
175 	    CTLFLAG_RD, &ctrlr->num_io_queues, 0, "Number of I/O queue pairs");
176 
177 	SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "cap", CTLFLAG_RD,
178 	    &ctrlr->cap, 0, "Number of I/O queue pairs");
179 
180 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "timeout_period",
181 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, &ctrlr->timeout_period,
182 	    0, ufshci_sysctl_timeout_period, "IU",
183 	    "Timeout period for I/O queues (in seconds)");
184 
185 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_cmds",
186 	    CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
187 	    ufshci_sysctl_num_cmds, "IU", "Number of commands submitted");
188 
189 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
190 	    "num_intr_handler_calls", CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
191 	    ctrlr, 0, ufshci_sysctl_num_intr_handler_calls, "IU",
192 	    "Number of times interrupt handler was invoked (will "
193 	    "typically be less than number of actual interrupts "
194 	    "generated due to coalescing)");
195 
196 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_retries",
197 	    CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
198 	    ufshci_sysctl_num_retries, "IU", "Number of commands retried");
199 
200 	SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "num_failures",
201 	    CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
202 	    ufshci_sysctl_num_failures, "IU",
203 	    "Number of commands ending in failure after all retries");
204 
205 	que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO, "utmrq",
206 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
207 	    "UTP Task Management Request Queue");
208 
209 	ufshci_sysctl_initialize_queue(
210 	    &ctrlr->task_mgmt_req_queue.hwq[UFSHCI_SDB_Q], ctrlr_ctx, que_tree);
211 
212 	/*
213 	 * Make sure that we've constructed the I/O queues before setting up the
214 	 * sysctls. Failed controllers won't allocate it, but we want the rest
215 	 * of the sysctls to diagnose things.
216 	 */
217 	if (ctrlr->transfer_req_queue.hwq != NULL) {
218 		ioq_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ctrlr_list, OID_AUTO,
219 		    "ioq", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
220 		    "UTP Transfer Request Queue (I/O Queue)");
221 		ioq_list = SYSCTL_CHILDREN(ioq_tree);
222 
223 		for (i = 0; i < ctrlr->num_io_queues; i++) {
224 			snprintf(queue_name, QUEUE_NAME_LENGTH, "%d", i);
225 			que_tree = SYSCTL_ADD_NODE(ctrlr_ctx, ioq_list,
226 			    OID_AUTO, queue_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
227 			    NULL, "IO Queue");
228 			ufshci_sysctl_initialize_queue(
229 			    &ctrlr->transfer_req_queue.hwq[i], ctrlr_ctx,
230 			    que_tree);
231 		}
232 	}
233 }
234