xref: /illumos-gate/usr/src/uts/common/io/ib/adapters/tavor/tavor_stats.c (revision e44e85a7f9935f0428e188393e3da61b17e83884)
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 2005 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 /*
51  * Tavor IB Performance Events structure
52  *    This structure is read-only and is used to setup the individual kstats
53  *    and to initialize the tki_ib_perfcnt[] array for each Tavor instance.
54  */
55 tavor_ks_mask_t tavor_ib_perfcnt_list[TAVOR_CNTR_NUMENTRIES] = {
56 	{"port_xmit_data", TAVOR_HW_PMEG_PORTXMITDATA_OFFSET,
57 	    0, 0xFFFFFFFF, 0, 0},
58 	{"port_recv_data", TAVOR_HW_PMEG_PORTRECVDATA_OFFSET,
59 	    0, 0xFFFFFFFF, 0, 0},
60 	{"port_xmit_pkts", TAVOR_HW_PMEG_PORTXMITPKTS_OFFSET,
61 	    0, 0xFFFFFFFF, 0, 0},
62 	{"port_recv_pkts", TAVOR_HW_PMEG_PORTRECVPKTS_OFFSET,
63 	    0, 0xFFFFFFFF, 0, 0},
64 	{"port_recv_err", TAVOR_HW_PMEG_PORTRECVERR_OFFSET,
65 	    0, 0xFFFF, 0, 0},
66 	{"port_xmit_discards", TAVOR_HW_PMEG_PORTXMITDISCARD_OFFSET,
67 	    0, 0xFFFF, 0, 0},
68 	{"vl15_dropped", TAVOR_HW_PMEG_VL15DROPPED_OFFSET,
69 	    0, 0xFFFF, 0, 0},
70 	{"port_xmit_wait", TAVOR_HW_PMEG_PORTXMITWAIT_OFFSET,
71 	    0, 0xFFFFFFFF, 0, 0},
72 	{"port_recv_remote_phys_err", TAVOR_HW_PMEG_PORTRECVREMPHYSERR_OFFSET,
73 	    0, 0xFFFF, 0, 0},
74 	{"port_xmit_constraint_err", TAVOR_HW_PMEG_PORTXMITCONSTERR_OFFSET,
75 	    0, 0xFF, 0, 0},
76 	{"port_recv_constraint_err", TAVOR_HW_PMEG_PORTRECVCONSTERR_OFFSET,
77 	    0, 0xFF, 0, 0},
78 	{"symbol_err_counter", TAVOR_HW_PMEG_SYMBOLERRCNT_OFFSET,
79 	    0, 0xFFFF, 0, 0},
80 	{"link_err_recovery_cnt", TAVOR_HW_PMEG_LINKERRRECOVERCNT_OFFSET,
81 	    0, 0xFFFF, 0, 0},
82 	{"link_downed_cnt", TAVOR_HW_PMEG_LINKDOWNEDCNT_OFFSET,
83 	    16, 0xFFFF, 0, 0},
84 	{"excessive_buffer_overruns", TAVOR_HW_PMEG_EXCESSBUFOVERRUN_OFFSET,
85 	    0, 0xF, 0, 0},
86 	{"local_link_integrity_err", TAVOR_HW_PMEG_LOCALLINKINTERR_OFFSET,
87 	    8, 0xF, 0, 0},
88 	{"clear_pic", 0, 0, 0, 0}
89 };
90 
91 
92 /*
93  * tavor_kstat_init()
94  *    Context: Only called from attach() path context
95  */
96 int
97 tavor_kstat_init(tavor_state_t *state)
98 {
99 	tavor_ks_info_t		*ksi;
100 	uint_t			numports;
101 	int			i;
102 
103 	TAVOR_TNF_ENTER(tavor_kstat_init);
104 
105 	/* Allocate a kstat info structure */
106 	ksi = (tavor_ks_info_t *)kmem_zalloc(sizeof (tavor_ks_info_t),
107 	    KM_SLEEP);
108 	if (ksi == NULL) {
109 		TNF_PROBE_0(tavor_kstat_init_kma_fail, TAVOR_TNF_ERROR, "");
110 		TAVOR_TNF_EXIT(tavor_kstat_init);
111 		return (DDI_FAILURE);
112 	}
113 	state->ts_ks_info = ksi;
114 
115 	/*
116 	 * Create as many "pic" kstats as we have IB ports.  Enable all
117 	 * of the events specified in the "tavor_ib_perfcnt_list" structure.
118 	 */
119 	numports = state->ts_cfg_profile->cp_num_ports;
120 	for (i = 0; i < numports; i++) {
121 		ksi->tki_picN_ksp[i] = tavor_kstat_picN_create(state, i,
122 		    TAVOR_CNTR_NUMENTRIES, tavor_ib_perfcnt_list);
123 		if (ksi->tki_picN_ksp[i] == NULL) {
124 			TNF_PROBE_0(tavor_kstat_init_picN_fail,
125 			    TAVOR_TNF_ERROR, "");
126 			goto kstat_init_fail;
127 		}
128 	}
129 
130 	/* Create the "counters" kstat too */
131 	ksi->tki_cntr_ksp = tavor_kstat_cntr_create(state, numports,
132 	    tavor_kstat_cntr_update);
133 	if (ksi->tki_cntr_ksp == NULL) {
134 		TNF_PROBE_0(tavor_kstat_init_cntr_fail, TAVOR_TNF_ERROR, "");
135 		goto kstat_init_fail;
136 	}
137 
138 	/* Initialize the control register and initial counter values */
139 	ksi->tki_pcr  = 0;
140 	ksi->tki_pic0 = 0;
141 	ksi->tki_pic1 = 0;
142 
143 	/*
144 	 * Initialize the Tavor tki_ib_perfcnt[] array values using the
145 	 * default values in tavor_ib_perfcnt_list[]
146 	 */
147 	for (i = 0; i < TAVOR_CNTR_NUMENTRIES; i++) {
148 		ksi->tki_ib_perfcnt[i] = tavor_ib_perfcnt_list[i];
149 	}
150 
151 	TAVOR_TNF_EXIT(tavor_kstat_init);
152 	return (DDI_SUCCESS);
153 
154 
155 kstat_init_fail:
156 
157 	/* Delete all the previously created kstats */
158 	if (ksi->tki_cntr_ksp != NULL) {
159 		kstat_delete(ksi->tki_cntr_ksp);
160 	}
161 	for (i = 0; i < numports; i++) {
162 		if (ksi->tki_picN_ksp[i] != NULL) {
163 			kstat_delete(ksi->tki_picN_ksp[i]);
164 		}
165 	}
166 
167 	/* Free the kstat info structure */
168 	kmem_free(ksi, sizeof (tavor_ks_info_t));
169 
170 	TAVOR_TNF_EXIT(tavor_kstat_init);
171 	return (DDI_FAILURE);
172 }
173 
174 
175 /*
176  * tavor_kstat_init()
177  *    Context: Only called from attach() and/or detach() path contexts
178  */
179 void
180 tavor_kstat_fini(tavor_state_t *state)
181 {
182 	tavor_ks_info_t		*ksi;
183 	uint_t			numports;
184 	int			i;
185 
186 	TAVOR_TNF_ENTER(tavor_kstat_fini);
187 
188 	/* Get pointer to kstat info */
189 	ksi = state->ts_ks_info;
190 
191 	/* Delete all the "pic" kstats (one per port) */
192 	numports = state->ts_cfg_profile->cp_num_ports;
193 	for (i = 0; i < numports; i++) {
194 		if (ksi->tki_picN_ksp[i] != NULL) {
195 			kstat_delete(ksi->tki_picN_ksp[i]);
196 		}
197 	}
198 
199 	/* Delete the "counter" kstats (one per port) */
200 	kstat_delete(ksi->tki_cntr_ksp);
201 
202 	/* Free the kstat info structure */
203 	kmem_free(ksi, sizeof (tavor_ks_info_t));
204 
205 	TAVOR_TNF_EXIT(tavor_kstat_fini);
206 }
207 
208 
209 /*
210  * tavor_kstat_picN_create()
211  *    Context: Only called from attach() path context
212  */
213 static kstat_t *
214 tavor_kstat_picN_create(tavor_state_t *state, int num_pic, int num_evt,
215     tavor_ks_mask_t *ev_array)
216 {
217 	kstat_t			*picN_ksp;
218 	struct kstat_named	*pic_named_data;
219 	int			drv_instance, i;
220 	char			*drv_name;
221 	char			pic_name[16];
222 
223 	TAVOR_TNF_ENTER(tavor_kstat_picN_create);
224 
225 	/*
226 	 * Create the "picN" kstat.  In the steps, below we will attach
227 	 * all of our named event types to it.
228 	 */
229 	drv_name = (char *)ddi_driver_name(state->ts_dip);
230 	drv_instance = ddi_get_instance(state->ts_dip);
231 	(void) sprintf(pic_name, "pic%d", num_pic);
232 	picN_ksp = kstat_create(drv_name, drv_instance, pic_name, "bus",
233 	    KSTAT_TYPE_NAMED, num_evt, NULL);
234 	if (picN_ksp == NULL) {
235 		TNF_PROBE_0(tavor_kstat_picN_create_kstat_fail,
236 		    TAVOR_TNF_ERROR, "");
237 		TAVOR_TNF_EXIT(tavor_kstat_picN_create);
238 		return (NULL);
239 	}
240 	pic_named_data = (struct kstat_named *)(picN_ksp->ks_data);
241 
242 	/*
243 	 * Write event names and their associated pcr masks. The last entry
244 	 * in the array (clear_pic) is added separately below (as its pic
245 	 * value must be inverted).
246 	 */
247 	for (i = 0; i < num_evt - 1; i++) {
248 		pic_named_data[i].value.ui64 =
249 		    ((uint64_t)i << (num_pic * TAVOR_CNTR_SIZE));
250 		kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
251 		    KSTAT_DATA_UINT64);
252 	}
253 
254 	/* Add the "clear_pic" entry */
255 	pic_named_data[i].value.ui64 =
256 	    ~((uint64_t)TAVOR_CNTR_MASK << (num_pic * TAVOR_CNTR_SIZE));
257 	kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name,
258 	    KSTAT_DATA_UINT64);
259 
260 	/* Install the kstat */
261 	kstat_install(picN_ksp);
262 
263 	TAVOR_TNF_EXIT(tavor_kstat_picN_create);
264 	return (picN_ksp);
265 }
266 
267 
268 /*
269  * tavor_kstat_cntr_create()
270  *    Context: Only called from attach() path context
271  */
272 static kstat_t *
273 tavor_kstat_cntr_create(tavor_state_t *state, int num_pic,
274     int (*update)(kstat_t *, int))
275 {
276 	struct kstat		*cntr_ksp;
277 	struct kstat_named	*cntr_named_data;
278 	int			drv_instance, i;
279 	char			*drv_name;
280 	char			pic_str[16];
281 
282 	TAVOR_TNF_ENTER(tavor_kstat_cntr_create);
283 
284 	/*
285 	 * Create the "counters" kstat.  In the steps, below we will attach
286 	 * all of our "pic" to it.   Note:  The size of this kstat is
287 	 * num_pic + 1 because it also contains the "%pcr".
288 	 */
289 	drv_name = (char *)ddi_driver_name(state->ts_dip);
290 	drv_instance = ddi_get_instance(state->ts_dip);
291 	cntr_ksp = kstat_create(drv_name, drv_instance, "counters", "bus",
292 	    KSTAT_TYPE_NAMED, num_pic + 1, KSTAT_FLAG_WRITABLE);
293 	if (cntr_ksp == NULL) {
294 		TNF_PROBE_0(tavor_kstat_picN_create_kstat_fail,
295 		    TAVOR_TNF_ERROR, "");
296 		TAVOR_TNF_EXIT(tavor_kstat_cntr_create);
297 		return (NULL);
298 	}
299 	cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data);
300 
301 	/*
302 	 * Initialize the named kstats (for the "pcr" and for the
303 	 * individual "pic" kstats)
304 	 */
305 	kstat_named_init(&cntr_named_data[0], "pcr", KSTAT_DATA_UINT64);
306 	for (i = 0; i < num_pic; i++) {
307 		(void) sprintf(pic_str, "pic%d", i);
308 		kstat_named_init(&cntr_named_data[i+1], pic_str,
309 		    KSTAT_DATA_UINT64);
310 	}
311 
312 	/*
313 	 * Store the Tavor softstate pointer in the kstat's private field so
314 	 * that it is available to the update function.
315 	 */
316 	cntr_ksp->ks_private = (void *)state;
317 	cntr_ksp->ks_update  = update;
318 
319 	/* Install the kstat */
320 	kstat_install(cntr_ksp);
321 
322 	TAVOR_TNF_ENTER(tavor_kstat_cntr_create);
323 	return (cntr_ksp);
324 }
325 
326 
327 /*
328  * tavor_kstat_cntr_update()
329  *    Context: Called from the kstat context
330  */
331 static int
332 tavor_kstat_cntr_update(kstat_t *ksp, int rw)
333 {
334 	tavor_state_t		*state;
335 	tavor_ks_mask_t		*ib_perf;
336 	tavor_ks_info_t		*ksi;
337 	struct kstat_named	*data;
338 	uint64_t		offset, pcr;
339 	uint32_t		pic0, pic1, tmp;
340 	uint32_t		shift, mask, oldval;
341 	uint_t			numports, indx;
342 
343 	TAVOR_TNF_ENTER(tavor_kstat_cntr_update);
344 
345 	/*
346 	 * Extract the Tavor softstate pointer, kstat data, pointer to the
347 	 * kstat info structure, and pointer to the tki_ib_perfcnt[] array
348 	 * from the input parameters.  Note: For warlock purposes, these
349 	 * parameters are all accessed only in this routine and are,
350 	 * therefore, protected by the lock used by the kstat framework.
351 	 */
352 	state	= ksp->ks_private;
353 	data	= (struct kstat_named *)(ksp->ks_data);
354 	ksi	= state->ts_ks_info;
355 	ib_perf = &ksi->tki_ib_perfcnt[0];
356 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ksi))
357 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*data))
358 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ib_perf))
359 
360 	/*
361 	 * Depending on whether we are reading the "pic" counters or
362 	 * writing the "pcr" control register, we need to handle and
363 	 * fill in the kstat data appropriately.
364 	 *
365 	 * If this is a write to the "pcr", then extract the value from
366 	 * the kstat data and store it in the kstat info structure.
367 	 *
368 	 * Otherwise, if this is a read of the "pic" counter(s), then
369 	 * extract the register offset, size, and mask values from the
370 	 * ib_perf[] array.  Then read the corresponding register and store
371 	 * it into the kstat data.  Note:  We only read/fill in pic1 if more
372 	 * than one port is configured.
373 	 */
374 	numports = state->ts_cfg_profile->cp_num_ports;
375 	if (rw == KSTAT_WRITE) {
376 		/* Update the stored "pcr" value */
377 		ksi->tki_pcr = data[0].value.ui64;
378 		TAVOR_TNF_EXIT(tavor_kstat_cntr_update);
379 		return (0);
380 	} else {
381 		/*
382 		 * Get the current "pcr" value and extract the lower
383 		 * portion (corresponding to the counters for "pic0")
384 		 */
385 		pcr  = ksi->tki_pcr;
386 		indx = pcr & TAVOR_CNTR_MASK;
387 		data[0].value.ui64 = pcr;
388 
389 		/*
390 		 * Fill in the "pic0" counter, corresponding to port 1.
391 		 * This involves reading in the current value in the register
392 		 * and calculating how many events have happened since this
393 		 * register was last polled.  Then we save away the current
394 		 * value for the counter and increment the "pic0" total by
395 		 * the number of new events.
396 		 */
397 		offset = ib_perf[indx].ks_reg_offset;
398 		shift  = ib_perf[indx].ks_reg_shift;
399 		mask   = ib_perf[indx].ks_reg_mask;
400 		oldval = ib_perf[indx].ks_old_pic0;
401 
402 		pic0   = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *)
403 		    (uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr +
404 		    offset));
405 		tmp = ((pic0 >> shift) & mask);
406 
407 		ib_perf[indx].ks_old_pic0 = tmp;
408 
409 		tmp = tmp - oldval;
410 		ksi->tki_pic0 += tmp;
411 		data[1].value.ui64 = ksi->tki_pic0;
412 
413 		/*
414 		 * If necessary, fill in the "pic1" counter for port 2.
415 		 * This works the same as above except that we extract the
416 		 * upper bits (corresponding to the counters for "pic1")
417 		 */
418 		if (numports == TAVOR_NUM_PORTS) {
419 			indx   = pcr >> TAVOR_CNTR_SIZE;
420 			offset = ib_perf[indx].ks_reg_offset;
421 			shift  = ib_perf[indx].ks_reg_shift;
422 			mask   = ib_perf[indx].ks_reg_mask;
423 			oldval = ib_perf[indx].ks_old_pic1;
424 
425 			pic1   = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *)
426 			    (uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr +
427 			    offset + TAVOR_HW_PORT_SIZE));
428 			tmp = ((pic1 >> shift) & mask);
429 
430 			ib_perf[indx].ks_old_pic1 = tmp;
431 
432 			tmp = tmp - oldval;
433 			ksi->tki_pic1 += tmp;
434 			data[2].value.ui64 = ksi->tki_pic1;
435 		}
436 
437 		TAVOR_TNF_EXIT(tavor_kstat_cntr_update);
438 		return (0);
439 	}
440 }
441