From 356e8f77785495af1ccfa851481d0c74f44d3ce7 Mon Sep 17 00:00:00 2001
From: Jin Yao <yao.jin@linux.intel.com>
Date: Wed, 15 Jun 2016 02:14:36 +0800
Subject: [PATCH 04/16] Add a feature to show per-Node memory controller
 bandwidth
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit

Signed-off-by: Jin Yao <yao.jin@linux.intel.com>
---
 common/cmd.c                  |   6 +-
 common/include/os/node.h      |  16 ++++++
 common/include/os/os_cmd.h    |   2 +-
 common/include/os/os_perf.h   |  10 ++--
 common/include/os/os_util.h   |   1 +
 common/include/os/pfwrapper.h |   4 ++
 common/include/perf.h         |  24 ++++----
 common/numatop.c              |   1 +
 common/os/node.c              |  29 ++++++++++
 common/os/os_cmd.c            |  11 ++--
 common/os/os_page.c           |   2 +-
 common/os/os_perf.c           |  93 +++++++++++++++++++++----------
 common/os/os_util.c           |  29 ++++++++++
 common/os/os_win.c            |  44 ++++++++++-----
 common/os/pfwrapper.c         | 124 ++++++++++++++++++++++++++++++++++++++++++
 common/perf.c                 |  48 ++++++++--------
 16 files changed, 348 insertions(+), 96 deletions(-)

diff --git a/common/cmd.c b/common/cmd.c
index ea252a4..beb6f13 100644
--- a/common/cmd.c
+++ b/common/cmd.c
@@ -135,9 +135,9 @@ preop_switch2pqosmbm(cmd_t *cmd, boolean_t *smpl)
 }
 
 static int
-preop_switch2uncoreqpi(cmd_t *cmd, boolean_t *smpl)
+preop_switch2uncore(cmd_t *cmd, boolean_t *smpl)
 {
-	return (os_preop_switch2uncoreqpi(cmd, smpl));
+	return (os_preop_switch2uncore(cmd, smpl));
 }
 
 int
@@ -426,7 +426,7 @@ switch_table_init(void)
 	s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_NODE_OVERVIEW_ID].op = NULL;
 	s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_BACK_ID].op = op_page_prev;
 	s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_NODE_DETAIL_ID].preop =
