/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* IO Performance Counter Driver
*/
#include "iospc.h"
/* Debugging level. */
#ifdef DEBUG
int iospc_debug = 0;
#endif /* DEBUG */
/* State structure anchor. */
void *iospc_state_p;
int num_kstats);
int iospc_kstat_inited = 0;
0,
NULL,
NULL,
};
extern struct mod_ops mod_driverops;
"IO Perf Counter Driver",
};
(void *)&md,
};
/*
* One-time module-wide initialization.
*/
int
_init(void)
{
int rval;
/* Initialize per-leaf soft state pointer. */
return (rval);
/* If all checks out, install the module. */
return (rval);
}
return (DDI_SUCCESS);
}
/*
* One-time module-wide cleanup, after last detach is done.
*/
int
_fini(void)
{
int rval;
/*
* Remove the module first as this operation is the only thing here
* which can fail.
*/
if (rval != DDI_SUCCESS)
return (rval);
if (iospc_leaf_grps != NULL) {
iospc_kstat_inited = 0;
(void) rfios_unbind_group();
}
/* Free px soft state */
return (DDI_SUCCESS);
}
int
{
}
/*
*/
static int
{
char *ptr;
IOSPC_DBG2("iospc: iospc_attach: enter\n");
switch (cmd) {
case DDI_RESUME:
case DDI_ATTACH:
/* Initialize one-time kstat structures. */
if (!iospc_kstat_inited) {
goto bad_property;
} else {
goto bad_property;
}
if (iospc_kstat_init() != DDI_SUCCESS)
goto bad_kstat_init;
}
DDI_SUCCESS) {
goto bad_softstate;
}
instance);
/* Set up kstats. */
goto bad_kstat_attach;
IOSPC_DBG2("iospc: iospc_attach: exit SUCCESS\n");
return (DDI_SUCCESS);
IOSPC_DBG2("iospc: iospc_attach: exit FAILURE\n");
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
}
/*
*/
static int
{
IOSPC_DBG2("iospc: iospc_detach: enter\n");
switch (cmd) {
case DDI_SUSPEND:
case DDI_DETACH:
IOSPC_DBG2("iospc: iospc_detach: exit - SUCCESS\n");
return (DDI_SUCCESS);
default:
IOSPC_DBG2("iospc: iospc_detach: exit - FAILURE\n");
return (DDI_FAILURE);
}
}
/*
* One-time initialization for this module.
*/
int
{
IOSPC_DBG2("iospc: kstat_init: enter\n");
/*
* Initialize the name kstats for each group, drawing upon the table
* for values.
*/
/* Create basic pic event-type pair. */
IOSPC_DBG1("iospc: init: failure exit\n");
return (DDI_FAILURE);
}
}
IOSPC_DBG2("iospc: kstat_init: success exit\n");
return (DDI_SUCCESS);
}
/*
* Per-instance initialization for this module.
*/
int
{
int i;
IOSPC_DBG2("iospc: kstat_attach %d: enter\n",
/* Set up kstats for each group. */
if (i >= IOSPC_MAX_NUM_GRPS)
goto err;
/*
* ksinfo_p keeps all info needed by iospc_kstat_update,
* which is fired off asynchronously on demand by the kstat
* framework.
*/
sizeof (iospc_ksinfo_t), KM_SLEEP);
/* Also save in state structure, for later cleanup. */
/* Create counter kstats */
goto err;
goto err;
}
IOSPC_DBG2("iospc: kstat_attach: success exit\n");
return (DDI_SUCCESS);
err:
IOSPC_DBG2("iospc: kstat_attach: failure exit\n");
return (DDI_FAILURE);
}
/*
* Create the name kstats for each group.
*/
static int
{
int i;
for (i = 0; i < grp_p->num_counters; i++) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Create the picN kstat. Returns a pointer to the
* kstat which the driver must store to allow it
* to be deleted when necessary.
*/
static kstat_t *
{
int event;
return (NULL);
}
/* NOTE: Number of events is assumed to always be non-zero. */
/*
* Fill up data section of the kstat
* Write event names and their associated pcr masks.
* num_ev - 1 is because CLEAR_PIC is added separately.
*/
}
/*
* add the clear_pic entry
*/
return (picN_ksp);
}
/*
* Create the "counters" kstat.
*/
static kstat_t *
{
int i;
IOSPC_DBG2("iospc_create_cntr_kstat: name: %s instance: %d\n",
/*
* Size of kstat is num_pics + 1. extra one for pcr.
*/
return (NULL);
}
for (i = 0; i < num_pics; i++) {
}
/*
* Store the reg type and other info. in the kstat's private field
* so that they are available to the update function.
*/
return (counters_ksp);
}
/*
* Program a performance counter.
*
* reggroup is which type of counter.
* counter is the counter number.
* event is the event to program for that counter.
*/
static int
{
int counter;
goto done_pgm;
continue;
"grp:%s, counter:%d, zero_regoff:0x%lx, "
"new&mask:0x%lx\n",
new_events & event_mask);
if ((old_events & event_mask) ==
(new_events & event_mask))
continue;
goto done_pgm;
}
if (old_events != new_events) {
IOSPC_DBG1("old != new, setting event reg %ld to 0x%lx\n",
!= SUCCESS) {
"Write of new event data failed, "
"select reg offset: %ld\n",
goto done_pgm;
}
}
return (rval);
}
/*
*/
static int
{
int counter;
if (rw == KSTAT_WRITE) {
IOSPC_DBG2("iospc_kstat_update: wr %ld\n",
/*
* Fields without programmable events won't be zeroed as
* iospc_perfcnt_program is what zeros them.
*/
/* This group has programmable events. */
IOSPC_DBG2("write: regoff has valid register\n");
return (EIO);
}
} else { /* Read the event register and all of the counters. */
/* This group has programmable events. */
IOSPC_DBG2("read: regoff has valid register\n");
!= SUCCESS)
return (EIO);
} else
IOSPC_DBG2("iospc_kstat_update: rd event %lx\n",
return (EIO);
}
}
return (SUCCESS);
}
void
{
int j;
IOSPC_DBG2("iospc_kstat_fini called\n");
}
}
}
static void
{
int i;
if (name_kstats_pp != NULL) {
for (i = 0; i < num_kstats; i++) {
if (name_kstats_pp[i] != NULL)
}
}
}
void
{
int i;
IOSPC_DBG2("iospc_kstat_detach called\n");
if (i >= IOSPC_MAX_NUM_GRPS)
return;
sizeof (iospc_ksinfo_t));
}
}
}