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