/*
* Copyright (c) 2013, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* This file contains code to handle the node. */
#include <inttypes.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <assert.h>
#include "../include/types.h"
#include "../include/util.h"
#include "../include/ui_perf_map.h"
#include "../include/os/os_util.h"
#include "../include/os/pfwrapper.h"
#include "../include/os/node.h"
static node_group_t s_node_group;
int g_ncpus;
static void
node_init(node_t *node, int nid, boolean_t hotadd)
{
memset(node, 0, sizeof (node_t));
os_perf_cpuarr_init(node->cpus, NCPUS_NODE_MAX, hotadd);
node->nid = nid;
node->hotadd = hotadd;
}
static void
node_fini(node_t *node)
{
os_perf_cpuarr_fini(node->cpus, NCPUS_NODE_MAX, B_FALSE);
node->ncpus = 0;
node->nid = INVALID_NID;
}
static void
node_hotremove(node_t *node)
{
node->hotremove = B_TRUE;
os_perf_cpuarr_fini(node->cpus, NCPUS_NODE_MAX, B_TRUE);
}
/*
* Initialization for the node group.
*/
int
node_group_init(void)
{
int i;
node_t *node;
(void) memset(&s_node_group, 0, sizeof (node_group_t));
if (pthread_mutex_init(&s_node_group.mutex, NULL) != 0) {
return (-1);
}
s_node_group.inited = B_TRUE;
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
node_init(node, INVALID_NID, B_FALSE);
}
return (node_group_refresh(B_TRUE));
}
/*
* Free the resources of all nodes in group.
*/
static void
node_group_reset(void)
{
node_t *node;
int i;
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
node_fini(node);
}
s_node_group.nnodes = 0;
}
/*
* Clean up all the resources of node group.
*/
void
node_group_fini(void)
{
if (s_node_group.inited) {
(void) pthread_mutex_destroy(&s_node_group.mutex);
node_group_reset();
}
}
static int
nid_find(int nid, int *arr, int num)
{
int i;
if (nid == INVALID_NID) {
return (-1);
}
for (i = 0; i < num; i++) {
if (arr[i] == nid) {
return (i);
}
}
return (-1);
}
static int
cpuid_max_get(int *cpu_arr, int num)
{
int i, cpuid_max = -1;
for (i = 0; i < num; i++) {
if (cpuid_max < cpu_arr[i]) {
cpuid_max = cpu_arr[i];
}
}
return (cpuid_max);
}
static int
cpu_refresh(boolean_t init)
{
int i, j, num, cpuid_max = -1;
int cpu_arr[NCPUS_NODE_MAX];
node_t *node;
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (NODE_VALID(node)) {
if (!os_sysfs_cpu_enum(node->nid, cpu_arr, NCPUS_NODE_MAX, &num)) {
return (-1);
}
if (os_perf_cpuarr_refresh(node->cpus, NCPUS_NODE_MAX, cpu_arr,
num, init) != 0) {
return (-1);
}
node->ncpus = num;
j = cpuid_max_get(cpu_arr, num);
if (cpuid_max < j) {
cpuid_max = j;
}
}
}
if (cpuid_max > s_node_group.cpuid_max) {
s_node_group.cpuid_max = cpuid_max;
}
/* Refresh the number of online CPUs */
g_ncpus = os_sysfs_online_ncpus();
return (0);
}
static int
meminfo_refresh(void)
{
int i;
node_t *node;
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (NODE_VALID(node)) {
if (!os_sysfs_meminfo(node->nid, &node->meminfo)) {
debug_print(NULL, 2, "meminfo_refresh:sysfs_meminfo failed\n");
return (-1);
}
}
}
return (0);
}
/*
* Refresh the information of each node in group. The information
* includes such as: CPU, physical memory size, free memory size.
* Get the information from /sys/devices/system/node.
*/
int
node_group_refresh(boolean_t init)
{
int node_arr[NNODES_MAX];
int num, i, j, ret = -1;
node_t *node;
node_group_lock();
if (!os_sysfs_node_enum(node_arr, NNODES_MAX, &num)) {
goto L_EXIT;
}
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (NODE_VALID(node)) {
if ((j = nid_find(node->nid, node_arr, num)) == -1) {
node_hotremove(node);
s_node_group.nnodes--;
} else {
node_arr[j] = INVALID_NID;
}
}
}
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (!NODE_VALID(node)) {
if ((j = nid_find(i, node_arr, num)) >= 0) {
ASSERT(node_arr[j] == i);
if (init) {
node_init(node, i, B_FALSE);
} else {
node_init(node, i, B_TRUE);
}
s_node_group.nnodes++;
node_arr[j] = INVALID_NID;
}
}
}
if (cpu_refresh(init) != 0) {
goto L_EXIT;
}
if (meminfo_refresh() != 0) {
goto L_EXIT;
}
ret = 0;
L_EXIT:
node_group_unlock();
return (ret);
}
node_t *
node_get(int nid)
{
return (&s_node_group.nodes[nid]);
}
int
node_num(void)
{
return (s_node_group.nnodes);
}
void
node_group_lock(void)
{
(void) pthread_mutex_lock(&s_node_group.mutex);
}
void
node_group_unlock(void)
{
(void) pthread_mutex_unlock(&s_node_group.mutex);
}
/*
* Get node by CPU id.
*/
node_t *
node_by_cpu(int cpuid)
{
node_t *node;
int i, j;
if (cpuid == INVALID_CPUID) {
return (NULL);
}
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (!NODE_VALID(node)) {
continue;
}
for (j = 0; j < NCPUS_NODE_MAX; j++) {
if (cpuid == node->cpus[j].cpuid) {
return (node);
}
}
}
return (NULL);
}
int
node_ncpus(node_t *node)
{
return (node->ncpus);
}
int
node_intval_get(void)
{
return (s_node_group.intval_ms);
}
/*
* Update the node's perf data.
*/
void
node_countval_update(node_t *node, perf_count_id_t perf_count_id, uint64_t value)
{
node->countval.counts[perf_count_id] += value;
}
/*
* Return the perf data of specified node and count.
*/
uint64_t
node_countval_get(node_t *node, ui_count_id_t ui_count_id)
{
return (ui_perf_count_aggr(ui_count_id, node->countval.counts));
}
/*
* Return the memory information for a specified node.
*/
void
node_meminfo(int nid, node_meminfo_t *info)
{
node_t *node;
if (((node = node_get(nid)) != NULL) &&
(NODE_VALID(node))) {
memcpy(info, &node->meminfo, sizeof (node_meminfo_t));
}
}
/*
* Walk through the CPUs in a node and call 'func()' for each CPU.
* The CPU might be INVALID. Note that the function could be only called
* in perf thread.
*/
int
node_cpu_traverse(pfn_perf_cpu_op_t func, void *arg, boolean_t err_ret,
pfn_perf_cpu_op_t hotadd_func)
{
node_t *node;
perf_cpu_t *cpu;
int i, j, ret;
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (!NODE_VALID(node)) {
continue;
}
for (j = 0; j < NCPUS_NODE_MAX; j++) {
cpu = &node->cpus[j];
if (cpu->hotremove) {
pf_resource_free(cpu);
cpu->hotremove = B_FALSE;
cpu->cpuid = INVALID_CPUID;
continue;
}
if ((cpu->hotadd) && (hotadd_func != NULL)) {
hotadd_func(cpu, arg);
cpu->hotadd = B_FALSE;
}
if ((func != NULL) && (cpu->cpuid != INVALID_CPUID) && (!cpu->hotadd)) {
if (((ret = func(cpu, arg)) != 0) && (err_ret)) {
return (ret);
}
}
}
if (node->hotremove) {
node->nid = INVALID_NID;
node->hotremove = B_FALSE;
}
}
return (0);
}
static uint64_t
countval_sum(count_value_t *countval_arr, int nid,
ui_count_id_t ui_count_id)
{
uint64_t value = 0;
node_t *node;
int i, cpuid, num = 0;
node = node_get(nid);
if (!NODE_VALID(node)) {
return (0);
}
for (i = 0; i < NCPUS_NODE_MAX; i++) {
if (num >= node->ncpus) {
break;
}
if ((cpuid = node->cpus[i].cpuid) != INVALID_CPUID) {
value += ui_perf_count_aggr(ui_count_id,
countval_arr[cpuid].counts);
num++;
}
}
return (value);
}
uint64_t
node_countval_sum(count_value_t *countval_arr, int nid,
ui_count_id_t ui_count_id)
{
int i;
uint64_t value = 0;
if (nid != NODE_ALL) {
return (countval_sum(countval_arr, nid, ui_count_id));
}
for (i = 0; i < NNODES_MAX; i++) {
value += countval_sum(countval_arr, i, ui_count_id);
}
return (value);
}
perf_cpu_t *
node_cpus(node_t *node)
{
return (node->cpus);
}
void
node_intval_update(int intval_ms)
{
s_node_group.intval_ms = intval_ms;
}
void
node_profiling_clear(void)
{
node_t *node;
int i;
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
(void) memset(&node->countval, 0, sizeof (count_value_t));
}
}
node_t *
node_valid_get(int node_idx)
{
int i, nvalid = 0;
node_t *node;
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (NODE_VALID(node)) {
if (node_idx == nvalid) {
return (node);
}
nvalid++;
}
}
return (NULL);
}
int
node_cpuid_max(void)
{
return (s_node_group.cpuid_max + 1);
}
int
node_qpi_init(void)
{
qpi_info_t qpi_tmp[NODE_QPI_MAX];
int qpi_num, i;
node_t *node;
qpi_num = os_sysfs_uncore_qpi_init(qpi_tmp, NODE_QPI_MAX);
if (qpi_num < 0)
return -1;
if (qpi_num == 0) {
qpi_num = os_sysfs_uncore_upi_init(qpi_tmp, NODE_QPI_MAX);
if (qpi_num < 0)
return -1;
}
node_group_lock();
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (NODE_VALID(node) && (qpi_num > 0)) {
memcpy(node->qpi.qpi_info, qpi_tmp,
sizeof(node->qpi.qpi_info));
node->qpi.qpi_num = qpi_num;
}
}
node_group_unlock();
debug_print(NULL, 2, "%d QPI/UPI links per node\n", qpi_num);
return 0;
}
int
node_imc_init(void)
{
imc_info_t imc_tmp[NODE_IMC_MAX];
int imc_num, i;
node_t *node;
imc_num = os_sysfs_uncore_imc_init(imc_tmp, NODE_IMC_MAX);
if (imc_num < 0)
return -1;
node_group_lock();
for (i = 0; i < NNODES_MAX; i++) {
node = node_get(i);
if (NODE_VALID(node) && (imc_num > 0)) {
memcpy(node->imc.imc_info, imc_tmp,
sizeof(node->imc.imc_info));
node->imc.imc_num = imc_num;
}
}
node_group_unlock();
debug_print(NULL, 2, "%d memory controllers per node\n", imc_num);
return 0;
}