-		preop_switch2uncoreqpi;	
+		preop_switch2uncore;	
 	s_switch[WIN_TYPE_NODE_OVERVIEW][CMD_NODE_DETAIL_ID].op = op_page_next;
 
 	/*
diff --git a/common/include/os/node.h b/common/include/os/node.h
index 620946b..5f8c321 100644
--- a/common/include/os/node.h
+++ b/common/include/os/node.h
@@ -45,6 +45,7 @@ extern "C" {
 #define	NODE_VALID(node) ((node)->nid != INVALID_NID)
 
 #define NODE_QPI_MAX	3
+#define NODE_IMC_MAX	8
 
 typedef struct _node_meminfo {
 	uint64_t mem_total;
@@ -69,6 +70,19 @@ typedef struct _node_qpi {
 	qpi_info_t qpi_info[NODE_QPI_MAX];
 } node_qpi_t;
 
+typedef struct _imc_info {
+	int id;
+	int type;
+	int fd;
+	uint64_t values[3];
+	uint64_t value_scaled;
+} imc_info_t;
+
+typedef struct _node_imc {
+	int imc_num;
+	imc_info_t imc_info[NODE_IMC_MAX];
+} node_imc_t;
+
 typedef struct _node {
 	int nid;
 	int ncpus;
@@ -76,6 +90,7 @@ typedef struct _node {
 	count_value_t countval;
 	node_meminfo_t meminfo;
 	node_qpi_t qpi;
+	node_imc_t imc;
 	boolean_t hotadd;
 	boolean_t hotremove;	
 } node_t;
@@ -111,6 +126,7 @@ extern void node_profiling_clear(void);
 extern node_t* node_valid_get(int);
 extern int node_cpuid_max(void);
 extern int node_qpi_init(void);
+extern int node_imc_init(void);
 
 #ifdef __cplusplus
 }
diff --git a/common/include/os/os_cmd.h b/common/include/os/os_cmd.h
index d994f70..737f883 100644
--- a/common/include/os/os_cmd.h
+++ b/common/include/os/os_cmd.h
@@ -51,7 +51,7 @@ extern int os_preop_switch2accdst(cmd_t *, boolean_t *);
 extern int os_preop_leavecallchain(cmd_t *, boolean_t *);
 extern int os_preop_switch2pqoscmt(cmd_t *, boolean_t *);
 extern int os_preop_switch2pqosmbm(cmd_t *, boolean_t *);
-extern int os_preop_switch2uncoreqpi(cmd_t *, boolean_t *);
+extern int os_preop_switch2uncore(cmd_t *, boolean_t *);
 
 extern int os_op_llmap_stop(cmd_t *, boolean_t);
 extern int os_op_lnmap_stop(cmd_t *, boolean_t);
diff --git a/common/include/os/os_perf.h b/common/include/os/os_perf.h
index c78a787..60a1593 100644
--- a/common/include/os/os_perf.h
+++ b/common/include/os/os_perf.h
@@ -135,11 +135,11 @@ extern int os_pqos_cmt_lwp_smpl(struct _track_lwp *, void *, boolean_t *);
 extern int os_pqos_cmt_proc_free(struct _track_proc *, void *, boolean_t *);
 extern boolean_t os_perf_pqos_cmt_started(struct _perf_ctl *);
 extern int os_pqos_proc_stop(struct _perf_ctl *, union _perf_task *);
-extern int os_uncoreqpi_stop(struct _perf_ctl *, union _perf_task *);
-extern int os_uncoreqpi_start(struct _perf_ctl *, union _perf_task *);
-extern int os_uncoreqpi_smpl(struct _perf_ctl *, union _perf_task *, int *);
-extern boolean_t os_perf_uncoreqpi_started(struct _perf_ctl *);
-extern int os_perf_uncoreqpi_smpl(struct _perf_ctl *, int);
+extern int os_uncore_stop(struct _perf_ctl *, union _perf_task *);
+extern int os_uncore_start(struct _perf_ctl *, union _perf_task *);
+extern int os_uncore_smpl(struct _perf_ctl *, union _perf_task *, int *);
+extern boolean_t os_perf_uncore_started(struct _perf_ctl *);
+extern int os_perf_uncore_smpl(struct _perf_ctl *, int);
 
 #ifdef __cplusplus
 }
diff --git a/common/include/os/os_util.h b/common/include/os/os_util.h
index e74c4e0..ae5e150 100644
--- a/common/include/os/os_util.h
+++ b/common/include/os/os_util.h
@@ -74,6 +74,7 @@ extern boolean_t os_sysfs_meminfo(int, node_meminfo_t *);
 extern int os_sysfs_online_ncpus(void);
 extern int os_sysfs_cqm_llc_scale(const char*, double *);
 extern int os_sysfs_uncore_qpi_init(qpi_info_t *, int);
+extern int os_sysfs_uncore_imc_init(imc_info_t *, int);
 
 #ifdef __cplusplus
 }
diff --git a/common/include/os/pfwrapper.h b/common/include/os/pfwrapper.h
index ca94e80..738927d 100644
--- a/common/include/os/pfwrapper.h
+++ b/common/include/os/pfwrapper.h
@@ -138,6 +138,10 @@ void pf_uncoreqpi_free(struct _node *);
 int pf_uncoreqpi_setup(struct _node *);
 int pf_uncoreqpi_start(struct _node *);
 int pf_uncoreqpi_smpl(struct _node *);
+void pf_uncoreimc_free(struct _node *);
+int pf_uncoreimc_setup(struct _node *);
+int pf_uncoreimc_start(struct _node *);
+int pf_uncoreimc_smpl(struct _node *);
 
 #ifdef __cplusplus
 }
diff --git a/common/include/perf.h b/common/include/perf.h
index 316f305..f621db5 100644
--- a/common/include/perf.h
+++ b/common/include/perf.h
@@ -53,8 +53,8 @@ typedef enum {
 	PERF_STATUS_LL_FAILED,
 	PERF_STATUS_PQOS_CMT_STARTED,
 	PERF_STATUS_PQOS_CMT_FAILED,
-	PERF_STATUS_UNCOREQPI_STARTED,
-	PERF_STATUS_UNCOREQPI_FAILED,
+	PERF_STATUS_UNCORE_STARTED,
+	PERF_STATUS_UNCORE_FAILED,
 } perf_status_t;
 
 typedef enum {
@@ -72,9 +72,9 @@ typedef enum {
 	PERF_PQOS_CMT_START_ID,
 	PERF_PQOS_CMT_SMPL_ID,
 	PERF_PQOS_CMT_STOP_ID,
-	PERF_UNCOREQPI_START_ID,
-	PERF_UNCOREQPI_SMPL_ID,
-	PERF_UNCOREQPI_STOP_ID,
+	PERF_UNCORE_START_ID,
+	PERF_UNCORE_SMPL_ID,
+	PERF_UNCORE_STOP_ID,
 } perf_taskid_t;
 
 typedef struct _task_quit {
@@ -119,10 +119,10 @@ typedef struct _task_pqos_cmt {
 	int flags;
 } task_pqos_cmt_t;
 
-typedef struct _task_uncoreqpi {
+typedef struct _task_uncore {
 	perf_taskid_t task_id;
 	int nid;
-} task_uncoreqpi_t;
+} task_uncore_t;
 
 typedef union _perf_task {
 	task_quit_t quit;
@@ -133,7 +133,7 @@ typedef union _perf_task {
 	task_callchain_t callchain;
 	task_ll_t ll;
 	task_pqos_cmt_t pqos_cmt;
-	task_uncoreqpi_t uncoreqpi;
+	task_uncore_t uncore;
 } perf_task_t;
 
 typedef struct _perf_llrecgrp {
@@ -213,10 +213,10 @@ extern int perf_pqos_active_proc_setup(int, boolean_t);
 extern boolean_t perf_pqos_cmt_started(void);
 extern int perf_pqos_cmt_stop(pid_t, int);
 extern int perf_pqos_proc_setup(int, int, int);
-extern int perf_uncoreqpi_stop(int);
-extern int perf_uncoreqpi_setup(int);
-extern int perf_uncoreqpi_smpl(int);
-extern boolean_t perf_uncoreqpi_started(void);
+extern int perf_uncore_stop(int);
+extern int perf_uncore_setup(int);
+extern int perf_uncore_smpl(int);
+extern boolean_t perf_uncore_started(void);
 
 #ifdef __cplusplus
 }
diff --git a/common/numatop.c b/common/numatop.c
index 63b1c24..1bf8384 100644
--- a/common/numatop.c
+++ b/common/numatop.c
@@ -247,6 +247,7 @@ main(int argc, char *argv[])
 	}
 
 	node_qpi_init();
+	node_imc_init();
 
 	debug_print(NULL, 2, "Detected %d online CPUs\n", g_ncpus);
 	debug_print(NULL, 2, "LLC scale: occupancy %.1f, total bw %.1f, local bw %.1f\n",
diff --git a/common/os/node.c b/common/os/node.c
index 34cf2af..d705bf5 100644
--- a/common/os/node.c
+++ b/common/os/node.c
@@ -537,3 +537,32 @@ node_qpi_init(void)
 
 	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;
+}
diff --git a/common/os/os_cmd.c b/common/os/os_cmd.c
index 1ea994c..8801891 100644
--- a/common/os/os_cmd.c
+++ b/common/os/os_cmd.c
@@ -60,8 +60,8 @@ os_preop_switch2profiling(cmd_t *cmd, boolean_t *smpl)
 		*smpl = B_TRUE;
 	}
 
-	if (perf_uncoreqpi_started()) {
-		perf_uncoreqpi_stop(-1);
+	if (perf_uncore_started()) {
+		perf_uncore_stop(-1);
 		*smpl = B_TRUE;
 	}
 
@@ -288,7 +288,7 @@ os_preop_switch2pqosmbm(cmd_t *cmd, boolean_t *smpl)
 }
 
 int
-os_preop_switch2uncoreqpi(cmd_t *cmd, boolean_t *smpl)
+os_preop_switch2uncore(cmd_t *cmd, boolean_t *smpl)
 {
 	page_t *cur = page_current_get();
 	win_type_t type = PAGE_WIN_TYPE(cur);
@@ -302,9 +302,8 @@ os_preop_switch2uncoreqpi(cmd_t *cmd, boolean_t *smpl)
 		if (disp_flag2_wait() != DISP_FLAG_PROFILING_DATA_READY)
 			return -1;
 
-		perf_uncoreqpi_stop(-1);
-
-		ret = perf_uncoreqpi_setup(CMD_NODE_DETAIL(cmd)->nid);
+		perf_uncore_stop(-1);
+		ret = perf_uncore_setup(CMD_NODE_DETAIL(cmd)->nid);
 	}
 
 	return (ret);
diff --git a/common/os/os_page.c b/common/os/os_page.c
index 07b3668..2851ef4 100644
--- a/common/os/os_page.c
+++ b/common/os/os_page.c
@@ -73,7 +73,7 @@ os_page_smpl_start(page_t *page)
 		if (disp_flag2_wait() != DISP_FLAG_PROFILING_DATA_READY)
 			break;
 
-		if (perf_uncoreqpi_smpl(CMD_NODE_DETAIL(cmd)->nid) != 0)
+		if (perf_uncore_smpl(CMD_NODE_DETAIL(cmd)->nid) != 0)
 			break;
 
 		return B_TRUE;
diff --git a/common/os/os_perf.c b/common/os/os_perf.c
index ec8fb09..709bd2c 100644
--- a/common/os/os_perf.c
+++ b/common/os/os_perf.c
@@ -842,15 +842,20 @@ os_perf_countchain_reset(perf_countchain_t *count_chain)
 }
 
 static int
-uncoreqpi_stop_all(void)
+uncore_stop_all(void)
 {
 	node_t *node;
 	int i;
 
 	for (i = 0; i < NNODES_MAX; i++) {
 		node = node_get(i);
-		if (NODE_VALID(node) && (node->qpi.qpi_num > 0))
-			pf_uncoreqpi_free(node);
+		if (NODE_VALID(node)) {
+			if (node->qpi.qpi_num > 0)
+				pf_uncoreqpi_free(node);
+				
+			if (node->imc.imc_num > 0)
+				pf_uncoreimc_free(node);
+		}
 	}
 
 	return 0;
@@ -871,8 +876,8 @@ os_allstop(void)
 		proc_pqos_func(NULL, os_pqos_cmt_proc_free);
 	}
 
-	if (perf_uncoreqpi_started()) {
-		uncoreqpi_stop_all();
+	if (perf_uncore_started()) {
+		uncore_stop_all();
 	}
 }
 
@@ -1258,65 +1263,90 @@ int os_pqos_proc_stop(perf_ctl_t *ctl, perf_task_t *task)
 	return (0);
 }
 
-int os_uncoreqpi_stop(perf_ctl_t *ctl, perf_task_t *task)
+int os_uncore_stop(perf_ctl_t *ctl, perf_task_t *task)
 {
-	task_uncoreqpi_t *t = (task_uncoreqpi_t *)task;
+	task_uncore_t *t = (task_uncore_t *)task;
 	node_t *node;
 	int i;
 	
 	if (t->nid >= 0) {
 		node = node_get(t->nid);
-		if (NODE_VALID(node) && (node->qpi.qpi_num > 0))
-			pf_uncoreqpi_free(node);
+		if (NODE_VALID(node)) {
+			if (node->qpi.qpi_num > 0)
+				pf_uncoreqpi_free(node);
+			
+			if (node->imc.imc_num > 0)
+				pf_uncoreimc_free(node);
+		}
 	} else {
 		for (i = 0; i < NNODES_MAX; i++) {
 			node = node_get(i);
-			if (NODE_VALID(node) && (node->qpi.qpi_num > 0))
-				pf_uncoreqpi_free(node);
+			if (NODE_VALID(node)) {
+				if (node->qpi.qpi_num > 0)
+					pf_uncoreqpi_free(node);
+
+				if (node->imc.imc_num > 0)
+					pf_uncoreimc_free(node);
+			}
 		}
 	}
 
 	return 0;
 }
 
-static int uncoreqpi_start(perf_ctl_t *ctl, int nid)
+static int uncore_start(perf_ctl_t *ctl, int nid)
 {
 	node_t *node;
+	int ret = -1;
 	
 	node = node_get(nid);
 	if (!NODE_VALID(node))
 		return -1;
 	
 	if (pf_uncoreqpi_setup(node) != 0)
-		return -1;
+		goto L_EXIT;
 	
+	if (pf_uncoreimc_setup(node) != 0)
+		goto L_EXIT;
+
 	if (pf_uncoreqpi_start(node) != 0)
-		return -1;
+		goto L_EXIT;
 	
-	return 0;
+	if (pf_uncoreimc_start(node) != 0)
+		goto L_EXIT;
+
+	ret = 0;
+
+L_EXIT:
+	if (ret < 0) {
+		pf_uncoreqpi_free(node);
+		pf_uncoreimc_free(node);
+	}
+
+	return ret;
 }
 
 int
-os_uncoreqpi_start(perf_ctl_t *ctl, perf_task_t *task)
+os_uncore_start(perf_ctl_t *ctl, perf_task_t *task)
 {
-	task_uncoreqpi_t *t = (task_uncoreqpi_t *)task;
+	task_uncore_t *t = (task_uncore_t *)task;
 
-	if (uncoreqpi_start(ctl, t->nid) != 0) {
+	if (uncore_start(ctl, t->nid) != 0) {
 		debug_print(NULL, 2,
-			"uncoreqpi_start is failed for node %d/%d\n",
+			"os_uncore_start is failed for node %d/%d\n",
 			t->nid);
-		perf_status_set(PERF_STATUS_UNCOREQPI_FAILED);
+		perf_status_set(PERF_STATUS_UNCORE_FAILED);
 		return (-1);
 	}
 
-	perf_status_set(PERF_STATUS_UNCOREQPI_STARTED);
+	perf_status_set(PERF_STATUS_UNCORE_STARTED);
 	return (0);
 }
 
 int
-os_uncoreqpi_smpl(perf_ctl_t *ctl, perf_task_t *task, int *intval_ms)
+os_uncore_smpl(perf_ctl_t *ctl, perf_task_t *task, int *intval_ms)
 {
-	task_uncoreqpi_t *t = (task_uncoreqpi_t *)task;
+	task_uncore_t *t = (task_uncore_t *)task;
 	node_t *node;
 	int ret;
 
@@ -1325,7 +1355,12 @@ os_uncoreqpi_smpl(perf_ctl_t *ctl, perf_task_t *task, int *intval_ms)
 		return -1;
 
 	ret = pf_uncoreqpi_smpl(node);
+	if (ret != 0) {
+		disp_profiling_data_fail();
+		return ret;
+	}
 
+	ret = pf_uncoreimc_smpl(node);
 	if (ret == 0)
 		disp_profiling_data_ready(*intval_ms);
 	else
@@ -1335,24 +1370,24 @@ os_uncoreqpi_smpl(perf_ctl_t *ctl, perf_task_t *task, int *intval_ms)
 }
 
 boolean_t
-os_perf_uncoreqpi_started(perf_ctl_t *ctl)
+os_perf_uncore_started(perf_ctl_t *ctl)
 {
-	if (ctl->status == PERF_STATUS_UNCOREQPI_STARTED)
+	if (ctl->status == PERF_STATUS_UNCORE_STARTED)
 		return (B_TRUE);
 
 	return (B_FALSE);
 }
 
 int
-os_perf_uncoreqpi_smpl(perf_ctl_t *ctl, int nid)
+os_perf_uncore_smpl(perf_ctl_t *ctl, int nid)
 {
 	perf_task_t task;
-	task_uncoreqpi_t *t;
+	task_uncore_t *t;
 
 	perf_smpl_wait();
 	memset(&task, 0, sizeof (perf_task_t));
-	t = (task_uncoreqpi_t *)&task;
-	t->task_id = PERF_UNCOREQPI_SMPL_ID;
+	t = (task_uncore_t *)&task;
+	t->task_id = PERF_UNCORE_SMPL_ID;
 	t->nid = nid;
 
 	perf_task_set(&task);
diff --git a/common/os/os_util.c b/common/os/os_util.c
index 1ded9ca..71e449c 100644
--- a/common/os/os_util.c
+++ b/common/os/os_util.c
@@ -673,4 +673,33 @@ os_sysfs_uncore_qpi_init(qpi_info_t *qpi, int num)
 	}
 
 	return qpi_num;
+}
+
+int
+os_sysfs_uncore_imc_init(imc_info_t *imc, int num)
+{
+	int i, fd, imc_num = 0;
+	char path[PATH_MAX], buf[32];
+	
+	for (i = 0; i < num; i++)
+	{
+		snprintf(path, PATH_MAX, "/sys/devices/uncore_imc_%d/type", i);
+		if ((fd = open(path, O_RDONLY)) < 0)
+			return imc_num;
+
+		if (read(fd, buf, sizeof(buf)) < 0) {
+			close(fd);
+			return imc_num;
+		}		
+
+		imc_num++;
+		imc[i].type = atoi(buf);
+		imc[i].id = i;
+		imc[i].value_scaled = 0;
+		memset(imc[i].values, 0, sizeof(imc[i].values));
+		imc[i].fd = INVALID_FD;
+		close(fd);
+	}
+
+	return imc_num;
 }
\ No newline at end of file
diff --git a/common/os/os_win.c b/common/os/os_win.c
index 20f2101..fde2d8b 100644
--- a/common/os/os_win.c
+++ b/common/os/os_win.c
@@ -140,8 +140,8 @@ node_cpu_string(node_t *node, char *s1, int size)
 
 	if (ncpus == 1) {
 		(void) snprintf(s2, sizeof (s2), "%d", cpuid_start);
-        (void) strncat(s1, s2, strlen(s2));
-        free(cpuid_arr);
+        	(void) strncat(s1, s2, strlen(s2));
+        	free(cpuid_arr);
 		return;
 	}
 
@@ -158,7 +158,7 @@ node_cpu_string(node_t *node, char *s1, int size)
 					(void) snprintf(s2, sizeof (s2),
 						"%d-%d ", cpuid_start, cpuid_start + l - 1);
 				}
-          	} else {
+          		} else {
 				if (l == 1) {
 					(void) snprintf(s2, sizeof (s2), "%d",
 						cpuid_start);
@@ -169,21 +169,21 @@ node_cpu_string(node_t *node, char *s1, int size)
 
 				(void) snprintf(s3, sizeof (s3), " %d",
 					cpuid_arr[j]);
-	          	(void) strncat(s2, s3, strlen(s3));
+	        	  	(void) strncat(s2, s3, strlen(s3));
 			}
 
-          	(void) strncat(s1, s2, strlen(s2));
-          	cpuid_start = cpuid_arr[j];
-           	l = 1;
+          		(void) strncat(s1, s2, strlen(s2));
+          		cpuid_start = cpuid_arr[j];
+           		l = 1;
 		} else {
-        	if (k == ncpus) {
-            	(void) snprintf(s2, sizeof (s2), "%d-%d",
-                	cpuid_start, cpuid_start + l);
-         		(void) strncat(s1, s2, strlen(s2));
-       		} else {
-            	l++;
+	        	if (k == ncpus) {
+        	    		(void) snprintf(s2, sizeof (s2), "%d-%d",
+                			cpuid_start, cpuid_start + l);
+         			(void) strncat(s1, s2, strlen(s2));
+       			} else {
+            			l++;
+       			}
        		}
-       	}
 	}
 
 	free(cpuid_arr);
@@ -194,7 +194,7 @@ nodedetail_line_show(win_reg_t *reg, char *title, char *value, int line)
 {
 	char s1[256];
 
-	snprintf(s1, sizeof (s1), "%-20s%15s", title, value);
+	snprintf(s1, sizeof (s1), "%-30s%15s", title, value);
 	reg_line_write(reg, line, ALIGN_LEFT, s1);
 	dump_write("%s\n", s1);
 }
@@ -211,6 +211,8 @@ os_nodedetail_data(dyn_nodedetail_t *dyn, win_reg_t *seg)
 	node_meminfo_t meminfo;
 	int i = 1, j;
 	node_qpi_t *qpi;
+	node_imc_t *imc;
+	uint64_t v = 0;
 
 	reg_erase(seg);
 	node = node_get(dyn->nid);
@@ -307,6 +309,18 @@ os_nodedetail_data(dyn_nodedetail_t *dyn, win_reg_t *seg)
 		nodedetail_line_show(seg, s2, s1, i++);	
 	}
 
+	/*
+	 * Display the memory controller bandwidth
+	 */
+	imc = &node->imc;
+
+	for (j = 0; j < imc->imc_num; j++) {
+		v += imc->imc_info[j].value_scaled * 64;
+	}
+
+	snprintf(s1, sizeof (s1), "%.1fMB", ratio(v, 1024 * 1024));
+	nodedetail_line_show(seg,  "Memory controller bandwidth:", s1, i++);	
+
 	reg_refresh_nout(seg);
 }
 
