kstat_fr.c revision 8cb09440f525b933af2e42d7aa9d24e33fce73b7
/*
* 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
*/
/*
*/
/*
* Kernel statistics framework
*/
#include <sys/sysmacros.h>
#include <sys/pool_pset.h>
#include <vm/seg_kmem.h>
/*
* Global lock to protect the AVL trees and kstat_chain_id.
*/
static kmutex_t kstat_chain_lock;
/*
*
*
* (2) kstat_create(), to assign a KID (kstat ID) to each new kstat.
*
* We reserve the first two IDs because some kstats are created before
* the well-known ones (kstat_headers = 0, kstat_types = 1).
*
* We also bump the kstat_chain_id if a zone is gaining or losing visibility
* into a particular kstat, which is logically equivalent to a kstat being
*/
/*
* As far as zones are concerned, there are 3 types of kstat:
*
* 1) Those which have a well-known name, and which should return per-zone data
* depending on which zone is doing the kstat_read(). sockfs:0:sock_unix_list
* is an example of this type of kstat.
*
* 2) Those which should only be exported to a particular list of zones.
* For example, in the case of nfs:*:mntinfo, we don't want zone A to be
* able to see NFS mounts associated with zone B, while we want the
* global zone to be able to see all mounts on the system.
*
* 3) Those that can be exported to all zones. Most system-related
* kstats fall within this category.
*
* An ekstat_t thus contains a list of kstats that the zone is to be
* exported to. The lookup of a name:instance:module thus translates to a
* lookup of name:instance:module:myzone; if the kstat is not exported
* to all zones, and does not have the caller's zoneid explicitly
* enumerated in the list of zones to be exported to, it is the same as
* if the kstat didn't exist.
*
* Writing to kstats is currently disallowed from within a non-global
* zone, although this restriction could be removed in the future.
*/
typedef struct kstat_zone {
struct kstat_zone *next;
} kstat_zone_t;
/*
* Extended kstat structure -- for internal use only.
*/
typedef struct ekstat {
} ekstat_t;
static void *kstat_initial_ptr = kstat_initial;
static vmem_t *kstat_arena;
#define KSTAT_ALIGN (sizeof (uint64_t))
static avl_tree_t kstat_avl_bykid;
static avl_tree_t kstat_avl_byname;
/*
* Various pointers we need to create kstats at boot time in kstat_init()
*/
extern kstat_named_t *segmapcnt_ptr;
extern uint_t segmapcnt_ndata;
extern int segmap_kstat_update(kstat_t *, int);
extern kstat_named_t *biostats_ptr;
extern uint_t biostats_ndata;
extern kstat_named_t *pollstats_ptr;
extern uint_t pollstats_ndata;
extern int vac;
struct {
} system_misc_kstat = {
{ "ncpus", KSTAT_DATA_UINT32 },
{ "lbolt", KSTAT_DATA_UINT32 },
{ "deficit", KSTAT_DATA_UINT32 },
{ "clk_intr", KSTAT_DATA_UINT32 },
{ "vac", KSTAT_DATA_UINT32 },
{ "nproc", KSTAT_DATA_UINT32 },
{ "avenrun_1min", KSTAT_DATA_UINT32 },
{ "avenrun_5min", KSTAT_DATA_UINT32 },
{ "avenrun_15min", KSTAT_DATA_UINT32 },
{ "boot_time", KSTAT_DATA_UINT32 },
};
struct {
} system_pages_kstat = {
{ "physmem", KSTAT_DATA_ULONG },
{ "nalloc", KSTAT_DATA_ULONG },
{ "nfree", KSTAT_DATA_ULONG },
{ "nalloc_calls", KSTAT_DATA_ULONG },
{ "nfree_calls", KSTAT_DATA_ULONG },
{ "kernelbase", KSTAT_DATA_ULONG },
{ "econtig", KSTAT_DATA_ULONG },
{ "freemem", KSTAT_DATA_ULONG },
{ "availrmem", KSTAT_DATA_ULONG },
{ "lotsfree", KSTAT_DATA_ULONG },
{ "desfree", KSTAT_DATA_ULONG },
{ "minfree", KSTAT_DATA_ULONG },
{ "fastscan", KSTAT_DATA_ULONG },
{ "slowscan", KSTAT_DATA_ULONG },
{ "nscan", KSTAT_DATA_ULONG },
{ "desscan", KSTAT_DATA_ULONG },
{ "pp_kernel", KSTAT_DATA_ULONG },
{ "pagesfree", KSTAT_DATA_ULONG },
{ "pageslocked", KSTAT_DATA_ULONG },
{ "pagestotal", KSTAT_DATA_ULONG },
};
static int header_kstat_update(kstat_t *, int);
static int header_kstat_snapshot(kstat_t *, void *, int);
static int system_misc_kstat_update(kstat_t *, int);
static int system_pages_kstat_update(kstat_t *, int);
static struct {
char name[KSTAT_STRLEN];
} kstat_data_type[KSTAT_NUM_TYPES] = {
};
int
{
return (1);
return (1);
}
return (0);
}
void
{
goto out;
}
break;
}
}
kz = t;
out:
}
void
{
return;
}
/*
* Compare the list of zones for the given kstats, returning 0 if they match
* (ie, one list contains ALL_ZONES or both lists contain the same zoneid).
* In practice, this is called indirectly by kstat_hold_byname(), so one of the
* two lists always has one element, and this is an O(n) operation rather than
* O(n^2).
*/
static int
{
return (0);
return (0);
}
}
}
/*
* Support for keeping kstats sorted in AVL trees for fast lookups.
*/
static int
{
return (-1);
return (1);
}
static int
{
int s;
if (s > 0)
return (1);
if (s < 0)
return (-1);
return (-1);
return (1);
if (s > 0)
return (1);
if (s < 0)
return (-1);
}
static kstat_t *
{
ekstat_t *e;
for (;;) {
break;
break;
}
}
return (ksp);
}
void
{
cv_broadcast(&e->e_cv);
}
kstat_t *
{
ekstat_t e;
return (kstat_hold(&kstat_avl_bykid, &e));
}
kstat_t *
{
ekstat_t e;
return (kstat_hold(&kstat_avl_byname, &e));
}
static ekstat_t *
{
if (kstat_arena == NULL) {
if (size <= kstat_initial_avail) {
e = kstat_initial_ptr;
}
} else {
}
if (e != NULL) {
}
return (e);
}
static void
kstat_free(ekstat_t *e)
{
cv_destroy(&e->e_cv);
}
/*
* Create various system kstats.
*/
void
kstat_init(void)
{
ekstat_t *e;
avl_tree_t *t = &kstat_avl_bykid;
/*
* Set up the kstat vmem arena.
*/
/*
* Make initial kstats appear as though they were allocated.
*/
0, 0, e, (char *)e + e->e_size,
/*
* The mother of all kstats. The first kstat in the system, which
* always has KID 0, has the headers for all kstats (including itself)
* as its data. Thus, the kstat driver does not need any special
* interface to extract the kstat chain.
*/
kstat_chain_id = 0;
if (ksp) {
} else {
panic("cannot create kstat 'kstat_headers'");
}
if (ksp) {
int i;
for (i = 0; i < KSTAT_NUM_TYPES; i++) {
}
}
sizeof (sysinfo_t), KSTAT_FLAG_VIRTUAL);
if (ksp) {
}
sizeof (vminfo_t), KSTAT_FLAG_VIRTUAL);
if (ksp) {
}
if (ksp) {
}
if (ksp) {
}
sizeof (struct var), KSTAT_FLAG_VIRTUAL);
if (ksp) {
}
sizeof (system_misc_kstat) / sizeof (kstat_named_t),
if (ksp) {
}
sizeof (system_pages_kstat) / sizeof (kstat_named_t),
if (ksp) {
}
if (ksp) {
}
}
/*
* Caller of this should ensure that the string pointed by src
* doesn't change while kstat's lock is held. Not doing so defeats
*/
void
{
panic("kstat_named_setstr('%p', '%p'): "
"named kstat is not of type KSTAT_DATA_STRING",
else
KSTAT_NAMED_STR_BUFLEN(knp) = 0;
}
void
{
}
void
{
if (data_type == KSTAT_DATA_STRING)
}
void
{
}
/* ARGSUSED */
static int
{
uint_t i;
/*
* Named kstats with variable-length long strings have a standard
* way of determining how much space is needed to hold the snapshot:
*/
/*
* Add in the space required for the strings
*/
}
ksp->ks_data_size =
}
return (0);
}
static int
{
if (rw == KSTAT_WRITE) {
return (EACCES);
return (0);
}
/*
* KSTAT_TYPE_NAMED kstats are defined to have ks_ndata
* number of kstat_named_t structures, followed by an optional
* string segment. The ks_data generally holds only the
* kstat_named_t structures. So we copy it first. The strings,
* if any, are copied below. For other kstat types, ks_data holds the
* entire buffer.
*/
else
/*
* Apply kstat type-specific data massaging
*/
case KSTAT_TYPE_IO:
/*
* Normalize time units and deal with incomplete transactions
*/
/* like kstat_waitq_exit */
}
/* like kstat_runq_exit */
}
break;
case KSTAT_TYPE_NAMED:
/*
* Massage any long strings in at the end of the buffer
*/
uint_t i;
/*
* Copy strings and update pointers
*/
}
}
}
break;
}
return (0);
}
static int
{
int nkstats = 0;
ekstat_t *e;
avl_tree_t *t = &kstat_avl_bykid;
if (rw == KSTAT_WRITE)
return (EACCES);
nkstats++;
}
}
return (0);
}
/*
* Copy out the data section of kstat 0, which consists of the list
* of all kstat headers. By specification, these headers must be
* copied out in order of increasing KID.
*/
static int
{
ekstat_t *e;
avl_tree_t *t = &kstat_avl_bykid;
if (rw == KSTAT_WRITE)
return (EACCES);
}
}
return (0);
}
/* ARGSUSED */
static int
{
int loadavg[LOADAVG_NSTATS];
if (rw == KSTAT_WRITE)
return (EACCES);
if (!INGLOBALZONE(curproc)) {
/*
* Here we grab cpu_lock which is OK as long as no-one in the
* future attempts to lookup this particular kstat
* (unix:0:system_misc) while holding cpu_lock.
*/
if (pool_pset_enabled()) {
int error;
}
}
if (INGLOBALZONE(curproc)) {
zone_lbolt = ddi_get_lbolt();
zone_nproc = nproc;
} else {
zone_hrtime = gethrtime();
}
return (0);
}
#ifdef __sparc
#else /* !__sparc */
#endif /* __sparc */
/* ARGSUSED */
static int
{
if (rw == KSTAT_WRITE) {
return (EACCES);
}
#ifdef __sparc
/*
* kstat should REALLY be modified to also report kmem64_base and
* [ kernelbase .. econtig ] no longer is truly reflective of the
* kernel's vallocs...
*/
#else /* !__sparc */
#endif /* __sparc */
/*
* pp_kernel represents total pages used by the kernel since the
* startup. This formula takes into account the boottime kernel
* footprint and also considers the availrmem changes because of
* user explicit page locking.
*/
return (0);
}
kstat_t *
{
}
/*
* Allocate and initialize a kstat structure. Or, if a dormant kstat with
* the specified name exists, reactivate it. Returns a pointer to the kstat
* on success, NULL on failure. The kstat will not be visible to the
* kstat driver until kstat_install().
*/
kstat_t *
{
ekstat_t *e;
if (avl_numnodes(&kstat_avl_bykid) == 0) {
}
/*
* If ks_name == NULL, set the ks_name to <module><instance>.
*/
char buf[KSTAT_STRLEN];
}
/*
* Make sure it's a valid kstat data type
*/
if (ks_type >= KSTAT_NUM_TYPES) {
"invalid kstat type %d",
return (NULL);
}
/*
* Don't allow persistent virtual kstats -- it makes no sense.
* ks_data points to garbage when the client goes away.
*/
if ((ks_flags & KSTAT_FLAG_PERSISTENT) &&
(ks_flags & KSTAT_FLAG_VIRTUAL)) {
"cannot create persistent virtual kstat",
return (NULL);
}
/*
* Don't allow variable-size physical kstats, since the framework's
* memory allocation for physical kstat data is fixed at creation time.
*/
if ((ks_flags & KSTAT_FLAG_VAR_SIZE) &&
!(ks_flags & KSTAT_FLAG_VIRTUAL)) {
"cannot create variable-size physical kstat",
return (NULL);
}
/*
* Make sure the number of data fields is within legal range
*/
"ks_ndata=%d out of range [%d, %d]",
return (NULL);
}
/*
* If the named kstat already exists and is dormant, reactivate it.
*/
/*
* The named kstat exists but is not dormant --
* this is a kstat namespace collision.
*/
"kstat_create('%s', %d, '%s'): namespace collision",
return (NULL);
}
(ks_flags & KSTAT_FLAG_VIRTUAL)) {
/*
* The name is the same, but the other key parameters
* differ from those of the dormant kstat -- bogus.
*/
"invalid reactivation of dormant kstat",
return (NULL);
}
/*
* Return dormant kstat pointer to caller. As usual,
* the kstat is marked invalid until kstat_install().
*/
return (ksp);
}
/*
* Allocate memory for the new kstat header and, if this is a physical
* kstat, the data section.
*/
if (e == NULL) {
"insufficient kernel memory",
return (NULL);
}
/*
* Initialize as many fields as we can. The caller may reset
* ks_lock, ks_update, ks_private, and ks_snapshot as necessary.
* Creators of virtual kstats may also reset ks_data. It is
* also up to the caller to initialize the kstat data section,
* if necessary. All initialization must be complete before
* calling kstat_install().
*/
if (ks_flags & KSTAT_FLAG_VIRTUAL)
else
/*
* Add our kstat to the AVL trees.
*/
"kstat_create('%s', %d, '%s'): namespace collision",
kstat_free(e);
return (NULL);
}
/*
* Loop around until we find an unused KID.
*/
do {
return (ksp);
}
/*
*/
void
{
/*
* If this is a variable-size kstat, it MUST provide kstat data locking
* to prevent data-size races with kstat readers.
*/
panic("kstat_install('%s', %d, '%s'): "
"cannot create variable-size kstat without data lock",
}
(void *)ksp);
return;
}
int has_long_strings = 0;
uint_t i;
has_long_strings = 1;
break;
}
}
/*
* It is an error for a named kstat with fields of
* KSTAT_DATA_STRING to be non-virtual.
*/
panic("kstat_install('%s', %d, '%s'): "
"named kstat containing KSTAT_DATA_STRING "
"is not virtual",
}
/*
* The default snapshot routine does not handle KSTAT_WRITE
* for long strings.
*/
panic("kstat_install('%s', %d, '%s'): "
"named kstat containing KSTAT_DATA_STRING "
"is writable but uses default snapshot routine",
}
}
/*
* We are reactivating a dormant kstat. Initialize the
* caller's underlying data to the value it had when the
* kstat went dormant, and mark the kstat as active.
* Grab the provider's kstat lock if it's not already held.
*/
mutex_exit(lp);
} else {
}
}
/*
* Now that the kstat is active, make it visible to the kstat driver.
*/
}
/*
* Remove a kstat from the system. Or, if it's a persistent kstat,
* just update the data and mark it as dormant.
*/
void
{
return;
panic("kstat_delete(%p): caller holds data lock %p",
}
(void *)ksp);
return;
}
/*
* Update the data one last time, so that all activity
* prior to going dormant has been accounted for.
*/
/*
* Mark the kstat as dormant and restore caller-modifiable
* fields to default values, so the kstat is readable during
* the dormant phase.
*/
return;
}
/*
* Remove the kstat from the framework's AVL trees,
* free the allocated memory, and increment kstat_chain_id so
*/
avl_remove(&kstat_avl_bykid, e);
avl_remove(&kstat_avl_byname, e);
kstat_zone_t *t = kz;
kmem_free(t, sizeof (*t));
}
kstat_free(e);
}
void
{
}
}
void
{
}
/*
* The sparc V9 versions of these routines can be much cheaper than
* the poor 32-bit compiler can comprehend, so they're in sparcv9_subr.s.
* For simplicity, however, we always feed the C versions to lint.
*/
void
{
new = gethrtime_unscaled();
if (wcnt != 0) {
}
}
void
{
new = gethrtime_unscaled();
}
void
{
new = gethrtime_unscaled();
if (rcnt != 0) {
}
}
void
{
new = gethrtime_unscaled();
}
void
{
new = gethrtime_unscaled();
if (rcnt != 0) {
}
}
void
{
new = gethrtime_unscaled();
if (wcnt != 0) {
}
}
#endif
void
{
}
void
{
}