xref: /illumos-gate/usr/src/uts/sun4v/cpu/niagara_perfctr.c (revision a07094369b21309434206d9b3601d162693466fc)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/async.h>
31 #include <sys/sunddi.h>
32 #include <sys/sunndi.h>
33 #include <sys/ddi_impldefs.h>
34 #include <sys/machsystm.h>
35 #include <sys/niagararegs.h>
36 #include <sys/hypervisor_api.h>
37 #include <sys/kstat.h>
38 
39 #define	NIAGARA_MODNAME	"SUNW,UltraSPARC-T1"
40 #define	NUM_OF_PICS	2
41 
42 /*
43  * Data structure used to build array of event-names and pcr-mask values
44  */
45 typedef struct ni_kev_mask {
46 	char		*event_name;
47 	uint64_t	pcr_mask;
48 } ni_kev_mask_t;
49 
50 /*
51  * Kstat data structure for DRAM and JBUS performance counters
52  *
53  * Note that these performance counters are only 31 bits wide. Since
54  * the "busstat" command assumes a 32-bit counter, we emulate a 32-bit
55  * counter by detecting overflow on read of these performance counters
56  * and using the least significant bit of the overflow count as the
57  * most significant bit (i.e. bit# 31) of the DRAM and JBUS performance
58  * counters.
59  */
60 typedef struct ni_ksinfo {
61 	uint8_t		pic_no_evs;			/* number of events */
62 	uint8_t		pic_sel_shift[NUM_OF_PICS];
63 	uint8_t		pic_shift[NUM_OF_PICS];
64 	uint64_t	pic_mask[NUM_OF_PICS];
65 	kstat_t		*pic_name_ksp[NUM_OF_PICS];
66 	kstat_t		*cntr_ksp;
67 	uint32_t	pic_reg;
68 	uint32_t	pcr_reg;
69 	uint32_t	pic_overflow[NUM_OF_PICS];	/* overflow count */
70 	uint32_t	pic_last_val[NUM_OF_PICS];	/* last PIC value */
71 } ni_ksinfo_t;
72 
73 static ni_ksinfo_t	*ni_dram_kstats[NIAGARA_DRAM_BANKS];
74 static ni_ksinfo_t	*ni_jbus_kstat;
75 
76 typedef struct ni_perf_regs {
77 	uint32_t	pcr_reg;
78 	uint32_t	pic_reg;
79 } ni_perf_regs_t;
80 
81 static ni_perf_regs_t dram_perf_regs[] = {
82 	{HV_NIAGARA_DRAM_CTL0, HV_NIAGARA_DRAM_COUNT0},
83 	{HV_NIAGARA_DRAM_CTL1, HV_NIAGARA_DRAM_COUNT1},
84 	{HV_NIAGARA_DRAM_CTL2, HV_NIAGARA_DRAM_COUNT2},
85 	{HV_NIAGARA_DRAM_CTL3, HV_NIAGARA_DRAM_COUNT3},
86 };
87 
88 
89 static void ni_create_name_kstat(char *, ni_ksinfo_t *, ni_kev_mask_t *);
90 static void ni_delete_name_kstat(ni_ksinfo_t *);
91 
92 static kstat_t *ni_create_cntr_kstat(char *, int,
93 	int (*update)(kstat_t *, int), void *);
94 
95 static int ni_cntr_kstat_update(kstat_t *, int);
96 
97 static kstat_t *ni_create_picN_kstat(char *, int, int, int,
98 	ni_kev_mask_t *);
99 
100 #ifdef DEBUG
101 static int	ni_perf_debug;
102 #endif
103 
104 /*
105  * Niagara DRAM Performance Events
106  */
107 static ni_kev_mask_t
108 niagara_dram_events[] = {
109 	{"mem_reads",		0x0},
110 	{"mem_writes",		0x1},
111 	{"mem_read_write",	0x2},
112 	{"bank_busy_stalls",	0x3},
113 	{"rd_queue_latency",	0x4},
114 	{"wr_queue_latency",	0x5},
115 	{"rw_queue_latency",	0x6},
116 	{"wb_buf_hits",		0x7},
117 	{"clear_pic", 0xf}
118 };
119 
120 
121 /*
122  * Niagara JBUS Performance Events
123  */
124 static ni_kev_mask_t
125 niagara_jbus_events[] = {
126 	{"jbus_cycles",		0x1},
127 	{"dma_reads",		0x2},
128 	{"dma_read_latency",	0x3},
129 	{"dma_writes",		0x4},
130 	{"dma_write8",		0x5},
131 	{"ordering_waits",	0x6},
132 	{"pio_reads",		0x8},
133 	{"pio_read_latency",	0x9},
134 	{"aok_dok_off_cycles",	0xc},
135 	{"aok_off_cycles",	0xd},
136 	{"dok_off_cycles",	0xe},
137 	{"clear_pic",		0xf}
138 };
139 
140 /*
141  * Create the picN kstats for DRAM and JBUS events
142  */
143 void
144 niagara_kstat_init()
145 {
146 	int i;
147 	ni_ksinfo_t *ksinfop;
148 
149 #ifdef DEBUG
150 	if (ni_perf_debug)
151 		printf("ni_kstat_init called\n");
152 #endif
153 
154 	/*
155 	 * Create DRAM perf events kstat
156 	 */
157 	for (i = 0; i < NIAGARA_DRAM_BANKS; i++) {
158 		ksinfop = (ni_ksinfo_t *)kmem_zalloc(sizeof (ni_ksinfo_t),
159 		    KM_NOSLEEP);
160 
161 		if (ksinfop == NULL) {
162 			cmn_err(CE_WARN,
163 			    "%s: no space for niagara dram kstat\n",
164 			    NIAGARA_MODNAME);
165 			break;
166 		}
167 		ksinfop->pic_no_evs =
168 			sizeof (niagara_dram_events) / sizeof (ni_kev_mask_t);
169 		ksinfop->pic_sel_shift[0] = NIAGARA_DRAM_PIC0_SEL_SHIFT;
170 		ksinfop->pic_shift[0] = NIAGARA_DRAM_PIC0_SHIFT;
171 		ksinfop->pic_mask[0] = NIAGARA_DRAM_PIC0_MASK;
172 		ksinfop->pic_sel_shift[1] = NIAGARA_DRAM_PIC1_SEL_SHIFT;
173 		ksinfop->pic_shift[1] = NIAGARA_DRAM_PIC1_SHIFT;
174 		ksinfop->pic_mask[1] = NIAGARA_DRAM_PIC1_MASK;
175 		ksinfop->pic_reg = dram_perf_regs[i].pic_reg;
176 		ksinfop->pcr_reg = dram_perf_regs[i].pcr_reg;
177 		ni_dram_kstats[i] = ksinfop;
178 
179 		/* create basic pic event/mask pair (only once) */
180 		if (i == 0)
181 			ni_create_name_kstat("dram", ksinfop,
182 				    niagara_dram_events);
183 
184 		/* create counter kstats */
185 		ni_dram_kstats[i]->cntr_ksp = ni_create_cntr_kstat("dram", i,
186 		    ni_cntr_kstat_update, ksinfop);
187 	}
188 
189 	/*
190 	 * Create JBUS perf events kstat
191 	 */
192 	ni_jbus_kstat = (ni_ksinfo_t *)kmem_alloc(sizeof (ni_ksinfo_t),
193 		KM_NOSLEEP);
194 
195 	if (ni_jbus_kstat == NULL) {
196 		cmn_err(CE_WARN, "%s: no space for niagara jbus kstat\n",
197 		    NIAGARA_MODNAME);
198 	} else {
199 		ni_jbus_kstat->pic_no_evs =
200 			sizeof (niagara_jbus_events) / sizeof (ni_kev_mask_t);
201 		ni_jbus_kstat->pic_sel_shift[0] = NIAGARA_JBUS_PIC0_SEL_SHIFT;
202 		ni_jbus_kstat->pic_shift[0] = NIAGARA_JBUS_PIC0_SHIFT;
203 		ni_jbus_kstat->pic_mask[0] = NIAGARA_JBUS_PIC0_MASK;
204 		ni_jbus_kstat->pic_sel_shift[1] = NIAGARA_JBUS_PIC1_SEL_SHIFT;
205 		ni_jbus_kstat->pic_shift[1] = NIAGARA_JBUS_PIC1_SHIFT;
206 		ni_jbus_kstat->pic_mask[1] = NIAGARA_JBUS_PIC1_MASK;
207 		ni_jbus_kstat->pic_reg = HV_NIAGARA_JBUS_COUNT;
208 		ni_jbus_kstat->pcr_reg = HV_NIAGARA_JBUS_CTL;
209 		ni_create_name_kstat("jbus", ni_jbus_kstat,
210 		    niagara_jbus_events);
211 		ni_jbus_kstat->cntr_ksp = ni_create_cntr_kstat("jbus", 0,
212 		    ni_cntr_kstat_update, ni_jbus_kstat);
213 	}
214 }
215 
216 void
217 niagara_kstat_fini()
218 {
219 	int i;
220 
221 #ifdef DEBUG
222 	if (ni_perf_debug)
223 		printf("ni_kstat_fini called\n");
224 #endif
225 	for (i = 0; i < NIAGARA_DRAM_BANKS; i++) {
226 		if (ni_dram_kstats[i] != NULL) {
227 			ni_delete_name_kstat(ni_dram_kstats[i]);
228 			if (ni_dram_kstats[i]->cntr_ksp != NULL)
229 				kstat_delete(ni_dram_kstats[i]->cntr_ksp);
230 			kmem_free(ni_dram_kstats[i], sizeof (ni_ksinfo_t));
231 			ni_dram_kstats[i] = NULL;
232 		}
233 	}
234 
235 	if (ni_jbus_kstat != NULL) {
236 		ni_delete_name_kstat(ni_jbus_kstat);
237 		if (ni_jbus_kstat->cntr_ksp != NULL)
238 			kstat_delete(ni_jbus_kstat->cntr_ksp);
239 		kmem_free(ni_jbus_kstat, sizeof (ni_ksinfo_t));
240 		ni_jbus_kstat = NULL;
241 	}
242 }
243 
244 static void
245 ni_create_name_kstat(char *name, ni_ksinfo_t *pp, ni_kev_mask_t *ev)
246 {
247 	int	i;
248 
249 #ifdef DEBUG
250 	if (ni_perf_debug > 1)
251 		printf("ni_create_name_kstat: name: %s\n", name);
252 #endif
253 	for (i = 0; i < NUM_OF_PICS; i++) {
254 		pp->pic_name_ksp[i] = ni_create_picN_kstat(name,
255 			i, pp->pic_sel_shift[i], pp->pic_no_evs, ev);
256 
257 		if (pp->pic_name_ksp[i] == NULL) {
258 			cmn_err(CE_WARN, "%s: unable to create name kstat",
259 			    NIAGARA_MODNAME);
260 		}
261 	}
262 }
263 
264 static void
265 ni_delete_name_kstat(ni_ksinfo_t *pp)
266 {
267 	int	i;
268 
269 	if (pp != NULL) {
270 		for (i = 0; i < NUM_OF_PICS; i++) {
271 			if (pp->pic_name_ksp[i] != NULL)
272 				kstat_delete(pp->pic_name_ksp[i]);
273 		}
274 	}
275 }
276 
277 /*
278  * Create the picN kstat. Returns a pointer to the
279  * kstat which the driver must store to allow it
280  * to be deleted when necessary.
281  */
282 static kstat_t *
283 ni_create_picN_kstat(char *mod_name, int pic, int pic_sel_shift,
284 	int num_ev, ni_kev_mask_t *ev_array)
285 {
286 	struct kstat_named *pic_named_data;
287 	int	inst = 0;
288 	int	event;
289 	char	pic_name[30];
290 	kstat_t	*picN_ksp = NULL;
291 
292 	(void) sprintf(pic_name, "pic%d", pic);
293 	if ((picN_ksp = kstat_create(mod_name, inst, pic_name,
294 	    "bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) {
295 		cmn_err(CE_WARN, "%s %s : kstat create failed",
296 			mod_name, pic_name);
297 
298 		/*
299 		 * It is up to the calling function to delete any kstats
300 		 * that may have been created already. We just
301 		 * return NULL to indicate an error has occured.
302 		 */
303 		return (NULL);
304 	}
305 
306 	pic_named_data = (struct kstat_named *)
307 	    picN_ksp->ks_data;
308 
309 	/*
310 	 * Write event names and their associated pcr masks. The
311 	 * last entry in the array (clear_pic) is added seperately
312 	 * below as the pic value must be inverted.
313 	 */
314 	for (event = 0; event < num_ev - 1; event++) {
315 		pic_named_data[event].value.ui64 =
316 			(ev_array[event].pcr_mask << pic_sel_shift);
317 
318 		kstat_named_init(&pic_named_data[event],
319 			ev_array[event].event_name,
320 			KSTAT_DATA_UINT64);
321 	}
322 
323 	/*
324 	 * add the clear_pic entry.
325 	 */
326 	pic_named_data[event].value.ui64 =
327 		(uint64_t)~(ev_array[event].pcr_mask << pic_sel_shift);
328 
329 	kstat_named_init(&pic_named_data[event], ev_array[event].event_name,
330 	    KSTAT_DATA_UINT64);
331 
332 	kstat_install(picN_ksp);
333 
334 	return (picN_ksp);
335 }
336 
337 /*
338  * Create the "counters" kstat.
339  */
340 static kstat_t *
341 ni_create_cntr_kstat(char *name, int instance, int (*update)(kstat_t *, int),
342 	void *ksinfop)
343 {
344 	struct kstat	*counters_ksp;
345 	struct kstat_named	*counters_named_data;
346 	char		pic_str[10];
347 	int		i;
348 	int		num_pics = NUM_OF_PICS;
349 
350 #ifdef DEBUG
351 	if (ni_perf_debug > 1)
352 		printf("ni_create_cntr_kstat: name: %s instance: %d\n",
353 		    name, instance);
354 #endif
355 
356 	/*
357 	 * Size of kstat is num_pics + 1 as it
358 	 * also contains the %pcr
359 	 */
360 	if ((counters_ksp = kstat_create(name, instance, "counters", "bus",
361 	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
362 		cmn_err(CE_WARN,
363 		    "%s: kstat_create for %s%d failed", NIAGARA_MODNAME,
364 		    name, instance);
365 		return (NULL);
366 	}
367 
368 	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
369 
370 	/*
371 	 * Iinitialize the named kstats
372 	 */
373 	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
374 
375 	for (i = 0; i < num_pics; i++) {
376 		(void) sprintf(pic_str, "pic%d", i);
377 
378 		kstat_named_init(&counters_named_data[i+1], pic_str,
379 		    KSTAT_DATA_UINT64);
380 	}
381 
382 	/*
383 	 * Store the register offset's in the kstat's
384 	 * private field so that they are available
385 	 * to the update function.
386 	 */
387 	counters_ksp->ks_private = (void *)ksinfop;
388 	counters_ksp->ks_update = update;
389 
390 	kstat_install(counters_ksp);
391 
392 	return (counters_ksp);
393 }
394 
395 /*
396  * kstat update function. Handles reads/writes
397  * from/to kstat.
398  */
399 static int
400 ni_cntr_kstat_update(kstat_t *ksp, int rw)
401 {
402 	struct kstat_named	*data_p;
403 	ni_ksinfo_t	*ksinfop = ksp->ks_private;
404 	uint64_t	pic, pcr;
405 	int		stat = 0;
406 	uint32_t	pic0, pic1;
407 
408 	data_p = (struct kstat_named *)ksp->ks_data;
409 
410 	if (rw == KSTAT_WRITE) {
411 #ifdef DEBUG
412 		if (ni_perf_debug)
413 			printf("ni_cntr_kstat_update: wr pcr-%d: %lx\n",
414 			    ksinfop->pcr_reg, data_p[0].value.ui64);
415 #endif
416 		if (hv_niagara_setperf(ksinfop->pcr_reg, data_p[0].value.ui64))
417 			stat = EACCES;
418 	} else {
419 		if (hv_niagara_getperf(ksinfop->pic_reg, &pic) != 0 ||
420 		    hv_niagara_getperf(ksinfop->pcr_reg, &pcr) != 0)
421 			stat = EACCES;
422 		else {
423 
424 			data_p[0].value.ui64 = pcr;
425 
426 			/*
427 			 * Generate a 32-bit PIC0 value by detecting overflow
428 			 */
429 			pic0 = (uint32_t)((pic >> ksinfop->pic_shift[0]) &
430 			    ksinfop->pic_mask[0]);
431 			if (pic0 < ksinfop->pic_last_val[0])
432 				ksinfop->pic_overflow[0]++;
433 			ksinfop->pic_last_val[0] = pic0;
434 			pic0 += (ksinfop->pic_overflow[0] & 1) << 31;
435 			data_p[1].value.ui64 = (uint64_t)pic0;
436 
437 			/*
438 			 * Generate a 32-bit PIC1 value by detecting overflow
439 			 */
440 			pic1 = (uint32_t)((pic >> ksinfop->pic_shift[1]) &
441 			    ksinfop->pic_mask[1]);
442 			if (pic1 < ksinfop->pic_last_val[1])
443 				ksinfop->pic_overflow[1]++;
444 			ksinfop->pic_last_val[1] = pic1;
445 			pic1 += (ksinfop->pic_overflow[1] & 1) << 31;
446 			data_p[2].value.ui64 = (uint64_t)pic1;
447 		}
448 #ifdef DEBUG
449 		if (ni_perf_debug)
450 			printf("ni_cntr_kstat_update: rd pcr%d: %lx  "
451 			    "pic%d: %16lx pic0: %8lx pic1: %8lx\n",
452 			    ksinfop->pcr_reg, pcr, ksinfop->pic_reg, pic,
453 			    data_p[1].value.ui64, data_p[2].value.ui64);
454 #endif
455 	}
456 	return (stat);
457 }
458