1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
27 */
28
29 #include <sys/mdb_modapi.h>
30 #include <mdb/mdb_ks.h>
31 #include <sys/modctl.h>
32 #include <note.h>
33 #include <sys/ddi_impldefs.h>
34 #include <sys/ddidmareq.h>
35 #include <sys/devops.h>
36 #include <time.h>
37 #include <sys/varargs.h>
38 #include <sys/sata/sata_hba.h>
39
40 /*
41 * SATA trace debug walker/dcmd code
42 */
43
44 /*
45 * Initialize the sata_trace_dmsg_t walker by either using the given starting
46 * address, or reading the value of the kernel's sata_debug_rbuf pointer.
47 * We also allocate a sata_trace_dmsg_t for storage, and save this using the
48 * walk_data pointer.
49 */
50 static int
sata_dmsg_walk_i(mdb_walk_state_t * wsp)51 sata_dmsg_walk_i(mdb_walk_state_t *wsp)
52 {
53 uintptr_t rbuf_addr;
54 sata_trace_rbuf_t rbuf;
55
56 if (wsp->walk_addr == NULL) {
57 if (mdb_readvar(&rbuf_addr, "sata_debug_rbuf") == -1) {
58 mdb_warn("failed to read 'sata_debug_rbuf'");
59 return (WALK_ERR);
60 }
61
62 if (mdb_vread(&rbuf, sizeof (sata_trace_rbuf_t), rbuf_addr)
63 == -1) {
64 mdb_warn("failed to read sata_trace_rbuf_t at %p",
65 rbuf_addr);
66 return (WALK_ERR);
67 }
68
69 wsp->walk_addr = (uintptr_t)(sata_trace_dmsg_t *)rbuf.dmsgh;
70 }
71
72 /*
73 * Save ptr to head of ring buffer to prevent looping.
74 */
75 wsp->walk_arg = (void *)wsp->walk_addr;
76 wsp->walk_data = mdb_alloc(sizeof (sata_trace_dmsg_t), UM_SLEEP);
77 return (WALK_NEXT);
78 }
79
80 /*
81 * At each step, read a sata_trace_dmsg_t into our private storage, and then
82 * invoke the callback function. We terminate when we reach a NULL next
83 * pointer.
84 */
85 static int
sata_dmsg_walk_s(mdb_walk_state_t * wsp)86 sata_dmsg_walk_s(mdb_walk_state_t *wsp)
87 {
88 int status;
89
90 if (wsp->walk_addr == NULL)
91 return (WALK_DONE);
92
93 if (mdb_vread(wsp->walk_data, sizeof (sata_trace_dmsg_t),
94 wsp->walk_addr) == -1) {
95 mdb_warn("failed to read sata_trace_dmsg_t at %p",
96 wsp->walk_addr);
97 return (WALK_ERR);
98 }
99
100 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
101 wsp->walk_cbdata);
102
103 wsp->walk_addr =
104 (uintptr_t)(((sata_trace_dmsg_t *)wsp->walk_data)->next);
105
106 /*
107 * If we've looped then we're done.
108 */
109 if (wsp->walk_addr == (uintptr_t)wsp->walk_arg)
110 wsp->walk_addr = NULL;
111
112 return (status);
113 }
114
115 /*
116 * The walker's fini function is invoked at the end of each walk. Since we
117 * dynamically allocated a sata_trace_dmsg_t in sata_dmsg_walk_i, we must
118 * free it now.
119 */
120 static void
sata_dmsg_walk_f(mdb_walk_state_t * wsp)121 sata_dmsg_walk_f(mdb_walk_state_t *wsp)
122 {
123 mdb_free(wsp->walk_data, sizeof (sata_trace_dmsg_t));
124 }
125
126 /*
127 * This routine is used by the sata_dmsg_dump dcmd to dump content of
128 * SATA trace ring buffer.
129 */
130 int
sata_dmsg_dump(sata_trace_dmsg_t * addr,int print_pathname,uint_t * printed)131 sata_dmsg_dump(sata_trace_dmsg_t *addr, int print_pathname, uint_t *printed)
132 {
133 sata_trace_dmsg_t dmsg, *dmsgh = addr;
134 struct dev_info dev;
135 char drivername[MODMAXNAMELEN];
136 char pathname[MAXPATHLEN];
137 char merge[1024];
138
139 while (addr != NULL) {
140 if (mdb_vread(&dmsg, sizeof (dmsg), (uintptr_t)addr) !=
141 sizeof (dmsg)) {
142 mdb_warn("failed to read message pointer in kernel");
143 return (DCMD_ERR);
144 }
145
146 if (dmsg.dip != NULL) {
147 if ((mdb_vread(&dev, sizeof (struct dev_info),
148 (uintptr_t)dmsg.dip)) == -1) {
149 (void) mdb_snprintf(merge, sizeof (merge),
150 "[%Y:%03d:%03d:%03d] : %s",
151 dmsg.timestamp.tv_sec,
152 (int)dmsg.timestamp.tv_nsec/1000000,
153 (int)(dmsg.timestamp.tv_nsec/1000)%1000,
154 (int)dmsg.timestamp.tv_nsec%1000,
155 dmsg.buf);
156 } else {
157 (void) mdb_devinfo2driver((uintptr_t)dmsg.dip,
158 drivername, sizeof (drivername));
159 (void) mdb_snprintf(merge, sizeof (merge),
160 "[%Y:%03d:%03d:%03d] %s%d: %s",
161 dmsg.timestamp.tv_sec,
162 (int)dmsg.timestamp.tv_nsec/1000000,
163 (int)(dmsg.timestamp.tv_nsec/1000)%1000,
164 (int)dmsg.timestamp.tv_nsec%1000,
165 drivername,
166 dev.devi_instance,
167 dmsg.buf);
168
169 if (print_pathname == TRUE) {
170 (void) mdb_ddi_pathname(
171 (uintptr_t)dmsg.dip, pathname,
172 sizeof (pathname));
173 mdb_printf("[%s]", pathname);
174 }
175 }
176 } else {
177 (void) mdb_snprintf(merge, sizeof (merge),
178 "[%Y:%03d:%03d:%03d] : %s",
179 dmsg.timestamp.tv_sec,
180 (int)dmsg.timestamp.tv_nsec/1000000,
181 (int)(dmsg.timestamp.tv_nsec/1000)%1000,
182 (int)dmsg.timestamp.tv_nsec%1000,
183 dmsg.buf);
184 }
185
186 mdb_printf("%s\n", merge);
187
188 if (printed != NULL) {
189 (*printed)++;
190 }
191
192 if (((addr = dmsg.next) == NULL) || (dmsg.next == dmsgh)) {
193 break;
194 }
195 }
196
197 return (DCMD_OK);
198 }
199
200 /*
201 * 1. Process flag passed to sata_dmsg_dump dcmd.
202 * 2. Obtain SATA trace ring buffer pointer.
203 * 3. Pass SATA trace ring buffer pointer to sata_dmsg_dump()
204 * to dump content of SATA trace ring buffer.
205 */
206 int
sata_rbuf_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)207 sata_rbuf_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
208 {
209 sata_trace_rbuf_t rbuf;
210 uint_t printed = 0; /* have we printed anything? */
211 int print_pathname = FALSE;
212 int rval = DCMD_OK;
213
214 if (argc > 1) {
215 return (DCMD_USAGE);
216 }
217
218 if (mdb_getopts(argc, argv,
219 'a', MDB_OPT_SETBITS, TRUE, &print_pathname) != argc) {
220 return (DCMD_USAGE);
221 }
222
223 /*
224 * If ring buffer address not provided try to obtain
225 * it using sata_debug_rbuf global.
226 */
227 if ((addr == NULL) || !(flags & DCMD_ADDRSPEC)) {
228 if (mdb_readvar(&addr, "sata_debug_rbuf") == -1) {
229 mdb_warn("Failed to read 'sata_debug_rbuf'.");
230 return (DCMD_ERR);
231 }
232 }
233
234 if (mdb_vread(&rbuf, sizeof (rbuf), addr) != sizeof (rbuf)) {
235 mdb_warn("Failed to read ring buffer in kernel.");
236 return (DCMD_ERR);
237 }
238
239 if (rbuf.dmsgh == NULL) {
240 mdb_printf("The sata trace ring buffer is empty.\n");
241 return (DCMD_OK);
242 }
243
244 rval = sata_dmsg_dump((sata_trace_dmsg_t *)rbuf.dmsgh,
245 print_pathname, &printed);
246
247 if (rval != DCMD_OK) {
248 return (rval);
249 }
250
251 if (printed == 0) {
252 mdb_warn("Failed to read sata trace ring buffer.");
253 return (DCMD_ERR);
254 }
255
256 return (rval);
257 }
258
259 /*
260 * MDB module linkage information:
261 *
262 * We declare a list of structures describing our dcmds, a list of structures
263 * describing our walkers, and a function named _mdb_init to return a pointer
264 * to our module information.
265 */
266
267 static const mdb_dcmd_t dcmds[] = {
268 { "sata_dmsg_dump", "[-a]", "Dump sata trace debug messages",
269 sata_rbuf_dump },
270 { NULL }
271 };
272
273 static const mdb_walker_t walkers[] = {
274 { "sata_dmsg",
275 "walk ring buffer containing sata trace debug messages",
276 sata_dmsg_walk_i, sata_dmsg_walk_s, sata_dmsg_walk_f },
277 { NULL }
278 };
279
280 static const mdb_modinfo_t modinfo = {
281 MDB_API_VERSION, dcmds, walkers
282 };
283
284 const mdb_modinfo_t *
_mdb_init(void)285 _mdb_init(void)
286 {
287 return (&modinfo);
288 }
289