/*
* 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
*/
/*
*/
#include <sys/cap_util.h>
/*
* Processor Groups: Hardware sharing relationship layer
*
* This file implements an extension to Processor Groups to capture
* hardware sharing relationships existing between logical CPUs. Examples of
* hardware sharing relationships include shared caches on some CMT
* procesoor architectures, or shared local memory controllers on NUMA
* based system architectures.
*
* The pghw_t structure represents the extended PG. The first member
* of the structure is the generic pg_t with the pghw specific members
* following. The generic pg_t *must* remain the first member of the
* structure as the code uses casting of structure references to access
* the generic pg_t structure elements.
*
* In addition to the generic CPU grouping, physical PGs have a hardware
* sharing relationship enumerated "type", and an instance id. The enumerated
* type is defined by the pghw_type_t enumeration, while the instance id
* uniquely identifies the sharing instance from among others of the same
* hardware sharing type.
*
* The physical PGs are organized into an overall hierarchy, and are tracked
* in a number of different per CPU, and per pghw_type_t type groups.
* As an example:
*
* -------------
* | pg_hw |
* | (group_t) |
* -------------
* || ============================
* ||\\-----------------------// \\ \\
* || | hwset (PGC_HW_CHIP) | ------------- -------------
* || | (group_t) | | pghw_t | | pghw_t |
* || ----------------------- | chip 0 | | chip 1 |
* || ------------- -------------
* || \\ \\ \\ \\ \\ \\ \\ \\
* || cpu cpu cpu cpu cpu cpu cpu cpu
* ||
* || ============================
* ||\\-----------------------// \\ \\
* || | hwset (PGC_HW_IPIPE)| ------------- -------------
* || | (group_t) | | pghw_t | | pghw_t |
* || ----------------------- | ipipe 0 | | ipipe 1 |
* || ------------- -------------
* || \\ \\ \\ \\
* || cpu cpu cpu cpu
* ...
*
*
* The top level pg_hw is a group of "hwset" groups. Each hwset holds of group
* of physical PGs of the same hardware sharing type. Within each hwset, the
* PG's instance id uniquely identifies the grouping relationshsip among other
* groupings of the same sharing type. The instance id for a grouping is
* platform defined, and in some cases may be used by platform code as a handle
* to search for a particular relationship instance.
*
* Each physical PG (by virtue of the embedded pg_t) contains a group of CPUs
* that participate in the sharing relationship. Each CPU also has associated
* with it a grouping tracking the PGs in which the CPU belongs. This can be
* used to iterate over the various relationships in which the CPU participates
* (the CPU's chip, cache, lgroup, etc.).
*
* The hwsets are created dynamically as new hardware sharing relationship types
* are instantiated. They are never destroyed, as once a given relationship
* type appears in the system, it is quite likely that at least one instance of
* that relationship will always persist as long as the system is running.
*/
/*
* Physical PG kstats
*/
struct pghw_kstat {
} pghw_kstat = {
{ "id", KSTAT_DATA_INT32 },
{ "pg_class", KSTAT_DATA_STRING },
{ "ncpus", KSTAT_DATA_UINT32 },
{ "instance_id", KSTAT_DATA_UINT32 },
{ "hardware", KSTAT_DATA_STRING },
{ "policy", KSTAT_DATA_STRING },
};
/*
* Capacity and Utilization PG kstats
*
* These kstats are updated one at a time, so we can have a single scratch space
* to fill the data.
*
* kstat fields:
*
* pg_id PG ID for PG described by this kstat
*
* pg_parent Parent PG ID. The value -1 means "no parent".
*
* pg_ncpus Number of CPUs within this PG
*
* pg_cpus String describing CPUs within this PG
*
* pg_relationship Name of sharing relationship for this PG
*
* pg_generation Generation value that increases whenever any CPU leaves
* or joins PG. Two kstat snapshots for the same
* CPU may only be compared if they have the same
* generation
*
* pg_hw_util Running value of PG utilization for the sharing
* relationship
*
* pg_hw_util_time_running
* Total time spent collecting CU data. The time may be
* less than wall time if CU counters were stopped for
* some time.
*
* pg_hw_util_time_stopped Total time the CU counters were stopped.
*
* pg_hw_util_rate Utilization rate, expressed in operations per second.
*
* pg_hw_util_rate_max Maximum observed value of utilization rate.
*/
struct pghw_cu_kstat {
} pghw_cu_kstat = {
{ "pg_id", KSTAT_DATA_INT32 },
{ "parent_pg_id", KSTAT_DATA_INT32 },
{ "ncpus", KSTAT_DATA_UINT32 },
{ "generation", KSTAT_DATA_UINT32 },
{ "hw_util", KSTAT_DATA_UINT64 },
{ "hw_util_time_running", KSTAT_DATA_UINT64 },
{ "hw_util_time_stopped", KSTAT_DATA_UINT64 },
{ "hw_util_rate", KSTAT_DATA_UINT64 },
{ "hw_util_rate_max", KSTAT_DATA_UINT64 },
{ "cpus", KSTAT_DATA_STRING },
{ "relationship", KSTAT_DATA_STRING },
};
/*
* Calculate the string size to represent NCPUS. Allow 5 digits for each CPU ID
* plus one space per CPU plus NUL byte in the end. This is only an estimate,
* since we try to compress CPU ranges as x-y. In the worst case the string
* representation of CPUs may be truncated.
*/
/*
* Maximum length of the string that represents list of CPUs
*/
static int pg_cpulist_maxlen = 0;
static void pghw_kstat_create(pghw_t *);
static int pghw_kstat_update(kstat_t *, int);
static int pghw_cu_kstat_update(kstat_t *, int);
static int cpu2id(void *);
/*
* hwset operations
*/
static void pghw_cpulist_alloc(pghw_t *);
static int cpu2id(void *);
/*
* Initialize the physical portion of a hardware PG
*/
void
{
/*
* Haven't seen this hardware type yet
*/
}
pg->pghw_generation = 0;
pg->pghw_instance =
/*
* Hardware sharing relationship specific initialization
*/
case PGHW_POW_ACTIVE:
pg->pghw_handle =
break;
case PGHW_POW_IDLE:
pg->pghw_handle =
break;
default:
}
}
/*
* Teardown the physical portion of a physical PG
*/
void
{
}
/*
* PG is removed from CMT hierarchy
*/
void
{
/*
* Destroy string representation of CPUs
*/
}
/*
* Destroy CU kstats
*/
}
}
/*
* Find an existing physical PG in which to place
* the given CPU for the specified hardware sharing
* relationship
*/
pghw_t *
{
return (NULL);
}
}
/*
* Find the pg representing the hw sharing relationship in which
* cp belongs
*/
pghw_t *
{
group_iter_t i;
group_iter_init(&i);
return (pg);
}
return (NULL);
}
/*
* Find the PG of the given hardware sharing relationship
* type with the given instance id
*/
pghw_t *
{
group_iter_t i;
if (!set)
return (NULL);
group_iter_init(&i);
return (pg);
}
return (NULL);
}
/*
* CPUs physical ID cache creation / destruction
* The cache's elements are initialized to the CPU's id
*/
void
{
int i;
for (i = 0; i < (sizeof (cpu_physid_t) / sizeof (id_t)); i++) {
}
}
void
{
if (cp->cpu_physid) {
}
}
/*
* Create a new, empty hwset.
* This routine may block, and must not be called from any
* paused CPU context.
*/
static group_t *
{
group_t *g;
int ret;
/*
* Create the top level PG hw group if it doesn't already exist
* This is a "set" of hardware sets, that is ordered (and indexed)
* by the pghw_type_t enum.
*/
}
/*
* Create the new hwset
* Add it to the top level pg_hw group.
*/
group_create(g);
return (g);
}
/*
* Find the hwset associated with the given hardware sharing type
*/
group_t *
{
return (NULL);
return (hwset);
}
/*
* Add a PG to a hwset
*/
static void
{
}
/*
* Remove a PG from a hwset
*/
static void
{
int result;
}
/*
* Return a string name given a pg_hw sharing type
*/
char *
{
switch (hw) {
case PGHW_IPIPE:
return ("Integer Pipeline");
case PGHW_CACHE:
return ("Cache");
case PGHW_FPU:
return ("Floating Point Unit");
case PGHW_MPIPE:
return ("Data Pipe to memory");
case PGHW_CHIP:
return ("Socket");
case PGHW_MEMORY:
return ("Memory");
case PGHW_POW_ACTIVE:
return ("CPU PM Active Power Domain");
case PGHW_POW_IDLE:
return ("CPU PM Idle Power Domain");
default:
return ("unknown");
}
}
/*
* Create / Update routines for PG hw kstats
*
* It is the intention of these kstats to provide some level
* of informational / debugging observability into the types
* and nature of the system's detected hardware sharing relationships
*/
void
{
/*
* Canonify PG name to conform to kstat name rules
*/
/*
* Create a hardware performance kstat
*/
"pg", "pg",
sizeof (pghw_kstat) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL)) != NULL) {
/* Class string, hw string, and policy string */
}
if (pg_cpulist_maxlen == 0)
/*
* Create a physical pg kstat
*/
name, "processor_group",
sizeof (pghw_cu_kstat) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL)) != NULL) {
/* Allow space for CPU strings */
}
}
int
{
if (rw == KSTAT_WRITE)
return (EACCES);
return (0);
}
int
{
if (rw == KSTAT_WRITE)
return (EACCES);
/*
* Check whether the caller has priv_cpc_cpu privilege. If he doesn't,
* he will not get hardware utilization data.
*/
/*
* Allocate memory for the string representing the list of CPUs in PG.
* This memory should persist past the call to pghw_cu_kstat_update()
* since the kstat snapshot routine will reference this memory.
*/
/*
* PG kstat generation number is out of sync with PG's
* generation mumber. It means that some CPUs could have joined
* or left PG and it is not possible to compare the numbers
* obtained before and after the generation change.
*
* Reset the maximum utilization rate and start computing it
* from scratch.
*/
hw_util->pghw_rate_max = 0;
}
/*
* We can't block on CPU lock because when PG is destroyed (under
* cpu_lock) it tries to delete this kstat and it will wait for us to
* complete which will never happen since we are waiting for cpu_lock to
* drop. Deadlocks are fun!
*/
if (mutex_tryenter(&cpu_lock)) {
}
if (has_cpc_privilege)
}
else
if (has_cpc_privilege) {
} else {
}
return (0);
}
/*
* Update the string representation of CPUs in PG (pg->pghw_cpulist).
* The string representation is used for kstats.
*
* The string is allocated if it has not already been or if it is already
* allocated and PG has more CPUs now. If PG has smaller or equal number of
* CPUs, but the actual CPUs may have changed, the string is reset to the empty
* string causes the string representation to be recreated. The pghw_generation
* field is used to detect whether CPUs within the pg may have changed.
*/
static void
{
/*
* If the pghw_cpulist string is already allocated we need to make sure
* that it has sufficient length. Also if the set of CPUs may have
* changed, we need to re-generate the string.
*/
/*
* There is sufficient space in the pghw_cpulist for
* the new set of CPUs. Just clear the string to trigger
* re-generation of list of CPUs
*/
} else {
/*
* There is, potentially, insufficient space in
* pghw_cpulist, so reallocate the string.
*/
pg->pghw_cpulist_len = 0;
}
}
/*
* Allocate space to hold cpulist.
*
* Length can not be bigger that the maximum space we have
* allowed for the kstat buffer
*/
if (len > pg_cpulist_maxlen)
if (len > 0) {
}
}
}
static int
cpu2id(void *v)
{
}
/*
* Return parent ID or -1 if there is no parent.
* All hardware PGs are currently also CMT PGs, but for safety we check the
* class matches cmt before we upcast the pghw pointer to pg_cmt_t.
*/
static pgid_t
{
}
return (parent_id);
}