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 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * IO Performance Counter Driver
29 */
30
31#include <sys/types.h>
32#include <sys/ddi.h>
33#include <sys/modctl.h>
34#include "iospc.h"
35
36/* Debugging level. */
37#ifdef DEBUG
38int iospc_debug = 0;
39#endif /* DEBUG */
40
41/* State structure anchor. */
42void *iospc_state_p;
43
44static int iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
45static int iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
46static int iospc_create_name_kstat(iospc_grp_t *grp);
47static void iospc_delete_name_kstats(kstat_t **name_kstats_pp,
48    int num_kstats);
49static kstat_t *iospc_create_cntr_kstat(char *name, int dev_inst,
50    int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics);
51static int iospc_kstat_update(kstat_t *ksp, int rw);
52static kstat_t *iospc_create_picN_kstat(char *mod_name, int pic,
53    uint64_t mask, int num_ev, iospc_event_t *ev_array);
54
55iospc_grp_t **iospc_leaf_grps = NULL;
56int iospc_kstat_inited = 0;
57kmutex_t iospc_mutex;
58
59static struct dev_ops iospc_ops = {
60	DEVO_REV,
61	0,
62	nulldev,
63	nulldev,
64	nulldev,
65	iospc_attach,
66	iospc_detach,
67	nodev,
68	NULL,
69	NULL,
70	nodev
71};
72
73extern struct mod_ops mod_driverops;
74
75static struct modldrv md = {
76	&mod_driverops,
77	"IO Perf Counter Driver",
78	&iospc_ops,
79};
80
81static struct modlinkage ml = {
82	MODREV_1,
83	(void *)&md,
84	NULL
85};
86
87/*
88 * One-time module-wide initialization.
89 */
90int
91_init(void)
92{
93	int rval;
94
95	/* Initialize per-leaf soft state pointer. */
96	if ((rval = ddi_soft_state_init(&iospc_state_p,
97	    sizeof (iospc_t), 1)) != DDI_SUCCESS)
98		return (rval);
99
100	/* If all checks out, install the module. */
101	if ((rval = mod_install(&ml)) != DDI_SUCCESS) {
102		ddi_soft_state_fini(&iospc_state_p);
103		return (rval);
104	}
105	mutex_init(&iospc_mutex, NULL, MUTEX_DRIVER, NULL);
106	return (DDI_SUCCESS);
107}
108
109/*
110 * One-time module-wide cleanup, after last detach is done.
111 */
112int
113_fini(void)
114{
115	int rval;
116
117	/*
118	 * Remove the module first as this operation is the only thing here
119	 * which can fail.
120	 */
121	rval = mod_remove(&ml);
122	if (rval != DDI_SUCCESS)
123		return (rval);
124
125	if (iospc_leaf_grps != NULL) {
126		iospc_kstat_fini();
127		mutex_enter(&iospc_mutex);
128		iospc_kstat_inited = 0;
129		(void) rfios_unbind_group();
130		iospc_leaf_grps = NULL;
131		mutex_exit(&iospc_mutex);
132	}
133
134	mutex_destroy(&iospc_mutex);
135
136	/* Free px soft state */
137	ddi_soft_state_fini(&iospc_state_p);
138
139	return (DDI_SUCCESS);
140}
141
142int
143_info(struct modinfo *modinfop)
144{
145	return (mod_info(&ml, modinfop));
146}
147
148/*
149 * Per-instance initialization.  Suspend/resume not supported.
150 */
151static int
152iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
153{
154	iospc_t *iospc_p;
155	int instance = ddi_get_instance(dip);
156	char *ptr;
157
158	IOSPC_DBG2("iospc: iospc_attach: enter\n");
159	switch (cmd) {
160	case DDI_RESUME:
161	case DDI_ATTACH:
162		/* Initialize one-time kstat structures. */
163		mutex_enter(&iospc_mutex);
164		if (!iospc_kstat_inited) {
165			if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
166			    0, "compatible", &ptr)) != DDI_PROP_SUCCESS)
167				goto bad_property;
168
169			if ((strcmp(ptr, "SUNW,ktios-pr") == 0) ||
170			    (strcmp(ptr, "SUNW,rfios-pr") == 0)) {
171				iospc_leaf_grps = rfios_bind_group();
172			} else {
173				ddi_prop_free(ptr);
174				goto bad_property;
175			}
176
177			ddi_prop_free(ptr);
178
179			if (iospc_kstat_init() != DDI_SUCCESS)
180				goto bad_kstat_init;
181
182			iospc_kstat_inited++;
183		}
184		mutex_exit(&iospc_mutex);
185
186		if (ddi_soft_state_zalloc(iospc_state_p, instance) !=
187		    DDI_SUCCESS) {
188			goto bad_softstate;
189		}
190
191		iospc_p = (iospc_t *)ddi_get_soft_state(iospc_state_p,
192		    instance);
193
194		iospc_p->iospc_dip = dip;
195
196		/* Set up kstats. */
197
198		if (iospc_kstat_attach(iospc_p) != DDI_SUCCESS)
199			goto bad_kstat_attach;
200
201		IOSPC_DBG2("iospc: iospc_attach: exit SUCCESS\n");
202
203		return (DDI_SUCCESS);
204
205bad_kstat_attach:
206		(void) ddi_soft_state_free(iospc_state_p, instance);
207bad_softstate:
208		iospc_kstat_fini();
209bad_kstat_init:
210bad_property:
211		mutex_enter(&iospc_mutex);
212		IOSPC_DBG2("iospc: iospc_attach: exit FAILURE\n");
213		return (DDI_FAILURE);
214
215	default:
216		return (DDI_FAILURE);
217	}
218}
219
220/*
221 * Per-instance cleanup.  Suspend/resume not supported.
222 */
223static int
224iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
225{
226	int instance = ddi_get_instance(dip);
227
228	IOSPC_DBG2("iospc: iospc_detach: enter\n");
229	iospc_t *iospc_p = (iospc_t *)ddi_get_soft_state(
230	    iospc_state_p, instance);
231
232	switch (cmd) {
233	case DDI_SUSPEND:
234	case DDI_DETACH:
235		iospc_kstat_detach(iospc_p);
236		(void) ddi_soft_state_free(iospc_state_p, instance);
237
238		IOSPC_DBG2("iospc: iospc_detach: exit - SUCCESS\n");
239		return (DDI_SUCCESS);
240
241	default:
242		IOSPC_DBG2("iospc: iospc_detach: exit - FAILURE\n");
243		return (DDI_FAILURE);
244	}
245}
246
247#define	PIC_STR_LEN	5	/* Size of a PICx name string. */
248
249/*
250 * One-time initialization for this module.
251 */
252int
253iospc_kstat_init()
254{
255	iospc_grp_t **grp_pp;
256	iospc_grp_t *grp_p;
257
258	IOSPC_DBG2("iospc: kstat_init: enter\n");
259
260	/*
261	 * Initialize the name kstats for each group, drawing upon the table
262	 * for values.
263	 */
264	for (grp_pp = iospc_leaf_grps; *grp_pp != NULL; grp_pp++) {
265
266		grp_p = *grp_pp;
267
268		IOSPC_DBG2("Setting up group for %s\n", grp_p->grp_name);
269
270		/* Create basic pic event-type pair. */
271		grp_p->name_kstats_pp = kmem_zalloc((grp_p->num_counters *
272		    sizeof (kstat_t)), KM_SLEEP);
273		if (iospc_create_name_kstat(grp_p) != DDI_SUCCESS) {
274			iospc_kstat_fini();
275			IOSPC_DBG1("iospc: init: failure exit\n");
276			return (DDI_FAILURE);
277		}
278	}
279
280	IOSPC_DBG2("iospc: kstat_init: success exit\n");
281
282	return (DDI_SUCCESS);
283}
284
285/*
286 * Per-instance initialization for this module.
287 */
288int
289iospc_kstat_attach(iospc_t *iospc_p)
290{
291	iospc_grp_t **grp_pp;
292	iospc_grp_t *grp_p;
293	iospc_ksinfo_t *ksinfo_p;
294
295	int i;
296
297	IOSPC_DBG2("iospc: kstat_attach %d: enter\n",
298	    ddi_get_instance(iospc_p->iospc_dip));
299
300	/* Set up kstats for each group. */
301	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
302
303		if (i >= IOSPC_MAX_NUM_GRPS)
304			goto err;
305
306		grp_p = *grp_pp;
307
308		/*
309		 * ksinfo_p keeps all info needed by iospc_kstat_update,
310		 * which is fired off asynchronously on demand by the kstat
311		 * framework.
312		 */
313		ksinfo_p = (iospc_ksinfo_t *)kmem_zalloc(
314		    sizeof (iospc_ksinfo_t), KM_SLEEP);
315
316		ksinfo_p->iospc_p = iospc_p;
317		ksinfo_p->grp_p  = grp_p;
318
319		/* Also save in state structure, for later cleanup. */
320		iospc_p->iospc_ksinfo_p[i] = ksinfo_p;
321
322		/* Create counter kstats */
323		ksinfo_p->cntr_ksp = iospc_create_cntr_kstat(grp_p->grp_name,
324		    ddi_get_instance(iospc_p->iospc_dip),
325		    iospc_kstat_update, ksinfo_p, grp_p->num_counters);
326
327		if (ksinfo_p->cntr_ksp == NULL)
328			goto err;
329
330		if (grp_p->access_init(iospc_p, ksinfo_p) != SUCCESS)
331			goto err;
332	}
333
334	IOSPC_DBG2("iospc: kstat_attach: success exit\n");
335	return (DDI_SUCCESS);
336err:
337	iospc_kstat_detach(iospc_p);
338	IOSPC_DBG2("iospc: kstat_attach: failure exit\n");
339	return (DDI_FAILURE);
340}
341
342/*
343 * Create the name kstats for each group.
344 */
345static int
346iospc_create_name_kstat(iospc_grp_t *grp_p)
347{
348	int i;
349
350	for (i = 0; i < grp_p->num_counters; i++) {
351		grp_p->name_kstats_pp[i] = iospc_create_picN_kstat(
352		    grp_p->grp_name, i,
353		    grp_p->regsel_p->fields_p[i].event_offset,
354		    grp_p->regsel_p->fields_p[i].num_events,
355		    grp_p->regsel_p->fields_p[i].events_p);
356
357		if (grp_p->name_kstats_pp[i] == NULL)
358			return (DDI_FAILURE);
359	}
360	return (DDI_SUCCESS);
361}
362
363/*
364 * Create the picN kstat. Returns a pointer to the
365 * kstat which the driver must store to allow it
366 * to be deleted when necessary.
367 */
368static kstat_t *
369iospc_create_picN_kstat(char *mod_name, int pic, uint64_t ev_offset,
370    int num_ev, iospc_event_t *ev_array)
371{
372	int event;
373	char pic_name[PIC_STR_LEN];
374	kstat_t	*picN_ksp = NULL;
375	struct kstat_named *pic_named_data;
376
377	(void) snprintf(pic_name, PIC_STR_LEN, "pic%1d", pic);
378
379	if ((picN_ksp = kstat_create(mod_name, 0, pic_name,
380	    "bus", KSTAT_TYPE_NAMED, num_ev, 0)) == NULL) {
381		return (NULL);
382	}
383
384	/* NOTE: Number of events is assumed to always be non-zero. */
385
386	pic_named_data = (struct kstat_named *)picN_ksp->ks_data;
387
388	/*
389	 * Fill up data section of the kstat
390	 * Write event names and their associated pcr masks.
391	 * num_ev - 1 is because CLEAR_PIC is added separately.
392	 */
393	for (event = 0; event < num_ev - 1; event++) {
394		pic_named_data[event].value.ui64 =
395		    ev_array[event].value << ev_offset;
396
397		kstat_named_init(&pic_named_data[event],
398		    ev_array[event].name, KSTAT_DATA_UINT64);
399	}
400
401	/*
402	 * add the clear_pic entry
403	 */
404	pic_named_data[event].value.ui64 =
405	    (uint64_t)~(ev_array[event].value << ev_offset);
406
407	kstat_named_init(&pic_named_data[event], ev_array[event].name,
408	    KSTAT_DATA_UINT64);
409
410	kstat_install(picN_ksp);
411
412	return (picN_ksp);
413}
414
415/*
416 * Create the "counters" kstat.
417 */
418static kstat_t *
419iospc_create_cntr_kstat(char *name, int dev_inst,
420    int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics)
421{
422	int i;
423	char pic_str[PIC_STR_LEN];
424	struct kstat *counters_ksp;
425	struct kstat_named *counters_named_data;
426
427	IOSPC_DBG2("iospc_create_cntr_kstat: name: %s instance: %d\n",
428	    name, dev_inst);
429
430	/*
431	 * Size of kstat is num_pics + 1. extra one for pcr.
432	 */
433
434	if ((counters_ksp = kstat_create(name, dev_inst, "counters", "bus",
435	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
436		return (NULL);
437	}
438
439	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
440	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
441
442	for (i = 0; i < num_pics; i++) {
443		(void) snprintf(pic_str, PIC_STR_LEN, "pic%1d", i);
444
445		kstat_named_init(&counters_named_data[i+1], pic_str,
446		    KSTAT_DATA_UINT64);
447	}
448
449	/*
450	 * Store the reg type and other info. in the kstat's private field
451	 * so that they are available to the update function.
452	 */
453	counters_ksp->ks_private = (void *)ksinfop;
454	counters_ksp->ks_update = update;
455
456	kstat_install(counters_ksp);
457
458	return (counters_ksp);
459}
460
461/*
462 * Program a performance counter.
463 *
464 * reggroup is which type of counter.
465 * counter is the counter number.
466 * event is the event to program for that counter.
467 */
468static int
469iospc_perfcnt_program(iospc_t *iospc_p, iospc_grp_t *grp_p,
470    iospc_ksinfo_t *ksinfo_p, uint64_t new_events)
471{
472	uint64_t old_events;
473	int rval = SUCCESS;
474	uint64_t event_mask;
475	int counter;
476
477	IOSPC_DBG1(
478	    "iospc_perfcnt_program enter: new_events:0x%" PRIx64 "\n",
479	    new_events);
480
481	if ((rval = grp_p->access(iospc_p, ksinfo_p->arg, IOSPC_REG_READ,
482	    grp_p->regsel_p->regoff, &old_events)) != SUCCESS)
483		goto done_pgm;
484
485	IOSPC_DBG1("  old_events:0x%" PRIx64 "\n", old_events);
486
487	for (counter = 0; counter < grp_p->num_counters; counter++) {
488
489		if (grp_p->counters_p[counter].zero_regoff == NO_REGISTER)
490			continue;
491
492		event_mask = grp_p->regsel_p->fields_p[counter].event_mask <<
493		    grp_p->regsel_p->fields_p[counter].event_offset;
494
495		IOSPC_DBG1(
496		    "grp:%s, counter:%d, zero_regoff:0x%lx, "
497		    "event_mask:0x%" PRIx64 ", old&mask:0x%lx, "
498		    "new&mask:0x%lx\n",
499		    grp_p->grp_name, counter,
500		    grp_p->counters_p[counter].zero_regoff,
501		    event_mask, old_events & event_mask,
502		    new_events & event_mask);
503
504		if ((old_events & event_mask) ==
505		    (new_events & event_mask))
506			continue;
507
508		IOSPC_DBG1("Zeroing counter %d\n", counter);
509
510		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
511		    IOSPC_REG_WRITE, grp_p->counters_p[counter].zero_regoff,
512		    &grp_p->counters_p[counter].zero_value)) != SUCCESS)
513			goto done_pgm;
514	}
515
516	if (old_events != new_events) {
517
518		IOSPC_DBG1("old != new, setting event reg %ld to 0x%lx\n",
519		    grp_p->regsel_p->regoff, new_events);
520
521		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
522		    IOSPC_REG_WRITE, grp_p->regsel_p->regoff, &new_events))
523		    != SUCCESS) {
524			IOSPC_DBG1(
525			    "Write of new event data failed, "
526			    "select reg offset: %ld\n",
527			    grp_p->regsel_p->regoff);
528			goto done_pgm;
529		}
530	}
531done_pgm:
532	IOSPC_DBG1("iospc_perfcnt_program: returning status %d.\n", rval);
533	return (rval);
534}
535
536/*
537 * kstat update function. Handles reads/writes
538 * from/to kstat.
539 */
540static int
541iospc_kstat_update(kstat_t *ksp, int rw)
542{
543	struct kstat_named *data_p;
544	int counter;
545	iospc_ksinfo_t *ksinfop = ksp->ks_private;
546	iospc_grp_t *grp_p = ksinfop->grp_p;
547	iospc_t *iospc_p = ksinfop->iospc_p;
548
549	data_p = (struct kstat_named *)ksp->ks_data;
550
551	if (rw == KSTAT_WRITE) {
552
553		IOSPC_DBG2("iospc_kstat_update: wr %ld\n",
554		    data_p[0].value.ui64);
555
556		/*
557		 * Fields without programmable events won't be zeroed as
558		 * iospc_perfcnt_program is what zeros them.
559		 */
560
561		/* This group has programmable events. */
562		if (grp_p->regsel_p->regoff != NO_REGISTER) {
563
564			IOSPC_DBG2("write: regoff has valid register\n");
565			if (iospc_perfcnt_program(iospc_p, grp_p, ksinfop,
566			    data_p[0].value.ui64) != SUCCESS)
567				return (EIO);
568		}
569
570	} else {	/* Read the event register and all of the counters. */
571
572		/* This group has programmable events. */
573		if (grp_p->regsel_p->regoff != NO_REGISTER) {
574
575			IOSPC_DBG2("read: regoff has valid register\n");
576
577			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
578			    grp_p->regsel_p->regoff, &data_p[0].value.ui64)
579			    != SUCCESS)
580				return (EIO);
581		} else
582			data_p[0].value.ui64 = 0ull;
583
584		IOSPC_DBG2("iospc_kstat_update: rd event %lx\n",
585		    data_p[0].value.ui64);
586
587		for (counter = 0; counter < grp_p->num_counters; counter++) {
588
589			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
590			    grp_p->counters_p[counter].regoff,
591			    &data_p[counter + 1].value.ui64) != SUCCESS)
592				return (EIO);
593
594			IOSPC_DBG2("cntr%d, off:0x%lx, val:0x%lx\n", counter,
595			    grp_p->counters_p[counter].regoff,
596			    data_p[counter + 1].value.ui64);
597		}
598	}
599	return (SUCCESS);
600}
601
602void
603iospc_kstat_fini()
604{
605	iospc_grp_t **grp_pp;
606	iospc_grp_t *grp_p;
607	int j;
608
609	IOSPC_DBG2("iospc_kstat_fini called\n");
610
611	for (j = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; j++, grp_pp++) {
612		grp_p = *grp_pp;
613		if (grp_p->name_kstats_pp != NULL) {
614			iospc_delete_name_kstats(grp_p->name_kstats_pp,
615			    grp_p->num_counters);
616			kmem_free(grp_p->name_kstats_pp,
617			    grp_p->num_counters * sizeof (kstat_t));
618			grp_p->name_kstats_pp = NULL;
619		}
620	}
621}
622
623static void
624iospc_delete_name_kstats(kstat_t **name_kstats_pp, int num_kstats)
625{
626	int i;
627
628	if (name_kstats_pp != NULL) {
629		for (i = 0; i < num_kstats; i++) {
630			if (name_kstats_pp[i] != NULL)
631				kstat_delete(name_kstats_pp[i]);
632		}
633	}
634}
635
636void
637iospc_kstat_detach(iospc_t *iospc_p)
638{
639	iospc_grp_t **grp_pp;
640	iospc_grp_t *grp_p;
641	int i;
642
643	IOSPC_DBG2("iospc_kstat_detach called\n");
644
645	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
646
647		if (i >= IOSPC_MAX_NUM_GRPS)
648			return;
649
650		grp_p = *grp_pp;
651		if (iospc_p->iospc_ksinfo_p[i] != NULL) {
652
653			grp_p->access_fini(iospc_p, iospc_p->iospc_ksinfo_p[i]);
654
655			if (iospc_p->iospc_ksinfo_p[i]->cntr_ksp != NULL)
656				kstat_delete(
657				    iospc_p->iospc_ksinfo_p[i]->cntr_ksp);
658			kmem_free(iospc_p->iospc_ksinfo_p[i],
659			    sizeof (iospc_ksinfo_t));
660		}
661
662	}
663}
664