diff --git a/common/os/pfwrapper.c b/common/os/pfwrapper.c
index 8e4dce8..3839799 100644
--- a/common/os/pfwrapper.c
+++ b/common/os/pfwrapper.c
@@ -1077,3 +1077,127 @@ int pf_uncoreqpi_smpl(struct _node *node)
 	
 	return 0;
 }
+
+void
+pf_uncoreimc_free(struct _node *node)
+{
+	int i;
+	node_imc_t *imc = &node->imc;
+
+	for (i = 0; i < imc->imc_num; i++) {
+		if (imc->imc_info[i].fd != INVALID_FD) {
+			debug_print(NULL, 2, "pf_uncoreimc_free: nid %d, imc %d, fd %d\n",
+				node->nid, i, imc->imc_info[i].fd);
+			close(imc->imc_info[i].fd);
+		}
+
+		imc->imc_info[i].fd = INVALID_FD;
+		imc->imc_info[i].value_scaled = 0;
+		memset(imc->imc_info[i].values, 0, sizeof(imc->imc_info[i].values));
+	}
+}
+
+int
+pf_uncoreimc_setup(struct _node *node)
+{
+	struct perf_event_attr attr;
+	node_imc_t *imc = &node->imc;
+	int i;
+
+	for (i = 0; i < imc->imc_num; i++) {
+		if (imc->imc_info[i].type == 0)
+			continue;
+
+		imc->imc_info[i].value_scaled = 0;
+		memset(imc->imc_info[i].values, 0, sizeof(imc->imc_info[i].values));
+		
+		memset(&attr, 0, sizeof (attr));
+		attr.type = imc->imc_info[i].type;
+		attr.size = sizeof(attr);
+		attr.config = 0xff04;
+		attr.disabled = 1;
+		attr.inherit = 1;
+		attr.read_format =
+			PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
+
+		if ((imc->imc_info[i].fd = pf_event_open(&attr, -1,
+			node->cpus[0].cpuid, -1, 0)) < 0) {
+			debug_print(NULL, 2, "pf_uncoreimc_setup: pf_event_open is failed "
+				"for node %d, imc %d, cpu %d, type %d, config 0x%x\n",
+				node->nid, i, node->cpus[0].cpuid, attr.type, attr.config);
+			imc->imc_info[i].fd = INVALID_FD;
+			return (-1);
+		}
+
+		debug_print(NULL, 2, "pf_uncoreimc_setup: pf_event_open is successful "
+			"for node %d, imc %d, cpu %d, type %d, config 0x%x, fd %d\n",
+			node->nid, i, node->cpus[0].cpuid, attr.type, attr.config,
+			imc->imc_info[i].fd);
+	}
+
+	return (0);
+}
+
+int pf_uncoreimc_start(struct _node *node)
+{
+	node_imc_t *imc = &node->imc;
+	int i;
+	
+	for (i = 0; i < imc->imc_num; i++) {
+		if (imc->imc_info[i].fd != INVALID_FD) {
+			debug_print(NULL, 2, "pf_uncoreimc_start: "
+				"for node %d, imc %d, cpu %d, type %d, fd %d\n",
+				node->nid, i, node->cpus[0].cpuid, imc->imc_info[i].type,
+				imc->imc_info[i].fd);
+			ioctl(imc->imc_info[i].fd, PERF_EVENT_IOC_ENABLE, 0);
+		}
+	}
+
+	return (0);
+}
+
+int pf_uncoreimc_smpl(struct _node *node)
+{
+	node_imc_t *imc = &node->imc;
+	uint64_t values[3];
+	int i;
+
+	for (i = 0; i < imc->imc_num; i++) {
+		if (imc->imc_info[i].fd != INVALID_FD) {
+
+			/*
+			 * struct read_format {
+			 *	{ u64	value; }
+			 *	{ u64	time_enabled; }
+			 *	{ u64	time_running; }
+			 * };
+			 */
+
+			if (read_fd(imc->imc_info[i].fd, values,
+				sizeof(values)) != 0) {
+
+				debug_print(NULL, 2,
+					"pf_uncoreimc_smpl: read fd %d fail\n",
+					imc->imc_info[i].fd);
+				continue;
+			}
+
+			imc->imc_info[i].value_scaled = scale(
+				values[0] - imc->imc_info[i].values[0],
+				values[1] - imc->imc_info[i].values[1],
+				values[2] - imc->imc_info[i].values[2]);
+
+			debug_print(NULL, 2, "pf_uncoreimc_smpl: "
+				"node %d, imc %d, fd %d: "
+				"%" PRIu64 ", %" PRIu64 ", %" PRIu64 "\n",
+				node->nid, i, imc->imc_info[i].fd,
+				values[0] - imc->imc_info[i].values[0],
+				values[1] - imc->imc_info[i].values[1],
+				values[2] - imc->imc_info[i].values[2]);
+
+			memcpy(imc->imc_info[i].values, values, sizeof(values));
+		}
+	}
+	
+	return 0;
+}
\ No newline at end of file
diff --git a/common/perf.c b/common/perf.c
index 30bb01b..6eac6f1 100644
--- a/common/perf.c
+++ b/common/perf.c
@@ -104,9 +104,9 @@ task_valid(perf_task_t *task)
 	case PERF_PQOS_CMT_START_ID:
 	case PERF_PQOS_CMT_SMPL_ID:
 	case PERF_PQOS_CMT_STOP_ID:
