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