1*ba52565bSPatrick Mooney /*
2*ba52565bSPatrick Mooney * This file and its contents are supplied under the terms of the
3*ba52565bSPatrick Mooney * Common Development and Distribution License ("CDDL"), version 1.0.
4*ba52565bSPatrick Mooney * You may only use this file in accordance with the terms of version
5*ba52565bSPatrick Mooney * 1.0 of the CDDL.
6*ba52565bSPatrick Mooney *
7*ba52565bSPatrick Mooney * A full copy of the text of the CDDL should have accompanied this
8*ba52565bSPatrick Mooney * source. A copy of the CDDL is also available via the Internet at
9*ba52565bSPatrick Mooney * http://www.illumos.org/license/CDDL.
10*ba52565bSPatrick Mooney */
11*ba52565bSPatrick Mooney
12*ba52565bSPatrick Mooney /*
13*ba52565bSPatrick Mooney * Copyright 2025 Oxide Computer Company
14*ba52565bSPatrick Mooney */
15*ba52565bSPatrick Mooney
16*ba52565bSPatrick Mooney
17*ba52565bSPatrick Mooney #include <sys/types.h>
18*ba52565bSPatrick Mooney #include <sys/mutex.h>
19*ba52565bSPatrick Mooney #include <sys/list.h>
20*ba52565bSPatrick Mooney #include <sys/time.h>
21*ba52565bSPatrick Mooney #include <sys/varargs.h>
22*ba52565bSPatrick Mooney #include <sys/debug.h>
23*ba52565bSPatrick Mooney #include <sys/systm.h>
24*ba52565bSPatrick Mooney #include <sys/sysmacros.h>
25*ba52565bSPatrick Mooney #include <sys/sunddi.h>
26*ba52565bSPatrick Mooney #include <sys/cmn_err.h>
27*ba52565bSPatrick Mooney
28*ba52565bSPatrick Mooney static kmutex_t t4_debug_lock;
29*ba52565bSPatrick Mooney static list_t t4_debug_msgs;
30*ba52565bSPatrick Mooney static uint_t t4_debug_size;
31*ba52565bSPatrick Mooney /* Rough counter of allocation failures during cxgb_printf() */
32*ba52565bSPatrick Mooney static uint64_t t4_debug_alloc_fail;
33*ba52565bSPatrick Mooney
34*ba52565bSPatrick Mooney /*
35*ba52565bSPatrick Mooney * Max ring buffer size for debug logs. Defaults to 16KiB.
36*ba52565bSPatrick Mooney *
37*ba52565bSPatrick Mooney * If set to 0, no debug messages will be stored, nor will the t4-dbgmsg SDT
38*ba52565bSPatrick Mooney * probe be fired.
39*ba52565bSPatrick Mooney *
40*ba52565bSPatrick Mooney * If set to < 0, then messages will be logged through the legacy cmn_err()
41*ba52565bSPatrick Mooney * behavior (and the SDT probe is also skipped).
42*ba52565bSPatrick Mooney */
43*ba52565bSPatrick Mooney int t4_debug_max_size = 16384;
44*ba52565bSPatrick Mooney
45*ba52565bSPatrick Mooney typedef struct t4_dbgmsg {
46*ba52565bSPatrick Mooney list_node_t tdm_node;
47*ba52565bSPatrick Mooney hrtime_t tdm_when;
48*ba52565bSPatrick Mooney dev_info_t *tdm_dip;
49*ba52565bSPatrick Mooney char tdm_msg[];
50*ba52565bSPatrick Mooney } t4_dbgmsg_t;
51*ba52565bSPatrick Mooney
52*ba52565bSPatrick Mooney static inline uint_t
t4_dbgmsg_sz(int sz)53*ba52565bSPatrick Mooney t4_dbgmsg_sz(int sz)
54*ba52565bSPatrick Mooney {
55*ba52565bSPatrick Mooney ASSERT(sz >= 0);
56*ba52565bSPatrick Mooney return (sizeof (t4_dbgmsg_t) + sz + 1);
57*ba52565bSPatrick Mooney }
58*ba52565bSPatrick Mooney
59*ba52565bSPatrick Mooney static uint_t
t4_debug_free(t4_dbgmsg_t * msg)60*ba52565bSPatrick Mooney t4_debug_free(t4_dbgmsg_t *msg)
61*ba52565bSPatrick Mooney {
62*ba52565bSPatrick Mooney const uint_t free_sz = t4_dbgmsg_sz(strlen(msg->tdm_msg));
63*ba52565bSPatrick Mooney kmem_free(msg, free_sz);
64*ba52565bSPatrick Mooney
65*ba52565bSPatrick Mooney return (free_sz);
66*ba52565bSPatrick Mooney }
67*ba52565bSPatrick Mooney
68*ba52565bSPatrick Mooney void
cxgb_printf(dev_info_t * dip,int level,const char * fmt,...)69*ba52565bSPatrick Mooney cxgb_printf(dev_info_t *dip, int level, const char *fmt, ...)
70*ba52565bSPatrick Mooney {
71*ba52565bSPatrick Mooney va_list adx;
72*ba52565bSPatrick Mooney
73*ba52565bSPatrick Mooney if (t4_debug_max_size == 0) {
74*ba52565bSPatrick Mooney /* User has opted out of debug messages completely */
75*ba52565bSPatrick Mooney return;
76*ba52565bSPatrick Mooney } else if (t4_debug_max_size < 0) {
77*ba52565bSPatrick Mooney /* User has opted into old cmn_err() behavior */
78*ba52565bSPatrick Mooney char pfmt[128];
79*ba52565bSPatrick Mooney
80*ba52565bSPatrick Mooney (void) snprintf(pfmt, sizeof (pfmt), "%s%d: %s",
81*ba52565bSPatrick Mooney ddi_driver_name(dip), ddi_get_instance(dip), fmt);
82*ba52565bSPatrick Mooney
83*ba52565bSPatrick Mooney va_start(adx, fmt);
84*ba52565bSPatrick Mooney vcmn_err(level, pfmt, adx);
85*ba52565bSPatrick Mooney va_end(adx);
86*ba52565bSPatrick Mooney
87*ba52565bSPatrick Mooney return;
88*ba52565bSPatrick Mooney }
89*ba52565bSPatrick Mooney
90*ba52565bSPatrick Mooney va_start(adx, fmt);
91*ba52565bSPatrick Mooney const int size = vsnprintf(NULL, 0, fmt, adx);
92*ba52565bSPatrick Mooney va_end(adx);
93*ba52565bSPatrick Mooney
94*ba52565bSPatrick Mooney const uint_t alloc_sz = t4_dbgmsg_sz(size);
95*ba52565bSPatrick Mooney t4_dbgmsg_t *msg = kmem_alloc(alloc_sz, KM_NOSLEEP);
96*ba52565bSPatrick Mooney if (msg == NULL) {
97*ba52565bSPatrick Mooney /*
98*ba52565bSPatrick Mooney * Just note the failure and bail if the system is so pressed
99*ba52565bSPatrick Mooney * for memory.
100*ba52565bSPatrick Mooney */
101*ba52565bSPatrick Mooney DTRACE_PROBE1(t4__dbgmsg__alloc_fail, dev_info_t *, dip);
102*ba52565bSPatrick Mooney t4_debug_alloc_fail++;
103*ba52565bSPatrick Mooney return;
104*ba52565bSPatrick Mooney }
105*ba52565bSPatrick Mooney msg->tdm_when = gethrtime();
106*ba52565bSPatrick Mooney msg->tdm_dip = dip;
107*ba52565bSPatrick Mooney
108*ba52565bSPatrick Mooney va_start(adx, fmt);
109*ba52565bSPatrick Mooney (void) vsnprintf(msg->tdm_msg, size + 1, fmt, adx);
110*ba52565bSPatrick Mooney va_end(adx);
111*ba52565bSPatrick Mooney
112*ba52565bSPatrick Mooney DTRACE_PROBE2(t4__dbgmsg, dev_info_t *, dip, char *, msg->tdm_msg);
113*ba52565bSPatrick Mooney
114*ba52565bSPatrick Mooney mutex_enter(&t4_debug_lock);
115*ba52565bSPatrick Mooney list_insert_tail(&t4_debug_msgs, msg);
116*ba52565bSPatrick Mooney t4_debug_size += alloc_sz;
117*ba52565bSPatrick Mooney while (t4_debug_size > t4_debug_max_size && t4_debug_size != 0) {
118*ba52565bSPatrick Mooney msg = list_remove_head(&t4_debug_msgs);
119*ba52565bSPatrick Mooney t4_debug_size -= t4_debug_free(msg);
120*ba52565bSPatrick Mooney }
121*ba52565bSPatrick Mooney mutex_exit(&t4_debug_lock);
122*ba52565bSPatrick Mooney }
123*ba52565bSPatrick Mooney
124*ba52565bSPatrick Mooney void
t4_debug_init(void)125*ba52565bSPatrick Mooney t4_debug_init(void)
126*ba52565bSPatrick Mooney {
127*ba52565bSPatrick Mooney mutex_init(&t4_debug_lock, NULL, MUTEX_DEFAULT, NULL);
128*ba52565bSPatrick Mooney list_create(&t4_debug_msgs, sizeof (t4_dbgmsg_t),
129*ba52565bSPatrick Mooney offsetof(t4_dbgmsg_t, tdm_node));
130*ba52565bSPatrick Mooney }
131*ba52565bSPatrick Mooney
132*ba52565bSPatrick Mooney void
t4_debug_fini(void)133*ba52565bSPatrick Mooney t4_debug_fini(void)
134*ba52565bSPatrick Mooney {
135*ba52565bSPatrick Mooney t4_dbgmsg_t *msg;
136*ba52565bSPatrick Mooney
137*ba52565bSPatrick Mooney while ((msg = list_remove_head(&t4_debug_msgs)) != NULL) {
138*ba52565bSPatrick Mooney t4_debug_size -= t4_debug_free(msg);
139*ba52565bSPatrick Mooney }
140*ba52565bSPatrick Mooney mutex_destroy(&t4_debug_lock);
141*ba52565bSPatrick Mooney }
142