-	case PERF_UNCOREQPI_START_ID:
-	case PERF_UNCOREQPI_SMPL_ID:
-	case PERF_UNCOREQPI_STOP_ID:
+	case PERF_UNCORE_START_ID:
+	case PERF_UNCORE_SMPL_ID:
+	case PERF_UNCORE_STOP_ID:
 		return (B_TRUE);
 	default:
 		break;
@@ -273,17 +273,17 @@ perf_handler(void *arg)
 			perf_status_set(PERF_STATUS_PROFILING_STARTED);
 			break;
 
-		case PERF_UNCOREQPI_STOP_ID:
-			os_uncoreqpi_stop(&s_perf_ctl, &task);
+		case PERF_UNCORE_STOP_ID:
+			os_uncore_stop(&s_perf_ctl, &task);
 			perf_status_set(PERF_STATUS_PROFILING_STARTED);
 			break;
 
-		case PERF_UNCOREQPI_START_ID:
-			os_uncoreqpi_start(&s_perf_ctl, &task);
+		case PERF_UNCORE_START_ID:
+			os_uncore_start(&s_perf_ctl, &task);
 			break;
 
-		case PERF_UNCOREQPI_SMPL_ID:
-			os_uncoreqpi_smpl(&s_perf_ctl, &task, &intval_ms);
+		case PERF_UNCORE_SMPL_ID:
+			os_uncore_smpl(&s_perf_ctl, &task, &intval_ms);
 			break;
 
 		default:
