xref: /titanic_52/usr/src/uts/common/io/ib/adapters/hermon/hermon_stats.c (revision 30e01c537fd78d139ff463ccb3ef064e7190f9a8)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * hermon_stats.c
29  *    Hermon IB Performance Statistics routines
30  *
31  *    Implements all the routines necessary for setting up, querying, and
32  *    (later) tearing down all the kstats necessary for implementing to
33  *    the interfaces necessary to provide busstat(1M) access.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/modctl.h>
41 
42 #include <sys/ib/adapters/hermon/hermon.h>
43 
44 static kstat_t *hermon_kstat_picN_create(hermon_state_t *state, int num_pic,
45     int num_evt, hermon_ks_mask_t *ev_array);
46 static kstat_t *hermon_kstat_cntr_create(hermon_state_t *state, int num_pic,
47     int (*update)(kstat_t *, int));
48 static int hermon_kstat_cntr_update(kstat_t *ksp, int rw);
49 
50 void hermon_kstat_perfcntr64_create(hermon_state_t *state, uint_t port_num);
51 static int hermon_kstat_perfcntr64_read(hermon_state_t *state, uint_t port,
52     int reset);
53 static void hermon_kstat_perfcntr64_thread_exit(hermon_ks_info_t *ksi);
54 static int hermon_kstat_perfcntr64_update(kstat_t *ksp, int rw);
55 
56 /*
57  * Hermon IB Performance Events structure
58  *    This structure is read-only and is used to setup the individual kstats
59  *    and to initialize the tki_ib_perfcnt[] array for each Hermon instance.
60  */
61 hermon_ks_mask_t hermon_ib_perfcnt_list[HERMON_CNTR_NUMENTRIES] = {
62 	{"port_xmit_data", 0, 0},
63 	{"port_recv_data", 0, 0},
64 	{"port_xmit_pkts", 0, 0},
65 	{"port_recv_pkts", 0, 0},
66 	{"port_recv_err", 0, 0},
67 	{"port_xmit_discards", 0, 0},
68 	{"vl15_dropped", 0, 0},
69 	{"port_xmit_wait", 0, 0},
70 	{"port_recv_remote_phys_err", 0, 0},
71 	{"port_xmit_constraint_err", 0, 0},
72 	{"port_recv_constraint_err", 0, 0},
73 	{"symbol_err_counter", 0, 0},
74 	{"link_err_recovery_cnt", 0, 0},
75 	{"link_downed_cnt", 0, 0},
76 	{"excessive_buffer_overruns", 0, 0},
77 	{"local_link_integrity_err", 0, 0},
78 	{"clear_pic", 0, 0}
79 };
80 
81 /*
82  * Return the maximum of (x) and (y)
83  */
84 #define	MAX(x, y)	(((x) > (y)) ? (x) : (y))
85 
86 /*
87  * Set (x) to the maximum of (x) and (y)
88  */
89 #define	SET_TO_MAX(x, y)	\
90 {				\
91 	if ((x) < (y))		\
92 		(x) = (y);	\
93 }
94 
95 /*
96  * hermon_kstat_init()
97  *    Context: Only called from attach() path context
98  */
99 int
100 hermon_kstat_init(hermon_state_t *state)
101 {
102 	hermon_ks_info_t		*ksi;
103 	uint_t			numports;
104 	int			i;
105 
106 	/* Allocate a kstat info structure */
107 	ksi = (hermon_ks_info_t *)kmem_zalloc(sizeof (hermon_ks_info_t),
108 	    KM_SLEEP);
109 	if (ksi == NULL) {
110 		return (DDI_FAILURE);
111 	}
112 	state->hs_ks_info = ksi;
113 
114 	/*
115 	 * Create as many "pic" and perfcntr64 kstats as we have IB ports.
116 	 * Enable all of the events specified in the "hermon_ib_perfcnt_list"
117 	 * structure.
118 	 */
119 	numports = state->hs_cfg_profile->cp_num_ports;
120 	for (i = 0; i < numports; i++) {
121 		ksi->hki_picN_ksp[i] = hermon_kstat_picN_create(state, i,
122 		    HERMON_CNTR_NUMENTRIES, hermon_ib_perfcnt_list);
123 		if (ksi->hki_picN_ksp[i] == NULL) {
124 			goto kstat_init_fail;
125 		}
126 
127 		hermon_kstat_perfcntr64_create(state, i + 1);
128 		if (ksi->hki_perfcntr64[i].hki64_ksp == NULL) {
129 			goto kstat_init_fail;
130 		}
131 	}
132 
133 	/* Create the "counters" kstat too */
134 	ksi->hki_cntr_ksp = hermon_kstat_cntr_create(state, numports,
135 	    hermon_kstat_cntr_update);
136 	if (ksi->hki_cntr_ksp == NULL) {
137 		goto kstat_init_fail;
138 	}
139 
140 	/* Initialize the control register and initial counter values */
141 	ksi->hki_pcr  = 0;
142 	ksi->hki_pic0 = 0;
143 	ksi->hki_pic1 = 0;
144 
145 	/*
146 	 * Initialize the Hermon hki_ib_perfcnt[] array values using the
147 	 * default values in hermon_ib_perfcnt_list[]
148 	 */
149 	for (i = 0; i < HERMON_CNTR_NUMENTRIES; i++) {
150 		ksi->hki_ib_perfcnt[i] = hermon_ib_perfcnt_list[i];
151 	}
152 
153 	mutex_init(&ksi->hki_perfcntr64_lock, NULL, MUTEX_DRIVER, NULL);
154 	cv_init(&ksi->hki_perfcntr64_cv, NULL, CV_DRIVER, NULL);
155 
156 	return (DDI_SUCCESS);
157 
158 
159 kstat_init_fail:
160 
161 	/* Delete all the previously created kstats */
162 	if (ksi->hki_cntr_ksp != NULL) {
163 		kstat_delete(ksi->hki_cntr_ksp);
164 	}
165 	for (i = 0; i < numports; i++) {
166 		if (ksi->hki_picN_ksp[i] != NULL) {
167 			kstat_delete(ksi->hki_picN_ksp[i]);
168 		}
169 		if (ksi->hki_perfcntr64[i].hki64_ksp != NULL) {
170 			kstat_delete(ksi->hki_perfcntr64[i].hki64_ksp);
171 		}
172 	}
173 
174 	/* Free the kstat info structure */
175 	kmem_free(ksi, sizeof (hermon_ks_info_t));
176 
177 	return (DDI_FAILURE);
178 }
179 
180 
181 /*
182  * hermon_kstat_init()
183  *    Context: Only called from attach() and/or detach() path contexts
184  */
185 void
186 hermon_kstat_fini(hermon_state_t *state)
187 {
188 	hermon_ks_info_t	*ksi;
189 	uint_t			numports;
190 	int			i;
191 
192 	/* Get pointer to kstat info */
193 	ksi = state->hs_ks_info;
194 
195 	/*
196 	 * Signal the perfcntr64_update_thread to exit and wait until the
197 	 * thread exits.
198 	 */
199 	mutex_enter(&ksi->hki_perfcntr64_lock);
200 	hermon_kstat_perfcntr64_thread_exit(ksi);
201 	mutex_exit(&ksi->hki_perfcntr64_lock);
202 
203 	/* Delete all the "pic" and perfcntr64 kstats (one per port) */
204 	numports = state->hs_cfg_profile->cp_num_ports;
205 	for (i = 0; i < numports; i++) {
206 		if (ksi->hki_picN_ksp[i] != NULL) {
207 			kstat_delete(ksi->hki_picN_ksp[i]);
208 		}
209 
210 		if (ksi->hki_perfcntr64[i].hki64_ksp != NULL) {
211 			kstat_delete(ksi->hki_perfcntr64[i].hki64_ksp);
212 		}
213 	}
214 
215 	/* Delete the "counter" kstats (one per port) */
216 	kstat_delete(ksi->hki_cntr_ksp);
217 
218 	cv_destroy(&ksi->hki_perfcntr64_cv);
219 	mutex_destroy(&ksi->hki_perfcntr64_lock);
220 
221 	/* Free the kstat info structure */
222 	kmem_free(ksi, sizeof (hermon_ks_info_t));
223 }
224 
225 
226 /*
227  * hermon_kstat_picN_create()
228  *    Context: Only called from attach() path context
229  */
230 static kstat_t *
231 hermon_kstat_picN_create(hermon_state_t *state, int num_pic, int num_evt,
232     hermon_ks_mask_t *ev_array)
233 {
234 	kstat_t			*picN_ksp;
235 	struct kstat_named	*pic_named_data;
236 	int			drv_instance, i;
237 	char			*drv_name;
238 	char			pic_name[16];
239 
240 	/*
241 	 * Create the "picN" kstat.  In the steps, below we will attach
242 	 * all of our named event types to it.
243 	 */
244 	drv_name = (char *)ddi_driver_name(state->hs_dip);
245 	drv_instance = ddi_get_instance(state->hs_dip);
246 	(void) sprintf(pic_name, "pic%d", num_pic);
247 	picN_ksp = kstat_create(drv_name, drv_instance, pic_name, "bus",
248 	    KSTAT_TYPE_NAMED, num_evt, NULL);
249 	if (picN_ksp == NULL) {
250 		return (NULL);
251 	}
252 	pic_named_data = (struct kstat_named *)(picN_ksp->ks_data);
253 
254 	/*
255 	 * Write event names and their associated pcr masks. The last entry
256 	 * in the array (clear_pic) is added separately below (as its pic
257 	 * value must be inverted).
258 	 */
259 	for (i = 0; i < num_evt - 1; i++) {
260 		pic_named_data[i].value.ui64 =
261 		    ((uint64_t)i << (num_pic * HERMON_CNTR_SIZE));
262 		kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
263 		    KSTAT_DATA_UINT64);
264 	}
265 
266 	/* Add the "clear_pic" entry */
267 	pic_named_data[i].value.ui64 =
268 	    ~((uint64_t)HERMON_CNTR_MASK << (num_pic * HERMON_CNTR_SIZE));
269 	kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
270 	    KSTAT_DATA_UINT64);
271 
272 	/* Install the kstat */
273 	kstat_install(picN_ksp);
274 
275 	return (picN_ksp);
276 }
277 
278 
279 /*
280  * hermon_kstat_cntr_create()
281  *    Context: Only called from attach() path context
282  */
283 static kstat_t *
284 hermon_kstat_cntr_create(hermon_state_t *state, int num_pic,
285     int (*update)(kstat_t *, int))
286 {
287 	struct kstat		*cntr_ksp;
288 	struct kstat_named	*cntr_named_data;
289 	int			drv_instance, i;
290 	char			*drv_name;
291 	char			pic_str[16];
292 
293 	/*
294 	 * Create the "counters" kstat.  In the steps, below we will attach
295 	 * all of our "pic" to it.   Note:  The size of this kstat is
296 	 * num_pic + 1 because it also contains the "%pcr".
297 	 */
298 	drv_name = (char *)ddi_driver_name(state->hs_dip);
299 	drv_instance = ddi_get_instance(state->hs_dip);
300 	cntr_ksp = kstat_create(drv_name, drv_instance, "counters", "bus",
301 	    KSTAT_TYPE_NAMED, num_pic + 1, KSTAT_FLAG_WRITABLE);
302 	if (cntr_ksp == NULL) {
303 		return (NULL);
304 	}
305 	cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data);
306 
307 	/*
308 	 * Initialize the named kstats (for the "pcr" and for the
309 	 * individual "pic" kstats)
310 	 */
311 	kstat_named_init(&cntr_named_data[0], "pcr", KSTAT_DATA_UINT64);
312 	for (i = 0; i < num_pic; i++) {
313 		(void) sprintf(pic_str, "pic%d", i);
314 		kstat_named_init(&cntr_named_data[i+1], pic_str,
315 		    KSTAT_DATA_UINT64);
316 	}
317 
318 	/*
319 	 * Store the Hermon softstate pointer in the kstat's private field so
320 	 * that it is available to the update function.
321 	 */
322 	cntr_ksp->ks_private = (void *)state;
323 	cntr_ksp->ks_update  = update;
324 
325 	/* Install the kstat */
326 	kstat_install(cntr_ksp);
327 
328 	return (cntr_ksp);
329 }
330 
331 
332 /*
333  * hermon_kstat_cntr_update()
334  *    Context: Called from the kstat context
335  */
336 static int
337 hermon_kstat_cntr_update(kstat_t *ksp, int rw)
338 {
339 	hermon_state_t		*state;
340 	hermon_ks_mask_t		*ib_perf;
341 	hermon_ks_info_t		*ksi;
342 	struct kstat_named	*data;
343 	uint64_t		pcr;
344 	uint32_t		tmp;
345 	uint32_t		oldval;
346 	uint_t			numports, indx;
347 	int			status;
348 	hermon_hw_sm_perfcntr_t	sm_perfcntr;
349 
350 	/*
351 	 * Extract the Hermon softstate pointer, kstat data, pointer to the
352 	 * kstat info structure, and pointer to the hki_ib_perfcnt[] array
353 	 * from the input parameters.  Note: For warlock purposes, these
354 	 * parameters are all accessed only in this routine and are,
355 	 * therefore, protected by the lock used by the kstat framework.
356 	 */
357 	state	= ksp->ks_private;
358 	data	= (struct kstat_named *)(ksp->ks_data);
359 	ksi	= state->hs_ks_info;
360 	ib_perf = &ksi->hki_ib_perfcnt[0];
361 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ksi))
362 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*data))
363 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ib_perf))
364 
365 	/*
366 	 * Depending on whether we are reading the "pic" counters or
367 	 * writing the "pcr" control register, we need to handle and
368 	 * fill in the kstat data appropriately.
369 	 *
370 	 * If this is a write to the "pcr", then extract the value from
371 	 * the kstat data and store it in the kstat info structure.
372 	 *
373 	 * Otherwise, if this is a read of the "pic" counter(s), then
374 	 * extract the register offset, size, and mask values from the
375 	 * ib_perf[] array.  Then read the corresponding register and store
376 	 * it into the kstat data.  Note:  We only read/fill in pic1 if more
377 	 * than one port is configured.
378 	 */
379 	numports = state->hs_cfg_profile->cp_num_ports;
380 	if (rw == KSTAT_WRITE) {
381 		/* Update the stored "pcr" value */
382 		ksi->hki_pcr = data[0].value.ui64;
383 		return (0);
384 	} else {
385 		/*
386 		 * Get the current "pcr" value and extract the lower
387 		 * portion (corresponding to the counters for "pic0")
388 		 */
389 		pcr  = ksi->hki_pcr;
390 		indx = pcr & HERMON_CNTR_MASK;
391 		data[0].value.ui64 = pcr;
392 
393 		/*
394 		 * Fill in the "pic0" counter, corresponding to port 1.
395 		 * This involves reading in the current value in the register
396 		 * and calculating how many events have happened since this
397 		 * register was last polled.  Then we save away the current
398 		 * value for the counter and increment the "pic0" total by
399 		 * the number of new events.
400 		 */
401 		oldval = ib_perf[indx].ks_old_pic0;
402 
403 		status = hermon_getperfcntr_cmd_post(state, 1,
404 		    HERMON_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0);
405 		if (status != HERMON_CMD_SUCCESS) {
406 			return (-1);
407 		}
408 		switch (indx) {
409 		case 0:		/* port_xmit_data */
410 			tmp = sm_perfcntr.portxmdata;
411 			break;
412 		case 1:		/* port_recv_data */
413 			tmp = sm_perfcntr.portrcdata;
414 			break;
415 		case 2:		/* port_xmit_pkts */
416 			tmp = sm_perfcntr.portxmpkts;
417 			break;
418 		case 3:		/* port_recv_pkts */
419 			tmp = sm_perfcntr.portrcpkts;
420 			break;
421 		case 4:		/* port_recv_err */
422 			tmp = sm_perfcntr.portrcv;
423 			break;
424 		case 5:		/* port_xmit_discards */
425 			tmp = sm_perfcntr.portxmdiscard;
426 			break;
427 		case 6:		/* vl15_dropped */
428 			tmp = sm_perfcntr.vl15drop;
429 			break;
430 		case 7:		/* port_xmit_wait */
431 			tmp = sm_perfcntr.portxmwait;
432 			break;
433 		case 8:		/* port_recv_remote_phys_err */
434 			tmp = sm_perfcntr.portrcvrem;
435 			break;
436 		case 9:		/* port_xmit_constraint_err */
437 			tmp = sm_perfcntr.portxmconstr;
438 			break;
439 		case 10:	/* port_recv_constraint_err */
440 			tmp = sm_perfcntr.portrcconstr;
441 			break;
442 		case 11:	/* symbol_err_counter */
443 			tmp = sm_perfcntr.symerr;
444 			break;
445 		case 12:	/* link_err_recovery_cnt */
446 			tmp = sm_perfcntr.linkerrrec;
447 			break;
448 		case 13:	/* link_downed_cnt */
449 			tmp = sm_perfcntr.linkdown;
450 			break;
451 		case 14:	/* excessive_buffer_overruns */
452 			tmp = sm_perfcntr.xsbuffovrun;
453 			break;
454 		case 15:	/* local_link_integrity_err */
455 			tmp = sm_perfcntr.locallinkint;
456 			break;
457 		case 16:	/* clear_pic */
458 			tmp = 0;	/* XXX */
459 			break;
460 		default:
461 			cmn_err(CE_CONT, "perf counter out of range\n");
462 		}
463 
464 		ib_perf[indx].ks_old_pic0 = tmp;
465 
466 		tmp = tmp - oldval;
467 		ksi->hki_pic0 += tmp;
468 		data[1].value.ui64 = ksi->hki_pic0;
469 
470 		/*
471 		 * If necessary, fill in the "pic1" counter for port 2.
472 		 * This works the same as above except that we extract the
473 		 * upper bits (corresponding to the counters for "pic1")
474 		 */
475 		if (numports == HERMON_MAX_PORTS) {
476 			indx   = pcr >> HERMON_CNTR_SIZE;
477 			oldval = ib_perf[indx].ks_old_pic1;
478 
479 			status = hermon_getperfcntr_cmd_post(state, 2,
480 			    HERMON_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0);
481 			if (status != HERMON_CMD_SUCCESS) {
482 				return (-1);
483 			}
484 			switch (indx) {
485 			case 0:		/* port_xmit_data */
486 				tmp = sm_perfcntr.portxmdata;
487 				break;
488 			case 1:		/* port_recv_data */
489 				tmp = sm_perfcntr.portrcdata;
490 				break;
491 			case 2:		/* port_xmit_pkts */
492 				tmp = sm_perfcntr.portxmpkts;
493 				break;
494 			case 3:		/* port_recv_pkts */
495 				tmp = sm_perfcntr.portrcpkts;
496 				break;
497 			case 4:		/* port_recv_err */
498 				tmp = sm_perfcntr.portrcv;
499 				break;
500 			case 5:		/* port_xmit_discards */
501 				tmp = sm_perfcntr.portxmdiscard;
502 				break;
503 			case 6:		/* vl15_dropped */
504 				tmp = sm_perfcntr.vl15drop;
505 				break;
506 			case 7:		/* port_xmit_wait */
507 				tmp = sm_perfcntr.portxmwait;
508 				break;
509 			case 8:		/* port_recv_remote_phys_err */
510 				tmp = sm_perfcntr.portrcvrem;
511 				break;
512 			case 9:		/* port_xmit_constraint_err */
513 				tmp = sm_perfcntr.portxmconstr;
514 				break;
515 			case 10:	/* port_recv_constraint_err */
516 				tmp = sm_perfcntr.portrcconstr;
517 				break;
518 			case 11:	/* symbol_err_counter */
519 				tmp = sm_perfcntr.symerr;
520 				break;
521 			case 12:	/* link_err_recovery_cnt */
522 				tmp = sm_perfcntr.linkerrrec;
523 				break;
524 			case 13:	/* link_downed_cnt */
525 				tmp = sm_perfcntr.linkdown;
526 				break;
527 			case 14:	/* excessive_buffer_overruns */
528 				tmp = sm_perfcntr.xsbuffovrun;
529 				break;
530 			case 15:	/* local_link_integrity_err */
531 				tmp = sm_perfcntr.locallinkint;
532 				break;
533 			case 16:	/* clear_pic */
534 				tmp = 0;	/* XXX */
535 				break;
536 			default:
537 				cmn_err(CE_CONT, "perf counter out of range\n");
538 			}
539 
540 			ib_perf[indx].ks_old_pic1 = tmp;
541 
542 			tmp = tmp - oldval;
543 			ksi->hki_pic1 += tmp;
544 			data[2].value.ui64 = ksi->hki_pic1;
545 		}
546 
547 		return (0);
548 	}
549 }
550 
551 /*
552  * 64 bit kstats for performance counters:
553  *
554  * Export 64 bit performance counters in kstats.
555  *
556  * If the HCA hardware supports 64 bit extended port counters, we use the
557  * hardware based counters. If the HCA hardware does not support extended port
558  * counters, we maintain 64 bit performance counters in software using the
559  * 32 bit hardware port counters.
560  *
561  * The software based counters are maintained as follows:
562  *
563  * We create a thread that, every one second, reads the values of 32 bit
564  * hardware counters and adds them to the 64 bit software counters. Immediately
565  * after reading, it resets the 32 bit hardware counters to zero (so that they
566  * start counting from zero again). At any time the current value of a counter
567  * is going to be the sum of the 64 bit software counter and the 32 bit
568  * hardware counter.
569  *
570  * Since this work need not be done if there is no consumer, by default
571  * we do not maintain 64 bit software counters. To enable this the consumer
572  * needs to write a non-zero value to the "enable" component of the of
573  * perf_counters kstat. Writing zero to this component will disable this work.
574  * NOTE: The enabling or disabling applies to software based counters only.
575  * Hardware based counters counters are always enabled.
576  *
577  * If performance monitor is enabled in subnet manager, the SM could
578  * periodically reset the hardware counters by sending perf-MADs. So only
579  * one of either our software 64 bit counters or the SM performance monitor
580  * could be enabled at the same time. However, if both of them are enabled at
581  * the same time we still do our best by keeping track of the values of the
582  * last read 32 bit hardware counters. If the current read of a 32 bit hardware
583  * counter is less than the last read of the counter, we ignore the current
584  * value and go with the last read value.
585  */
586 
587 /*
588  * hermon_kstat_perfcntr64_create()
589  *    Context: Only called from attach() path context
590  *
591  * Create "port#/perf_counters" kstat for the specified port number.
592  */
593 void
594 hermon_kstat_perfcntr64_create(hermon_state_t *state, uint_t port_num)
595 {
596 	hermon_ks_info_t	*ksi = state->hs_ks_info;
597 	struct kstat		*cntr_ksp;
598 	struct kstat_named	*cntr_named_data;
599 	int			drv_instance;
600 	char			*drv_name;
601 	char			kname[32];
602 	int			status, ext_width_supported;
603 
604 	ASSERT(port_num != 0);
605 
606 	status = hermon_is_ext_port_counters_supported(state, port_num,
607 	    HERMON_CMD_NOSLEEP_SPIN, &ext_width_supported);
608 	if (status == HERMON_CMD_SUCCESS) {
609 		ksi->hki_perfcntr64[port_num - 1].
610 		    hki64_ext_port_counters_supported = ext_width_supported;
611 	}
612 
613 	drv_name = (char *)ddi_driver_name(state->hs_dip);
614 	drv_instance = ddi_get_instance(state->hs_dip);
615 	(void) snprintf(kname, sizeof (kname), "port%u/perf_counters",
616 	    port_num);
617 	cntr_ksp = kstat_create(drv_name, drv_instance, kname, "ib",
618 	    KSTAT_TYPE_NAMED, HERMON_PERFCNTR64_NUM_COUNTERS,
619 	    KSTAT_FLAG_WRITABLE);
620 	if (cntr_ksp == NULL) {
621 		return;
622 	}
623 	cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data);
624 
625 	kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_ENABLE_IDX],
626 	    "enable", KSTAT_DATA_UINT32);
627 	kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_XMIT_DATA_IDX],
628 	    "xmit_data", KSTAT_DATA_UINT64);
629 	kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_RECV_DATA_IDX],
630 	    "recv_data", KSTAT_DATA_UINT64);
631 	kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_XMIT_PKTS_IDX],
632 	    "xmit_pkts", KSTAT_DATA_UINT64);
633 	kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_RECV_PKTS_IDX],
634 	    "recv_pkts", KSTAT_DATA_UINT64);
635 
636 	ksi->hki_perfcntr64[port_num - 1].hki64_ksp = cntr_ksp;
637 	ksi->hki_perfcntr64[port_num - 1].hki64_port_num = port_num;
638 	ksi->hki_perfcntr64[port_num - 1].hki64_state = state;
639 
640 	cntr_ksp->ks_private = &ksi->hki_perfcntr64[port_num - 1];
641 	cntr_ksp->ks_update  = hermon_kstat_perfcntr64_update;
642 
643 	/* Install the kstat */
644 	kstat_install(cntr_ksp);
645 }
646 
647 /*
648  * hermon_kstat_perfcntr64_read()
649  *
650  * Read the values of 32 bit hardware counters.
651  *
652  * If reset is true, reset the 32 bit hardware counters. Add the values of the
653  * 32 bit hardware counters to the 64 bit software counters.
654  *
655  * If reset is false, just save the values read from the 32 bit hardware
656  * counters in hki64_last_read[].
657  *
658  * See the general comment on the 64 bit performance counters
659  * regarding the use of last read 32 bit hardware counter values.
660  */
661 static int
662 hermon_kstat_perfcntr64_read(hermon_state_t *state, uint_t port, int reset)
663 {
664 	hermon_ks_info_t	*ksi = state->hs_ks_info;
665 	hermon_perfcntr64_ks_info_t *ksi64 = &ksi->hki_perfcntr64[port - 1];
666 	int			status, i;
667 	uint32_t		tmp;
668 	hermon_hw_sm_perfcntr_t	sm_perfcntr;
669 
670 	ASSERT(MUTEX_HELD(&ksi->hki_perfcntr64_lock));
671 	ASSERT(port != 0);
672 
673 	/* read the 32 bit hardware counters */
674 	status = hermon_getperfcntr_cmd_post(state, port,
675 	    HERMON_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0);
676 	if (status != HERMON_CMD_SUCCESS) {
677 		return (status);
678 	}
679 
680 	if (reset) {
681 		/* reset the hardware counters */
682 		status = hermon_getperfcntr_cmd_post(state, port,
683 		    HERMON_CMD_NOSLEEP_SPIN, NULL, 1);
684 		if (status != HERMON_CMD_SUCCESS) {
685 			return (status);
686 		}
687 
688 		/*
689 		 * Update 64 bit software counters
690 		 */
691 		tmp = MAX(sm_perfcntr.portxmdata,
692 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_DATA_IDX]);
693 		ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_DATA_IDX] += tmp;
694 
695 		tmp = MAX(sm_perfcntr.portrcdata,
696 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_DATA_IDX]);
697 		ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_DATA_IDX] += tmp;
698 
699 		tmp = MAX(sm_perfcntr.portxmpkts,
700 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_PKTS_IDX]);
701 		ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_PKTS_IDX] += tmp;
702 
703 		tmp = MAX(sm_perfcntr.portrcpkts,
704 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_PKTS_IDX]);
705 		ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_PKTS_IDX] += tmp;
706 
707 		for (i = 0; i < HERMON_PERFCNTR64_NUM_COUNTERS; i++)
708 			ksi64->hki64_last_read[i] = 0;
709 
710 	} else {
711 		/*
712 		 * Update ksi64->hki64_last_read[]
713 		 */
714 		SET_TO_MAX(
715 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_DATA_IDX],
716 		    sm_perfcntr.portxmdata);
717 
718 		SET_TO_MAX(
719 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_DATA_IDX],
720 		    sm_perfcntr.portrcdata);
721 
722 		SET_TO_MAX(
723 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_PKTS_IDX],
724 		    sm_perfcntr.portxmpkts);
725 
726 		SET_TO_MAX(
727 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_PKTS_IDX],
728 		    sm_perfcntr.portrcpkts);
729 	}
730 
731 	return (HERMON_CMD_SUCCESS);
732 }
733 
734 /*
735  * hermon_kstat_perfcntr64_update_thread()
736  *    Context: Entry point for a kernel thread
737  *
738  * Maintain 64 bit performance counters in software using the 32 bit
739  * hardware counters.
740  */
741 static void
742 hermon_kstat_perfcntr64_update_thread(void *arg)
743 {
744 	hermon_state_t		*state = (hermon_state_t *)arg;
745 	hermon_ks_info_t	*ksi = state->hs_ks_info;
746 	uint_t			i;
747 	clock_t			delta = drv_usectohz(1000000);
748 
749 	mutex_enter(&ksi->hki_perfcntr64_lock);
750 	/*
751 	 * Every one second update the values 64 bit software counters
752 	 * for all ports. Exit if HERMON_PERFCNTR64_THREAD_EXIT flag is set.
753 	 */
754 	while (!(ksi->hki_perfcntr64_flags & HERMON_PERFCNTR64_THREAD_EXIT)) {
755 		for (i = 0; i < state->hs_cfg_profile->cp_num_ports; i++) {
756 			if (ksi->hki_perfcntr64[i].hki64_enabled) {
757 				(void) hermon_kstat_perfcntr64_read(state,
758 				    i + 1, 1);
759 			}
760 		}
761 		/* sleep for a second */
762 		(void) cv_reltimedwait(&ksi->hki_perfcntr64_cv,
763 		    &ksi->hki_perfcntr64_lock, delta, TR_CLOCK_TICK);
764 	}
765 	ksi->hki_perfcntr64_flags = 0;
766 	mutex_exit(&ksi->hki_perfcntr64_lock);
767 }
768 
769 /*
770  * hermon_kstat_perfcntr64_thread_create()
771  *    Context: Called from the kstat context
772  *
773  * Create a thread that maintains 64 bit performance counters in software.
774  */
775 static void
776 hermon_kstat_perfcntr64_thread_create(hermon_state_t *state)
777 {
778 	hermon_ks_info_t	*ksi = state->hs_ks_info;
779 	kthread_t		*thr;
780 
781 	ASSERT(MUTEX_HELD(&ksi->hki_perfcntr64_lock));
782 
783 	/*
784 	 * One thread per hermon instance. Don't create a thread if already
785 	 * created.
786 	 */
787 	if (!(ksi->hki_perfcntr64_flags & HERMON_PERFCNTR64_THREAD_CREATED)) {
788 		thr = thread_create(NULL, 0,
789 		    hermon_kstat_perfcntr64_update_thread,
790 		    state, 0, &p0, TS_RUN, minclsyspri);
791 		ksi->hki_perfcntr64_thread_id = thr->t_did;
792 		ksi->hki_perfcntr64_flags |= HERMON_PERFCNTR64_THREAD_CREATED;
793 	}
794 }
795 
796 /*
797  * hermon_kstat_perfcntr64_thread_exit()
798  *    Context: Called from attach, detach or kstat context
799  */
800 static void
801 hermon_kstat_perfcntr64_thread_exit(hermon_ks_info_t *ksi)
802 {
803 	kt_did_t	tid;
804 
805 	ASSERT(MUTEX_HELD(&ksi->hki_perfcntr64_lock));
806 
807 	if (ksi->hki_perfcntr64_flags & HERMON_PERFCNTR64_THREAD_CREATED) {
808 		/*
809 		 * Signal the thread to exit and wait until the thread exits.
810 		 */
811 		ksi->hki_perfcntr64_flags |= HERMON_PERFCNTR64_THREAD_EXIT;
812 		tid = ksi->hki_perfcntr64_thread_id;
813 		cv_signal(&ksi->hki_perfcntr64_cv);
814 
815 		mutex_exit(&ksi->hki_perfcntr64_lock);
816 		thread_join(tid);
817 		mutex_enter(&ksi->hki_perfcntr64_lock);
818 	}
819 }
820 
821 /*
822  * hermon_kstat_perfcntr64_update_ext()
823  *    Context: Called from the kstat context
824  *
825  * Update perf_counters kstats with the values of the extended port counters
826  * from the hardware.
827  */
828 static int
829 hermon_kstat_perfcntr64_update_ext(hermon_perfcntr64_ks_info_t *ksi64, int rw,
830     struct kstat_named *data)
831 {
832 	hermon_hw_sm_extperfcntr_t	sm_extperfcntr;
833 
834 	/*
835 	 * The "enable" component of the kstat is the only writable kstat.
836 	 * It is a no-op when the hardware supports extended port counters.
837 	 */
838 	if (rw == KSTAT_WRITE)
839 		return (0);
840 
841 	/*
842 	 * Read the counters and update kstats.
843 	 */
844 	if (hermon_getextperfcntr_cmd_post(ksi64->hki64_state,
845 	    ksi64->hki64_port_num, HERMON_CMD_NOSLEEP_SPIN, &sm_extperfcntr) !=
846 	    HERMON_CMD_SUCCESS) {
847 		return (EIO);
848 	}
849 
850 	data[HERMON_PERFCNTR64_ENABLE_IDX].value.ui32 = 1;
851 
852 	data[HERMON_PERFCNTR64_XMIT_DATA_IDX].value.ui64 =
853 	    sm_extperfcntr.portxmdata;
854 
855 	data[HERMON_PERFCNTR64_RECV_DATA_IDX].value.ui64 =
856 	    sm_extperfcntr.portrcdata;
857 
858 	data[HERMON_PERFCNTR64_XMIT_PKTS_IDX].value.ui64 =
859 	    sm_extperfcntr.portxmpkts;
860 
861 	data[HERMON_PERFCNTR64_RECV_PKTS_IDX].value.ui64 =
862 	    sm_extperfcntr.portrcpkts;
863 
864 	return (0);
865 }
866 
867 /*
868  * hermon_kstat_perfcntr64_update()
869  *    Context: Called from the kstat context
870  *
871  * See the general comment on 64 bit kstats for performance counters:
872  */
873 static int
874 hermon_kstat_perfcntr64_update(kstat_t *ksp, int rw)
875 {
876 	hermon_state_t			*state;
877 	struct kstat_named		*data;
878 	hermon_ks_info_t		*ksi;
879 	hermon_perfcntr64_ks_info_t	*ksi64;
880 	int				i, thr_exit;
881 	int				rv;
882 
883 	ksi64	= ksp->ks_private;
884 	state	= ksi64->hki64_state;
885 	ksi	= state->hs_ks_info;
886 	data	= (struct kstat_named *)(ksp->ks_data);
887 
888 	mutex_enter(&ksi->hki_perfcntr64_lock);
889 
890 	if (ksi64->hki64_ext_port_counters_supported) {
891 		rv = hermon_kstat_perfcntr64_update_ext(ksi64, rw, data);
892 		mutex_exit(&ksi->hki_perfcntr64_lock);
893 		return (rv);
894 	}
895 
896 	/*
897 	 * 64 bit performance counters maintained by the software is not
898 	 * enabled by default. Enable them upon a writing a non-zero value
899 	 * to "enable" kstat. Disable them upon a writing zero to the
900 	 * "enable" kstat.
901 	 */
902 	if (rw == KSTAT_WRITE) {
903 		if (data[HERMON_PERFCNTR64_ENABLE_IDX].value.ui32) {
904 			if (ksi64->hki64_enabled == 0) {
905 				/*
906 				 * Reset the hardware counters to ensure that
907 				 * the hardware counter doesn't max out
908 				 * (and hence stop counting) before we get
909 				 * a chance to reset the counter in
910 				 * hermon_kstat_perfcntr64_update_thread.
911 				 */
912 				if (hermon_getperfcntr_cmd_post(state,
913 				    ksi64->hki64_port_num,
914 				    HERMON_CMD_NOSLEEP_SPIN, NULL, 1) !=
915 				    HERMON_CMD_SUCCESS) {
916 					mutex_exit(&ksi->hki_perfcntr64_lock);
917 					return (EIO);
918 				}
919 
920 				/* Enable 64 bit software counters */
921 				ksi64->hki64_enabled = 1;
922 				for (i = 0;
923 				    i < HERMON_PERFCNTR64_NUM_COUNTERS; i++) {
924 					ksi64->hki64_counters[i] = 0;
925 					ksi64->hki64_last_read[i] = 0;
926 				}
927 				hermon_kstat_perfcntr64_thread_create(state);
928 			}
929 
930 		} else if (ksi64->hki64_enabled) {
931 			/* Disable 64 bit software counters */
932 			ksi64->hki64_enabled = 0;
933 			thr_exit = 1;
934 			for (i = 0; i < state->hs_cfg_profile->cp_num_ports;
935 			    i++) {
936 				if (ksi->hki_perfcntr64[i].hki64_enabled) {
937 					thr_exit = 0;
938 					break;
939 				}
940 			}
941 			if (thr_exit)
942 				hermon_kstat_perfcntr64_thread_exit(ksi);
943 		}
944 	} else if (ksi64->hki64_enabled) {
945 		/*
946 		 * Read the counters and update kstats.
947 		 */
948 		if (hermon_kstat_perfcntr64_read(state, ksi64->hki64_port_num,
949 		    0) != HERMON_CMD_SUCCESS) {
950 			mutex_exit(&ksi->hki_perfcntr64_lock);
951 			return (EIO);
952 		}
953 
954 		data[HERMON_PERFCNTR64_ENABLE_IDX].value.ui32 = 1;
955 
956 		data[HERMON_PERFCNTR64_XMIT_DATA_IDX].value.ui64 =
957 		    ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_DATA_IDX] +
958 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_DATA_IDX];
959 
960 		data[HERMON_PERFCNTR64_RECV_DATA_IDX].value.ui64 =
961 		    ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_DATA_IDX] +
962 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_DATA_IDX];
963 
964 		data[HERMON_PERFCNTR64_XMIT_PKTS_IDX].value.ui64 =
965 		    ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_PKTS_IDX] +
966 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_PKTS_IDX];
967 
968 		data[HERMON_PERFCNTR64_RECV_PKTS_IDX].value.ui64 =
969 		    ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_PKTS_IDX] +
970 		    ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_PKTS_IDX];
971 
972 	} else {
973 		/* return 0 in kstats if not enabled */
974 		data[HERMON_PERFCNTR64_ENABLE_IDX].value.ui32 = 0;
975 		for (i = 1; i < HERMON_PERFCNTR64_NUM_COUNTERS; i++)
976 			data[i].value.ui64 = 0;
977 	}
978 
979 	mutex_exit(&ksi->hki_perfcntr64_lock);
980 	return (0);
981 }
982