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 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * PCI nexus driver general debug support
28 */
29 #include <sys/sysmacros.h>
30 #include <sys/async.h>
31 #include <sys/sunddi.h> /* dev_info_t */
32 #include <sys/ddi_impldefs.h>
33 #include <sys/disp.h>
34 #include <sys/archsystm.h> /* getpil() */
35 #include "px_obj.h"
36
37 /*LINTLIBRARY*/
38
39 #ifdef DEBUG
40 uint64_t px_debug_flags = 0;
41
42 static char *px_debug_sym [] = { /* same sequence as px_debug_bit */
43 /* 0 */ "attach",
44 /* 1 */ "detach",
45 /* 2 */ "map",
46 /* 3 */ "nex-ctlops",
47
48 /* 4 */ "introps",
49 /* 5 */ "intx-add",
50 /* 6 */ "intx-rem",
51 /* 7 */ "intx-intr",
52
53 /* 8 */ "msiq",
54 /* 9 */ "msiq-intr",
55 /* 10 */ "msg",
56 /* 11 */ "msg-intr",
57
58 /* 12 */ "msix-add",
59 /* 13 */ "msix-rem",
60 /* 14 */ "msix-intr",
61 /* 15 */ "err",
62
63 /* 16 */ "dma-alloc",
64 /* 17 */ "dma-free",
65 /* 18 */ "dma-bind",
66 /* 19 */ "dma-unbind",
67
68 /* 20 */ "chk-dma-mode",
69 /* 21 */ "bypass-dma",
70 /* 22 */ "fast-dvma",
71 /* 23 */ "init_child",
72
73 /* 24 */ "dma-map",
74 /* 25 */ "dma-win",
75 /* 26 */ "map-win",
76 /* 27 */ "unmap-win",
77
78 /* 28 */ "dma-ctl",
79 /* 29 */ "dma-sync",
80 /* 30 */ NULL,
81 /* 31 */ NULL,
82
83 /* 32 */ "ib",
84 /* 33 */ "cb",
85 /* 34 */ "dmc",
86 /* 35 */ "pec",
87
88 /* 36 */ "ilu",
89 /* 37 */ "tlu",
90 /* 38 */ "lpu",
91 /* 39 */ "mmu",
92
93 /* 40 */ "open",
94 /* 41 */ "close",
95 /* 42 */ "ioctl",
96 /* 43 */ "pwr",
97
98 /* 44 */ "lib-cfg",
99 /* 45 */ "lib-intr",
100 /* 46 */ "lib-dma",
101 /* 47 */ "lib-msiq",
102
103 /* 48 */ "lib-msi",
104 /* 49 */ "lib-msg",
105 /* 50 */ "NULL",
106 /* 51 */ "NULL",
107
108 /* 52 */ "tools",
109 /* 53 */ "phys_acc",
110
111 /* 54 */ "hotplug",
112 /* LAST */ "unknown"
113 };
114
115 /* Tunables */
116 static int px_dbg_msg_size = 16; /* # of Qs. Must be ^2 */
117
118 /* Non-Tunables */
119 static int px_dbg_qmask = 0xFFFF; /* Mask based on Q size */
120 static px_dbg_msg_t *px_dbg_msgq = NULL; /* Debug Msg Queue */
121 static uint8_t px_dbg_reference = 0; /* Reference Counter */
122 static kmutex_t px_dbg_mutex; /* Mutex for dequeuing */
123 static uint8_t px_dbg_qtail = 0; /* Pointer to q tail */
124 static uint8_t px_dbg_qhead = 0; /* Pointer to q head */
125 static uint_t px_dbg_qsize = 0; /* # of pending messages */
126 static uint_t px_dbg_failed = 0; /* # of overflows */
127
128 /* Forward Declarations */
129 static void px_dbg_print(px_debug_bit_t bit, dev_info_t *dip, char *fmt,
130 va_list args);
131 static void px_dbg_queue(px_debug_bit_t bit, dev_info_t *dip, char *fmt,
132 va_list args);
133 static uint_t px_dbg_drain(caddr_t arg1, caddr_t arg2);
134
135 /*
136 * Print function called either directly by px_dbg or through soft interrupt.
137 * This function cannot be called directly in threads with PIL above clock.
138 */
139 static void
px_dbg_print(px_debug_bit_t bit,dev_info_t * dip,char * fmt,va_list args)140 px_dbg_print(px_debug_bit_t bit, dev_info_t *dip, char *fmt, va_list args)
141 {
142 int cont = bit >> DBG_BITS;
143
144 if (cont)
145 goto body;
146
147 if (dip)
148 prom_printf("%s(%d): %s: ", ddi_driver_name(dip),
149 ddi_get_instance(dip), px_debug_sym[bit]);
150 else
151 prom_printf("px: %s: ", px_debug_sym[bit]);
152 body:
153 if (args)
154 prom_vprintf(fmt, args);
155 else
156 prom_printf(fmt);
157 }
158
159 /*
160 * Queueing mechanism to log px_dbg messages if calling thread is running with a
161 * PIL above clock. It's Multithreaded safe.
162 */
163 static void
px_dbg_queue(px_debug_bit_t bit,dev_info_t * dip,char * fmt,va_list args)164 px_dbg_queue(px_debug_bit_t bit, dev_info_t *dip, char *fmt, va_list args)
165 {
166 int instance = DIP_TO_INST(dip);
167 px_t *px_p = INST_TO_STATE(instance);
168 uint8_t q_no;
169 px_dbg_msg_t *msg_p;
170
171 /* Check to make sure the queue hasn't overflowed */
172 if (atomic_inc_uint_nv(&px_dbg_qsize) >= px_dbg_msg_size) {
173 px_dbg_failed++;
174 atomic_dec_uint(&px_dbg_qsize);
175 return;
176 }
177
178 /*
179 * Grab the next available queue bucket. Incrementing the tail here
180 * doesn't need to be protected, as it is guaranteed to not overflow.
181 */
182 q_no = ++px_dbg_qtail & px_dbg_qmask;
183 msg_p = &px_dbg_msgq[q_no];
184
185 ASSERT(msg_p->active == B_FALSE);
186
187 /* Print the message in the buffer */
188 vsnprintf(msg_p->msg, DBG_MSG_SIZE, fmt, args);
189 msg_p->bit = bit;
190 msg_p->dip = dip;
191 msg_p->active = B_TRUE;
192
193 /* Trigger Soft Int */
194 ddi_intr_trigger_softint(px_p->px_dbg_hdl, (caddr_t)NULL);
195 }
196
197 /*
198 * Callback function for queuing px_dbg in high PIL by soft intr. This code
199 * assumes it will be called serially for every msg.
200 */
201 static uint_t
px_dbg_drain(caddr_t arg1,caddr_t arg2)202 px_dbg_drain(caddr_t arg1, caddr_t arg2) {
203 uint8_t q_no;
204 px_dbg_msg_t *msg_p;
205 uint_t ret = DDI_INTR_UNCLAIMED;
206
207 mutex_enter(&px_dbg_mutex);
208 while (px_dbg_qsize) {
209 atomic_dec_uint(&px_dbg_qsize);
210 if (px_dbg_failed) {
211 cmn_err(CE_WARN, "%d msg(s) were lost",
212 px_dbg_failed);
213 px_dbg_failed = 0;
214 }
215
216 q_no = ++px_dbg_qhead & px_dbg_qmask;
217 msg_p = &px_dbg_msgq[q_no];
218
219 if (msg_p->active) {
220 px_dbg_print(msg_p->bit, msg_p->dip, msg_p->msg, NULL);
221 msg_p->active = B_FALSE;
222 }
223 ret = DDI_INTR_CLAIMED;
224 }
225
226 mutex_exit(&px_dbg_mutex);
227 return (ret);
228 }
229
230 void
px_dbg(px_debug_bit_t bit,dev_info_t * dip,char * fmt,...)231 px_dbg(px_debug_bit_t bit, dev_info_t *dip, char *fmt, ...)
232 {
233 va_list ap;
234
235 bit &= DBG_MASK;
236 if (bit >= sizeof (px_debug_sym) / sizeof (char *))
237 return;
238 if (!(1ull << bit & px_debug_flags))
239 return;
240
241 va_start(ap, fmt);
242 if (getpil() > LOCK_LEVEL)
243 px_dbg_queue(bit, dip, fmt, ap);
244 else
245 px_dbg_print(bit, dip, fmt, ap);
246 va_end(ap);
247 }
248 #endif /* DEBUG */
249
250 void
px_dbg_attach(dev_info_t * dip,ddi_softint_handle_t * dbg_hdl)251 px_dbg_attach(dev_info_t *dip, ddi_softint_handle_t *dbg_hdl)
252 {
253 #ifdef DEBUG
254 if (px_dbg_reference++ == 0) {
255 int size = px_dbg_msg_size;
256
257 /* Check if px_dbg_msg_size is ^2 */
258 /*
259 * WARNING: The bellow statement makes no sense. If size is
260 * not a power of 2, it will set size to zero.
261 */
262 size = !ISP2(size) ? ((size | ~size) + 1) : size;
263 px_dbg_msg_size = size;
264 px_dbg_qmask = size - 1;
265 px_dbg_msgq = kmem_zalloc(sizeof (px_dbg_msg_t) * size,
266 KM_SLEEP);
267
268 mutex_init(&px_dbg_mutex, NULL, MUTEX_DRIVER, NULL);
269 }
270
271 if (ddi_intr_add_softint(dip, dbg_hdl,
272 DDI_INTR_SOFTPRI_MAX, px_dbg_drain, NULL) != DDI_SUCCESS) {
273 DBG(DBG_ATTACH, dip,
274 "Unable to allocate soft int for DBG printing.\n");
275 dbg_hdl = NULL;
276 }
277 #endif /* DEBUG */
278 }
279
280 /* ARGSUSED */
281 void
px_dbg_detach(dev_info_t * dip,ddi_softint_handle_t * dbg_hdl)282 px_dbg_detach(dev_info_t *dip, ddi_softint_handle_t *dbg_hdl)
283 {
284 #ifdef DEBUG
285 if (dbg_hdl != NULL)
286 (void) ddi_intr_remove_softint(*dbg_hdl);
287
288 if (--px_dbg_reference == 0) {
289 if (px_dbg_msgq != NULL)
290 kmem_free(px_dbg_msgq,
291 sizeof (px_dbg_msg_t) * px_dbg_msg_size);
292 mutex_destroy(&px_dbg_mutex);
293 }
294 #endif /* DEBUG */
295 }
296