@@ -697,48 +697,48 @@ L_EXIT:
 	return ret;
 }
 
-int perf_uncoreqpi_stop(int nid)
+int perf_uncore_stop(int nid)
 {
 	perf_task_t task;
-	task_uncoreqpi_t *t;
+	task_uncore_t *t;
 
 	(void) memset(&task, 0, sizeof (perf_task_t));
-	t = (task_uncoreqpi_t *)&task;
-	t->task_id = PERF_UNCOREQPI_STOP_ID;
+	t = (task_uncore_t *)&task;
+	t->task_id = PERF_UNCORE_STOP_ID;
 	t->nid = nid;
 	perf_task_set(&task);
 	return (perf_status_wait(PERF_STATUS_PROFILING_STARTED));
 }
 
 static int
-perf_uncoreqpi_start(int nid)
+perf_uncore_start(int nid)
 {
 	perf_task_t task;
-	task_uncoreqpi_t *t;
+	task_uncore_t *t;
 
 	(void) memset(&task, 0, sizeof (perf_task_t));
-	t = (task_uncoreqpi_t *)&task;
-	t->task_id = PERF_UNCOREQPI_START_ID;
+	t = (task_uncore_t *)&task;
+	t->task_id = PERF_UNCORE_START_ID;
 	t->nid = nid;
 	perf_task_set(&task);
-	return (perf_status_wait(PERF_STATUS_UNCOREQPI_STARTED));
+	return (perf_status_wait(PERF_STATUS_UNCORE_STARTED));
 }
 
-int perf_uncoreqpi_setup(int nid)
+int perf_uncore_setup(int nid)
 {
-	if (perf_uncoreqpi_start(nid) != 0)
+	if (perf_uncore_start(nid) != 0)
 		return -1;
 		
 	return 0;
 }
 
-int perf_uncoreqpi_smpl(int nid)
+int perf_uncore_smpl(int nid)
 {
-	return (os_perf_uncoreqpi_smpl(&s_perf_ctl, nid));
+	return (os_perf_uncore_smpl(&s_perf_ctl, nid));
 }
 
 boolean_t
-perf_uncoreqpi_started(void)
+perf_uncore_started(void)
 {
-	return (os_perf_uncoreqpi_started(&s_perf_ctl));
+	return (os_perf_uncore_started(&s_perf_ctl));
 }
\ No newline at end of file
-- 
2.10.2

