xref: /titanic_50/usr/src/uts/common/io/ib/adapters/tavor/tavor_stats.c (revision e9dc6bff6e018821c8c8ac7fe3e3b42e621e93ae)
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  * tavor_stats.c
29  *    Tavor 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/tavor/tavor.h>
43 
44 static kstat_t *tavor_kstat_picN_create(tavor_state_t *state, int num_pic,
45     int num_evt, tavor_ks_mask_t *ev_array);
46 static kstat_t *tavor_kstat_cntr_create(tavor_state_t *state, int num_pic,
47     int (*update)(kstat_t *, int));
48 static int tavor_kstat_cntr_update(kstat_t *ksp, int rw);
49 
50 void tavor_kstat_perfcntr64_create(tavor_state_t *state, uint_t port_num);
51 static int tavor_kstat_perfcntr64_read(tavor_state_t *state, uint_t port,
52     int reset);
53 static void tavor_kstat_perfcntr64_thread_exit(tavor_ks_info_t *ksi);
54 static int tavor_kstat_perfcntr64_update(kstat_t *ksp, int rw);
55 
56 /*
57  * Tavor 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 Tavor instance.
60  */
61 tavor_ks_mask_t tavor_ib_perfcnt_list[TAVOR_CNTR_NUMENTRIES] = {
62 	{"port_xmit_data", TAVOR_HW_PMEG_PORTXMITDATA_OFFSET,
63 	    0, 0xFFFFFFFF, 0, 0},
64 	{"port_recv_data", TAVOR_HW_PMEG_PORTRECVDATA_OFFSET,
65 	    0, 0xFFFFFFFF, 0, 0},
66 	{"port_xmit_pkts", TAVOR_HW_PMEG_PORTXMITPKTS_OFFSET,
67 	    0, 0xFFFFFFFF, 0, 0},
68 	{"port_recv_pkts", TAVOR_HW_PMEG_PORTRECVPKTS_OFFSET,
69 	    0, 0xFFFFFFFF, 0, 0},
70 	{"port_recv_err", TAVOR_HW_PMEG_PORTRECVERR_OFFSET,
71 	    0, 0xFFFF, 0, 0},
72 	{"port_xmit_discards", TAVOR_HW_PMEG_PORTXMITDISCARD_OFFSET,
73 	    0, 0xFFFF, 0, 0},
74 	{"vl15_dropped", TAVOR_HW_PMEG_VL15DROPPED_OFFSET,
75 	    0, 0xFFFF, 0, 0},
76 	{"port_xmit_wait", TAVOR_HW_PMEG_PORTXMITWAIT_OFFSET,
77 	    0, 0xFFFFFFFF, 0, 0},
78 	{"port_recv_remote_phys_err", TAVOR_HW_PMEG_PORTRECVREMPHYSERR_OFFSET,
79 	    0, 0xFFFF, 0, 0},
80 	{"port_xmit_constraint_err", TAVOR_HW_PMEG_PORTXMITCONSTERR_OFFSET,
81 	    0, 0xFF, 0, 0},
82 	{"port_recv_constraint_err", TAVOR_HW_PMEG_PORTRECVCONSTERR_OFFSET,
83 	    0, 0xFF, 0, 0},
84 	{"symbol_err_counter", TAVOR_HW_PMEG_SYMBOLERRCNT_OFFSET,
85 	    0, 0xFFFF, 0, 0},
86 	{"link_err_recovery_cnt", TAVOR_HW_PMEG_LINKERRRECOVERCNT_OFFSET,
87 	    0, 0xFFFF, 0, 0},
88 	{"link_downed_cnt", TAVOR_HW_PMEG_LINKDOWNEDCNT_OFFSET,
89 	    16, 0xFFFF, 0, 0},
90 	{"excessive_buffer_overruns", TAVOR_HW_PMEG_EXCESSBUFOVERRUN_OFFSET,
91 	    0, 0xF, 0, 0},
92 	{"local_link_integrity_err", TAVOR_HW_PMEG_LOCALLINKINTERR_OFFSET,
93 	    8, 0xF, 0, 0},
94 	{"clear_pic", 0, 0, 0, 0}
95 };
96 
97 /*
98  * Return the maximum of (x) and (y)
99  */
100 #define	MAX(x, y)	(((x) > (y)) ? (x) : (y))
101 
102 /*
103  * Set (x) to the maximum of (x) and (y)
104  */
105 #define	SET_TO_MAX(x, y)	\
106 {				\
107 	if ((x) < (y))		\
108 		(x) = (y);	\
109 }
110 
111 /*
112  * tavor_kstat_init()
113  *    Context: Only called from attach() path context
114  */
115 int
tavor_kstat_init(tavor_state_t * state)116 tavor_kstat_init(tavor_state_t *state)
117 {
118 	tavor_ks_info_t		*ksi;
119 	uint_t			numports;
120 	int			i;
121 
122 	TAVOR_TNF_ENTER(tavor_kstat_init);
123 
124 	/* Allocate a kstat info structure */
125 	ksi = (tavor_ks_info_t *)kmem_zalloc(sizeof (tavor_ks_info_t),
126 	    KM_SLEEP);
127 	if (ksi == NULL) {
128 		TNF_PROBE_0(tavor_kstat_init_kma_fail, TAVOR_TNF_ERROR, "");
129 		TAVOR_TNF_EXIT(tavor_kstat_init);
130 		return (DDI_FAILURE);
131 	}
132 	state->ts_ks_info = ksi;
133 
134 	/*
135 	 * Create as many "pic" and perfcntr64 kstats as we have IB ports.
136 	 * Enable all of the events specified in the "tavor_ib_perfcnt_list"
137 	 * structure.
138 	 */
139 	numports = state->ts_cfg_profile->cp_num_ports;
140 	for (i = 0; i < numports; i++) {
141 		ksi->tki_picN_ksp[i] = tavor_kstat_picN_create(state, i,
142 		    TAVOR_CNTR_NUMENTRIES, tavor_ib_perfcnt_list);
143 		if (ksi->tki_picN_ksp[i] == NULL) {
144 			TNF_PROBE_0(tavor_kstat_init_picN_fail,
145 			    TAVOR_TNF_ERROR, "");
146 			goto kstat_init_fail;
147 		}
148 
149 		tavor_kstat_perfcntr64_create(state, i + 1);
150 		if (ksi->tki_perfcntr64[i].tki64_ksp == NULL) {
151 			goto kstat_init_fail;
152 		}
153 	}
154 
155 	/* Create the "counters" kstat too */
156 	ksi->tki_cntr_ksp = tavor_kstat_cntr_create(state, numports,
157 	    tavor_kstat_cntr_update);
158 	if (ksi->tki_cntr_ksp == NULL) {
159 		TNF_PROBE_0(tavor_kstat_init_cntr_fail, TAVOR_TNF_ERROR, "");
160 		goto kstat_init_fail;
161 	}
162 
163 	/* Initialize the control register and initial counter values */
164 	ksi->tki_pcr  = 0;
165 	ksi->tki_pic0 = 0;
166 	ksi->tki_pic1 = 0;
167 
168 	/*
169 	 * Initialize the Tavor tki_ib_perfcnt[] array values using the
170 	 * default values in tavor_ib_perfcnt_list[]
171 	 */
172 	for (i = 0; i < TAVOR_CNTR_NUMENTRIES; i++) {
173 		ksi->tki_ib_perfcnt[i] = tavor_ib_perfcnt_list[i];
174 	}
175 
176 	mutex_init(&ksi->tki_perfcntr64_lock, NULL, MUTEX_DRIVER, NULL);
177 	cv_init(&ksi->tki_perfcntr64_cv, NULL, CV_DRIVER, NULL);
178 
179 	TAVOR_TNF_EXIT(tavor_kstat_init);
180 	return (DDI_SUCCESS);
181 
182 
183 kstat_init_fail:
184 
185 	/* Delete all the previously created kstats */
186 	if (ksi->tki_cntr_ksp != NULL) {
187 		kstat_delete(ksi->tki_cntr_ksp);
188 	}
189 	for (i = 0; i < numports; i++) {
190 		if (ksi->tki_picN_ksp[i] != NULL) {
191 			kstat_delete(ksi->tki_picN_ksp[i]);
192 		}
193 		if (ksi->tki_perfcntr64[i].tki64_ksp != NULL) {
194 			kstat_delete(ksi->tki_perfcntr64[i].tki64_ksp);
195 		}
196 	}
197 
198 	/* Free the kstat info structure */
199 	kmem_free(ksi, sizeof (tavor_ks_info_t));
200 
201 	TAVOR_TNF_EXIT(tavor_kstat_init);
202 	return (DDI_FAILURE);
203 }
204 
205 
206 /*
207  * tavor_kstat_init()
208  *    Context: Only called from attach() and/or detach() path contexts
209  */
210 void
tavor_kstat_fini(tavor_state_t * state)211 tavor_kstat_fini(tavor_state_t *state)
212 {
213 	tavor_ks_info_t		*ksi;
214 	uint_t			numports;
215 	int			i;
216 
217 	TAVOR_TNF_ENTER(tavor_kstat_fini);
218 
219 	/* Get pointer to kstat info */
220 	ksi = state->ts_ks_info;
221 
222 	/*
223 	 * Signal the perfcntr64_update_thread to exit and wait until the
224 	 * thread exits.
225 	 */
226 	mutex_enter(&ksi->tki_perfcntr64_lock);
227 	tavor_kstat_perfcntr64_thread_exit(ksi);
228 	mutex_exit(&ksi->tki_perfcntr64_lock);
229 
230 	/* Delete all the "pic" and perfcntr64 kstats (one per port) */
231 	numports = state->ts_cfg_profile->cp_num_ports;
232 	for (i = 0; i < numports; i++) {
233 		if (ksi->tki_picN_ksp[i] != NULL) {
234 			kstat_delete(ksi->tki_picN_ksp[i]);
235 		}
236 		if (ksi->tki_perfcntr64[i].tki64_ksp != NULL) {
237 			kstat_delete(ksi->tki_perfcntr64[i].tki64_ksp);
238 		}
239 	}
240 
241 	/* Delete the "counter" kstats (one per port) */
242 	kstat_delete(ksi->tki_cntr_ksp);
243 
244 	cv_destroy(&ksi->tki_perfcntr64_cv);
245 	mutex_destroy(&ksi->tki_perfcntr64_lock);
246 
247 	/* Free the kstat info structure */
248 	kmem_free(ksi, sizeof (tavor_ks_info_t));
249 
250 	TAVOR_TNF_EXIT(tavor_kstat_fini);
251 }
252 
253 
254 /*
255  * tavor_kstat_picN_create()
256  *    Context: Only called from attach() path context
257  */
258 static kstat_t *
tavor_kstat_picN_create(tavor_state_t * state,int num_pic,int num_evt,tavor_ks_mask_t * ev_array)259 tavor_kstat_picN_create(tavor_state_t *state, int num_pic, int num_evt,
260     tavor_ks_mask_t *ev_array)
261 {
262 	kstat_t			*picN_ksp;
263 	struct kstat_named	*pic_named_data;
264 	int			drv_instance, i;
265 	char			*drv_name;
266 	char			pic_name[16];
267 
268 	TAVOR_TNF_ENTER(tavor_kstat_picN_create);
269 
270 	/*
271 	 * Create the "picN" kstat.  In the steps, below we will attach
272 	 * all of our named event types to it.
273 	 */
274 	drv_name = (char *)ddi_driver_name(state->ts_dip);
275 	drv_instance = ddi_get_instance(state->ts_dip);
276 	(void) sprintf(pic_name, "pic%d", num_pic);
277 	picN_ksp = kstat_create(drv_name, drv_instance, pic_name, "bus",
278 	    KSTAT_TYPE_NAMED, num_evt, NULL);
279 	if (picN_ksp == NULL) {
280 		TNF_PROBE_0(tavor_kstat_picN_create_kstat_fail,
281 		    TAVOR_TNF_ERROR, "");
282 		TAVOR_TNF_EXIT(tavor_kstat_picN_create);
283 		return (NULL);
284 	}
285 	pic_named_data = (struct kstat_named *)(picN_ksp->ks_data);
286 
287 	/*
288 	 * Write event names and their associated pcr masks. The last entry
289 	 * in the array (clear_pic) is added separately below (as its pic
290 	 * value must be inverted).
291 	 */
292 	for (i = 0; i < num_evt - 1; i++) {
293 		pic_named_data[i].value.ui64 =
294 		    ((uint64_t)i << (num_pic * TAVOR_CNTR_SIZE));
295 		kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
296 		    KSTAT_DATA_UINT64);
297 	}
298 
299 	/* Add the "clear_pic" entry */
300 	pic_named_data[i].value.ui64 =
301 	    ~((uint64_t)TAVOR_CNTR_MASK << (num_pic * TAVOR_CNTR_SIZE));
302 	kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
303 	    KSTAT_DATA_UINT64);
304 
305 	/* Install the kstat */
306 	kstat_install(picN_ksp);
307 
308 	TAVOR_TNF_EXIT(tavor_kstat_picN_create);
309 	return (picN_ksp);
310 }
311 
312 
313 /*
314  * tavor_kstat_cntr_create()
315  *    Context: Only called from attach() path context
316  */
317 static kstat_t *
tavor_kstat_cntr_create(tavor_state_t * state,int num_pic,int (* update)(kstat_t *,int))318 tavor_kstat_cntr_create(tavor_state_t *state, int num_pic,
319     int (*update)(kstat_t *, int))
320 {
321 	struct kstat		*cntr_ksp;
322 	struct kstat_named	*cntr_named_data;
323 	int			drv_instance, i;
324 	char			*drv_name;
325 	char			pic_str[16];
326 
327 	TAVOR_TNF_ENTER(tavor_kstat_cntr_create);
328 
329 	/*
330 	 * Create the "counters" kstat.  In the steps, below we will attach
331 	 * all of our "pic" to it.   Note:  The size of this kstat is
332 	 * num_pic + 1 because it also contains the "%pcr".
333 	 */
334 	drv_name = (char *)ddi_driver_name(state->ts_dip);
335 	drv_instance = ddi_get_instance(state->ts_dip);
336 	cntr_ksp = kstat_create(drv_name, drv_instance, "counters", "bus",
337 	    KSTAT_TYPE_NAMED, num_pic + 1, KSTAT_FLAG_WRITABLE);
338 	if (cntr_ksp == NULL) {
339 		TNF_PROBE_0(tavor_kstat_picN_create_kstat_fail,
340 		    TAVOR_TNF_ERROR, "");
341 		TAVOR_TNF_EXIT(tavor_kstat_cntr_create);
342 		return (NULL);
343 	}
344 	cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data);
345 
346 	/*
347 	 * Initialize the named kstats (for the "pcr" and for the
348 	 * individual "pic" kstats)
349 	 */
350 	kstat_named_init(&cntr_named_data[0], "pcr", KSTAT_DATA_UINT64);
351 	for (i = 0; i < num_pic; i++) {
352 		(void) sprintf(pic_str, "pic%d", i);
353 		kstat_named_init(&cntr_named_data[i+1], pic_str,
354 		    KSTAT_DATA_UINT64);
355 	}
356 
357 	/*
358 	 * Store the Tavor softstate pointer in the kstat's private field so
359 	 * that it is available to the update function.
360 	 */
361 	cntr_ksp->ks_private = (void *)state;
362 	cntr_ksp->ks_update  = update;
363 
364 	/* Install the kstat */
365 	kstat_install(cntr_ksp);
366 
367 	TAVOR_TNF_ENTER(tavor_kstat_cntr_create);
368 	return (cntr_ksp);
369 }
370 
371 
372 /*
373  * tavor_kstat_cntr_update()
374  *    Context: Called from the kstat context
375  */
376 static int
tavor_kstat_cntr_update(kstat_t * ksp,int rw)377 tavor_kstat_cntr_update(kstat_t *ksp, int rw)
378 {
379 	tavor_state_t		*state;
380 	tavor_ks_mask_t		*ib_perf;
381 	tavor_ks_info_t		*ksi;
382 	struct kstat_named	*data;
383 	uint64_t		offset, pcr;
384 	uint32_t		pic0, pic1, tmp;
385 	uint32_t		shift, mask, oldval;
386 	uint_t			numports, indx;
387 
388 	TAVOR_TNF_ENTER(tavor_kstat_cntr_update);
389 
390 	/*
391 	 * Extract the Tavor softstate pointer, kstat data, pointer to the
392 	 * kstat info structure, and pointer to the tki_ib_perfcnt[] array
393 	 * from the input parameters.  Note: For warlock purposes, these
394 	 * parameters are all accessed only in this routine and are,
395 	 * therefore, protected by the lock used by the kstat framework.
396 	 */
397 	state	= ksp->ks_private;
398 	data	= (struct kstat_named *)(ksp->ks_data);
399 	ksi	= state->ts_ks_info;
400 	ib_perf = &ksi->tki_ib_perfcnt[0];
401 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ksi))
402 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*data))
403 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ib_perf))
404 
405 	/*
406 	 * Depending on whether we are reading the "pic" counters or
407 	 * writing the "pcr" control register, we need to handle and
408 	 * fill in the kstat data appropriately.
409 	 *
410 	 * If this is a write to the "pcr", then extract the value from
411 	 * the kstat data and store it in the kstat info structure.
412 	 *
413 	 * Otherwise, if this is a read of the "pic" counter(s), then
414 	 * extract the register offset, size, and mask values from the
415 	 * ib_perf[] array.  Then read the corresponding register and store
416 	 * it into the kstat data.  Note:  We only read/fill in pic1 if more
417 	 * than one port is configured.
418 	 */
419 	numports = state->ts_cfg_profile->cp_num_ports;
420 	if (rw == KSTAT_WRITE) {
421 		/* Update the stored "pcr" value */
422 		ksi->tki_pcr = data[0].value.ui64;
423 		TAVOR_TNF_EXIT(tavor_kstat_cntr_update);
424 		return (0);
425 	} else {
426 		/*
427 		 * Get the current "pcr" value and extract the lower
428 		 * portion (corresponding to the counters for "pic0")
429 		 */
430 		pcr  = ksi->tki_pcr;
431 		indx = pcr & TAVOR_CNTR_MASK;
432 		data[0].value.ui64 = pcr;
433 
434 		/*
435 		 * Fill in the "pic0" counter, corresponding to port 1.
436 		 * This involves reading in the current value in the register
437 		 * and calculating how many events have happened since this
438 		 * register was last polled.  Then we save away the current
439 		 * value for the counter and increment the "pic0" total by
440 		 * the number of new events.
441 		 */
442 		offset = ib_perf[indx].ks_reg_offset;
443 		shift  = ib_perf[indx].ks_reg_shift;
444 		mask   = ib_perf[indx].ks_reg_mask;
445 		oldval = ib_perf[indx].ks_old_pic0;
446 
447 		pic0   = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *)
448 		    (uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr +
449 		    offset));
450 		tmp = ((pic0 >> shift) & mask);
451 
452 		ib_perf[indx].ks_old_pic0 = tmp;
453 
454 		tmp = tmp - oldval;
455 		ksi->tki_pic0 += tmp;
456 		data[1].value.ui64 = ksi->tki_pic0;
457 
458 		/*
459 		 * If necessary, fill in the "pic1" counter for port 2.
460 		 * This works the same as above except that we extract the
461 		 * upper bits (corresponding to the counters for "pic1")
462 		 */
463 		if (numports == TAVOR_NUM_PORTS) {
464 			indx   = pcr >> TAVOR_CNTR_SIZE;
465 			offset = ib_perf[indx].ks_reg_offset;
466 			shift  = ib_perf[indx].ks_reg_shift;
467 			mask   = ib_perf[indx].ks_reg_mask;
468 			oldval = ib_perf[indx].ks_old_pic1;
469 
470 			pic1   = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *)
471 			    (uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr +
472 			    offset + TAVOR_HW_PORT_SIZE));
473 			tmp = ((pic1 >> shift) & mask);
474 
475 			ib_perf[indx].ks_old_pic1 = tmp;
476 
477 			tmp = tmp - oldval;
478 			ksi->tki_pic1 += tmp;
479 			data[2].value.ui64 = ksi->tki_pic1;
480 		}
481 
482 		TAVOR_TNF_EXIT(tavor_kstat_cntr_update);
483 		return (0);
484 	}
485 }
486 
487 /*
488  * 64 bit kstats for performance counters:
489  *
490  * Since the hardware as of now does not support 64 bit performance counters,
491  * we maintain 64 bit performance counters in software using the 32 bit
492  * hardware counters.
493  *
494  * We create a thread that, every one second, reads the values of 32 bit
495  * hardware counters and adds them to the 64 bit software counters. Immediately
496  * after reading, it resets the 32 bit hardware counters to zero (so that they
497  * start counting from zero again). At any time the current value of a counter
498  * is going to be the sum of the 64 bit software counter and the 32 bit
499  * hardware counter.
500  *
501  * Since this work need not be done if there is no consumer, by default
502  * we do not maintain 64 bit software counters. To enable this the consumer
503  * needs to write a non-zero value to the "enable" component of the of
504  * perf_counters kstat. Writing zero to this component will disable this work.
505  *
506  * If performance monitor is enabled in subnet manager, the SM could
507  * periodically reset the hardware counters by sending perf-MADs. So only
508  * one of either our software 64 bit counters or the SM performance monitor
509  * could be enabled at the same time. However, if both of them are enabled at
510  * the same time we still do our best by keeping track of the values of the
511  * last read 32 bit hardware counters. If the current read of a 32 bit hardware
512  * counter is less than the last read of the counter, we ignore the current
513  * value and go with the last read value.
514  */
515 
516 /*
517  * tavor_kstat_perfcntr64_create()
518  *    Context: Only called from attach() path context
519  *
520  * Create "port#/perf_counters" kstat for the specified port number.
521  */
522 void
tavor_kstat_perfcntr64_create(tavor_state_t * state,uint_t port_num)523 tavor_kstat_perfcntr64_create(tavor_state_t *state, uint_t port_num)
524 {
525 	tavor_ks_info_t		*ksi = state->ts_ks_info;
526 	struct kstat		*cntr_ksp;
527 	struct kstat_named	*cntr_named_data;
528 	int			drv_instance;
529 	char			*drv_name;
530 	char			kname[32];
531 
532 	ASSERT(port_num != 0);
533 
534 	drv_name = (char *)ddi_driver_name(state->ts_dip);
535 	drv_instance = ddi_get_instance(state->ts_dip);
536 	(void) snprintf(kname, sizeof (kname), "port%u/perf_counters",
537 	    port_num);
538 	cntr_ksp = kstat_create(drv_name, drv_instance, kname, "ib",
539 	    KSTAT_TYPE_NAMED, TAVOR_PERFCNTR64_NUM_COUNTERS,
540 	    KSTAT_FLAG_WRITABLE);
541 	if (cntr_ksp == NULL) {
542 		return;
543 	}
544 	cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data);
545 
546 	kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_ENABLE_IDX],
547 	    "enable", KSTAT_DATA_UINT32);
548 	kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_XMIT_DATA_IDX],
549 	    "xmit_data", KSTAT_DATA_UINT64);
550 	kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_RECV_DATA_IDX],
551 	    "recv_data", KSTAT_DATA_UINT64);
552 	kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_XMIT_PKTS_IDX],
553 	    "xmit_pkts", KSTAT_DATA_UINT64);
554 	kstat_named_init(&cntr_named_data[TAVOR_PERFCNTR64_RECV_PKTS_IDX],
555 	    "recv_pkts", KSTAT_DATA_UINT64);
556 
557 	ksi->tki_perfcntr64[port_num - 1].tki64_ksp = cntr_ksp;
558 	ksi->tki_perfcntr64[port_num - 1].tki64_port_num = port_num;
559 	ksi->tki_perfcntr64[port_num - 1].tki64_state = state;
560 
561 	cntr_ksp->ks_private = &ksi->tki_perfcntr64[port_num - 1];
562 	cntr_ksp->ks_update  = tavor_kstat_perfcntr64_update;
563 
564 	/* Install the kstat */
565 	kstat_install(cntr_ksp);
566 }
567 
568 /*
569  * tavor_kstat_perfcntr64_read()
570  *
571  * Read the values of 32 bit hardware counters.
572  *
573  * If reset is true, reset the 32 bit hardware counters. Add the values of the
574  * 32 bit hardware counters to the 64 bit software counters.
575  *
576  * If reset is false, just save the values read from the 32 bit hardware
577  * counters in tki64_last_read[].
578  *
579  * See the general comment on the 64 bit performance counters
580  * regarding the use of last read 32 bit hardware counter values.
581  */
582 static int
tavor_kstat_perfcntr64_read(tavor_state_t * state,uint_t port,int reset)583 tavor_kstat_perfcntr64_read(tavor_state_t *state, uint_t port, int reset)
584 {
585 	tavor_ks_info_t	*ksi = state->ts_ks_info;
586 	tavor_perfcntr64_ks_info_t *ksi64 = &ksi->tki_perfcntr64[port - 1];
587 	int			status, i;
588 	uint32_t		tmp;
589 	tavor_hw_sm_perfcntr_t	sm_perfcntr;
590 
591 	ASSERT(MUTEX_HELD(&ksi->tki_perfcntr64_lock));
592 	ASSERT(port != 0);
593 
594 	/* read the 32 bit hardware counters */
595 	status = tavor_getperfcntr_cmd_post(state, port,
596 	    TAVOR_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0);
597 	if (status != TAVOR_CMD_SUCCESS) {
598 		return (status);
599 	}
600 
601 	if (reset) {
602 		/* reset the hardware counters */
603 		status = tavor_getperfcntr_cmd_post(state, port,
604 		    TAVOR_CMD_NOSLEEP_SPIN, NULL, 1);
605 		if (status != TAVOR_CMD_SUCCESS) {
606 			return (status);
607 		}
608 
609 		/*
610 		 * Update 64 bit software counters
611 		 */
612 		tmp = MAX(sm_perfcntr.portxmdata,
613 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_DATA_IDX]);
614 		ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_DATA_IDX] += tmp;
615 
616 		tmp = MAX(sm_perfcntr.portrcdata,
617 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_DATA_IDX]);
618 		ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_DATA_IDX] += tmp;
619 
620 		tmp = MAX(sm_perfcntr.portxmpkts,
621 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_PKTS_IDX]);
622 		ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_PKTS_IDX] += tmp;
623 
624 		tmp = MAX(sm_perfcntr.portrcpkts,
625 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_PKTS_IDX]);
626 		ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_PKTS_IDX] += tmp;
627 
628 		for (i = 0; i < TAVOR_PERFCNTR64_NUM_COUNTERS; i++)
629 			ksi64->tki64_last_read[i] = 0;
630 
631 	} else {
632 		/*
633 		 * Update ksi64->tki64_last_read[]
634 		 */
635 		SET_TO_MAX(
636 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_DATA_IDX],
637 		    sm_perfcntr.portxmdata);
638 
639 		SET_TO_MAX(
640 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_DATA_IDX],
641 		    sm_perfcntr.portrcdata);
642 
643 		SET_TO_MAX(
644 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_PKTS_IDX],
645 		    sm_perfcntr.portxmpkts);
646 
647 		SET_TO_MAX(
648 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_PKTS_IDX],
649 		    sm_perfcntr.portrcpkts);
650 	}
651 
652 	return (TAVOR_CMD_SUCCESS);
653 }
654 
655 /*
656  * tavor_kstat_perfcntr64_update_thread()
657  *    Context: Entry point for a kernel thread
658  *
659  * Maintain 64 bit performance counters in software using the 32 bit
660  * hardware counters.
661  */
662 static void
tavor_kstat_perfcntr64_update_thread(void * arg)663 tavor_kstat_perfcntr64_update_thread(void *arg)
664 {
665 	tavor_state_t		*state = (tavor_state_t *)arg;
666 	tavor_ks_info_t		*ksi = state->ts_ks_info;
667 	uint_t			i;
668 
669 	mutex_enter(&ksi->tki_perfcntr64_lock);
670 	/*
671 	 * Every one second update the values 64 bit software counters
672 	 * for all ports. Exit if TAVOR_PERFCNTR64_THREAD_EXIT flag is set.
673 	 */
674 	while (!(ksi->tki_perfcntr64_flags & TAVOR_PERFCNTR64_THREAD_EXIT)) {
675 		for (i = 0; i < state->ts_cfg_profile->cp_num_ports; i++) {
676 			if (ksi->tki_perfcntr64[i].tki64_enabled) {
677 				(void) tavor_kstat_perfcntr64_read(state,
678 				    i + 1, 1);
679 			}
680 		}
681 		/* sleep for a second */
682 		(void) cv_timedwait(&ksi->tki_perfcntr64_cv,
683 		    &ksi->tki_perfcntr64_lock,
684 		    ddi_get_lbolt() + drv_usectohz(1000000));
685 	}
686 	ksi->tki_perfcntr64_flags = 0;
687 	mutex_exit(&ksi->tki_perfcntr64_lock);
688 }
689 
690 /*
691  * tavor_kstat_perfcntr64_thread_create()
692  *    Context: Called from the kstat context
693  *
694  * Create a thread that maintains 64 bit performance counters in software.
695  */
696 static void
tavor_kstat_perfcntr64_thread_create(tavor_state_t * state)697 tavor_kstat_perfcntr64_thread_create(tavor_state_t *state)
698 {
699 	tavor_ks_info_t	*ksi = state->ts_ks_info;
700 	kthread_t		*thr;
701 
702 	ASSERT(MUTEX_HELD(&ksi->tki_perfcntr64_lock));
703 
704 	/*
705 	 * One thread per tavor instance. Don't create a thread if already
706 	 * created.
707 	 */
708 	if (!(ksi->tki_perfcntr64_flags & TAVOR_PERFCNTR64_THREAD_CREATED)) {
709 		thr = thread_create(NULL, 0,
710 		    tavor_kstat_perfcntr64_update_thread,
711 		    state, 0, &p0, TS_RUN, minclsyspri);
712 		ksi->tki_perfcntr64_thread_id = thr->t_did;
713 		ksi->tki_perfcntr64_flags |= TAVOR_PERFCNTR64_THREAD_CREATED;
714 	}
715 }
716 
717 /*
718  * tavor_kstat_perfcntr64_thread_exit()
719  *    Context: Called from attach, detach or kstat context
720  */
721 static void
tavor_kstat_perfcntr64_thread_exit(tavor_ks_info_t * ksi)722 tavor_kstat_perfcntr64_thread_exit(tavor_ks_info_t *ksi)
723 {
724 	kt_did_t	tid;
725 
726 	ASSERT(MUTEX_HELD(&ksi->tki_perfcntr64_lock));
727 
728 	if (ksi->tki_perfcntr64_flags & TAVOR_PERFCNTR64_THREAD_CREATED) {
729 		/*
730 		 * Signal the thread to exit and wait until the thread exits.
731 		 */
732 		ksi->tki_perfcntr64_flags |= TAVOR_PERFCNTR64_THREAD_EXIT;
733 		tid = ksi->tki_perfcntr64_thread_id;
734 		cv_signal(&ksi->tki_perfcntr64_cv);
735 
736 		mutex_exit(&ksi->tki_perfcntr64_lock);
737 		thread_join(tid);
738 		mutex_enter(&ksi->tki_perfcntr64_lock);
739 	}
740 }
741 
742 /*
743  * tavor_kstat_perfcntr64_update()
744  *    Context: Called from the kstat context
745  *
746  * See the general comment on 64 bit kstats for performance counters:
747  */
748 static int
tavor_kstat_perfcntr64_update(kstat_t * ksp,int rw)749 tavor_kstat_perfcntr64_update(kstat_t *ksp, int rw)
750 {
751 	tavor_state_t			*state;
752 	struct kstat_named		*data;
753 	tavor_ks_info_t		*ksi;
754 	tavor_perfcntr64_ks_info_t	*ksi64;
755 	int				i, thr_exit;
756 
757 	ksi64	= ksp->ks_private;
758 	state	= ksi64->tki64_state;
759 	ksi	= state->ts_ks_info;
760 	data	= (struct kstat_named *)(ksp->ks_data);
761 
762 	mutex_enter(&ksi->tki_perfcntr64_lock);
763 
764 	/*
765 	 * 64 bit performance counters maintained by the software is not
766 	 * enabled by default. Enable them upon a writing a non-zero value
767 	 * to "enable" kstat. Disable them upon a writing zero to the
768 	 * "enable" kstat.
769 	 */
770 	if (rw == KSTAT_WRITE) {
771 		if (data[TAVOR_PERFCNTR64_ENABLE_IDX].value.ui32) {
772 			if (ksi64->tki64_enabled == 0) {
773 				/*
774 				 * Reset the hardware counters to ensure that
775 				 * the hardware counter doesn't max out
776 				 * (and hence stop counting) before we get
777 				 * a chance to reset the counter in
778 				 * tavor_kstat_perfcntr64_update_thread.
779 				 */
780 				if (tavor_getperfcntr_cmd_post(state,
781 				    ksi64->tki64_port_num,
782 				    TAVOR_CMD_NOSLEEP_SPIN, NULL, 1) !=
783 				    TAVOR_CMD_SUCCESS) {
784 					mutex_exit(&ksi->tki_perfcntr64_lock);
785 					return (EIO);
786 				}
787 
788 				/* Enable 64 bit software counters */
789 				ksi64->tki64_enabled = 1;
790 				for (i = 0;
791 				    i < TAVOR_PERFCNTR64_NUM_COUNTERS; i++) {
792 					ksi64->tki64_counters[i] = 0;
793 					ksi64->tki64_last_read[i] = 0;
794 				}
795 				tavor_kstat_perfcntr64_thread_create(state);
796 			}
797 
798 		} else if (ksi64->tki64_enabled) {
799 			/* Disable 64 bit software counters */
800 			ksi64->tki64_enabled = 0;
801 			thr_exit = 1;
802 			for (i = 0; i < state->ts_cfg_profile->cp_num_ports;
803 			    i++) {
804 				if (ksi->tki_perfcntr64[i].tki64_enabled) {
805 					thr_exit = 0;
806 					break;
807 				}
808 			}
809 			if (thr_exit)
810 				tavor_kstat_perfcntr64_thread_exit(ksi);
811 		}
812 	} else if (ksi64->tki64_enabled) {
813 		/*
814 		 * Read the counters and update kstats.
815 		 */
816 		if (tavor_kstat_perfcntr64_read(state, ksi64->tki64_port_num,
817 		    0) != TAVOR_CMD_SUCCESS) {
818 			mutex_exit(&ksi->tki_perfcntr64_lock);
819 			return (EIO);
820 		}
821 
822 		data[TAVOR_PERFCNTR64_ENABLE_IDX].value.ui32 = 1;
823 
824 		data[TAVOR_PERFCNTR64_XMIT_DATA_IDX].value.ui64 =
825 		    ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_DATA_IDX] +
826 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_DATA_IDX];
827 
828 		data[TAVOR_PERFCNTR64_RECV_DATA_IDX].value.ui64 =
829 		    ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_DATA_IDX] +
830 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_DATA_IDX];
831 
832 		data[TAVOR_PERFCNTR64_XMIT_PKTS_IDX].value.ui64 =
833 		    ksi64->tki64_counters[TAVOR_PERFCNTR64_XMIT_PKTS_IDX] +
834 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_XMIT_PKTS_IDX];
835 
836 		data[TAVOR_PERFCNTR64_RECV_PKTS_IDX].value.ui64 =
837 		    ksi64->tki64_counters[TAVOR_PERFCNTR64_RECV_PKTS_IDX] +
838 		    ksi64->tki64_last_read[TAVOR_PERFCNTR64_RECV_PKTS_IDX];
839 
840 	} else {
841 		/* return 0 in kstats if not enabled */
842 		data[TAVOR_PERFCNTR64_ENABLE_IDX].value.ui32 = 0;
843 		for (i = 1; i < TAVOR_PERFCNTR64_NUM_COUNTERS; i++)
844 			data[i].value.ui64 = 0;
845 	}
846 
847 	mutex_exit(&ksi->tki_perfcntr64_lock);
848 	return (0);
849 }
850