tavor_stats.c revision 9517:b4839b0aa7a4
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
44static kstat_t *tavor_kstat_picN_create(tavor_state_t *state, int num_pic,
45    int num_evt, tavor_ks_mask_t *ev_array);
46static kstat_t *tavor_kstat_cntr_create(tavor_state_t *state, int num_pic,
47    int (*update)(kstat_t *, int));
48static 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 */
55tavor_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 */
96int
97tavor_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
155kstat_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 */
179void
180tavor_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 */
213static kstat_t *
214tavor_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 */
272static kstat_t *
273tavor_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 */
331static int
332tavor_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