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/*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * This file is more or less the same as the Solaris IBTL debug
28 * implementation. The debug functions and conf variables are
29 * similar. One significant change is :
30 * 	sol_ofs_supress_above_l2
31 * This has to be set to 0, in /etc/system to enable debug prints
32 * above level 2.
33 */
34#include <sys/types.h>
35#include <sys/cmn_err.h>
36#include <sys/ddi.h>
37#include <sys/sunddi.h>
38#include <sys/ib/clients/of/sol_ofs/sol_ofs_common.h>
39
40#define	SOL_OFS_PRINT_BUF_LEN		4096
41#define	SOL_OFS_DEBUG_BUF_SIZE		0x10000
42#define	SOL_OFS_DEBUG_EXTRA_SIZE	8
43#define	SOL_OFS_LOG_L5			5
44#define	SOL_OFS_LOG_L4			4
45#define	SOL_OFS_LOG_L3			3
46#define	SOL_OFS_LOG_L2			2
47#define	SOL_OFS_LOG_L1			1
48#define	SOL_OFS_LOG_L0			0
49
50static kmutex_t	sol_ofs_debug_mutex;
51static char	sol_ofs_print_buf[SOL_OFS_PRINT_BUF_LEN];
52static char	*sol_ofs_debug_sptr = NULL;
53static char	*sol_ofs_debug_eptr = NULL;
54
55char	*sol_ofs_debug_buf = NULL;
56int	sol_ofs_clear_debug_buf_flag = 0;
57int	sol_ofs_debug_buf_size = SOL_OFS_DEBUG_BUF_SIZE;
58int	sol_ofs_suppress_dprintf = 0;
59int	sol_ofs_buffer_dprintf = 1;
60int	sol_ofs_supress_above_l2 = 1;
61
62int	sol_ucma_errlevel = 2;		/* sol_ucma driver */
63int	sol_uverbs_errlevel = 2;	/* sol_uverbs driver */
64int	sol_umad_errlevel = 2;		/* sol_umad driver */
65
66int	sol_rdmacm_errlevel = 2;	/* rdmacm part of sol_ofs */
67int	sol_kverbs_errlevel = 2;	/* kverbs part of sol_ofs */
68/* sol_ofs module (except rdmacm and kverbs) */
69int	sol_ofs_module_errlevel = 2;
70
71/* Global error levels for all OF related modules */
72int	sol_of_errlevel = 2;
73
74static void
75sol_ofs_clear_dbg_buf()
76{
77	ASSERT(MUTEX_HELD(&sol_ofs_debug_mutex));
78	if (sol_ofs_debug_buf) {
79		sol_ofs_debug_sptr = sol_ofs_debug_buf;
80		sol_ofs_debug_eptr = sol_ofs_debug_buf +
81		    sol_ofs_debug_buf_size - SOL_OFS_DEBUG_EXTRA_SIZE;
82		bzero(sol_ofs_debug_sptr, sol_ofs_debug_buf_size);
83	}
84}
85
86/*
87 * sol_ofs_dprintf_init() and sol_ofs_dprintf_fini() must be called
88 * from the _init of the sol_ofs module.
89 */
90void
91sol_ofs_dprintf_init()
92{
93	char	*dbg_buf;
94
95	mutex_init(&sol_ofs_debug_mutex, NULL, MUTEX_DRIVER, NULL);
96
97	if (sol_ofs_debug_buf_size < SOL_OFS_DEBUG_EXTRA_SIZE) {
98#ifdef DEBUG
99		cmn_err(CE_NOTE, "sol_ofs:\t debug buf size 0x%x too small, "
100		    "setting to 0x%x", sol_ofs_debug_buf_size,
101		    SOL_OFS_DEBUG_BUF_SIZE);
102#endif
103		sol_ofs_debug_buf_size = SOL_OFS_DEBUG_BUF_SIZE;
104	}
105
106	dbg_buf = kmem_zalloc(sol_ofs_debug_buf_size, KM_SLEEP);
107	mutex_enter(&sol_ofs_debug_mutex);
108	sol_ofs_debug_buf = dbg_buf;
109	sol_ofs_clear_dbg_buf();
110	mutex_exit(&sol_ofs_debug_mutex);
111}
112
113void
114sol_ofs_dprintf_fini()
115{
116	char	*dbg_buf;
117
118	mutex_enter(&sol_ofs_debug_mutex);
119	dbg_buf = sol_ofs_debug_buf;
120	sol_ofs_debug_buf = NULL;
121	mutex_exit(&sol_ofs_debug_mutex);
122
123	kmem_free(dbg_buf, sol_ofs_debug_buf_size);
124	mutex_destroy(&sol_ofs_debug_mutex);
125}
126
127static void
128sol_ofs_dprintf_vlog(char *name, uint_t level, char *fmt, va_list ap)
129{
130	char	*label = (name == NULL) ? "sol_ofs_ulp" : name;
131	char	*msg_ptr;
132	size_t	len;
133
134	mutex_enter(&sol_ofs_debug_mutex);
135	/* if not using logging scheme; quit */
136	if (sol_ofs_suppress_dprintf || (sol_ofs_debug_buf == NULL)) {
137		mutex_exit(&sol_ofs_debug_mutex);
138		return;
139	}
140	/* if level doesn't match, we are done */
141	if (level > SOL_OFS_LOG_L5) {
142		mutex_exit(&sol_ofs_debug_mutex);
143		return;
144	}
145
146	/* If user requests to clear debug buffer, go ahead */
147	if (sol_ofs_clear_debug_buf_flag) {
148		sol_ofs_clear_dbg_buf();
149		sol_ofs_clear_debug_buf_flag = 0;
150	}
151
152	/* Skip printing to buffer, if too small */
153	if (sol_ofs_debug_buf_size <= 0) {
154		sol_ofs_buffer_dprintf = 0;
155	}
156
157	/* Put label and debug info into buffer */
158	len = snprintf((char *)sol_ofs_print_buf, SOL_OFS_DRV_NAME_LEN,
159	    "%s:\t", label);
160	msg_ptr = (char *)sol_ofs_print_buf + len;
161	len += vsnprintf(msg_ptr, SOL_OFS_PRINT_BUF_LEN - len - 2, fmt, ap);
162	len = min(len, SOL_OFS_PRINT_BUF_LEN - 2);
163	ASSERT(len == strlen(sol_ofs_print_buf));
164	sol_ofs_print_buf[len++] = '\n';
165	sol_ofs_print_buf[len] = '\0';
166
167	/* Stuff into debug buffer */
168	if (sol_ofs_buffer_dprintf) {
169		/*
170		 * overwrite >>>> that might be over the end of the
171		 * buffer.
172		 */
173		*sol_ofs_debug_sptr = '\0';
174
175		if (sol_ofs_debug_sptr + len > sol_ofs_debug_eptr) {
176			size_t left;
177
178			left = sol_ofs_debug_eptr - sol_ofs_debug_sptr;
179			bcopy((caddr_t)sol_ofs_print_buf,
180			    (caddr_t)sol_ofs_debug_sptr, left);
181			bcopy((caddr_t)sol_ofs_print_buf + left,
182			    (caddr_t)sol_ofs_debug_buf, len - left);
183			sol_ofs_debug_sptr = sol_ofs_debug_buf + len - left;
184		} else {
185			bcopy((caddr_t)sol_ofs_print_buf,
186			    (caddr_t)sol_ofs_debug_sptr, len);
187			sol_ofs_debug_sptr += len;
188		}
189	}
190
191	/*
192	 * L5-L2 message may go to the sol_ofs_debug_buf
193	 * L1 messages will go to the log buf in non-debug kernels and
194	 * to console and log buf in debug kernels
195	 * L0 messages are warnings and will go to console and log buf
196	 */
197	switch (level) {
198	case SOL_OFS_LOG_L5:
199	case SOL_OFS_LOG_L4:
200	case SOL_OFS_LOG_L3:
201	case SOL_OFS_LOG_L2:
202		if (!sol_ofs_buffer_dprintf) {
203			cmn_err(CE_CONT, "^%s", sol_ofs_print_buf);
204		}
205		break;
206	case SOL_OFS_LOG_L1 :
207#ifdef	DEBUG
208		cmn_err(CE_CONT, "%s", sol_ofs_print_buf);
209#else
210		if (!sol_ofs_buffer_dprintf) {
211			cmn_err(CE_CONT, "^%s", sol_ofs_print_buf);
212		}
213#endif
214		break;
215	case SOL_OFS_LOG_L0 :
216		/* Strip the "\n" added earlier */
217		if (sol_ofs_print_buf[len - 1] == '\n') {
218			sol_ofs_print_buf[len - 1] = '\0';
219		}
220		if (msg_ptr[len - 1] == '\n') {
221			msg_ptr[len - 1] = '\0';
222		}
223		cmn_err(CE_WARN, sol_ofs_print_buf);
224		break;
225	}
226
227	mutex_exit(&sol_ofs_debug_mutex);
228}
229
230/* Check individual error levels */
231#define	SOL_OFS_CHECK_ERR_LEVEL(level)			\
232	if (!(uint_t)strncmp(name, "sol_ucma", 8)) {	\
233		if (sol_ucma_errlevel < level)		\
234			return;				\
235	} else if (!(uint_t)strncmp(name, "sol_rdmacm", 10)) {	\
236		if (sol_rdmacm_errlevel < level)	\
237			return;				\
238	} else if (!(uint_t)strncmp(name, "sol_uverbs", 10)) {	\
239		if (sol_uverbs_errlevel < level)	\
240			return;				\
241	} else if (!(uint_t)strncmp(name, "sol_umad", 8)) {	\
242		if (sol_umad_errlevel < level)		\
243			return;				\
244	} else if (!(uint_t)strncmp(name, "sol_ofs_mod", 12)) {	\
245		if (sol_ofs_module_errlevel < level)	\
246			return;				\
247	} else if (strncmp(name, "sol_kverbs", 10) == 0) {	\
248		if (sol_kverbs_errlevel < level)		\
249			return;				\
250	} else if (sol_of_errlevel < level)		\
251		return;
252
253void
254sol_ofs_dprintf_l5(char *name, char *fmt, ...)
255{
256	va_list	ap;
257
258	if (sol_ofs_supress_above_l2)
259		return;
260	SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L5);
261
262	va_start(ap, fmt);
263	sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L5, fmt, ap);
264	va_end(ap);
265}
266
267void
268sol_ofs_dprintf_l4(char *name, char *fmt, ...)
269{
270	va_list	ap;
271
272	if (sol_ofs_supress_above_l2)
273		return;
274	SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L4);
275
276	va_start(ap, fmt);
277	sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L4, fmt, ap);
278	va_end(ap);
279}
280
281void
282sol_ofs_dprintf_l3(char *name, char *fmt, ...)
283{
284	va_list	ap;
285
286	if (sol_ofs_supress_above_l2)
287		return;
288	SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L3);
289
290	va_start(ap, fmt);
291	sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L3, fmt, ap);
292	va_end(ap);
293}
294
295void
296sol_ofs_dprintf_l2(char *name, char *fmt, ...)
297{
298	va_list	ap;
299
300	SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L2);
301
302	va_start(ap, fmt);
303	sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L2, fmt, ap);
304	va_end(ap);
305}
306
307void
308sol_ofs_dprintf_l1(char *name, char *fmt, ...)
309{
310	va_list	ap;
311
312	SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L1);
313
314	va_start(ap, fmt);
315	sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L1, fmt, ap);
316	va_end(ap);
317}
318
319void
320sol_ofs_dprintf_l0(char *name, char *fmt, ...)
321{
322	va_list	ap;
323
324	if (sol_of_errlevel < SOL_OFS_LOG_L0)
325		return;
326
327	va_start(ap, fmt);
328	sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L1, fmt, ap);
329	va_end(ap);
330}
331