xref: /dpdk/examples/vm_power_manager/oob_monitor_x86.c (revision 3cb46d40d35960ca0478704d1e84e8d96b5676cd)
14b1a631bSDavid Hunt /* SPDX-License-Identifier: BSD-3-Clause
24b1a631bSDavid Hunt  * Copyright(c) 2018 Intel Corporation
34b1a631bSDavid Hunt  */
44b1a631bSDavid Hunt 
54b1a631bSDavid Hunt #include <unistd.h>
64b1a631bSDavid Hunt #include <fcntl.h>
74b1a631bSDavid Hunt #include <rte_log.h>
84b1a631bSDavid Hunt 
94b1a631bSDavid Hunt #include "oob_monitor.h"
104b1a631bSDavid Hunt #include "power_manager.h"
114b1a631bSDavid Hunt #include "channel_manager.h"
124b1a631bSDavid Hunt 
134b1a631bSDavid Hunt static volatile unsigned run_loop = 1;
144b1a631bSDavid Hunt static uint64_t g_branches, g_branch_misses;
154b1a631bSDavid Hunt static int g_active;
164b1a631bSDavid Hunt 
branch_monitor_exit(void)174b1a631bSDavid Hunt void branch_monitor_exit(void)
184b1a631bSDavid Hunt {
194b1a631bSDavid Hunt 	run_loop = 0;
204b1a631bSDavid Hunt }
214b1a631bSDavid Hunt 
224b1a631bSDavid Hunt /* Number of microseconds between each poll */
234b1a631bSDavid Hunt #define INTERVAL 100
244b1a631bSDavid Hunt #define PRINT_LOOP_COUNT (1000000/INTERVAL)
254b1a631bSDavid Hunt #define IA32_PERFEVTSEL0 0x186
264b1a631bSDavid Hunt #define IA32_PERFEVTSEL1 0x187
274b1a631bSDavid Hunt #define IA32_PERFCTR0 0xc1
284b1a631bSDavid Hunt #define IA32_PERFCTR1 0xc2
294b1a631bSDavid Hunt #define IA32_PERFEVT_BRANCH_HITS 0x05300c4
304b1a631bSDavid Hunt #define IA32_PERFEVT_BRANCH_MISS 0x05300c5
314b1a631bSDavid Hunt 
324b1a631bSDavid Hunt static float
apply_policy(int core)334b1a631bSDavid Hunt apply_policy(int core)
344b1a631bSDavid Hunt {
354b1a631bSDavid Hunt 	struct core_info *ci;
367e7b7a1fSReshma Pattan 	uint64_t counter = 0;
374b1a631bSDavid Hunt 	uint64_t branches, branch_misses;
387e7b7a1fSReshma Pattan 	uint64_t last_branches, last_branch_misses;
397e7b7a1fSReshma Pattan 	int64_t hits_diff, miss_diff;
404b1a631bSDavid Hunt 	float ratio;
414b1a631bSDavid Hunt 	int ret;
4231c9a664SDavid Hunt 	int freq_window_idx, up_count = 0, i;
434b1a631bSDavid Hunt 
444b1a631bSDavid Hunt 	g_active = 0;
454b1a631bSDavid Hunt 	ci = get_core_info();
464b1a631bSDavid Hunt 
474b1a631bSDavid Hunt 	last_branches = ci->cd[core].last_branches;
484b1a631bSDavid Hunt 	last_branch_misses = ci->cd[core].last_branch_misses;
494b1a631bSDavid Hunt 
504b1a631bSDavid Hunt 	ret = pread(ci->cd[core].msr_fd, &counter,
514b1a631bSDavid Hunt 			sizeof(counter), IA32_PERFCTR0);
524b1a631bSDavid Hunt 	if (ret < 0)
534b1a631bSDavid Hunt 		RTE_LOG(ERR, POWER_MANAGER,
544b1a631bSDavid Hunt 				"unable to read counter for core %u\n",
554b1a631bSDavid Hunt 				core);
564b1a631bSDavid Hunt 	branches = counter;
574b1a631bSDavid Hunt 
587e7b7a1fSReshma Pattan 	counter = 0;
594b1a631bSDavid Hunt 	ret = pread(ci->cd[core].msr_fd, &counter,
604b1a631bSDavid Hunt 			sizeof(counter), IA32_PERFCTR1);
614b1a631bSDavid Hunt 	if (ret < 0)
624b1a631bSDavid Hunt 		RTE_LOG(ERR, POWER_MANAGER,
634b1a631bSDavid Hunt 				"unable to read counter for core %u\n",
644b1a631bSDavid Hunt 				core);
654b1a631bSDavid Hunt 	branch_misses = counter;
664b1a631bSDavid Hunt 
674b1a631bSDavid Hunt 
684b1a631bSDavid Hunt 	ci->cd[core].last_branches = branches;
694b1a631bSDavid Hunt 	ci->cd[core].last_branch_misses = branch_misses;
704b1a631bSDavid Hunt 
717e7b7a1fSReshma Pattan 	/*
727e7b7a1fSReshma Pattan 	 * Intentional right shift to make MSB 0 to avoid
737e7b7a1fSReshma Pattan 	 * possible signed overflow or truncation.
747e7b7a1fSReshma Pattan 	 */
757e7b7a1fSReshma Pattan 	branches >>= 1;
767e7b7a1fSReshma Pattan 	last_branches >>= 1;
777e7b7a1fSReshma Pattan 	hits_diff = (int64_t)branches - (int64_t)last_branches;
784b1a631bSDavid Hunt 	if (hits_diff <= 0) {
794b1a631bSDavid Hunt 		/* Likely a counter overflow condition, skip this round */
804b1a631bSDavid Hunt 		return -1.0;
814b1a631bSDavid Hunt 	}
824b1a631bSDavid Hunt 
837e7b7a1fSReshma Pattan 	/*
847e7b7a1fSReshma Pattan 	 * Intentional right shift to make MSB 0 to avoid
857e7b7a1fSReshma Pattan 	 * possible signed overflow or truncation.
867e7b7a1fSReshma Pattan 	 */
877e7b7a1fSReshma Pattan 	branch_misses >>= 1;
887e7b7a1fSReshma Pattan 	last_branch_misses >>= 1;
897e7b7a1fSReshma Pattan 	miss_diff = (int64_t)branch_misses - (int64_t)last_branch_misses;
904b1a631bSDavid Hunt 	if (miss_diff <= 0) {
914b1a631bSDavid Hunt 		/* Likely a counter overflow condition, skip this round */
924b1a631bSDavid Hunt 		return -1.0;
934b1a631bSDavid Hunt 	}
944b1a631bSDavid Hunt 
954b1a631bSDavid Hunt 	g_branches = hits_diff;
964b1a631bSDavid Hunt 	g_branch_misses = miss_diff;
974b1a631bSDavid Hunt 
984b1a631bSDavid Hunt 	if (hits_diff < (INTERVAL*100)) {
99*3cb46d40SRory Sexton 		/* Likely no workload running on this core. */
100*3cb46d40SRory Sexton 		ratio = 0.0;
101*3cb46d40SRory Sexton 	} else {
1024b1a631bSDavid Hunt 		ratio = (float)miss_diff * (float)100 / (float)hits_diff;
103*3cb46d40SRory Sexton 	}
1044b1a631bSDavid Hunt 
10531c9a664SDavid Hunt 	/*
10631c9a664SDavid Hunt 	 * Store the last few directions that the ratio indicates
10731c9a664SDavid Hunt 	 * we should take. If there's on 'up', then we scale up
10831c9a664SDavid Hunt 	 * quickly. If all indicate 'down', only then do we scale
10931c9a664SDavid Hunt 	 * down. Each core_details struct has it's own array.
11031c9a664SDavid Hunt 	 */
11131c9a664SDavid Hunt 	freq_window_idx = ci->cd[core].freq_window_idx;
11295f648ffSRory Sexton 	if (ratio > ci->cd[core].branch_ratio_threshold)
11331c9a664SDavid Hunt 		ci->cd[core].freq_directions[freq_window_idx] = 1;
1144b1a631bSDavid Hunt 	else
11531c9a664SDavid Hunt 		ci->cd[core].freq_directions[freq_window_idx] = 0;
11631c9a664SDavid Hunt 
11731c9a664SDavid Hunt 	freq_window_idx++;
11831c9a664SDavid Hunt 	freq_window_idx = freq_window_idx & (FREQ_WINDOW_SIZE-1);
11931c9a664SDavid Hunt 	ci->cd[core].freq_window_idx = freq_window_idx;
12031c9a664SDavid Hunt 
12131c9a664SDavid Hunt 	up_count = 0;
12231c9a664SDavid Hunt 	for (i = 0; i < FREQ_WINDOW_SIZE; i++)
12331c9a664SDavid Hunt 		up_count +=  ci->cd[core].freq_directions[i];
12431c9a664SDavid Hunt 
12531c9a664SDavid Hunt 	if (up_count == 0) {
12631c9a664SDavid Hunt 		if (ci->cd[core].freq_state != FREQ_MIN) {
12731c9a664SDavid Hunt 			power_manager_scale_core_min(core);
12831c9a664SDavid Hunt 			ci->cd[core].freq_state = FREQ_MIN;
12931c9a664SDavid Hunt 		}
13031c9a664SDavid Hunt 	} else {
13131c9a664SDavid Hunt 		if (ci->cd[core].freq_state != FREQ_MAX) {
1324b1a631bSDavid Hunt 			power_manager_scale_core_max(core);
13331c9a664SDavid Hunt 			ci->cd[core].freq_state = FREQ_MAX;
13431c9a664SDavid Hunt 		}
13531c9a664SDavid Hunt 	}
1364b1a631bSDavid Hunt 
1374b1a631bSDavid Hunt 	g_active = 1;
1384b1a631bSDavid Hunt 	return ratio;
1394b1a631bSDavid Hunt }
1404b1a631bSDavid Hunt 
1414b1a631bSDavid Hunt int
add_core_to_monitor(int core)1424b1a631bSDavid Hunt add_core_to_monitor(int core)
1434b1a631bSDavid Hunt {
1444b1a631bSDavid Hunt 	struct core_info *ci;
1454b1a631bSDavid Hunt 	char proc_file[UNIX_PATH_MAX];
1464b1a631bSDavid Hunt 	int ret;
1474b1a631bSDavid Hunt 
1484b1a631bSDavid Hunt 	ci = get_core_info();
1494b1a631bSDavid Hunt 
1504b1a631bSDavid Hunt 	if (core < ci->core_count) {
1514b1a631bSDavid Hunt 		long setup;
1524b1a631bSDavid Hunt 
1534b1a631bSDavid Hunt 		snprintf(proc_file, UNIX_PATH_MAX, "/dev/cpu/%d/msr", core);
1544b1a631bSDavid Hunt 		ci->cd[core].msr_fd = open(proc_file, O_RDWR | O_SYNC);
1554b1a631bSDavid Hunt 		if (ci->cd[core].msr_fd < 0) {
1564b1a631bSDavid Hunt 			RTE_LOG(ERR, POWER_MANAGER,
1574b1a631bSDavid Hunt 					"Error opening MSR file for core %d "
1584b1a631bSDavid Hunt 					"(is msr kernel module loaded?)\n",
1594b1a631bSDavid Hunt 					core);
1604b1a631bSDavid Hunt 			return -1;
1614b1a631bSDavid Hunt 		}
1624b1a631bSDavid Hunt 		/*
1634b1a631bSDavid Hunt 		 * Set up branch counters
1644b1a631bSDavid Hunt 		 */
1654b1a631bSDavid Hunt 		setup = IA32_PERFEVT_BRANCH_HITS;
1664b1a631bSDavid Hunt 		ret = pwrite(ci->cd[core].msr_fd, &setup,
1674b1a631bSDavid Hunt 				sizeof(setup), IA32_PERFEVTSEL0);
1684b1a631bSDavid Hunt 		if (ret < 0) {
1694b1a631bSDavid Hunt 			RTE_LOG(ERR, POWER_MANAGER,
1704b1a631bSDavid Hunt 					"unable to set counter for core %u\n",
1714b1a631bSDavid Hunt 					core);
1724b1a631bSDavid Hunt 			return ret;
1734b1a631bSDavid Hunt 		}
1744b1a631bSDavid Hunt 		setup = IA32_PERFEVT_BRANCH_MISS;
1754b1a631bSDavid Hunt 		ret = pwrite(ci->cd[core].msr_fd, &setup,
1764b1a631bSDavid Hunt 				sizeof(setup), IA32_PERFEVTSEL1);
1774b1a631bSDavid Hunt 		if (ret < 0) {
1784b1a631bSDavid Hunt 			RTE_LOG(ERR, POWER_MANAGER,
1794b1a631bSDavid Hunt 					"unable to set counter for core %u\n",
1804b1a631bSDavid Hunt 					core);
1814b1a631bSDavid Hunt 			return ret;
1824b1a631bSDavid Hunt 		}
1834b1a631bSDavid Hunt 		/*
1844b1a631bSDavid Hunt 		 * Close the file and re-open as read only so
1854b1a631bSDavid Hunt 		 * as not to hog the resource
1864b1a631bSDavid Hunt 		 */
1874b1a631bSDavid Hunt 		close(ci->cd[core].msr_fd);
1884b1a631bSDavid Hunt 		ci->cd[core].msr_fd = open(proc_file, O_RDONLY);
1894b1a631bSDavid Hunt 		if (ci->cd[core].msr_fd < 0) {
1904b1a631bSDavid Hunt 			RTE_LOG(ERR, POWER_MANAGER,
1914b1a631bSDavid Hunt 					"Error opening MSR file for core %d "
1924b1a631bSDavid Hunt 					"(is msr kernel module loaded?)\n",
1934b1a631bSDavid Hunt 					core);
1944b1a631bSDavid Hunt 			return -1;
1954b1a631bSDavid Hunt 		}
1964b1a631bSDavid Hunt 		ci->cd[core].oob_enabled = 1;
1974b1a631bSDavid Hunt 	}
1984b1a631bSDavid Hunt 	return 0;
1994b1a631bSDavid Hunt }
2004b1a631bSDavid Hunt 
2014b1a631bSDavid Hunt int
remove_core_from_monitor(int core)2024b1a631bSDavid Hunt remove_core_from_monitor(int core)
2034b1a631bSDavid Hunt {
2044b1a631bSDavid Hunt 	struct core_info *ci;
2054b1a631bSDavid Hunt 	char proc_file[UNIX_PATH_MAX];
2064b1a631bSDavid Hunt 	int ret;
2074b1a631bSDavid Hunt 
2084b1a631bSDavid Hunt 	ci = get_core_info();
2094b1a631bSDavid Hunt 
2104b1a631bSDavid Hunt 	if (ci->cd[core].oob_enabled) {
2114b1a631bSDavid Hunt 		long setup;
2124b1a631bSDavid Hunt 
2134b1a631bSDavid Hunt 		/*
2144b1a631bSDavid Hunt 		 * close the msr file, then reopen rw so we can
2154b1a631bSDavid Hunt 		 * disable the counters
2164b1a631bSDavid Hunt 		 */
2174b1a631bSDavid Hunt 		if (ci->cd[core].msr_fd != 0)
2184b1a631bSDavid Hunt 			close(ci->cd[core].msr_fd);
2194b1a631bSDavid Hunt 		snprintf(proc_file, UNIX_PATH_MAX, "/dev/cpu/%d/msr", core);
2204b1a631bSDavid Hunt 		ci->cd[core].msr_fd = open(proc_file, O_RDWR | O_SYNC);
2214b1a631bSDavid Hunt 		if (ci->cd[core].msr_fd < 0) {
2224b1a631bSDavid Hunt 			RTE_LOG(ERR, POWER_MANAGER,
2234b1a631bSDavid Hunt 					"Error opening MSR file for core %d "
2244b1a631bSDavid Hunt 					"(is msr kernel module loaded?)\n",
2254b1a631bSDavid Hunt 					core);
2264b1a631bSDavid Hunt 			return -1;
2274b1a631bSDavid Hunt 		}
2284b1a631bSDavid Hunt 		setup = 0x0; /* clear event */
2294b1a631bSDavid Hunt 		ret = pwrite(ci->cd[core].msr_fd, &setup,
2304b1a631bSDavid Hunt 				sizeof(setup), IA32_PERFEVTSEL0);
2314b1a631bSDavid Hunt 		if (ret < 0) {
2324b1a631bSDavid Hunt 			RTE_LOG(ERR, POWER_MANAGER,
2334b1a631bSDavid Hunt 					"unable to set counter for core %u\n",
2344b1a631bSDavid Hunt 					core);
2354b1a631bSDavid Hunt 			return ret;
2364b1a631bSDavid Hunt 		}
2374b1a631bSDavid Hunt 		setup = 0x0; /* clear event */
2384b1a631bSDavid Hunt 		ret = pwrite(ci->cd[core].msr_fd, &setup,
2394b1a631bSDavid Hunt 				sizeof(setup), IA32_PERFEVTSEL1);
2404b1a631bSDavid Hunt 		if (ret < 0) {
2414b1a631bSDavid Hunt 			RTE_LOG(ERR, POWER_MANAGER,
2424b1a631bSDavid Hunt 					"unable to set counter for core %u\n",
2434b1a631bSDavid Hunt 					core);
2444b1a631bSDavid Hunt 			return ret;
2454b1a631bSDavid Hunt 		}
2464b1a631bSDavid Hunt 
2474b1a631bSDavid Hunt 		close(ci->cd[core].msr_fd);
2484b1a631bSDavid Hunt 		ci->cd[core].msr_fd = 0;
2494b1a631bSDavid Hunt 		ci->cd[core].oob_enabled = 0;
2504b1a631bSDavid Hunt 	}
2514b1a631bSDavid Hunt 	return 0;
2524b1a631bSDavid Hunt }
2534b1a631bSDavid Hunt 
2544b1a631bSDavid Hunt int
branch_monitor_init(void)2554b1a631bSDavid Hunt branch_monitor_init(void)
2564b1a631bSDavid Hunt {
2574b1a631bSDavid Hunt 	return 0;
2584b1a631bSDavid Hunt }
2594b1a631bSDavid Hunt 
2604b1a631bSDavid Hunt void
run_branch_monitor(void)2614b1a631bSDavid Hunt run_branch_monitor(void)
2624b1a631bSDavid Hunt {
2634b1a631bSDavid Hunt 	struct core_info *ci;
2644b1a631bSDavid Hunt 	int print = 0;
2654b1a631bSDavid Hunt 	float ratio;
2664b1a631bSDavid Hunt 	int printed;
2674b1a631bSDavid Hunt 	int reads = 0;
2684b1a631bSDavid Hunt 
2694b1a631bSDavid Hunt 	ci = get_core_info();
2704b1a631bSDavid Hunt 
2714b1a631bSDavid Hunt 	while (run_loop) {
2724b1a631bSDavid Hunt 
2734b1a631bSDavid Hunt 		if (!run_loop)
2744b1a631bSDavid Hunt 			break;
2754b1a631bSDavid Hunt 		usleep(INTERVAL);
2764b1a631bSDavid Hunt 		int j;
2774b1a631bSDavid Hunt 		print++;
2784b1a631bSDavid Hunt 		printed = 0;
2794b1a631bSDavid Hunt 		for (j = 0; j < ci->core_count; j++) {
2804b1a631bSDavid Hunt 			if (ci->cd[j].oob_enabled) {
2814b1a631bSDavid Hunt 				ratio = apply_policy(j);
2824b1a631bSDavid Hunt 				if ((print > PRINT_LOOP_COUNT) && (g_active)) {
2834b1a631bSDavid Hunt 					printf("  %d: %.4f {%lu} {%d}", j,
2844b1a631bSDavid Hunt 							ratio, g_branches,
2854b1a631bSDavid Hunt 							reads);
2864b1a631bSDavid Hunt 					printed = 1;
2874b1a631bSDavid Hunt 					reads = 0;
2884b1a631bSDavid Hunt 				} else {
2894b1a631bSDavid Hunt 					reads++;
2904b1a631bSDavid Hunt 				}
2914b1a631bSDavid Hunt 			}
2924b1a631bSDavid Hunt 		}
2934b1a631bSDavid Hunt 		if (print > PRINT_LOOP_COUNT) {
2944b1a631bSDavid Hunt 			if (printed)
2954b1a631bSDavid Hunt 				printf("\n");
2964b1a631bSDavid Hunt 			print = 0;
2974b1a631bSDavid Hunt 		}
2984b1a631bSDavid Hunt 	}
2994b1a631bSDavid Hunt }
300