xref: /illumos-gate/usr/src/uts/sun4v/io/n2piupc/n2piupc_kstats.c (revision a629ded1d7b2e67c2028ccbc5ba9099328cc4e1b)
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 2006 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/kstat.h>
31 #include "n2piupc_acc.h"
32 #include "n2piupc_tables.h"
33 #include "n2piupc.h"
34 #include "n2piupc_biterr.h"
35 
36 #define	PIC_STR_LEN	5	/* Size of a PICx name string. */
37 
38 static int n2piupc_create_name_kstat(n2piu_grp_t *grp);
39 static void n2piupc_delete_name_kstats(kstat_t **name_kstats_pp,
40     int num_kstats);
41 static kstat_t *n2piupc_create_cntr_kstat(char *name, int dev_inst,
42     int (*update)(kstat_t *, int), n2piu_ksinfo_t *ksinfop, int num_pics);
43 static int n2piupc_kstat_update(kstat_t *ksp, int rw);
44 static kstat_t *n2piupc_create_picN_kstat(char *mod_name, int pic,
45     uint64_t mask, int num_ev, n2piu_event_t *ev_array);
46 static int n2piupc_write(n2piupc_t *n2piupc_p, int regid, uint64_t data);
47 
48 /*
49  * One-time initialization for this module.
50  */
51 int
52 n2piupc_kstat_init()
53 {
54 	n2piu_grp_t **grp_pp;
55 	n2piu_grp_t *grp_p;
56 
57 	N2PIUPC_DBG2("n2piupc: kstat_init: enter\n");
58 
59 	/*
60 	 * Initialize the name kstats for each group, drawing upon the table
61 	 * for values.
62 	 */
63 	for (grp_pp = leaf_grps; *grp_pp != NULL; grp_pp++) {
64 
65 		grp_p = *grp_pp;
66 
67 		N2PIUPC_DBG2("Setting up group for %s\n", grp_p->grp_name);
68 
69 		/* Create basic pic event-type pair. */
70 		grp_p->name_kstats_pp = kmem_zalloc((grp_p->num_counters *
71 			sizeof (kstat_t)), KM_SLEEP);
72 		if (n2piupc_create_name_kstat(grp_p) != DDI_SUCCESS) {
73 			n2piupc_kstat_fini();
74 			N2PIUPC_DBG1("n2piupc: init: failure exit\n");
75 			return (DDI_FAILURE);
76 		}
77 	}
78 
79 	N2PIUPC_DBG2("n2piupc: kstat_init: success exit\n");
80 
81 	return (DDI_SUCCESS);
82 }
83 
84 /*
85  * Per-instance initialization for this module.
86  */
87 int
88 n2piupc_kstat_attach(n2piupc_t *n2piupc_p)
89 {
90 	n2piu_grp_t **grp_pp;
91 	n2piu_grp_t *grp_p;
92 	n2piu_ksinfo_t *ksinfo_p;
93 
94 	int i;
95 
96 	N2PIUPC_DBG2("n2piupc: kstat_attach %d: enter\n",
97 	    ddi_get_instance(n2piupc_p->n2piupc_dip));
98 
99 	/* Initialize biterr module.  Save opaque result. */
100 	if (n2piupc_biterr_attach(&n2piupc_p->n2piupc_biterr_p) != DDI_SUCCESS)
101 		goto err;
102 
103 	/* Set up kstats for each group. */
104 	for (i = 0, grp_pp = leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
105 
106 		grp_p = *grp_pp;
107 
108 		/*
109 		 * ksinfo_p keeps all info needed by n2piupc_kstat_update,
110 		 * which is fired off asynchronously on demand by the kstat
111 		 * framework.
112 		 */
113 		ksinfo_p = (n2piu_ksinfo_t *)kmem_zalloc(
114 		    sizeof (n2piu_ksinfo_t), KM_SLEEP);
115 
116 		ksinfo_p->n2piupc_p = n2piupc_p;
117 		ksinfo_p->grp_p  = grp_p;
118 
119 		/* Also save in state structure, for later cleanup. */
120 		n2piupc_p->n2piupc_ksinfo_p[i] = ksinfo_p;
121 
122 		/* Create counter kstats */
123 		ksinfo_p->cntr_ksp = n2piupc_create_cntr_kstat(grp_p->grp_name,
124 		    ddi_get_instance(n2piupc_p->n2piupc_dip),
125 		    n2piupc_kstat_update, ksinfo_p, grp_p->num_counters);
126 		if (ksinfo_p->cntr_ksp == NULL)
127 			goto err;
128 	}
129 
130 	/*
131 	 * Special treatment for bit err registers: enable them so they start
132 	 * counting now.
133 	 */
134 	if (n2piupc_write(n2piupc_p, leaf_grps[BIT_ERR_GRP]->regsel_p->regoff,
135 	    BTERR_CTR_ENABLE) != SUCCESS) {
136 		goto err;
137 	}
138 
139 	N2PIUPC_DBG2("n2piupc: kstat_attach: success exit\n");
140 	return (DDI_SUCCESS);
141 err:
142 	n2piupc_kstat_detach(n2piupc_p);
143 	N2PIUPC_DBG2("n2piupc: kstat_attach: failure exit\n");
144 	return (DDI_FAILURE);
145 }
146 
147 /*
148  * Create the name kstats for each group.
149  */
150 static int
151 n2piupc_create_name_kstat(n2piu_grp_t *grp_p)
152 {
153 	int i;
154 
155 	for (i = 0; i < grp_p->num_counters; i++) {
156 		grp_p->name_kstats_pp[i] = n2piupc_create_picN_kstat(
157 		    grp_p->grp_name, i,
158 		    grp_p->regsel_p->fields_p[i].event_offset,
159 		    grp_p->regsel_p->fields_p[i].num_events,
160 		    grp_p->regsel_p->fields_p[i].events_p);
161 
162 		if (grp_p->name_kstats_pp[i] == NULL)
163 			return (DDI_FAILURE);
164 	}
165 	return (DDI_SUCCESS);
166 }
167 
168 /*
169  * Create the picN kstat. Returns a pointer to the
170  * kstat which the driver must store to allow it
171  * to be deleted when necessary.
172  */
173 static kstat_t *
174 n2piupc_create_picN_kstat(char *mod_name, int pic, uint64_t ev_offset,
175     int num_ev, n2piu_event_t *ev_array)
176 {
177 	int event;
178 	char pic_name[PIC_STR_LEN];
179 	kstat_t	*picN_ksp = NULL;
180 	struct kstat_named *pic_named_data;
181 
182 
183 	(void) snprintf(pic_name, PIC_STR_LEN, "pic%1d", pic);
184 
185 	if ((picN_ksp = kstat_create(mod_name, 0, pic_name,
186 	    "bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) {
187 		cmn_err(CE_WARN, "%s %s : kstat create failed",
188 		    mod_name, pic_name);
189 		return (NULL);
190 	}
191 
192 	/* NOTE: Number of events is assumed to always be non-zero. */
193 
194 	pic_named_data = (struct kstat_named *)picN_ksp->ks_data;
195 
196 	/*
197 	 * Fill up data section of the kstat
198 	 * Write event names and their associated pcr masks.
199 	 * num_ev - 1 is because CLEAR_PIC is added separately.
200 	 */
201 	for (event = 0; event < num_ev - 1; event++) {
202 		pic_named_data[event].value.ui64 =
203 		    ev_array[event].value << ev_offset;
204 
205 		kstat_named_init(&pic_named_data[event],
206 		    ev_array[event].name, KSTAT_DATA_UINT64);
207 	}
208 
209 	/*
210 	 * add the clear_pic entry
211 	 */
212 	pic_named_data[event].value.ui64 =
213 	    (uint64_t)~(ev_array[event].value << ev_offset);
214 
215 	kstat_named_init(&pic_named_data[event], ev_array[event].name,
216 	    KSTAT_DATA_UINT64);
217 
218 	kstat_install(picN_ksp);
219 
220 	return (picN_ksp);
221 }
222 
223 /*
224  * Create the "counters" kstat.
225  */
226 static kstat_t *
227 n2piupc_create_cntr_kstat(char *name, int dev_inst,
228     int (*update)(kstat_t *, int), n2piu_ksinfo_t *ksinfop, int num_pics)
229 {
230 	int i;
231 	char pic_str[PIC_STR_LEN];
232 	struct kstat *counters_ksp;
233 	struct kstat_named *counters_named_data;
234 
235 	N2PIUPC_DBG2("n2piupc_create_cntr_kstat: name: %s instance: %d\n",
236 	    name, dev_inst);
237 
238 	/*
239 	 * Size of kstat is num_pics + 1. extra one for pcr.
240 	 */
241 
242 	if ((counters_ksp = kstat_create(name, dev_inst, "counters", "bus",
243 	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
244 		cmn_err(CE_WARN, "%s%d: kstat_create for %s counters failed",
245 		    NAMEINST(ksinfop->n2piupc_p->n2piupc_dip), name);
246 		return (NULL);
247 	}
248 
249 	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
250 	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
251 
252 	for (i = 0; i < num_pics; i++) {
253 		(void) snprintf(pic_str, PIC_STR_LEN, "pic%1d", i);
254 
255 		kstat_named_init(&counters_named_data[i+1], pic_str,
256 		    KSTAT_DATA_UINT64);
257 	}
258 
259 	/*
260 	 * Store the reg type and other info. in the kstat's private field
261 	 * so that they are available to the update function.
262 	 */
263 	counters_ksp->ks_private = (void *)ksinfop;
264 	counters_ksp->ks_update = update;
265 
266 	kstat_install(counters_ksp);
267 
268 	return (counters_ksp);
269 }
270 
271 /* Higher-level register write, hides SW abstractions. */
272 static int
273 n2piupc_write(n2piupc_t *n2piupc_p, int regid, uint64_t data)
274 {
275 	int rval = SUCCESS;
276 
277 	switch (regid) {
278 	case SW_N2PIU_BITERR_SEL:
279 	case SW_N2PIU_BITERR_CLR:
280 		rval = n2piupc_biterr_write(n2piupc_p, regid, data);
281 		break;
282 
283 	default:
284 		if (n2piupc_set_perfreg(n2piupc_p->n2piupc_handle,
285 		    regid, data) != H_EOK)
286 			rval = EIO;
287 		break;
288 	}
289 
290 	N2PIUPC_DBG1("n2piupc_write: status:%d\n", rval);
291 	return (rval);
292 }
293 
294 
295 /* Higher-level register read, hides SW abstractions. */
296 static int
297 n2piupc_read(n2piupc_t *n2piupc_p, int regid, uint64_t *data)
298 {
299 	int rval = SUCCESS;
300 
301 	N2PIUPC_DBG2("n2piupc_read enter: regid:%d\n", regid);
302 
303 	/* This "register" is a layered SW-implemented reg. */
304 	switch (regid) {
305 	case SW_N2PIU_BITERR_CNT1_DATA:
306 	case SW_N2PIU_BITERR_CNT2_DATA:
307 	case SW_N2PIU_BITERR_SEL:
308 		rval = n2piupc_biterr_read(n2piupc_p, regid, data);
309 		break;
310 
311 	default:
312 		if (n2piupc_get_perfreg(n2piupc_p->n2piupc_handle,
313 		    regid, data) != H_EOK)
314 			rval = EIO;
315 		break;
316 	}
317 
318 	N2PIUPC_DBG1("n2piupc_read exit: data:0x%lx, status:%d\n", *data,
319 	    rval);
320 
321 	return (rval);
322 }
323 
324 
325 /*
326  * Program a performance counter.
327  *
328  * reggroup is which type of counter.
329  * counter is the counter number.
330  * event is the event to program for that counter.
331  */
332 static int
333 n2piupc_perfcnt_program(n2piupc_t *n2piupc_p, n2piu_grp_t *grp_p,
334     uint64_t new_events)
335 {
336 	uint64_t old_events;
337 	int rval = SUCCESS;
338 	uint64_t event_mask;
339 	int counter;
340 
341 	N2PIUPC_DBG1(
342 	    "n2piupc_perfcnt_program enter: new_events:0x%" PRIx64 "\n",
343 	    new_events);
344 
345 	if ((rval = n2piupc_read(n2piupc_p, grp_p->regsel_p->regoff,
346 	    &old_events)) != SUCCESS) {
347 		N2PIUPC_DBG1(
348 		    "Read of old event data failed, select reg offset:%ld\n",
349 		    grp_p->regsel_p->regoff);
350 		goto done_pgm;
351 	}
352 
353 	N2PIUPC_DBG1("  old_events:0x%" PRIx64 "\n", old_events);
354 
355 	for (counter = 0; counter < grp_p->num_counters; counter++) {
356 
357 		if (grp_p->counters_p[counter].zero_regoff == NO_REGISTER)
358 			continue;
359 
360 		event_mask = grp_p->regsel_p->fields_p[counter].event_mask <<
361 		    grp_p->regsel_p->fields_p[counter].event_offset;
362 
363 		N2PIUPC_DBG1(
364 		    "grp:%s, counter:%d, zero_regoff:0x%lx, "
365 		    "event_mask:0x%" PRIx64 ", old&mask:0x%lx, "
366 		    "new&mask:0x%lx\n",
367 		    grp_p->grp_name, counter,
368 		    grp_p->counters_p[counter].zero_regoff,
369 		    event_mask, old_events & event_mask,
370 		    new_events & event_mask);
371 
372 		if ((old_events & event_mask) ==
373 		    (new_events & event_mask))
374 			continue;
375 
376 		N2PIUPC_DBG1("Zeroing counter %d\n", counter);
377 		if ((rval = n2piupc_write(n2piupc_p,
378 		    grp_p->counters_p[counter].zero_regoff,
379 		    grp_p->counters_p[counter].zero_value)) != SUCCESS)
380 			goto done_pgm;
381 	}
382 
383 	if (old_events != new_events) {
384 		N2PIUPC_DBG1("old != new, setting event reg %ld to 0x%lx\n",
385 		    grp_p->regsel_p->regoff, new_events);
386 		if ((rval = n2piupc_write(n2piupc_p, grp_p->regsel_p->regoff,
387 		    new_events)) != SUCCESS) {
388 			N2PIUPC_DBG1(
389 			    "Write of new event data failed, "
390 			    "select reg offset: %ld\n",
391 			    grp_p->regsel_p->regoff);
392 			goto done_pgm;
393 		}
394 	}
395 done_pgm:
396 	N2PIUPC_DBG1("n2piupc_perfcnt_program: returning status %d.\n", rval);
397 	return (rval);
398 }
399 
400 /*
401  * kstat update function. Handles reads/writes
402  * from/to kstat.
403  */
404 static int
405 n2piupc_kstat_update(kstat_t *ksp, int rw)
406 {
407 	struct kstat_named *data_p;
408 	int counter;
409 	n2piu_ksinfo_t *ksinfop = ksp->ks_private;
410 	n2piu_grp_t *grp_p = ksinfop->grp_p;
411 	n2piupc_t *n2piupc_p = ksinfop->n2piupc_p;
412 
413 	data_p = (struct kstat_named *)ksp->ks_data;
414 
415 	if (rw == KSTAT_WRITE) {
416 
417 		N2PIUPC_DBG2("n2piupc_kstat_update: wr %ld\n",
418 		    data_p[0].value.ui64);
419 
420 		/*
421 		 * Fields without programmable events won't be zeroed as
422 		 * n2piupc_perfcnt_program is what zeros them.
423 		 */
424 
425 		/* This group has programmable events. */
426 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
427 
428 			N2PIUPC_DBG2("write: regoff has valid register\n");
429 			if (n2piupc_perfcnt_program(n2piupc_p, grp_p,
430 			    data_p[0].value.ui64) != SUCCESS)
431 				return (EIO);
432 		}
433 
434 	} else {	/* Read the event register and all of the counters. */
435 
436 		/* This group has programmable events. */
437 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
438 
439 			N2PIUPC_DBG2("read: regoff has valid register\n");
440 			if (n2piupc_read(n2piupc_p, grp_p->regsel_p->regoff,
441 			    &data_p[0].value.ui64) != SUCCESS)
442 				return (EIO);
443 		} else
444 			data_p[0].value.ui64 = 0ull;
445 
446 		N2PIUPC_DBG2("n2piupc_kstat_update: rd event %ld",
447 		    data_p[0].value.ui64);
448 
449 		for (counter = 0; counter < grp_p->num_counters; counter++) {
450 			if (n2piupc_read(n2piupc_p,
451 			    grp_p->counters_p[counter].regoff,
452 			    &data_p[counter + 1].value.ui64) != SUCCESS)
453 				return (EIO);
454 
455 			N2PIUPC_DBG2("cntr%d, off:0x%lx, val:0x%ld", counter,
456 			    grp_p->counters_p[counter].regoff,
457 			    data_p[counter + 1].value.ui64);
458 		}
459 	}
460 	return (SUCCESS);
461 }
462 
463 void
464 n2piupc_kstat_fini()
465 {
466 	n2piu_grp_t **grp_pp;
467 	n2piu_grp_t *grp_p;
468 	int j;
469 
470 	N2PIUPC_DBG2("n2piupc_kstat_fini called\n");
471 
472 	for (j = 0, grp_pp = leaf_grps; *grp_pp != NULL; j++, grp_pp++) {
473 		grp_p = *grp_pp;
474 		if (grp_p->name_kstats_pp != NULL) {
475 			n2piupc_delete_name_kstats(grp_p->name_kstats_pp,
476 			    grp_p->num_counters);
477 			kmem_free(grp_p->name_kstats_pp,
478 			    grp_p->num_counters * sizeof (kstat_t));
479 			grp_p->name_kstats_pp = NULL;
480 		}
481 	}
482 }
483 
484 static void
485 n2piupc_delete_name_kstats(kstat_t **name_kstats_pp, int num_kstats)
486 {
487 	int i;
488 
489 	if (name_kstats_pp != NULL) {
490 		for (i = 0; i < num_kstats; i++) {
491 			if (name_kstats_pp[i] != NULL)
492 				kstat_delete(name_kstats_pp[i]);
493 		}
494 	}
495 }
496 
497 void
498 n2piupc_kstat_detach(n2piupc_t *n2piupc_p)
499 {
500 	n2piu_grp_t **grp_pp;
501 	int i;
502 
503 	N2PIUPC_DBG2("n2piupc_kstat_detach called\n");
504 
505 	for (i = 0, grp_pp = leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
506 		if (n2piupc_p->n2piupc_ksinfo_p[i] != NULL) {
507 			if (n2piupc_p->n2piupc_ksinfo_p[i]->cntr_ksp != NULL)
508 				kstat_delete(
509 				    n2piupc_p->n2piupc_ksinfo_p[i]->cntr_ksp);
510 			kmem_free(n2piupc_p->n2piupc_ksinfo_p[i],
511 			    sizeof (n2piu_ksinfo_t));
512 		}
513 
514 	}
515 
516 	n2piupc_biterr_detach(n2piupc_p->n2piupc_biterr_p);
517 }
518