├── .gitignore ├── AddrSequence.cc ├── AddrSequence.h ├── AddressRangeFilter.cc ├── AddressRangeFilter.h ├── BandwidthLimit.cc ├── BandwidthLimit.h ├── COPYING ├── EPTMigrate.cc ├── EPTMigrate.h ├── EPTScan.cc ├── EPTScan.h ├── ExclusiveRun.rb ├── Formatter.h ├── GlobalScan.cc ├── GlobalScan.h ├── Makefile ├── MovePages.cc ├── MovePages.h ├── Numa.cc ├── Numa.h ├── Option.cc ├── Option.h ├── OptionParser.cc ├── OptionParser.h ├── PidContext.h ├── ProcIdlePages.cc ├── ProcIdlePages.h ├── ProcMaps.cc ├── ProcMaps.h ├── ProcNumaMaps.rb ├── ProcPid.cc ├── ProcPid.h ├── ProcStatus.cc ├── ProcStatus.h ├── ProcStatus.rb ├── ProcVmstat.cc ├── ProcVmstat.h ├── ProcVmstat.rb ├── Process.cc ├── Process.h ├── Queue.h ├── README.md ├── Sysfs.cc ├── Sysfs.h ├── VMAInspect.cc ├── VMAInspect.h ├── VMTest.rb ├── aepwatch.rb ├── aepwatch_gnuplot.sh ├── aepwatch_parser.rb ├── common.h ├── doc ├── GPL-2.0 └── classes.yaml ├── edp.rb ├── edp_gnuplot.sh ├── emon.rb ├── get_version.sh ├── install-deps.sh ├── lib ├── debug.c ├── debug.h ├── iomem_parse.c ├── iomem_parse.h ├── memparse.c ├── memparse.h ├── page-types.c ├── page-types.h └── stats.h ├── page-refs.c ├── plot-page-refs.sh ├── pmu-refs ├── .gitignore ├── Makefile ├── cmsk.cc ├── cmsk.h ├── hmd-common.cc ├── hmd-common.h ├── hmd-config.cc ├── hmd-config.h ├── hmd.cc ├── migration.cc ├── migration.h ├── pmu.cc ├── pmu.h └── tests │ ├── Makefile │ └── filter-test.cc ├── show-vmstat.cc ├── sys-refs.cc ├── task-maps.cc ├── task-numa-maps.rb ├── task-refs.cc ├── test-migratepages.sh ├── tests ├── 2lm-vs-kunpeng │ ├── 2lm-kunpeng-global.sh │ ├── 2lm-kunpeng.sh │ ├── 2lm.sh │ ├── aepwatch_gnuplot.sh │ ├── edp_gnuplot.sh │ ├── kvm.sh │ ├── run-sysbench-memory-1t.sh │ ├── run-sysbench-memory.sh │ ├── scheme-sysbench-memory-2lm-16t-global.yaml │ ├── scheme-sysbench-memory-2lm-16t.yaml │ ├── scheme-sysbench-memory-2lm-1t-global.yaml │ ├── scheme-sysbench-memory-2lm-1t.yaml │ ├── scheme-sysbench-memory-2lm-32t-global.yaml │ ├── scheme-sysbench-memory-2lm-32t.yaml │ ├── scheme-sysbench-memory-2lm-4t-global.yaml │ ├── scheme-sysbench-memory-2lm-4t.yaml │ ├── scheme-sysbench-memory-kunpeng-16t-global.yaml │ ├── scheme-sysbench-memory-kunpeng-16t.yaml │ ├── scheme-sysbench-memory-kunpeng-1t-global.yaml │ ├── scheme-sysbench-memory-kunpeng-1t.yaml │ ├── scheme-sysbench-memory-kunpeng-32t-global.yaml │ ├── scheme-sysbench-memory-kunpeng-32t.yaml │ ├── scheme-sysbench-memory-kunpeng-4t-global.yaml │ ├── scheme-sysbench-memory-kunpeng-4t.yaml │ ├── sys-refs-sysbench-memory-16t.yaml │ ├── sys-refs-sysbench-memory-1t.yaml │ ├── sys-refs-sysbench-memory-32t.yaml │ └── sys-refs-sysbench-memory-4t.yaml ├── aepwatch_gnuplot.sh ├── edp_gnuplot.sh ├── gaussian-stddev-global │ ├── 2lm-kunpeng.sh │ ├── 2lm.sh │ ├── aepwatch_gnuplot.sh │ ├── edp_gnuplot.sh │ ├── kvm.sh │ ├── run-sysbench-memory-1t.sh │ ├── run-sysbench-memory.sh │ ├── scheme-sysbench-memory-2lm-16t.yaml │ ├── scheme-sysbench-memory-2lm-1t.yaml │ ├── scheme-sysbench-memory-2lm-32t.yaml │ ├── scheme-sysbench-memory-2lm-4t.yaml │ ├── scheme-sysbench-memory-kunpeng-16t.yaml │ ├── scheme-sysbench-memory-kunpeng-1t.yaml │ ├── scheme-sysbench-memory-kunpeng-32t.yaml │ ├── scheme-sysbench-memory-kunpeng-4t.yaml │ ├── sys-refs-sysbench-memory-16t.yaml │ ├── sys-refs-sysbench-memory-1t.yaml │ ├── sys-refs-sysbench-memory-32t.yaml │ └── sys-refs-sysbench-memory-4t.yaml ├── grc-conf.sys-refs ├── kernel-build.sh ├── kvm-hugetlb.sh ├── kvm.sh ├── run-sysbench-memory.sh ├── run-vm-tests.rb ├── scheme-sysbench-memory-32t-hugetlb.yaml ├── scheme-sysbench-memory-32t.yaml ├── scheme-sysbench-memory.yaml ├── sys-refs-example-config.yaml ├── sys-refs-sysbench-memory-32t.yaml ├── sysbench │ ├── config │ │ ├── sysbench-memory-hugetlb-1t.yaml │ │ ├── sysbench-memory-hugetlb-32t.yaml │ │ ├── sysbench-memory-thp-1t.yaml │ │ └── sysbench-memory-thp-32t.yaml │ ├── run-sysbench-hugetlb.sh │ ├── run-sysbench-memory.sh │ ├── run-sysbench-thp.sh │ ├── scheme-sysbench-memory-hugetlb-1t.yaml │ ├── scheme-sysbench-memory-hugetlb-32t.yaml │ ├── scheme-sysbench-memory-thp-1t.yaml │ └── scheme-sysbench-memory-thp-32t.yaml ├── test-sysbench │ ├── kvm-dax.sh │ ├── kvm-dram.sh │ ├── kvm-numa.sh │ ├── sysbench-memory.sh │ └── sysbench-memory.yaml └── vm │ ├── kvm-dax.sh │ ├── kvm-dram.sh │ ├── kvm-hugetlb.sh │ ├── kvm-image.sh │ └── kvm-numa.sh └── utility.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | addr-seq 4 | page-refs 5 | pid-list 6 | show-vmstat 7 | sys-refs 8 | task-maps 9 | task-refs 10 | version.h 11 | -------------------------------------------------------------------------------- /AddrSequence.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Yao Yuan 7 | * Fengguang Wu 8 | */ 9 | 10 | #ifndef AEP_ADDR_SEQUENCE_H 11 | #define AEP_ADDR_SEQUENCE_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | struct DeltaPayload 18 | { 19 | uint8_t delta; // in pagesize unit 20 | uint8_t payload; // stores refs count 21 | }__attribute__((packed)); 22 | 23 | struct AddrCluster 24 | { 25 | unsigned long start; 26 | int size; 27 | DeltaPayload *deltas; // points into AddrSequence::bufs 28 | }; 29 | 30 | // A sparse array for ordered addresses. 31 | // There will be 2 such arrays, one for 4K pages, another or 2M pages. 32 | // 33 | // Since addresses will have good spacial locality, organize them by clusters. 34 | // Each AddrCluster represents a range of addresses that are close enough to 35 | // each other, within 255 pages distance from prev/next neighbors, thus can be 36 | // encoded by 8bit delta to save space. 37 | // 38 | // The addresses will be appended, updated or visited all sequentially. 39 | class AddrSequence 40 | { 41 | public: 42 | enum error { 43 | CREATE_CLUSTER_FAILED = 100, 44 | ADDR_NOT_FOUND, 45 | IGNORE_DUPLICATED_ADDR, 46 | END_OF_SEQUENCE, 47 | }; 48 | 49 | public: 50 | AddrSequence(); 51 | ~AddrSequence(); 52 | size_t size() const { return addr_size; } 53 | bool empty() const { return 0 == addr_size; } 54 | unsigned long get_top_bytes() const { return top_bytes; } 55 | unsigned long get_young_bytes() const { return young_bytes; } 56 | int get_pageshift() const { return pageshift; } 57 | 58 | void set_pageshift(int shift); 59 | void clear(); 60 | 61 | // call me before starting each walk 62 | int rewind(); 63 | 64 | // n=0: [addr]=0 if not already set 65 | // n=1: [addr]=1 if not already set, [addr]++ if already there 66 | // 67 | // Depending on nr_walks, it'll behave differently 68 | // - appending: for the first walk 69 | // will add addresses and set payload to 0 or 1 70 | // will allocate bufs 71 | // 72 | // - updating: for (nr_walks >= 2) walks 73 | // will do ++payload 74 | // will ignore addresses not already there 75 | int inc_payload(unsigned long addr, int n); 76 | int smooth_payloads(); 77 | 78 | // for sequential visiting 79 | bool prepare_get(); 80 | int get_first(unsigned long& addr, uint8_t& payload); 81 | int get_next(unsigned long& addr, uint8_t& payload); 82 | 83 | #ifdef ADDR_SEQ_SELF_TEST 84 | int self_test(); 85 | int do_self_test(unsigned long pagesize, int max_loop, bool is_pref); 86 | int do_self_test_walk(unsigned long pagesize, bool is_perf); 87 | int do_self_test_compare(unsigned long pagesize, bool is_perf); 88 | #endif 89 | 90 | private: 91 | struct walk_iterator{ 92 | unsigned long cluster_iter; 93 | unsigned long cluster_iter_end; 94 | unsigned long delta_sum; 95 | int delta_index; 96 | 97 | AddrCluster* cur_cluster_ptr; 98 | DeltaPayload* cur_delta_ptr; 99 | }; 100 | 101 | int append_addr(unsigned long addr, int n); 102 | int update_addr(unsigned long addr, int n); 103 | 104 | int create_cluster(unsigned long addr, int n); 105 | AddrCluster new_cluster(unsigned long addr, void* buffer); 106 | int save_into_cluster(AddrCluster& cluster, unsigned long addr, int n); 107 | bool can_merge_into_cluster(AddrCluster& cluster, unsigned long addr); 108 | 109 | int get_free_buffer(void** free_ptr); 110 | int allocate_buf(); 111 | void free_all_buf(); 112 | int is_buffer_full() { 113 | return buf_used_count == MAX_ITEM_COUNT; 114 | } 115 | 116 | void reset_iterator(walk_iterator& iter, unsigned long new_start) { 117 | iter.cluster_iter = new_start; 118 | iter.cluster_iter_end = addr_clusters.size(); 119 | iter.delta_sum = 0; 120 | iter.delta_index = 0; 121 | 122 | if (!addr_clusters.empty()) 123 | do_walk_update_current_ptr(iter); 124 | } 125 | 126 | int in_append_period() { return nr_walks < 2; } 127 | 128 | int do_walk(walk_iterator& iter, unsigned long& addr, uint8_t& payload); 129 | void do_walk_move_next(walk_iterator& iter); 130 | void do_walk_update_payload(walk_iterator& iter, 131 | unsigned addr, uint8_t payload); 132 | bool do_walk_continue(int rc) { return rc >=0 && rc != END_OF_SEQUENCE; } 133 | 134 | void do_walk_update_current_ptr(walk_iterator& iter) { 135 | iter.cur_cluster_ptr = &addr_clusters[iter.cluster_iter]; 136 | iter.cur_delta_ptr = iter.cur_cluster_ptr->deltas; 137 | } 138 | 139 | private: 140 | const static int BUF_SIZE = 0x10000; // 64KB; 141 | const static int ITEM_SIZE = sizeof(struct DeltaPayload); 142 | const static int MAX_ITEM_COUNT = BUF_SIZE / ITEM_SIZE; 143 | const static unsigned long MAX_DELTA_DIST = ( 1 << ( sizeof(uint8_t) * 8 ) ) - 1; 144 | 145 | int nr_walks; 146 | int pageshift; 147 | unsigned long pagesize; 148 | unsigned long addr_size; // # of addrs stored 149 | unsigned long top_bytes; // nr_top_pages * pagesize 150 | unsigned long young_bytes; // all accessed in one scan 151 | 152 | std::vector addr_clusters; 153 | 154 | // inc_payload() will allocate new fixed size buf on demand, 155 | // avoiding internal/external fragmentations. 156 | // Only freed on clear(). 157 | std::allocator buf_allocator; 158 | std::vector buf_pool; 159 | int buf_used_count; 160 | 161 | walk_iterator walk_iter; 162 | walk_iterator find_iter; 163 | 164 | unsigned long last_cluster_end; 165 | 166 | #ifdef ADDR_SEQ_SELF_TEST 167 | std::map test_map; 168 | #endif 169 | }; 170 | 171 | #endif 172 | // vim:set ts=2 sw=2 et: 173 | -------------------------------------------------------------------------------- /AddressRangeFilter.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #include "AddressRangeFilter.h" 11 | 12 | AddressRangeFilter::Iterator AddressRangeFilter::search_range( 13 | int pid, unsigned long addr, 14 | AddressRangeFilter::Iterator *lower_p, 15 | AddressRangeFilter::Iterator *upper_p) 16 | { 17 | Key k(pid, addr); 18 | Range r; 19 | auto lower = rmap.end(), upper = rmap.end(), it = rmap.end(); 20 | 21 | if (rmap.empty()) 22 | goto exit; 23 | 24 | upper = rmap.upper_bound(k); 25 | it = upper; 26 | it--; 27 | if (it == upper) { 28 | it = rmap.end(); 29 | goto exit; 30 | } 31 | 32 | k = it->first; 33 | r = it->second; 34 | 35 | if (k.pid != (unsigned long)pid) { 36 | it = rmap.end(); 37 | goto exit; 38 | } 39 | 40 | if (addr < k.start + r.size) { 41 | /* Range is found, try to get the lower range */ 42 | lower = it; 43 | lower--; 44 | if (lower == it) { 45 | /* It's possible when it and lower are all rmap.begin() */ 46 | lower = rmap.end(); 47 | } 48 | goto exit; 49 | } 50 | 51 | lower = it; 52 | it = rmap.end(); 53 | 54 | exit: 55 | if (lower_p) 56 | *lower_p = lower; 57 | 58 | if (upper_p) 59 | *upper_p = upper; 60 | 61 | return it; 62 | } 63 | 64 | bool AddressRangeFilter::search_address(int pid, unsigned long addr) 65 | { 66 | Iterator it; 67 | 68 | it = search_range(pid, addr, NULL, NULL); 69 | return it != rmap.end(); 70 | } 71 | 72 | void AddressRangeFilter::insert_new_range(int pid, unsigned long start, 73 | unsigned long size) 74 | { 75 | Key k(pid, start); 76 | Range r; 77 | 78 | r.size = size; 79 | rmap.insert(std::pair(k, r)); 80 | } 81 | 82 | void AddressRangeFilter::remove_ranges( 83 | AddressRangeFilter::Iterator rm_start, AddressRangeFilter::Iterator rm_end, 84 | int pid, unsigned long new_start, unsigned long new_end) 85 | { 86 | Iterator begin = rm_start; 87 | Iterator it; 88 | 89 | if (begin == rmap.end()) 90 | begin = rmap.begin(); 91 | 92 | it = begin; 93 | while (it != rmap.end() && it != rm_end) { 94 | Key k; 95 | Range r; 96 | 97 | k = it->first; 98 | r = it->second; 99 | 100 | if ((unsigned long)pid != k.pid) { 101 | it++; 102 | continue; 103 | } 104 | 105 | if (new_start <= k.start && new_end >= k.start + r.size) { 106 | it = rmap.erase(it); 107 | continue; 108 | } 109 | 110 | it++; 111 | } 112 | 113 | if (it != rmap.end()) 114 | rmap.erase(it); 115 | } 116 | 117 | void AddressRangeFilter::insert_range(int pid, unsigned long start, 118 | unsigned long size) 119 | { 120 | Iterator rm_start, rm_end, lower, upper; 121 | unsigned long new_start = start, new_end = start + size; 122 | Key k; 123 | Range r; 124 | 125 | /* 126 | * $lower$ |new| 127 | * $---------$ |-----| 128 | * $lower$ |new| 129 | * $---------|-$-----| 130 | */ 131 | rm_start = search_range(pid, start, &lower, NULL); 132 | 133 | if (rm_start != rmap.end()) { 134 | k = rm_start->first; 135 | new_start = k.start; 136 | } else if (lower != rmap.end()) { 137 | rm_start = lower; 138 | k = rm_start->first; 139 | r = rm_start->second; 140 | 141 | if ((unsigned long)pid == k.pid && k.start + r.size == start) 142 | new_start = k.start; 143 | else 144 | rm_start++; 145 | } 146 | 147 | /* 148 | * |new| $upper$ 149 | * |---------| $-------$ 150 | * |new| $upper$ 151 | * |---------$-|-------$ 152 | */ 153 | rm_end = search_range(pid, start + size - 1, NULL, &upper); 154 | 155 | if (rm_end != rmap.end()) { 156 | k = rm_end->first; 157 | r = rm_end->second; 158 | new_end = k.start + r.size; 159 | } else if (upper != rmap.end()) { 160 | k = upper->first; 161 | r = upper->second; 162 | 163 | if ((unsigned long)pid == k.pid && new_end == k.start) { 164 | rm_end = upper; 165 | new_end = k.start + r.size; 166 | } 167 | } 168 | 169 | remove_ranges(rm_start, rm_end, pid, new_start, new_end); 170 | insert_new_range(pid, new_start, new_end - new_start); 171 | } 172 | 173 | void AddressRangeFilter::clear(void) 174 | { 175 | rmap.clear(); 176 | } 177 | 178 | void AddressRangeFilter::show(void) 179 | { 180 | auto it = rmap.begin(); 181 | 182 | while (it != rmap.end()) { 183 | Key k = it->first; 184 | Range *r = &it->second; 185 | 186 | printf("pid: %d, start: %ld, size: %ld\n", (int)k.pid, k.start, r->size); 187 | it++; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /AddressRangeFilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #ifndef __ADDRESS_RANGE_FILTER__HH__ 11 | #define __ADDRESS_RANGE_FILTER__HH__ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | class AddressRangeFilter 19 | { 20 | struct Range { 21 | unsigned long size; 22 | }; 23 | 24 | class Key 25 | { 26 | public: 27 | unsigned long pid; 28 | unsigned long start; 29 | 30 | public: 31 | Key(unsigned long p, unsigned long s) : 32 | pid(p), start(s) {} 33 | 34 | Key(void) : 35 | pid(0), start(0) {} 36 | 37 | bool operator<(const Key& src) const 38 | { 39 | if (pid == src.pid) 40 | return start < src.start; 41 | else 42 | return pid < src.pid; 43 | } 44 | }; 45 | 46 | typedef std::map::iterator Iterator; 47 | 48 | public: 49 | bool search_address(int pid, unsigned long addr); 50 | void insert_range(int pid, unsigned long start, unsigned long size); 51 | void clear(void); 52 | void show(void); 53 | 54 | private: 55 | Iterator search_range(int pid, unsigned long addr, 56 | Iterator *lower_p, Iterator *upper_p); 57 | void insert_new_range(int pid, unsigned long start, unsigned long size); 58 | void remove_ranges(Iterator rm_start, Iterator rm_end, 59 | int pid, unsigned long new_start, unsigned long new_end); 60 | 61 | private: 62 | std::map rmap; 63 | }; 64 | 65 | #endif /* __ADDRESS_RANGE_FILTER__HH__ */ 66 | -------------------------------------------------------------------------------- /BandwidthLimit.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Yao Yuan 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "BandwidthLimit.h" 16 | #include "lib/stats.h" 17 | 18 | const float BandwidthLimit::MAX_TIME_HISTORY = 3; 19 | 20 | void BandwidthLimit::add_and_sleep(unsigned long bytes) 21 | { 22 | float time_delta; 23 | timeval cur_time; 24 | 25 | if (0 == bwlimit_byteps) 26 | return; 27 | 28 | if (!last_time.tv_sec && !last_time.tv_usec) { 29 | allow_bytes = 0; 30 | gettimeofday(&last_time, NULL); 31 | return; 32 | } 33 | 34 | std::unique_lock lock(mlock); 35 | 36 | gettimeofday(&cur_time, NULL); 37 | time_delta = tv_secs(last_time, cur_time); 38 | if (time_delta > MAX_TIME_HISTORY) 39 | time_delta = MAX_TIME_HISTORY; 40 | 41 | last_time = cur_time; 42 | 43 | allow_bytes += time_delta * bwlimit_byteps; 44 | allow_bytes -= bytes; 45 | 46 | if (allow_bytes < 0) { 47 | float sleep_time; 48 | sleep_time = ((-allow_bytes) / bwlimit_byteps) * 1000000; 49 | //printf("thread will sleep %f microseconds\n", sleep_time); 50 | usleep(sleep_time); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BandwidthLimit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Yao Yuan 7 | * Fengguang Wu 8 | */ 9 | 10 | #ifndef _BANDWIDTH_LIMIT_H 11 | #define _BANDWIDTH_LIMIT_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | class BandwidthLimit 18 | { 19 | public: 20 | void set_bwlimit_mbps(float mbps) 21 | { bwlimit_byteps = (mbps * 1024 * 1024) / 8; } 22 | 23 | void add_and_sleep(unsigned long bytes); 24 | 25 | private: 26 | unsigned long bwlimit_byteps = 0; 27 | long allow_bytes = 0; 28 | 29 | timeval last_time = {0,0}; 30 | 31 | std::mutex mlock; 32 | 33 | // second unit 34 | static const float MAX_TIME_HISTORY; 35 | }; 36 | 37 | #endif 38 | // vim:set ts=2 sw=2 et: 39 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The memory-optimizer tools are provided under: 2 | 3 | SPDX-License-Identifier: GPL-2.0 4 | 5 | Being under the terms of the GNU General Public License version 2 only, 6 | according with: 7 | 8 | doc/GPL-2.0 9 | 10 | With explicit exceptions listed below: 11 | 12 | Queue.h BSD-2-Clause 13 | -------------------------------------------------------------------------------- /EPTMigrate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Yao Yuan 7 | * Fengguang Wu 8 | */ 9 | 10 | #ifndef _MIGRATION_H 11 | #define _MIGRATION_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "Option.h" 20 | #include "Formatter.h" 21 | #include "MovePages.h" 22 | #include "ProcVmstat.h" 23 | #include "ProcIdlePages.h" 24 | #include "EPTScan.h" 25 | #include "PidContext.h" 26 | 27 | class BandwidthLimit; 28 | class NumaNodeCollection; 29 | class ProcVmstat; 30 | 31 | struct MigrateStats: public MoveStats 32 | { 33 | unsigned long anon_kb; 34 | 35 | void clear(); 36 | void add(MigrateStats *s); 37 | void show(Formatter& fmt, MigrateWhat mwhat); 38 | void show_move_result_state(Formatter& fmt); 39 | }; 40 | 41 | class EPTMigrate : public EPTScan 42 | { 43 | public: 44 | int dram_percent; 45 | 46 | EPTMigrate(); 47 | 48 | int migrate(); 49 | int migrate(ProcIdlePageType type); 50 | 51 | void set_throttler(BandwidthLimit* new_throttler) 52 | { migrator.set_throttler(new_throttler); } 53 | 54 | void set_numacollection(NumaNodeCollection* new_numa_collection) { 55 | numa_collection = new_numa_collection; 56 | migrator.set_numacollection(new_numa_collection); 57 | } 58 | 59 | void set_pid_context(PidContext *new_context) 60 | { context = new_context; } 61 | 62 | static void reset_sys_migrate_stats(); 63 | void count_migrate_stats(); 64 | 65 | MigrateStats& get_migrate_stats() { 66 | return migrate_stats; 67 | } 68 | 69 | private: 70 | size_t get_threshold_refs(ProcIdlePageType type, int& min_refs, int& max_refs); 71 | 72 | // select max counted pages in page_refs_4k and page_refs_2m 73 | int select_top_pages(ProcIdlePageType type); 74 | 75 | long do_move_pages(ProcIdlePageType type); 76 | 77 | // status => count 78 | std::unordered_map calc_migrate_stats(); 79 | 80 | unsigned long calc_numa_anon_capacity(ProcIdlePageType type, ProcVmstat& proc_vmstat); 81 | 82 | public: 83 | static MigrateStats sys_migrate_stats; 84 | 85 | private: 86 | // The Virtual Address of hot/cold pages. 87 | // [0...n] = [VA0...VAn] 88 | //std::vector hot_pages; 89 | std::vector pages_addr[PMD_ACCESSED + 1]; 90 | 91 | // Get the status after migration 92 | std::vector migrate_status; 93 | 94 | MigrateStats migrate_stats; 95 | MovePages migrator; 96 | NumaNodeCollection* numa_collection; 97 | 98 | Formatter fmt; 99 | PidContext *context = NULL; 100 | }; 101 | 102 | #endif 103 | // vim:set ts=2 sw=2 et: 104 | -------------------------------------------------------------------------------- /EPTScan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #ifndef AEP_EPT_SCAN_H 10 | #define AEP_EPT_SCAN_H 11 | 12 | #include 13 | 14 | #include "ProcIdlePages.h" 15 | 16 | class EPTScan: public ProcIdlePages 17 | { 18 | public: 19 | void prepare_walks(int max_walks); 20 | int walk_multi(int nr, float interval); 21 | void gather_walk_stats(unsigned long& young_bytes, 22 | unsigned long& top_bytes, 23 | unsigned long& all_bytes); 24 | 25 | static void reset_sys_refs_count(int nr_walks); 26 | 27 | void count_refs(); 28 | static int save_counts(std::string filename); 29 | 30 | private: 31 | bool should_stop(); 32 | void count_refs_one(ProcIdleRefs& prc); 33 | 34 | private: 35 | static std::vector sys_refs_count[MAX_ACCESSED + 1]; 36 | }; 37 | 38 | #endif 39 | // vim:set ts=2 sw=2 et: 40 | -------------------------------------------------------------------------------- /ExclusiveRun.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | class ExclusiveRun 11 | def initialize(file) 12 | unless File.new(file, File::RDWR|File::CREAT).flock(File::LOCK_NB|File::LOCK_EX) 13 | puts "Failed to grab #{file} -- check parallel runs?" 14 | exit 15 | end 16 | 17 | yield 18 | 19 | File.delete file 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Formatter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #ifndef AEP_FORMATTER_H 10 | #define AEP_FORMATTER_H 11 | 12 | // source: https://stackoverflow.com/questions/12247646/c-delay-printf-until-needed 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | class Formatter 19 | { 20 | private: 21 | std::string buf; 22 | 23 | public: 24 | 25 | void clear() { buf.clear(); } 26 | bool empty() { return buf.empty(); } 27 | void reserve(size_t capacity) { buf.reserve(capacity); } 28 | std::string const & str() const { return buf; } 29 | 30 | void print(char const * fmt, ...) 31 | { 32 | int ret1; 33 | int ret2; 34 | std::size_t cur; 35 | std::va_list ap, aq; 36 | va_start(ap, fmt); 37 | va_copy(aq, ap); 38 | 39 | ret1 = std::vsnprintf(NULL, 0, fmt, ap); 40 | if (ret1 < 0) 41 | goto out; 42 | 43 | cur = buf.size(); 44 | buf.resize(cur + ret1 + 1); 45 | 46 | ret2 = std::vsnprintf(&buf[cur], ret1 + 1, fmt, aq); 47 | if (ret2 < 0) 48 | goto out; 49 | buf.resize(cur + ret1); 50 | 51 | out: 52 | va_end(aq); 53 | va_end(ap); 54 | } 55 | }; 56 | 57 | #endif 58 | // vim:set ts=2 sw=2 et: 59 | -------------------------------------------------------------------------------- /GlobalScan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef AEP_GLOBAL_SCAN_H 11 | #define AEP_GLOBAL_SCAN_H 12 | 13 | #include 14 | #include 15 | 16 | #include "Queue.h" 17 | #include "Process.h" 18 | #include "EPTMigrate.h" 19 | #include "BandwidthLimit.h" 20 | #include "Sysfs.h" 21 | #include "Numa.h" 22 | 23 | enum JobIntent 24 | { 25 | JOB_WALK, 26 | JOB_MIGRATE, 27 | JOB_QUIT, 28 | }; 29 | 30 | typedef std::shared_ptr EPTMigratePtr; 31 | 32 | struct Job 33 | { 34 | EPTMigratePtr migration; 35 | JobIntent intent; 36 | }; 37 | 38 | class GlobalScan 39 | { 40 | public: 41 | GlobalScan(); 42 | 43 | void main_loop(); 44 | void create_threads(); 45 | void stop_threads(); 46 | 47 | int collect(); 48 | void walk_multi(); 49 | void migrate(); 50 | void count_refs(); 51 | void count_migrate_stats(); 52 | void update_interval(bool finished); 53 | void request_reload_conf(); 54 | void apply_option(); 55 | 56 | private: 57 | void consumer_loop(); 58 | int consumer_job(Job& job); 59 | void walk_once(); 60 | bool should_stop_walk(); 61 | void update_dram_free_anon_bytes(); 62 | void reload_conf(); 63 | bool exit_on_stabilized(); 64 | bool exit_on_exceeded(); 65 | bool check_exit_on_exceeded(pid_t pid); 66 | void update_pid_context(); 67 | 68 | unsigned long accept_hot_bytes() { return dram_hot_target * 12 / 8; } 69 | unsigned long target_young_bytes() { return dram_hot_target * 10 / 8; } 70 | unsigned long target_hot_bytes() { return dram_hot_target; } 71 | 72 | unsigned long get_dram_free_and_anon_bytes() 73 | { return get_dram_anon_bytes(true) << PAGE_SHIFT; } 74 | 75 | unsigned long get_dram_anon_bytes(bool is_include_free); 76 | 77 | unsigned long calc_migrated_bytes(); 78 | void show_migrate_speed(float delta_time); 79 | bool is_all_migration_done(); 80 | 81 | private: 82 | static const float MIN_INTERVAL; 83 | static const float MAX_INTERVAL; 84 | unsigned int nround; 85 | int nr_walks; 86 | int nr_acceptable_scans; 87 | float interval; 88 | float real_interval; 89 | struct timeval last_scan_start; 90 | unsigned long young_bytes; 91 | unsigned long top_bytes; 92 | unsigned long all_bytes; 93 | unsigned long dram_free_anon_bytes; 94 | unsigned long dram_hot_target; 95 | 96 | ProcessCollection process_collection; 97 | std::vector> idle_ranges; 98 | std::vector worker_threads; 99 | Queue work_queue; 100 | Queue done_queue; 101 | 102 | std::atomic_int conf_reload_flag; 103 | 104 | BandwidthLimit throttler; 105 | NumaNodeCollection numa_collection; 106 | ProcVmstat proc_vmstat; 107 | Sysfs sysfs; 108 | }; 109 | 110 | #endif 111 | // vim:set ts=2 sw=2 et: 112 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (c) 2018 Intel Corporation 5 | # 6 | # Authors: Fengguang Wu 7 | # Yao Yuan 8 | # Bo Peng 9 | # Liu Jingqi 10 | # Huang Ying 11 | # 12 | 13 | CC = gcc 14 | CXX = g++ 15 | RM = rm 16 | DEBUG_FLAGS = -g -fsanitize=address -static-libasan 17 | DEBUG_FLAGS = -g -O3 18 | CFLAGS = $(DEBUG_FLAGS) -Wall 19 | CXXFLAGS = $(DEBUG_FLAGS) -Wall --std=c++11 20 | LIB_SOURCE_FILES = lib/memparse.c lib/iomem_parse.c lib/page-types.c 21 | TASK_REFS_SOURCE_FILES = Option.cc ProcIdlePages.cc ProcMaps.cc ProcVmstat.cc EPTMigrate.cc AddrSequence.cc \ 22 | MovePages.cc VMAInspect.cc EPTScan.cc BandwidthLimit.cc Numa.cc \ 23 | lib/debug.c lib/stats.h Formatter.h lib/memparse.c lib/memparse.h 24 | TASK_REFS_HEADER_FILES = $(TASK_REFS_SOURCE_FILES:.cc=.h) 25 | SYS_REFS_SOURCE_FILES = $(TASK_REFS_SOURCE_FILES) ProcPid.cc ProcStatus.cc Process.cc GlobalScan.cc Queue.h \ 26 | OptionParser.cc Sysfs.cc 27 | SYS_REFS_HEADER_FILES = $(SYS_REFS_SOURCE_FILES:.cc=.h) 28 | 29 | OBJS = sys-refs page-refs task-maps show-vmstat addr-seq task-refs pid-list 30 | all: $(OBJS) 31 | [ -x ./update ] && ./update || true 32 | 33 | sys-refs: sys-refs.cc $(SYS_REFS_SOURCE_FILES) $(SYS_REFS_HEADER_FILES) 34 | ./get_version.sh 35 | $(CXX) $< $(SYS_REFS_SOURCE_FILES) -o $@ $(CXXFLAGS) -lnuma -pthread -lyaml-cpp 36 | 37 | page-refs: page-refs.c $(LIB_SOURCE_FILES) 38 | $(CC) $< $(LIB_SOURCE_FILES) -o $@ $(CFLAGS) 39 | 40 | task-refs: task-refs.cc $(TASK_REFS_SOURCE_FILES) $(TASK_REFS_HEADER_FILES) 41 | ./get_version.sh 42 | $(CXX) $< $(TASK_REFS_SOURCE_FILES) -o $@ $(CXXFLAGS) -lnuma 43 | 44 | task-maps: task-maps.cc ProcMaps.cc ProcMaps.h 45 | $(CXX) $< ProcMaps.cc -o $@ $(CXXFLAGS) 46 | 47 | show-vmstat: show-vmstat.cc ProcVmstat.cc 48 | $(CXX) $< ProcVmstat.cc -o $@ $(CXXFLAGS) -lnuma 49 | 50 | addr-seq: AddrSequence.cc AddrSequence.h 51 | $(CXX) AddrSequence.cc -o $@ $(CXXFLAGS) -DADDR_SEQ_SELF_TEST 52 | 53 | pid-list: ProcPid.cc ProcPid.h ProcStatus.cc ProcStatus.h 54 | $(CXX) ProcPid.cc ProcStatus.cc -o $@ $(CXXFLAGS) -DPID_LIST_SELF_TEST 55 | 56 | cscope: 57 | cscope-indexer -r 58 | ctags -R --links=no 59 | clean: 60 | $(RM) $(OBJS) 61 | -------------------------------------------------------------------------------- /MovePages.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef _MOVE_PAGES_H 11 | #define _MOVE_PAGES_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "ProcIdlePages.h" 18 | 19 | class BandwidthLimit; 20 | class Formatter; 21 | class NumaNodeCollection; 22 | class NumaNode; 23 | 24 | #define MPOL_MF_SW_YOUNG (1<<7) 25 | 26 | typedef std::unordered_map MovePagesStatusCount; 27 | class NumaNodeCollection; 28 | class PidContext; 29 | 30 | struct MoveStats 31 | { 32 | unsigned long to_move_kb; 33 | unsigned long skip_kb; 34 | unsigned long move_kb; 35 | 36 | const unsigned int from_shift = 0; 37 | const unsigned int to_shift = 8; 38 | const unsigned int result_shift = 16; 39 | MovePagesStatusCount move_page_status; 40 | 41 | MoveStats() { clear(); } 42 | void clear(); 43 | void add(MoveStats *s); 44 | void account(MovePagesStatusCount& status_count, int page_shift, int target_node); 45 | void save_move_states(int status, 46 | int target_nodes, 47 | int status_after_move, 48 | unsigned long page_shift); 49 | void show_move_state(Formatter& fmt); 50 | unsigned long get_moved_bytes(); 51 | 52 | static int default_failed; 53 | static bool is_page_moved(int from, int to, int move_state) 54 | { return from >= 0 && from != to && to == move_state; } 55 | 56 | static bool is_page_move_failed(int from, int to, int move_state) 57 | { return from >= 0 && from != to && move_state < 0; } 58 | private: 59 | int box_movestate(int status, int target_node, int result); 60 | void unbox_movestate(int key, 61 | int& status, int& target_node, int& result); 62 | }; 63 | 64 | class MovePages 65 | { 66 | public: 67 | unsigned long dram_kb; 68 | unsigned long pmem_kb; 69 | 70 | MovePages(); 71 | ~MovePages() {}; 72 | 73 | void set_pid(pid_t i) { pid = i; } 74 | void set_page_shift(int t) { page_shift = t; } 75 | void set_batch_size(unsigned long npages) { batch_size = npages; } 76 | void set_flags(int f) { flags = f; } 77 | void set_throttler(BandwidthLimit* new_throttler) { throttler = new_throttler; } 78 | void set_migration_type(ProcIdlePageType new_type) { type = new_type; } 79 | long move_pages(std::vector& addrs, bool is_locate); 80 | long move_pages(void **addrs, std::vector &move_status, 81 | unsigned long count, bool is_locate); 82 | long locate_move_pages(PidContext* pid_context, std::vector& addrs, 83 | MoveStats *stats); 84 | void set_numacollection(NumaNodeCollection* new_collection) 85 | { numa_collection = new_collection; } 86 | 87 | std::vector& get_status() { return status; } 88 | MovePagesStatusCount& get_status_count() { return status_count; } 89 | void clear_status_count() { status_count.clear(); } 90 | void calc_status_count(); 91 | void add_status_count_to(MovePagesStatusCount& status_sum); 92 | void show_status_count(Formatter* fmt, MovePagesStatusCount& status_sum); 93 | void account_stats_count(MoveStats *stats); 94 | void calc_target_nodes(void **addrs, long size); 95 | int get_target_node(NumaNode* node_obj); 96 | bool is_node_in_target_set(int node_id); 97 | long find_last_good(std::vector& status, long end_pos); 98 | void dump_target_nodes(void); 99 | void calc_memory_state(MovePagesStatusCount& status_sum, 100 | unsigned long &total_kb, 101 | unsigned long &total_dram_kb, 102 | unsigned long &total_pmem_kb); 103 | private: 104 | bool is_exceed_dram_quota(PidContext* pid_context); 105 | void dec_dram_quota(PidContext* pid_context, long dec_value); 106 | long calc_and_save_state(MoveStats* stats, 107 | std::vector& status, 108 | std::vector& target_nodes, 109 | std::vector& status_after_move); 110 | private: 111 | pid_t pid; 112 | int flags; 113 | 114 | unsigned long page_shift; 115 | unsigned long batch_size; // used by locate_move_pages() 116 | 117 | // Get the status after migration 118 | std::vector status; 119 | std::vector status_after_move; 120 | std::vector target_nodes; 121 | 122 | MovePagesStatusCount status_count; 123 | 124 | BandwidthLimit* throttler; 125 | 126 | ProcIdlePageType type; 127 | 128 | NumaNodeCollection* numa_collection; 129 | }; 130 | 131 | #endif 132 | // vim:set ts=2 sw=2 et: 133 | -------------------------------------------------------------------------------- /Option.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #include "Option.h" 11 | #include "lib/stats.h" 12 | #include 13 | 14 | std::unordered_map Option::bool_name_map = { 15 | {"true", true}, 16 | {"yes", true}, 17 | {"false", false}, 18 | {"no", false}, 19 | }; 20 | 21 | std::unordered_map Option::migrate_name_map = { 22 | {"none", MIGRATE_NONE}, 23 | {"hot", MIGRATE_HOT}, 24 | {"cold", MIGRATE_COLD}, 25 | {"both", MIGRATE_BOTH}, 26 | }; 27 | 28 | std::unordered_map Option::placement_name_map = { 29 | {"none", PLACEMENT_NONE}, 30 | {"dram", PLACEMENT_DRAM}, 31 | {"pmem", PLACEMENT_PMEM}, 32 | }; 33 | 34 | int Option::set_dram_percent(int dp) 35 | { 36 | if (dp < 0 || dp > 100) { 37 | std::cerr << "dram percent out of range [0, 100]" << std::endl; 38 | return -ERANGE; 39 | } 40 | 41 | dram_percent = dp; 42 | return 0; 43 | } 44 | 45 | void Option::dump() 46 | { 47 | printf("option dump begin:\n"); 48 | printf("debug_level = %d\n", debug_level); 49 | printf("pid = %d\n", pid); 50 | printf("initial_interval = %f\n", initial_interval); 51 | printf("interval = %f\n", interval); 52 | printf("sleep_secs = %f\n", sleep_secs); 53 | printf("nr_walks = %d\n", nr_walks); 54 | printf("max_walks = %d\n", max_walks); 55 | printf("nr_loops = %d\n", nr_loops); 56 | printf("dram_percent = %d\n", dram_percent); 57 | printf("exit_on_stabilized = %d\n", exit_on_stabilized); 58 | printf("exit_on_exceeded = %d\n", (int)exit_on_exceeded); 59 | printf("dump_options = %d\n", (int)dump_options); 60 | printf("dump_processes = %d\n", (int)dump_processes); 61 | printf("hot_min_refs = %d\n", hot_min_refs); 62 | printf("cold_max_refs = %d\n", cold_max_refs); 63 | printf("max_threads = %d\n", max_threads); 64 | printf("split_rss_size = %s\n", split_rss_size.c_str()); 65 | printf("bandwidth_mbps = %g\n", bandwidth_mbps); 66 | printf("migrate_what = %d\n", migrate_what); 67 | printf("output_file = %s\n", output_file.c_str()); 68 | printf("config_file = %s\n", config_file.c_str()); 69 | printf("daemon = %d\n", (int)daemon); 70 | 71 | for (size_t i = 0; i < policies.size(); ++i) { 72 | printf("policy %ld:\n", i); 73 | printf("pid: %d\n", policies[i].pid); 74 | printf("name: %s\n", policies[i].name.c_str()); 75 | printf("migration: %d\n", policies[i].migrate_what); 76 | printf("placement: %d\n", policies[i].placement); 77 | printf("dump_distribution: %d\n", policies[i].dump_distribution); 78 | printf("\n"); 79 | } 80 | 81 | printf("Numa HW Config V2:\n"); 82 | for(auto &numa_config_item : numa_hw_config_v2) { 83 | std::string id = "unknown"; 84 | std::string type = "unknown"; 85 | std::string link_to = "unknown"; 86 | 87 | find_map(numa_config_item, "id", id); 88 | find_map(numa_config_item, "type", type); 89 | find_map(numa_config_item, "promote_to", link_to); 90 | find_map(numa_config_item, "demote_to", link_to); 91 | 92 | printf("id: %s type: %s linkto: %s\n", 93 | id.c_str(), type.c_str(), link_to.c_str()); 94 | } 95 | 96 | printf("option dump end.\n"); 97 | } 98 | 99 | MigrateWhat Option::parse_migrate_name(std::string name) 100 | { 101 | MigrateWhat ret_val = MIGRATE_NONE; 102 | 103 | parse_name_map(migrate_name_map, name, ret_val, MIGRATE_END); 104 | 105 | return ret_val; 106 | } 107 | 108 | int Option::add_policy(Policy& new_policy) 109 | { 110 | // a policy should have at least pid or name 111 | if (new_policy.pid < 0 112 | && new_policy.name.empty()) { 113 | std::cerr << "invalid policy: no pid and no name" << std::endl; 114 | return -1; 115 | } 116 | 117 | policies.push_back(new_policy); 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /Option.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef _OPTION_H 11 | #define _OPTION_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | typedef enum { 19 | MIGRATE_NONE, 20 | MIGRATE_HOT, 21 | MIGRATE_COLD, 22 | MIGRATE_BOTH = MIGRATE_HOT | MIGRATE_COLD, 23 | MIGRATE_END, 24 | } MigrateWhat; 25 | 26 | 27 | typedef enum { 28 | PLACEMENT_NONE, 29 | PLACEMENT_DRAM, // skip scan, assuming mlock'ed in DRAM 30 | PLACEMENT_PMEM, // no effect for now 31 | PLACEMENT_END, 32 | } Placement; 33 | 34 | 35 | struct Policy 36 | { 37 | Policy() { 38 | pid = -1; 39 | migrate_what = MIGRATE_NONE; 40 | placement = PLACEMENT_NONE; 41 | dump_distribution = false; 42 | } 43 | 44 | pid_t pid; 45 | std::string name; 46 | MigrateWhat migrate_what; 47 | Placement placement; 48 | bool dump_distribution; 49 | }; 50 | typedef std::vector PolicySet; 51 | 52 | 53 | struct NumaHWConfig{ 54 | bool is_valid() { 55 | return numa_dram_list.size() 56 | || numa_pmem_list.size() 57 | || pmem_dram_map.size(); 58 | } 59 | std::string numa_dram_list; 60 | std::string numa_pmem_list; 61 | std::string pmem_dram_map; 62 | }; 63 | 64 | typedef std::unordered_map NumaHWConfigEntry; 65 | typedef std::vector NumaHWConfigV2; 66 | 67 | struct Option 68 | { 69 | int set_dram_percent(int dp); 70 | 71 | int add_policy(Policy& new_policy); 72 | PolicySet& get_policies() { 73 | return policies; 74 | } 75 | 76 | void dump(); 77 | 78 | static MigrateWhat parse_migrate_name(std::string name); 79 | 80 | template 81 | static int parse_str_from_map(Tmap& map, std::string &name, Tval& val) 82 | { 83 | auto search = map.find(name); 84 | if (search != map.end()) { 85 | val = search->second; 86 | return 0; 87 | } 88 | return -1; 89 | } 90 | 91 | template 92 | static int parse_name_map(Tmap& map, std::string name, Tval& val, int max_val) 93 | { 94 | if (isdigit(name[0])) { 95 | int m = atoi(name.c_str()); 96 | if (m >= max_val) { 97 | std::cerr << "invalid value: " << name << std::endl; 98 | return -1; 99 | } 100 | val = (Tval) m; 101 | return 0; 102 | } 103 | 104 | if (parse_str_from_map(map, name, val) < 0) { 105 | std::cerr << "invalid value: " << name << std::endl; 106 | return -2; 107 | } 108 | 109 | return 0; 110 | } 111 | 112 | public: 113 | int debug_level = 0; 114 | 115 | static const int DRAM_NUMA_NODE = 0; 116 | static const int PMEM_NUMA_NODE = 1; 117 | 118 | static std::unordered_map bool_name_map; 119 | static std::unordered_map migrate_name_map; 120 | static std::unordered_map placement_name_map; 121 | 122 | pid_t pid = -1; 123 | 124 | float initial_interval = 0.1; 125 | float interval = 0; // auto adjust 126 | float sleep_secs = 1; 127 | int max_walks = 10; 128 | int nr_walks = 0; // auto stop when nr_top_pages can fit in half DRAM size 129 | int nr_loops = 0; 130 | 131 | // set either dram_percent or hot_min_refs/cold_max_refs, but not both 132 | int dram_percent = 0; 133 | int hot_min_refs = -1; 134 | int cold_max_refs = -1; 135 | 136 | int exit_on_stabilized = 0; // percent moved 137 | bool exit_on_exceeded = false; // when exceed dram_percent 138 | bool dump_options = false; 139 | bool dump_processes = false; 140 | 141 | int hugetlb = 0; 142 | int thp = 0; 143 | 144 | int max_threads = 0; 145 | std::string split_rss_size; // no split task address space 146 | 147 | float bandwidth_mbps = 0; 148 | MigrateWhat migrate_what = MIGRATE_HOT; 149 | 150 | std::string output_file; 151 | std::string config_file; 152 | 153 | NumaHWConfig numa_hw_config; 154 | NumaHWConfigV2 numa_hw_config_v2; 155 | 156 | int debug_move_pages = 0; 157 | 158 | bool daemon = false; 159 | bool show_numa_stats = false; 160 | // Not used for now, so current sys-refs behavior is to ignore all processes 161 | // w/o a policy defined. In future, may consider applying this to all 162 | // processes in ProcessCollection::collect(). 163 | // Policy default_policy; 164 | 165 | private: 166 | PolicySet policies; 167 | }; 168 | 169 | #endif 170 | // vim:set ts=2 sw=2 et: 171 | -------------------------------------------------------------------------------- /OptionParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef __OPTION_PARSER_H__ 11 | #define __OPTION_PARSER_H__ 12 | 13 | #include 14 | #include 15 | #include "Option.h" 16 | 17 | // OptionParser only adds parsing functions. 18 | // 19 | // All data members and accessing functions are in Option, 20 | // for easy access by other classes. 21 | // 22 | class OptionParser: public Option 23 | { 24 | public: 25 | OptionParser(); 26 | ~OptionParser(); 27 | 28 | int parse_file(std::string filename); 29 | int reparse(); 30 | 31 | private: 32 | int parse_option(YAML::Node &&option_node); 33 | int parse_policies(YAML::Node &&policies_node); 34 | void parse_one_policy(YAML::Node &&policy_node); 35 | void parse_common_policy(const YAML::const_iterator& iter, Policy& policy); 36 | void parse_numa_nodes(YAML::Node &numa_nodes); 37 | void parse_one_numa_node(YAML::Node &one_numa_node, NumaHWConfigEntry &one_entry); 38 | 39 | template 40 | int get_value(const YAML::const_iterator &iter, 41 | const char* key_name, Tval &value); 42 | //specialize for YAML::Node as value type 43 | int get_value(const YAML::const_iterator &iter, 44 | const char* key_name, YAML::Node &value); 45 | 46 | }; 47 | 48 | #endif 49 | // vim:set ts=2 sw=2 et: 50 | -------------------------------------------------------------------------------- /PidContext.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Yuan Yao 7 | * 8 | */ 9 | 10 | #ifndef _PIDCONTEXT_H_ 11 | #define _PIDCONTEXT_H_ 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | class PidContext 19 | { 20 | public: 21 | void add_dram_quota(long value) 22 | { dram_quota += value; } 23 | 24 | void sub_dram_quota(long value) 25 | { dram_quota -= value; } 26 | 27 | long get_dram_quota(void) 28 | { return dram_quota.load(std::memory_order_acquire); } 29 | 30 | void set_dram_quota(long value) 31 | { dram_quota.store(value, std::memory_order_release); } 32 | 33 | void set_pid(pid_t value) 34 | { pid = value; } 35 | 36 | pid_t get_pid(void) 37 | { return pid; } 38 | 39 | private: 40 | std::atomic_long dram_quota = {0}; 41 | pid_t pid = -1; 42 | }; 43 | 44 | 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /ProcIdlePages.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | * Liu Jingqi 9 | */ 10 | 11 | #ifndef AEP_PROC_IDLE_PAGES_H 12 | #define AEP_PROC_IDLE_PAGES_H 13 | 14 | // interface to /proc/PID/idle_pages 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "ProcMaps.h" 21 | #include "AddrSequence.h" 22 | #include "Option.h" 23 | 24 | static const unsigned long PTE_SIZE = 1UL << 12; 25 | static const unsigned long PMD_SIZE = 1UL << 21; 26 | static const unsigned long PUD_SIZE = 1UL << 30; 27 | static const unsigned long P4D_SIZE = 1UL << 39; 28 | static const unsigned long PGDIR_SIZE = 1UL << 39; 29 | static const unsigned long TASK_SIZE_MAX = (1UL << 47) - PAGE_SIZE; 30 | 31 | #define SCAN_HUGE_PAGE O_NONBLOCK 32 | #define SCAN_SKIM_IDLE O_NOFOLLOW 33 | 34 | enum ProcIdlePageType 35 | { 36 | PTE_ACCESSED, /* 4k page */ 37 | PMD_ACCESSED, /* 2M page */ 38 | PUD_PRESENT, /* 1G page */ 39 | MAX_ACCESSED = PUD_PRESENT, 40 | 41 | PTE_DIRTY, /* 3 */ 42 | PMD_DIRTY, 43 | 44 | PTE_IDLE, /* 5 */ 45 | PMD_IDLE, 46 | PMD_IDLE_PTES, /* all PTE idle */ 47 | 48 | PTE_HOLE, /* 8 */ 49 | PMD_HOLE, 50 | 51 | PIP_CMD, /* 10 */ 52 | 53 | IDLE_PAGE_TYPE_MAX 54 | }; 55 | 56 | #define PIP_TYPE(a) (0xf & (a >> 4)) 57 | #define PIP_SIZE(a) (0xf & a) 58 | #define PIP_COMPOSE(type, nr) ((type << 4) | nr) 59 | 60 | #define PIP_CMD_SET_HVA PIP_COMPOSE(PIP_CMD, 0) 61 | 62 | #define EPT_IDLE_BUF_MIN ((sizeof(uint64_t) + 2) * 2) 63 | 64 | extern unsigned long pagetype_size[IDLE_PAGE_TYPE_MAX]; 65 | extern const char* pagetype_name[IDLE_PAGE_TYPE_MAX]; 66 | extern int pagetype_shift[IDLE_PAGE_TYPE_MAX]; 67 | extern int pagetype_index[]; 68 | extern int pagetype_batchsize[IDLE_PAGE_TYPE_MAX]; 69 | 70 | typedef std::unordered_map page_refs_map; 71 | 72 | struct ProcIdleRefs 73 | { 74 | // VA => refs 75 | // accumulated by walk() 76 | AddrSequence page_refs; 77 | 78 | // refs => page count 79 | // accumulated by count_refs() 80 | std::vector refs_count; 81 | }; 82 | 83 | class ProcIdlePages 84 | { 85 | public: 86 | ProcIdlePages(); 87 | 88 | void set_pid(pid_t i) { pid = i; } 89 | pid_t get_pid() { return pid; } 90 | 91 | void set_va_range(unsigned long start, unsigned long end); 92 | void set_policy(Policy &pol); 93 | 94 | int walk(); 95 | int has_io_error() const { return io_error; } 96 | 97 | ProcIdleRefs& get_pagetype_refs(ProcIdlePageType type) 98 | { return pagetype_refs[pagetype_index[type]]; } 99 | 100 | int get_nr_walks() { return nr_walks; } 101 | 102 | private: 103 | int walk_vma(proc_maps_entry& vma); 104 | 105 | int open_file(void); 106 | 107 | uint64_t u8_to_u64(uint8_t a[]); 108 | void parse_idlepages(proc_maps_entry& vma, 109 | unsigned long& va, 110 | unsigned long end, 111 | int bytes); 112 | void dump_idlepages(proc_maps_entry& vma, int bytes); 113 | 114 | void inc_page_refs(ProcIdlePageType type, int nr, 115 | unsigned long va, unsigned long end); 116 | 117 | unsigned long va_to_offset(unsigned long va); 118 | unsigned long offset_to_va(unsigned long offset); 119 | 120 | protected: 121 | pid_t pid; 122 | Policy policy; 123 | 124 | ProcMaps proc_maps; 125 | unsigned long va_start; 126 | unsigned long va_end; 127 | 128 | // if negative, indicates exited process; 129 | // if positive, indicates skipped process 130 | int io_error; 131 | 132 | protected: 133 | int nr_walks; 134 | ProcIdleRefs pagetype_refs[MAX_ACCESSED + 1]; 135 | 136 | private: 137 | static const int READ_BUF_SIZE = 1 << 20; 138 | 139 | int idle_fd; 140 | std::vector read_buf; 141 | 142 | unsigned long min_read_size; 143 | unsigned long next_va; 144 | }; 145 | 146 | #endif 147 | // vim:set ts=2 sw=2 et: 148 | -------------------------------------------------------------------------------- /ProcMaps.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "ProcMaps.h" 17 | 18 | 19 | #if 1 20 | static int parse_proc_maps(pid_t pid, std::vector& maps) 21 | { 22 | char filename[PATH_MAX]; 23 | proc_maps_entry e; 24 | char line[4096]; 25 | int path_index; 26 | FILE *f; 27 | int ret = 0; 28 | 29 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 30 | 31 | f = fopen(filename, "r"); 32 | if (!f) { 33 | perror(filename); 34 | return -1; 35 | } 36 | 37 | while (fgets(line, sizeof(line), f)) 38 | { 39 | ret = sscanf(line, "%lx-%lx %4s %lx %x:%x %lu%*[ ]%n", 40 | &e.start, 41 | &e.end, 42 | e.perms, 43 | &e.offset, 44 | (unsigned int*)&e.dev_major, 45 | (unsigned int*)&e.dev_minor, 46 | &e.ino, 47 | &path_index); 48 | 49 | if (ret < 7) 50 | { 51 | fprintf(stderr, "parse failed: %d %s\n%s", ret, filename, line); 52 | ret = -EINVAL; 53 | break; 54 | } 55 | 56 | e.read = (e.perms[0] == 'r'); 57 | e.write = (e.perms[1] == 'w'); 58 | e.exec = (e.perms[2] == 'x'); 59 | e.mayshare = (e.perms[3] != 'p'); 60 | 61 | e.path = line + path_index; 62 | e.path.pop_back(); // trim trailing '\n' 63 | 64 | maps.push_back(e); 65 | } 66 | 67 | fclose(f); 68 | 69 | return ret; 70 | } 71 | 72 | #else 73 | /* 74 | reimplement this because the it can not handle heap area before 75 | */ 76 | static int parse_proc_maps(pid_t pid, std::vector& maps) 77 | { 78 | std::ifstream input_file; 79 | std::string each_line, str_value[6]; 80 | size_t each_split, split_array[7]; 81 | int i, ret; 82 | char filename[PATH_MAX]; 83 | proc_maps_entry e; 84 | 85 | ret = -1; 86 | 87 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 88 | 89 | input_file.open(filename, std::ios_base::in); 90 | while (std::getline(input_file, each_line)) 91 | { 92 | split_array[0] = 0; 93 | 94 | for (i = 1; i < 6; ++i) 95 | { 96 | each_split = each_line.find_first_of(" ", split_array[i-1]); 97 | 98 | if (std::string::npos == each_split) 99 | {break;} 100 | 101 | //+1 for begin from next non-space item 102 | split_array[i] = each_split + 1; 103 | 104 | //-1 because we have +1 above 105 | str_value[i-1] = each_line.substr(split_array[i-1], split_array[i] - split_array[i-1] - 1); 106 | } 107 | 108 | split_array[i] = each_line.find_first_not_of(" ", split_array[i-1]); 109 | if (std::string::npos != split_array[i]) 110 | { 111 | str_value[i-1] = each_line.substr(split_array[i], std::string::npos); 112 | } 113 | else 114 | { 115 | str_value[i-1] = "Anonymous"; 116 | } 117 | 118 | sscanf(str_value[0].c_str(), "%lx-%lx", &e.start, &e.end); 119 | sscanf(str_value[2].c_str(), "%lx", &e.offset); 120 | sscanf(str_value[3].c_str(), "%d:%d", &e.dev_major, &e.dev_minor); 121 | sscanf(str_value[4].c_str(), "%lu", &e.ino); 122 | 123 | e.read = (str_value[1][0] == 'r'); 124 | e.write = (str_value[1][1] == 'w'); 125 | e.exec = (str_value[1][2] == 'x'); 126 | e.mayshare = (str_value[1][3] != 'p'); 127 | e.path = str_value[5]; 128 | 129 | maps.push_back(e); 130 | 131 | ret = 0; 132 | } 133 | 134 | input_file.close(); 135 | 136 | return ret; 137 | } 138 | 139 | #endif 140 | 141 | std::vector ProcMaps::load(pid_t pid) 142 | { 143 | std::vector maps; 144 | 145 | parse_proc_maps(pid, maps); 146 | 147 | return maps; 148 | } 149 | 150 | void ProcMaps::show(const proc_maps_entry& vma) 151 | { 152 | printf("%lx-%lx %4s %08lx %02x:%02x %-8lu\t\t%s\n", 153 | vma.start, 154 | vma.end, 155 | vma.perms, 156 | vma.offset, 157 | vma.dev_major, 158 | vma.dev_minor, 159 | vma.ino, 160 | vma.path.c_str()); 161 | } 162 | 163 | void ProcMaps::show(const std::vector& maps) 164 | { 165 | for (const proc_maps_entry& vma: maps) 166 | show(vma); 167 | } 168 | 169 | bool ProcMaps::is_anonymous(proc_maps_entry& vma) 170 | { 171 | if (vma.mayshare) 172 | return false; 173 | 174 | if (vma.exec) 175 | return false; 176 | 177 | // 560dd67c9000-560dd67ea000 rw-p 00000000 00:00 0 [heap] 178 | // 7ffdcc7fe000-7ffdcc820000 rw-p 00000000 00:00 0 [stack] 179 | if (vma.ino == 0) 180 | return true; 181 | 182 | // 7f3b6a034000-7f3b6a036000 rw-p 001eb000 00:11 3016841 /lib/x86_64-linux-gnu/libc-2.27.so 183 | if (vma.write) 184 | return true; 185 | 186 | return false; 187 | } 188 | -------------------------------------------------------------------------------- /ProcMaps.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef AEP_PROC_MAPS_H 11 | #define AEP_PROC_MAPS_H 12 | 13 | // interface to /proc/PID/maps 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | struct proc_maps_entry 21 | { 22 | unsigned long start; 23 | unsigned long end; 24 | 25 | char perms[5]; 26 | bool read; 27 | bool write; 28 | bool exec; 29 | bool mayshare; 30 | 31 | unsigned long offset; 32 | int dev_major; 33 | int dev_minor; 34 | unsigned long ino; 35 | 36 | std::string path; 37 | }; 38 | 39 | class ProcMaps 40 | { 41 | public: 42 | std::vector load(pid_t pid); 43 | void show(const std::vector& maps); 44 | void show(const proc_maps_entry &vma); 45 | bool is_anonymous(proc_maps_entry& vma); 46 | }; 47 | 48 | #endif 49 | // vim:set ts=2 sw=2 et: 50 | -------------------------------------------------------------------------------- /ProcNumaMaps.rb: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (c) 2018 Intel Corporation 5 | # 6 | # Authors: Fengguang Wu 7 | # 8 | 9 | class ProcNumaMaps 10 | 11 | attr_reader :numa_maps 12 | 13 | def load(pid) 14 | @numa_maps = Hash.new 15 | @numa_kb = nil 16 | File.open("/proc/#{pid}/numa_maps").each do |line| 17 | fields = line.chomp.split 18 | addr = fields.shift.hex 19 | mpol = fields.shift 20 | pairs = Hash.new 21 | fields.each do |field| 22 | key, val = field.split '=' 23 | if val 24 | val = val.to_i if val =~ /^[0-9]+$/ 25 | pairs[key] = val 26 | else 27 | # handle heap, stack 28 | end 29 | end 30 | @numa_maps[addr] = pairs 31 | end 32 | end 33 | 34 | def numa_kb 35 | @numa_kb ||= calc_numa_kb 36 | end 37 | 38 | def calc_numa_kb 39 | numa_kb = Hash.new 40 | @numa_maps.each do |k, v| 41 | pagesize = v["kernelpagesize_kB"] 42 | next unless pagesize 43 | v.each do |kk, vv| 44 | next if kk == "kernelpagesize_kB" 45 | next if String === vv 46 | numa_kb[kk] ||= 0 47 | numa_kb[kk] += vv * pagesize 48 | end 49 | end 50 | numa_kb 51 | end 52 | 53 | def total_numa_kb 54 | sum = 0 55 | numa_kb.each do |k, v| 56 | next unless k =~ /^N\d+$/ 57 | sum += v; 58 | end 59 | sum 60 | end 61 | 62 | def show_numa_placement 63 | sum = total_numa_kb 64 | return unless sum && sum > 0 65 | 66 | numa_kb.each do |k, v| 67 | next unless k =~ /^N\d+$/ 68 | percent = 100 * v / sum 69 | puts "#{k} #{v >> 10}M #{percent}%" 70 | end 71 | end 72 | 73 | end 74 | -------------------------------------------------------------------------------- /ProcPid.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ProcPid.h" 16 | 17 | std::vector& ProcPid::get_pids() 18 | { 19 | if (pids.empty()) 20 | collect(); 21 | 22 | return pids; 23 | } 24 | 25 | int ProcPid::collect() 26 | { 27 | DIR *dir; 28 | struct dirent *dirent; 29 | pid_t pid; 30 | int rc = 0; 31 | 32 | dir = opendir("/proc/"); 33 | if (!dir) 34 | return -errno; 35 | 36 | pids.clear(); 37 | 38 | for (;;) { 39 | errno = 0; 40 | dirent = readdir(dir); 41 | if (!dirent) { 42 | if (errno) 43 | rc = -errno; 44 | 45 | break; 46 | } 47 | 48 | if (DT_DIR != dirent->d_type) 49 | continue; 50 | 51 | if (!isdigit(dirent->d_name[0])) 52 | continue; 53 | 54 | pid = atoi(dirent->d_name); 55 | pids.push_back(pid); 56 | } 57 | 58 | closedir(dir); 59 | 60 | return rc; 61 | } 62 | 63 | 64 | #ifdef PID_LIST_SELF_TEST 65 | #include "ProcStatus.h" 66 | 67 | int main(int argc, char* argv[]) 68 | { 69 | ProcPid pp; 70 | ProcStatus ps; 71 | int err; 72 | 73 | err = pp.collect(); 74 | if (err) { 75 | fprintf(stderr, "get pid list failed! err = %d\n", err); 76 | return err; 77 | } 78 | 79 | setlocale(LC_NUMERIC, ""); 80 | 81 | printf("\nList all pids:\n"); 82 | for (auto &pid : pp.get_pids()) { 83 | ps.load(pid); 84 | printf("%8u %'15lu %s\n", 85 | pid, 86 | ps.get_number("RssAnon"), 87 | ps.get_name().c_str()); 88 | } 89 | 90 | printf("\nList kthreadd by name:\n"); 91 | for (auto &pid : pp.get_pids()) { 92 | ps.load(pid); 93 | if (ps.get_name() == "kthreadd") 94 | printf("%8u %'15lu %s\n", 95 | pid, 96 | ps.get_number("RssAnon"), 97 | ps.get_name().c_str()); 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /ProcPid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #ifndef AEP_PROC_PID_H 10 | #define AEP_PROC_PID_H 11 | 12 | #include 13 | #include 14 | 15 | class ProcPid 16 | { 17 | public: 18 | void clear() { pids.clear(); } 19 | void empty() { pids.empty(); } 20 | 21 | int collect(); 22 | std::vector& get_pids(); 23 | 24 | private: 25 | std::vector pids; 26 | }; 27 | 28 | #endif 29 | // vim:set ts=2 sw=2 et: 30 | -------------------------------------------------------------------------------- /ProcStatus.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "ProcStatus.h" 14 | 15 | void ProcStatus::clear() 16 | { 17 | status_map.clear(); 18 | name.clear(); 19 | pid = 0; 20 | } 21 | 22 | unsigned long ProcStatus::get_number(std::string key) const 23 | { 24 | auto it = status_map.find(key); 25 | 26 | if (it != status_map.end()) 27 | return it->second; 28 | else 29 | return 0; // kthreadd does not has RssAnon 30 | } 31 | 32 | int ProcStatus::load(pid_t n) 33 | { 34 | int rc; 35 | FILE *file; 36 | char filename[PATH_MAX]; 37 | 38 | pid = n; 39 | 40 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); 41 | file = fopen(filename, "r"); 42 | if (!file) { 43 | fprintf(stderr, "open %s failed\n", filename); 44 | return errno; 45 | } 46 | 47 | rc = parse_file(file); 48 | 49 | fclose(file); 50 | 51 | return rc; 52 | } 53 | 54 | int ProcStatus::parse_file(FILE *file) 55 | { 56 | int rc; 57 | char line[4096]; 58 | 59 | while (fgets(line, sizeof(line), file)) { 60 | rc = parse_line(line); 61 | if (rc < 0) 62 | return rc; 63 | } 64 | 65 | return 0; 66 | } 67 | 68 | int ProcStatus::parse_line(char* line) 69 | { 70 | char* val; 71 | 72 | val = strstr(line, ":\t"); 73 | if (!val) { 74 | printf("failed to parse status line:\n%s", line); 75 | return -EINVAL; 76 | } 77 | 78 | *val++ = '\0'; 79 | while (*++val == ' ') 80 | ; 81 | 82 | if (!strcmp("Name", line)) { 83 | name = val; 84 | name.pop_back(); // remove trailing '\n' 85 | return 0; 86 | } 87 | 88 | if (isdigit(val[0])) { 89 | status_map[line] = atoi(val); 90 | // printf("%s = %lu\n", line, status_map[line]); 91 | return 0; 92 | } 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /ProcStatus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #ifndef AEP_PROC_STATUS_H 10 | #define AEP_PROC_STATUS_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | class ProcStatus 17 | { 18 | public: 19 | int load(pid_t n); 20 | 21 | void clear(); 22 | bool empty() const { return status_map.empty(); } 23 | 24 | const std::string& get_name() const { return name; } 25 | unsigned long get_number(std::string key) const; 26 | 27 | private: 28 | 29 | int parse_file(FILE *file); 30 | int parse_line(char *line); 31 | 32 | private: 33 | pid_t pid; 34 | std::string name; 35 | std::unordered_map status_map; 36 | }; 37 | 38 | #endif 39 | // vim:set ts=2 sw=2 et: 40 | -------------------------------------------------------------------------------- /ProcStatus.rb: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (c) 2018 Intel Corporation 5 | # 6 | # Authors: Fengguang Wu 7 | # 8 | 9 | class ProcStatus 10 | 11 | def load(pid) 12 | @map = Hash.new 13 | File.open("/proc/#{pid}/status").each do |line| 14 | key, val = line.chomp.split /:\t */ 15 | @map[key] = val 16 | end 17 | end 18 | 19 | def [](key) 20 | @map[key] 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /ProcVmstat.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #include 16 | #include 17 | #include "lib/stats.h" 18 | #include "ProcVmstat.h" 19 | 20 | #include "Numa.h" 21 | 22 | int ProcVmstat::load_vmstat() 23 | { 24 | proc_vmstat = __load_vmstat("/proc/vmstat"); 25 | return proc_vmstat.empty(); 26 | } 27 | 28 | int ProcVmstat::load_numa_vmstat() 29 | { 30 | char path[50]; 31 | int rc = 0; 32 | 33 | int max_node = numa_max_node(); 34 | 35 | numa_vmstat.clear(); 36 | numa_vmstat.resize(max_node + 1); 37 | 38 | for (int i = 0; i <= max_node; ++i) { 39 | snprintf(path, sizeof(path), "/sys/devices/system/node/node%d/vmstat", i); 40 | numa_vmstat[i] = __load_vmstat(path); 41 | if (numa_vmstat[i].empty()) 42 | ++rc; 43 | } 44 | 45 | return rc; 46 | } 47 | 48 | vmstat_map ProcVmstat::__load_vmstat(const char *path) 49 | { 50 | vmstat_map map; 51 | char line[80]; 52 | char key[50]; 53 | unsigned long val; 54 | 55 | FILE *f = fopen(path, "r"); 56 | if (!f) { 57 | perror(path); 58 | goto out; 59 | } 60 | 61 | while (fgets(line, sizeof(line), f)) 62 | { 63 | int ret = sscanf(line, "%49s %lu\n", key, &val); 64 | if (ret < 2) { 65 | if (line[0] != ' ') // ignore known kernel bug 66 | fprintf(stderr, "parse failed: %d %s\n%s", ret, path, line); 67 | continue; 68 | } 69 | map[key] = val; 70 | } 71 | 72 | fclose(f); 73 | out: 74 | return map; 75 | } 76 | 77 | unsigned long ProcVmstat::vmstat(std::string name) 78 | { 79 | if (proc_vmstat.empty()) 80 | load_vmstat(); 81 | 82 | return proc_vmstat.at(name); 83 | } 84 | 85 | unsigned long ProcVmstat::vmstat(int nid, std::string name) 86 | { 87 | if (proc_vmstat.empty()) 88 | load_numa_vmstat(); 89 | 90 | return numa_vmstat.at(nid).at(name); 91 | } 92 | 93 | unsigned long ProcVmstat::anon_capacity() 94 | { 95 | unsigned long sum = vmstat("nr_free_pages"); 96 | 97 | sum += vmstat("nr_inactive_anon"); 98 | sum += vmstat("nr_active_anon"); 99 | 100 | return sum; 101 | } 102 | 103 | unsigned long ProcVmstat::anon_capacity(int nid) 104 | { 105 | unsigned long sum = vmstat(nid, "nr_free_pages"); 106 | 107 | sum += vmstat(nid, "nr_inactive_anon"); 108 | sum += vmstat(nid, "nr_active_anon"); 109 | 110 | return sum; 111 | } 112 | 113 | unsigned long ProcVmstat::vmstat(std::vector& nid) 114 | { 115 | unsigned long sum = 0; 116 | 117 | for (auto& i : nid) 118 | sum += anon_capacity(i); 119 | 120 | return sum; 121 | } 122 | 123 | void ProcVmstat::show_numa_stats(NumaNodeCollection* numa_collection) 124 | { 125 | unsigned long pmem_anon_kb = 0; 126 | unsigned long dram_anon_kb = 0; 127 | NumaNode* numa_obj; 128 | 129 | load_vmstat(); 130 | load_numa_vmstat(); 131 | 132 | const auto& numa_vmstat = get_numa_vmstat(); 133 | unsigned long total_anon_kb = vmstat("nr_inactive_anon") + 134 | vmstat("nr_active_anon") + 135 | vmstat("nr_isolated_anon"); 136 | 137 | total_anon_kb *= PAGE_SIZE >> 10; 138 | printf("\nAnonymous page distribution across NUMA nodes:\n"); 139 | printf("%'15lu anon total\n", total_anon_kb); 140 | 141 | int nid = 0; 142 | for (auto& map: numa_vmstat) { 143 | unsigned long anon_kb = map.at("nr_inactive_anon") + 144 | map.at("nr_active_anon") + 145 | map.at("nr_isolated_anon"); 146 | anon_kb *= PAGE_SIZE >> 10; 147 | printf("%'15lu %2d%% anon node %d\n", anon_kb, percent(anon_kb, total_anon_kb), nid); 148 | 149 | if (!numa_collection) { 150 | ++nid; 151 | continue; 152 | } 153 | numa_obj = numa_collection->get_node(nid); 154 | if (!numa_obj) { 155 | ++nid; 156 | continue; 157 | } 158 | 159 | switch (numa_obj->type()) { 160 | case NUMA_NODE_DRAM: 161 | dram_anon_kb += anon_kb; 162 | break; 163 | case NUMA_NODE_PMEM: 164 | pmem_anon_kb += anon_kb; 165 | break; 166 | default: 167 | //for unknown type do nothing 168 | break; 169 | } 170 | 171 | ++nid; 172 | } 173 | 174 | if (numa_collection) { 175 | printf("Anon DRAM nodes size: %'15lu %2d%%\n", 176 | dram_anon_kb, percent(dram_anon_kb, total_anon_kb)); 177 | printf("Anon PMEM nodes size: %'15lu %2d%%\n", 178 | pmem_anon_kb, percent(pmem_anon_kb, total_anon_kb)); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /ProcVmstat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef AEP_PROC_VMSTAT_H 11 | #define AEP_PROC_VMSTAT_H 12 | 13 | // interface to /proc/vmstat and /sys/devices/system/node/node*/vmstat 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | typedef std::unordered_map vmstat_map; 20 | 21 | class NumaNodeCollection; 22 | class ProcVmstat 23 | { 24 | public: 25 | int load_vmstat(); 26 | int load_numa_vmstat(); 27 | void clear() { proc_vmstat.clear(); numa_vmstat.clear(); }; 28 | 29 | unsigned long vmstat(std::string name); 30 | unsigned long vmstat(int nid, std::string name); 31 | unsigned long vmstat(std::vector& nid); 32 | 33 | const std::vector& get_numa_vmstat() { return numa_vmstat; } 34 | const vmstat_map & get_proc_vmstat() { return proc_vmstat; } 35 | 36 | unsigned long anon_capacity(); 37 | unsigned long anon_capacity(int nid); 38 | 39 | void show_numa_stats(NumaNodeCollection* numa_collection); 40 | 41 | private: 42 | vmstat_map __load_vmstat(const char *path); 43 | 44 | private: 45 | std::vector numa_vmstat; 46 | vmstat_map proc_vmstat; 47 | }; 48 | 49 | #endif 50 | // vim:set ts=2 sw=2 et: 51 | -------------------------------------------------------------------------------- /ProcVmstat.rb: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (c) 2018 Intel Corporation 5 | # 6 | # Authors: Fengguang Wu 7 | # 8 | 9 | class ProcVmstat 10 | 11 | PAGE_SIZE = `getconf PAGESIZE`.to_i rescue 4096 12 | 13 | def load_hash(file) 14 | hash = Hash.new 15 | File.open(file).each_line do |line| 16 | key, val = line.split 17 | hash["#{key}"] = val.to_i if val != nil 18 | end 19 | hash 20 | end 21 | 22 | def vmstat 23 | @vmstat ||= load_hash("/proc/vmstat") 24 | end 25 | 26 | def numa_vmstat 27 | @numa_vmstat || load_numa_vmstat 28 | end 29 | 30 | def load_numa_vmstat 31 | @numa_vmstat = Array.new 32 | Dir.glob("/sys/devices/system/node/node*/vmstat") do |file| 33 | nid = file[29..-1].to_i 34 | @numa_vmstat[nid] = load_hash(file) 35 | end 36 | @numa_vmstat 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /Process.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #include "lib/debug.h" 11 | #include "lib/memparse.h" 12 | #include "Process.h" 13 | #include "ProcMaps.h" 14 | #include "ProcStatus.h" 15 | #include "EPTMigrate.h" 16 | 17 | extern Option option; 18 | 19 | int Process::load(pid_t n) 20 | { 21 | pid = n; 22 | return proc_status.load(pid); 23 | } 24 | 25 | void Process::add_range(unsigned long start, unsigned long end) 26 | { 27 | std::shared_ptr p; 28 | 29 | p = std::make_shared(); 30 | p->set_pid(pid); 31 | p->set_va_range(start, end); 32 | p->set_pid_context(&context); 33 | idle_ranges.push_back(p); 34 | 35 | printdd("pid=%d add_range %lx-%lx=%lx\n", pid, start, end, end - start); 36 | } 37 | 38 | int Process::split_ranges() 39 | { 40 | unsigned long rss_anon = proc_status.get_number("RssAnon") << 10; 41 | unsigned long max_bytes = 0; 42 | 43 | if (option.split_rss_size.c_str()) 44 | max_bytes = memparse(option.split_rss_size.c_str(), 0); 45 | 46 | if(option.hugetlb) 47 | rss_anon = proc_status.get_number("HugetlbPages") << 10; 48 | 49 | if (rss_anon <= 0) 50 | return 0; 51 | 52 | if (max_bytes == 0) 53 | max_bytes = TASK_SIZE_MAX; 54 | 55 | if (rss_anon < max_bytes) { 56 | add_range(0, TASK_SIZE_MAX); 57 | return 0; 58 | } 59 | 60 | unsigned long sum = 0; 61 | unsigned long start = 0; 62 | unsigned long end; 63 | auto vmas = proc_maps.load(pid); 64 | 65 | for (auto& vma: vmas) { 66 | 67 | if (vma.start >= TASK_SIZE_MAX) 68 | continue; 69 | 70 | unsigned long vma_size = vma.end - vma.start; 71 | unsigned long offset; 72 | sum += vma_size; 73 | for (offset = max_bytes; offset < vma_size; offset += max_bytes) { 74 | end = (vma.start + offset) & ~(PMD_SIZE - 1); 75 | add_range(start, end); 76 | start = end; 77 | sum = 0; 78 | } 79 | 80 | if (sum > max_bytes) { 81 | end = vma.end; 82 | add_range(start, end); 83 | start = end; 84 | sum = 0; 85 | } 86 | } 87 | 88 | if (sum) 89 | add_range(start, TASK_SIZE_MAX); 90 | 91 | return 0; 92 | } 93 | 94 | void Process::set_policy(Policy* pol) 95 | { 96 | policy = *pol; 97 | 98 | // pass policy to all VMAs 99 | for (auto &migration: idle_ranges) 100 | migration->set_policy(*pol); 101 | } 102 | 103 | bool Process::match_policy(Policy& policy) 104 | { 105 | if (policy.pid >= 0) { 106 | if (policy.pid == pid) 107 | return true; 108 | } 109 | 110 | if (!policy.name.empty()) { 111 | if (!policy.name.compare(proc_status.get_name())) { 112 | printd("find policy for %s\n", policy.name.c_str()); 113 | return true; 114 | } 115 | } 116 | 117 | return false; 118 | } 119 | 120 | Policy* Process::match_policies(PolicySet& policies) 121 | { 122 | for (Policy &policy: policies) 123 | if (match_policy(policy)) 124 | return &policy; 125 | 126 | return NULL; 127 | } 128 | 129 | 130 | int ProcessCollection::collect() 131 | { 132 | int err; 133 | 134 | proccess_hash.clear(); 135 | 136 | err = pids.collect(); 137 | if (err) 138 | return err; 139 | 140 | for (pid_t pid: pids.get_pids()) 141 | { 142 | auto p = std::make_shared(); 143 | 144 | err = p->load(pid); 145 | if (err) 146 | continue; 147 | 148 | err = p->split_ranges(); 149 | if (err) 150 | continue; 151 | 152 | proccess_hash[pid] = p; 153 | p->context.set_pid(pid); 154 | } 155 | 156 | return 0; 157 | } 158 | 159 | int ProcessCollection::collect(PolicySet& policies) 160 | { 161 | int err; 162 | 163 | proccess_hash.clear(); 164 | 165 | err = pids.collect(); 166 | if (err) 167 | return err; 168 | 169 | for (pid_t pid: pids.get_pids()) { 170 | auto p = std::make_shared(); 171 | 172 | err = p->load(pid); 173 | if (err) 174 | continue; 175 | 176 | Policy* policy = p->match_policies(policies); 177 | if (!policy) 178 | continue; 179 | 180 | err = p->split_ranges(); 181 | if (err) 182 | continue; 183 | 184 | p->set_policy(policy); 185 | 186 | proccess_hash[pid] = p; 187 | p->context.set_pid(pid); 188 | } 189 | 190 | return 0; 191 | } 192 | 193 | void ProcessCollection::dump() 194 | { 195 | printf("dump process collection start:\n"); 196 | for (auto &iter: proccess_hash) { 197 | printf("pid: %5d, name: %s\n", 198 | iter.first, 199 | iter.second->proc_status.get_name().c_str()); 200 | } 201 | printf("dump process collection end.\n"); 202 | } 203 | -------------------------------------------------------------------------------- /Process.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef AEP_PROCESS_H 11 | #define AEP_PROCESS_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "ProcPid.h" 18 | #include "ProcMaps.h" 19 | #include "ProcStatus.h" 20 | #include "Option.h" 21 | #include "PidContext.h" 22 | 23 | class EPTMigrate; 24 | typedef std::vector> IdleRanges; 25 | 26 | class Process 27 | { 28 | public: 29 | int load(pid_t n); 30 | int split_ranges(); 31 | IdleRanges& get_ranges() { return idle_ranges; } 32 | void set_policy(Policy* pol); 33 | bool match_policy(Policy& policy); 34 | Policy* match_policies(PolicySet& policies); 35 | 36 | private: 37 | void add_range(unsigned long start, unsigned long end); 38 | 39 | public: 40 | pid_t pid; 41 | Policy policy; 42 | ProcStatus proc_status; 43 | ProcMaps proc_maps; 44 | IdleRanges idle_ranges; 45 | PidContext context; 46 | }; 47 | 48 | typedef std::unordered_map> ProcessHash; 49 | 50 | class ProcessCollection 51 | { 52 | public: 53 | int collect(); 54 | int collect(PolicySet& policies); 55 | ProcessHash& get_proccesses() { return proccess_hash; } 56 | void dump(); 57 | 58 | private: 59 | ProcPid pids; 60 | ProcessHash proccess_hash; 61 | }; 62 | 63 | #endif 64 | // vim:set ts=2 sw=2 et: 65 | -------------------------------------------------------------------------------- /Queue.h: -------------------------------------------------------------------------------- 1 | // source: https://github.com/juanchopanza/cppblog/blob/master/Concurrency/Queue/Queue.h 2 | // 3 | // Copyright (c) 2013 Juan Palacios juan.palacios.puyana@gmail.com 4 | // Subject to the BSD 2-Clause License 5 | // - see < http://opensource.org/licenses/BSD-2-Clause> 6 | // 7 | 8 | #ifndef CONCURRENT_QUEUE_ 9 | #define CONCURRENT_QUEUE_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | class Queue 18 | { 19 | public: 20 | 21 | T pop() 22 | { 23 | std::unique_lock mlock(mutex_); 24 | while (queue_.empty()) 25 | { 26 | cond_.wait(mlock); 27 | } 28 | auto val = queue_.front(); 29 | queue_.pop(); 30 | return val; 31 | } 32 | 33 | void pop(T& item) 34 | { 35 | std::unique_lock mlock(mutex_); 36 | while (queue_.empty()) 37 | { 38 | cond_.wait(mlock); 39 | } 40 | item = queue_.front(); 41 | queue_.pop(); 42 | } 43 | 44 | void push(const T& item) 45 | { 46 | std::unique_lock mlock(mutex_); 47 | queue_.push(item); 48 | mlock.unlock(); 49 | cond_.notify_one(); 50 | } 51 | Queue()=default; 52 | Queue(const Queue&) = delete; // disable copying 53 | Queue& operator=(const Queue&) = delete; // disable assignment 54 | 55 | private: 56 | std::queue queue_; 57 | std::mutex mutex_; 58 | std::condition_variable cond_; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MemoryOptimizer -- hot page accounting and migration daemon 2 | =========================================================== 3 | 4 | PREREQUISITE 5 | ------------ 6 | 7 | ### Hardware 8 | 9 | - Intel Skylake/Cascade Lake processors 10 | - NVDIMM persistent memory 11 | 12 | ### Kernel 13 | 14 | - LKML discussions: 15 | 16 | [[RFC][PATCH v2 00/21] PMEM NUMA node and hotness accounting/migration](https://lkml.org/lkml/2018/12/26/138) 17 | 18 | - git repo: 19 | 20 | git clone https://git.kernel.org/pub/scm/linux/kernel/git/wfg/linux.git 21 | git checkout -B ept-idle-v2-4.20 origin/ept-idle-v2-4.20 22 | 23 | - kconfig: 24 | 25 | CONFIG_NUMA=y 26 | CONFIG_MEM_SOFT_DIRTY=y 27 | CONFIG_PROC_PAGE_MONITOR=y 28 | CONFIG_IDLE_PAGE_TRACKING=y 29 | CONFIG_KVM=m 30 | CONFIG_KVM_EPT_IDLE=m 31 | 32 | ### OS packages 33 | 34 | Debian/Ubuntu and RHEL users can run this script to install packages: 35 | 36 | ./install-deps.sh 37 | 38 | ### Build 39 | 40 | make 41 | 42 | TOOLS 43 | ----- 44 | 45 | - page-refs: PFN based page table scan 46 | - task-refs: page table scan and migration for one process/VM 47 | - sys-refs: page table scan and migration for a set of process/VMs 48 | 49 | sys-refs is most powerful and can run as daemon to manage hot/cold page 50 | placement on DRAM/PMEM nodes. 51 | -------------------------------------------------------------------------------- /Sysfs.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "Sysfs.h" 14 | 15 | #include 16 | #include 17 | 18 | bool Sysfs::file_exists(char path[]) 19 | { 20 | struct stat buf; 21 | 22 | return stat(path, &buf) == 0; 23 | } 24 | 25 | int Sysfs::read_int(std::string dir, std::string name) 26 | { 27 | std::ifstream infile(dir + '/' + name); 28 | int val; 29 | infile >> val; 30 | return val; 31 | } 32 | 33 | void Sysfs::load_hugetlb() 34 | { 35 | int max_node = numa_max_node(); 36 | char path[100]; 37 | 38 | hugetlb_map.resize(max_node + 1); 39 | 40 | for (int i = 0; i < max_node; ++i) 41 | { 42 | snprintf(path, sizeof(path), 43 | "/sys/devices/system/node/node%d/hugepages/hugepages-2048kB", i); 44 | 45 | if (!file_exists(path)) 46 | continue; 47 | 48 | #define read_hugetlb(name) hugetlb_map[i][name] = read_int(path, name) 49 | read_hugetlb("nr_hugepages"); 50 | read_hugetlb("free_hugepages"); 51 | read_hugetlb("surplus_hugepages"); 52 | #undef read_hugetlb 53 | } 54 | 55 | load_global_hugetlb(); 56 | } 57 | 58 | void Sysfs::load_global_hugetlb() 59 | { 60 | const char path[] = "/sys/kernel/mm/hugepages/hugepages-2048kB"; 61 | #define read_hugetlb(name) global_hugetlb_map[name] = read_int(path, name) 62 | read_hugetlb("nr_hugepages"); 63 | read_hugetlb("free_hugepages"); 64 | read_hugetlb("surplus_hugepages"); 65 | read_hugetlb("nr_overcommit_hugepages"); 66 | read_hugetlb("resv_hugepages"); 67 | #undef read_hugetlb 68 | } 69 | 70 | int Sysfs::hugetlb(int nid, std::string name) 71 | { 72 | if (hugetlb_map.empty()) 73 | load_hugetlb(); 74 | return hugetlb_map.at(nid).at(name); 75 | } 76 | 77 | int Sysfs::hugetlb(std::string name) 78 | { 79 | if (global_hugetlb_map.empty()) 80 | load_global_hugetlb(); 81 | return global_hugetlb_map.at(name); 82 | } 83 | -------------------------------------------------------------------------------- /Sysfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #ifndef AEP_SYSFS_H 10 | #define AEP_SYSFS_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | typedef std::unordered_map IntMap; 17 | 18 | class Sysfs 19 | { 20 | public: 21 | int hugetlb(int nid, std::string name); 22 | int hugetlb(std::string name); 23 | void load_hugetlb(); 24 | 25 | bool file_exists(char path[]); 26 | int read_int(std::string dir, std::string name); 27 | 28 | private: 29 | void load_global_hugetlb(); 30 | 31 | private: 32 | std::vector> hugetlb_map; 33 | std::unordered_map global_hugetlb_map; 34 | }; 35 | 36 | #endif 37 | // vim:set ts=2 sw=2 et: 38 | -------------------------------------------------------------------------------- /VMAInspect.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | * Huang Ying 9 | * Liu Jingqi 10 | */ 11 | 12 | #include 13 | 14 | #include "lib/debug.h" 15 | #include "lib/stats.h" 16 | #include "ProcMaps.h" 17 | #include "Formatter.h" 18 | #include "VMAInspect.h" 19 | #include "Numa.h" 20 | 21 | void VMAInspect::fill_addrs(std::vector& addrs, unsigned long start) 22 | { 23 | void **p = &addrs[0]; 24 | void **pp = &addrs[addrs.size()-1]; 25 | 26 | for (; p <= pp; ++p) { 27 | *p = (void *)start; 28 | start += PAGE_SIZE; 29 | } 30 | } 31 | 32 | void VMAInspect::dump_node_percent(Formatter* fmt, int slot) 33 | { 34 | auto status_count = locator.get_status_count(); 35 | size_t dram_nodes_size = 0; 36 | 37 | if (!fmt) 38 | return; 39 | 40 | if (numa_collection) { 41 | for (auto node: numa_collection->get_dram_nodes()) 42 | dram_nodes_size += (size_t)status_count[node->id()]; 43 | } 44 | 45 | int pct = percent(dram_nodes_size, locator.get_status().size()); 46 | fmt->print("%2d %3d%% |", slot, pct); 47 | for (int i = 0; i < pct; ++i) 48 | fmt->print("#"); 49 | fmt->print("\n"); 50 | } 51 | 52 | int VMAInspect::dump_vma_nodes(Formatter* fmt, int is_split_vma, 53 | proc_maps_entry& vma, MovePagesStatusCount& status_sum) 54 | { 55 | unsigned long nr_pages; 56 | int err = 0; 57 | 58 | nr_pages = (vma.end - vma.start) >> PAGE_SHIFT; 59 | if (!nr_pages) 60 | return 0; 61 | 62 | unsigned long total_mb = (vma.end - vma.start) >> 20; 63 | int nr_slots = 1; 64 | unsigned long slot_pages = nr_pages; 65 | 66 | if (total_mb >= (1<<10) && is_split_vma) { 67 | if (fmt) { 68 | fmt->print("\nDRAM page distribution across 10 VMA slots: "); 69 | fmt->print("(pid=%d vma_mb=%'lu)\n", pid, total_mb); 70 | } 71 | nr_slots = 10; 72 | slot_pages = nr_pages / nr_slots; 73 | } 74 | 75 | locator.set_pid(pid); 76 | 77 | std::vector addrs; 78 | addrs.resize(slot_pages); 79 | 80 | for (int i = 0; i < nr_slots; ++i) { 81 | fill_addrs(addrs, vma.start + i * addrs.size() * PAGE_SIZE); 82 | 83 | locator.clear_status_count(); 84 | err = locator.move_pages(addrs, true); 85 | if (err) { 86 | perror("move_pages"); 87 | return err; 88 | } 89 | 90 | locator.calc_status_count(); 91 | // only show when it's bigger than 1G 92 | if (nr_slots != 1) 93 | dump_node_percent(fmt, i); 94 | 95 | locator.add_status_count_to(status_sum); 96 | } 97 | 98 | return err; 99 | } 100 | 101 | int VMAInspect::dump_task_nodes(pid_t i, Formatter* m) 102 | { 103 | ProcMaps proc_maps; 104 | int err = 0; 105 | MovePagesStatusCount status_sum; 106 | auto maps = proc_maps.load(i); 107 | 108 | pid = i; 109 | for (auto &vma: maps) { 110 | err = dump_vma_nodes(m, true, vma, status_sum); 111 | if (err) 112 | break; 113 | } 114 | 115 | if (!err) { 116 | m->print("\nAnonymous page distribution across NUMA nodes in pid %d:\n", pid); 117 | locator.set_pid(pid); 118 | locator.show_status_count(m, status_sum); 119 | } 120 | 121 | return err; 122 | } 123 | 124 | 125 | int VMAInspect::calc_memory_state(pid_t i, 126 | unsigned long &total_kb, 127 | unsigned long &total_dram_kb, 128 | unsigned long &total_pmem_kb) 129 | { 130 | ProcMaps proc_maps; 131 | int err = 0; 132 | MovePagesStatusCount status_sum; 133 | auto maps = proc_maps.load(i); 134 | 135 | pid = i; 136 | for (auto &vma: maps) { 137 | err = dump_vma_nodes(NULL, false, vma, status_sum); 138 | if (err) 139 | break; 140 | } 141 | 142 | if (!err) { 143 | locator.set_pid(pid); 144 | locator.calc_memory_state(status_sum, 145 | total_kb, 146 | total_dram_kb, 147 | total_pmem_kb); 148 | } 149 | 150 | return err; 151 | } 152 | -------------------------------------------------------------------------------- /VMAInspect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Yao Yuan 7 | * Fengguang Wu 8 | * Liu Jingqi 9 | */ 10 | 11 | #ifndef _VMA_INSPECT_H 12 | #define _VMA_INSPECT_H 13 | 14 | #include 15 | #include "MovePages.h" 16 | 17 | struct proc_maps_entry; 18 | class Formatter; 19 | class NumaNodeCollection; 20 | 21 | class VMAInspect 22 | { 23 | public: 24 | VMAInspect() {}; 25 | ~VMAInspect() {}; 26 | 27 | int dump_task_nodes(pid_t i, Formatter* m); 28 | int dump_vma_nodes(Formatter* m, int is_split_vma, 29 | proc_maps_entry& vma, MovePagesStatusCount& status_sum); 30 | void set_numa_collection(NumaNodeCollection* new_numa_collection) { 31 | numa_collection = new_numa_collection; 32 | locator.set_numacollection(new_numa_collection); 33 | } 34 | int calc_memory_state(pid_t i, 35 | unsigned long &total_kb, 36 | unsigned long &total_dram_kb, 37 | unsigned long &total_pmem_kb); 38 | private: 39 | void fill_addrs(std::vector& addrs, unsigned long start); 40 | void dump_node_percent(Formatter* fmt, int slot); 41 | 42 | private: 43 | pid_t pid; 44 | MovePages locator; 45 | NumaNodeCollection* numa_collection = NULL; 46 | }; 47 | 48 | #endif 49 | // vim:set ts=2 sw=2 et: 50 | -------------------------------------------------------------------------------- /aepwatch.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require_relative 'utility' 4 | 5 | class Aepwatch 6 | 7 | def initialize 8 | set_install("/opt/intel/ipmwatch") 9 | set_output_dir(File.join(Dir.pwd, Time.now.strftime("aepwatch-%F.%T"))) 10 | end 11 | 12 | def set_install(install) 13 | @install = install 14 | @start_bin = File.join(@install, "bin64", "ipmwatch") 15 | @stop_bin = File.join(@install, "bin64", "ipmwatch-stop") 16 | end 17 | 18 | def set_output_dir(output_dir) 19 | @output_dir = output_dir 20 | end 21 | 22 | def output_file(file) 23 | File.join(@output_dir, file) 24 | end 25 | 26 | def start() 27 | cmds = [ 28 | { 29 | :cmd => @start_bin + " 1", 30 | :out => output_file("aep-watch.csv"), 31 | :err => output_file("aep-watch.err"), 32 | :wait => false, 33 | :pid => nil, 34 | }, 35 | ] 36 | 37 | system("mkdir", "-p", @output_dir) 38 | 39 | cmds.each do |cmd| new_proc(cmd) end 40 | end 41 | 42 | def stop() 43 | cmd = { 44 | :cmd => @stop_bin, 45 | :out => output_file("aep-watch-stop.out"), 46 | :err => output_file("aep-watch-stop.err"), 47 | :wait => true, 48 | :pid => nil, 49 | } 50 | 51 | # meet sometime stop failure, so called 3 times here, just a 52 | # workaround 53 | new_proc(cmd) 54 | sleep(10) 55 | new_proc(cmd) 56 | sleep(10) 57 | new_proc(cmd) 58 | end 59 | end 60 | 61 | 62 | if __FILE__ == $0 63 | 64 | if ARGV.size < 2 65 | 66 | puts "Usage:" 67 | puts "Arg 0: install path of aepwatch" 68 | puts "Arg 1: output dir" 69 | puts "Example:" 70 | puts "#{$0} /opt/intel/ipmwatch /tmp/aep-watch" 71 | 72 | return -1 73 | end 74 | 75 | aepwatch = Aepwatch.new 76 | aepwatch.set_install(ARGV[0]) 77 | aepwatch.set_output_dir(ARGV[1]) 78 | aepwatch.start 79 | sleep ARGV[2].to_i 80 | aepwatch.stop 81 | 82 | return 0 83 | end 84 | -------------------------------------------------------------------------------- /aepwatch_parser.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'fileutils' 3 | require_relative 'utility' 4 | 5 | if __FILE__ == $0 6 | 7 | if ARGV.size < 1 8 | puts "Usage:" 9 | puts "Arg 0: directory path of aep-watch data" 10 | puts "Example:" 11 | puts "#{$0} /home/goodboy/test/today" 12 | return -1 13 | end 14 | 15 | aepwatch_csv_gnuplot = "aepwatch_gnuplot.sh" 16 | aepwatch_csv_file = "aep-watch.csv" 17 | 18 | #gnuplot phase 19 | search_dir=File.expand_path("#{ARGV[0]}") 20 | Dir.glob(File.join("**", aepwatch_csv_file), base:search_dir).each do |file| 21 | base_dir = File.join(search_dir, File.dirname(file)) 22 | puts "JOB #{$in_run}: found edp csv in #{base_dir}" 23 | FileUtils.cp(aepwatch_csv_gnuplot, base_dir, verbose:true) 24 | gnuplot_proc = { 25 | :cmd => "/usr/bin/gnuplot " + File.join(base_dir, aepwatch_csv_gnuplot), 26 | :out => File.join(base_dir, "aepwatch_gnuplot.out"), 27 | :err => File.join(base_dir, "aepwatch_gnuplot.err"), 28 | :wait => true, 29 | :cwd => base_dir, 30 | :pid => nil, 31 | } 32 | new_proc(gnuplot_proc) 33 | end 34 | end 35 | 36 | 37 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Yao Yuan 8 | */ 9 | 10 | /* 11 | * Copyright (c) 2018 Intel Corporation 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that: (1) source code distributions 15 | * retain the above copyright notice and this paragraph in its entirety, (2) 16 | * distributions including binary code include the above copyright notice and 17 | * this paragraph in its entirety in the documentation or other materials 18 | * provided with the distribution 19 | * 20 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 21 | * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 22 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 23 | */ 24 | 25 | #ifndef __COMMON__HH__ 26 | #define __COMMON__HH__ 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #define MB (1024*1024) 34 | typedef unsigned long long u64; 35 | typedef long long s64; 36 | 37 | #define MS_PER_SEC 1000ULL 38 | #define NS_PER_SEC 1000000000ULL 39 | #define NS_PER_MSEC 1000000ULL 40 | 41 | inline void sys_err(const std::string& what) 42 | { 43 | throw std::system_error(errno, std::generic_category(), what); 44 | } 45 | 46 | inline void mb() 47 | { 48 | asm volatile("" ::: "memory"); 49 | } 50 | 51 | #endif /* __COMMON__HH__ */ 52 | -------------------------------------------------------------------------------- /doc/classes.yaml: -------------------------------------------------------------------------------- 1 | # Overview on split of data and functionalities 2 | # . prefix means member object 3 | # otherwise means sub-class 4 | 5 | ProcIdlePages: 6 | .ProcIdleRefs: 7 | .ProcMaps: 8 | .Policy: 9 | EPTScan: 10 | EPTMigrate: 11 | .Formatter: 12 | .MovePages: 13 | .MigrateStats: 14 | 15 | ProcessCollection: 16 | .ProcPid: 17 | .ProcessHash: 18 | Process: 19 | .ProcMaps: 20 | .ProcStatus: 21 | .IdleRanges: 22 | 23 | GlobalScan: 24 | .ProcessCollection: 25 | .EPTMigrate: 26 | .thread: 27 | .Queue: 28 | 29 | Option: 30 | .PolicySet: 31 | .Policy: 32 | OptionParser: 33 | 34 | VMAInspect: 35 | .MovePages: 36 | 37 | # vim:set ts=2 sw=2 et: 38 | -------------------------------------------------------------------------------- /edp.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'fileutils' 3 | require_relative 'utility' 4 | 5 | class Edp 6 | attr_accessor :edp_data 7 | attr_accessor :edp_m_data 8 | attr_accessor :edp_v_data 9 | attr_accessor :edp_process_path 10 | attr_accessor :output_path 11 | 12 | def run 13 | cmd = [ 14 | @edp_process_path, 15 | @edp_data, 16 | @edp_v_data, 17 | @edp_m_data, 18 | @output_path, 19 | ].join(' ') 20 | 21 | run_out = File.join(output_path, "edp.out") 22 | run_err = File.join(output_path, "edp.err") 23 | 24 | puts "Running cmd: #{cmd}" 25 | pid = Process.spawn(cmd, 26 | :out => [run_out, 'w'], 27 | :err => [run_err, 'w']) 28 | end 29 | end 30 | 31 | 32 | if __FILE__ == $0 33 | 34 | if ARGV.size < 1 35 | puts "Usage:" 36 | puts "Arg 0: directory path of emon data" 37 | puts "Example:" 38 | puts "#{$0} /home/goodboy/test/today" 39 | return -1 40 | end 41 | max_run = 32 42 | $in_run = 0 43 | 44 | edp_process_path = "/opt/intel/edp/process.sh" 45 | edp_data = "emon.dat" 46 | edp_m_data = "emon-m.dat" 47 | edp_v_data = "emon-v.dat" 48 | edp_data = "emon.dat" 49 | edp_csv_file = "__edp_socket_view_details.csv" 50 | edp_csv_gnuplot = "edp_gnuplot.sh" 51 | 52 | edp_array = Array.new(max_run) 53 | 54 | search_dir=File.expand_path("#{ARGV[0]}") 55 | Dir.glob(File.join("**", edp_data), base:search_dir).each do |file| 56 | 57 | if ($in_run < max_run) 58 | base_dir = File.join(search_dir, File.dirname(file)) 59 | puts "JOB #{$in_run}: found emon data in #{base_dir}" 60 | 61 | edp_array[$in_run] = Edp.new 62 | edp_array[$in_run].edp_process_path = edp_process_path 63 | edp_array[$in_run].edp_data = File.join(base_dir, edp_data) 64 | edp_array[$in_run].edp_m_data = File.join(base_dir, edp_m_data) 65 | edp_array[$in_run].edp_v_data = File.join(base_dir, edp_v_data) 66 | edp_array[$in_run].output_path = base_dir 67 | edp_array[$in_run].run 68 | $in_run += 1 69 | else 70 | Process.waitpid -1 71 | $in_run -= 1 72 | end 73 | end 74 | 75 | #wait remain worker processes 76 | while $in_run > 0 do 77 | Process.waitpid -1 78 | $in_run -= 1 79 | end 80 | 81 | #gnuplot phase 82 | Dir.glob(File.join("**", edp_csv_file), base:search_dir).each do |file| 83 | base_dir = File.join(search_dir, File.dirname(file)) 84 | puts "JOB #{$in_run}: found edp csv in #{base_dir}" 85 | FileUtils.cp(edp_csv_gnuplot, base_dir, verbose:true) 86 | gnuplot_proc = { 87 | :cmd => "/usr/bin/gnuplot " + File.join(base_dir, edp_csv_gnuplot), 88 | :out => File.join(base_dir, "edp_gnuplot.out"), 89 | :err => File.join(base_dir, "edp_gnuplot.err"), 90 | :wait => true, 91 | :cwd => base_dir, 92 | :pid => nil, 93 | } 94 | new_proc(gnuplot_proc) 95 | end 96 | end 97 | 98 | 99 | -------------------------------------------------------------------------------- /emon.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require_relative 'utility' 3 | 4 | class Emon 5 | 6 | def initialize 7 | set_emon_install("/opt/intel/sep") 8 | set_output_dir(File.join(Dir.pwd, Time.now.strftime("emon-%F.%T"))) 9 | @event_file = "" 10 | end 11 | 12 | def set_emon_install(emon_install) 13 | @emon_install = emon_install 14 | @emon_bin = File.join(@emon_install, "bin64", "emon") 15 | @emon_ko_dir = File.join(@emon_install, "sepdk", "src") 16 | end 17 | 18 | def set_output_dir(output_dir) 19 | @output_dir = output_dir 20 | @log_file = File.join(@output_dir, "log") 21 | end 22 | 23 | def set_event_file(event_file) 24 | @event_file = event_file 25 | end 26 | 27 | def output_file(file) 28 | File.join(@output_dir, file) 29 | end 30 | 31 | def load_kernel_module() 32 | cmds = [ 33 | { 34 | :cmd => File.join(@emon_ko_dir, "rmmod-sep"), 35 | :out => output_file("rmmod-sep.dat"), 36 | :err => output_file("rmmod-sep.err"), 37 | :wait => true, 38 | :pid => nil, 39 | }, 40 | { 41 | :cmd => File.join(@emon_ko_dir, "insmod-sep"), 42 | :out => output_file("insmod-sep.dat"), 43 | :err => output_file("insmod-sep.err"), 44 | :wait => true, 45 | :pid => nil, 46 | } 47 | ] 48 | 49 | cmds.each do |cmd| new_proc(cmd) end 50 | end 51 | 52 | def start() 53 | cmds = [ 54 | { 55 | :cmd => @emon_bin + " -v", 56 | :out => output_file("emon-v.dat"), 57 | :err => output_file("emon-v.err"), 58 | :wait => true, 59 | :pid => nil, 60 | }, 61 | { 62 | :cmd => @emon_bin + " -M", 63 | :out => output_file("emon-m.dat"), 64 | :err => output_file("emon-m.err"), 65 | :wait => true, 66 | :pid => nil, 67 | }, 68 | { 69 | :cmd => @emon_bin + " -i " + "\"" + @event_file + "\"", 70 | :out => output_file("emon.dat"), 71 | :err => output_file("emon.err"), 72 | :wait => false, 73 | :pid => nil, 74 | } 75 | ] 76 | system("mkdir", "-p", @output_dir) 77 | # change to manually 78 | # load_kernel_module 79 | cmds.each do |cmd| new_proc(cmd) end 80 | end 81 | 82 | def stop() 83 | sleep 5 84 | cmd = { 85 | :cmd => @emon_bin + " -stop", 86 | :out => output_file("emon-stop.out"), 87 | :err => output_file("emon-stop.err"), 88 | :wait => true, 89 | :pid => nil, 90 | } 91 | new_proc(cmd) 92 | end 93 | end 94 | 95 | 96 | if __FILE__ == $0 97 | 98 | if ARGV.size < 3 99 | 100 | puts "Usage:" 101 | puts "Arg 0: install path of sep" 102 | puts "Arg 1: the Architecture Specific txt file path" 103 | puts "Arg 2: collection duration, in seconds" 104 | puts "Example:" 105 | puts "#{$0} /opt/intel/sep /opt/intel/edp/as/CascadeLake/CLX-2S/clx-2s-events.txt 10" 106 | 107 | return -1 108 | end 109 | 110 | emon = Emon.new 111 | emon.set_emon_install(ARGV[0]) 112 | emon.set_event_file(ARGV[1]) 113 | emon.start 114 | sleep ARGV[2].to_i 115 | emon.stop 116 | 117 | return 0 118 | end 119 | -------------------------------------------------------------------------------- /get_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yao Yuan 9 | # 10 | 11 | commit=$(git rev-list -1 HEAD) 12 | 13 | diff=$(git diff HEAD) 14 | if [[ -n "$diff" ]]; then 15 | diff_sum=+$(echo "$diff" | md5sum | cut -f1 -d ' ') 16 | diff_stat=$(echo "$diff" | diffstat | sed 's/$/\\n\\/') 17 | diff_stat="\n\\"$'\n'"${diff_stat::-3}" 18 | else 19 | diff_sum= 20 | diff_stat= 21 | fi 22 | 23 | cat > version.h < 8 | # 9 | 10 | has_cmd() 11 | { 12 | command -v "$1" >/dev/null 13 | } 14 | 15 | debian_install() 16 | { 17 | pkgs=( 18 | make 19 | gcc 20 | g++ 21 | numactl 22 | libnuma-dev 23 | libyaml-cpp-dev 24 | ruby 25 | diffstat 26 | 27 | libasan4 # for gcc-7 28 | ) 29 | sudo apt-get install "${pkgs[@]}" 30 | } 31 | 32 | rhel_install() 33 | { 34 | pkgs=( 35 | gcc-c++ 36 | libstdc++ 37 | numactl 38 | numactl-libs 39 | numactl-devel 40 | yaml-cpp-devel 41 | diffstat 42 | 43 | libasan 44 | ) 45 | sudo yum install "${pkgs[@]}" 46 | } 47 | 48 | if has_cmd apt-get; then 49 | debian_install 50 | elif has_cmd yum; then 51 | rhel_install 52 | else 53 | echo "unknown system" 54 | fi 55 | 56 | cat < 7 | */ 8 | 9 | #include 10 | #include 11 | #include "debug.h" 12 | 13 | int verbose_printf(int level, const char *format, ...) 14 | { 15 | if (debug_level() < level) 16 | return 0; 17 | 18 | va_list args; 19 | va_start(args, format); 20 | int ret = vprintf(format, args); 21 | va_end(args); 22 | 23 | return ret; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /lib/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef AEP_DEBUG_H 11 | #define AEP_DEBUG_H 12 | 13 | #define printd(fmt, args...) verbose_printf(1, fmt, ##args) 14 | #define printdd(fmt, args...) verbose_printf(2, fmt, ##args) 15 | #define err(x) perror(x), exit(1) 16 | 17 | extern int debug_level(void); 18 | extern int verbose_printf(int level, const char *format, ...); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /lib/iomem_parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * borrowed from kexec-tools 3 | * 4 | * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) 5 | * Copyright (C) 2018 Fengguang Wu 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation (version 2 of the License). 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #include "iomem_parse.h" 22 | #include 23 | #include 24 | #include 25 | 26 | #define MAX_LINE 160 27 | #define MAX_MEMORY_RANGES 2048 28 | static struct memory_range memory_range[MAX_MEMORY_RANGES]; 29 | 30 | /** 31 | * /proc/iomem parsing code. 32 | * 33 | * @param[out] range pointer that will be set to an array that holds the 34 | * memory ranges 35 | * @param[out] ranges number of ranges valid in @p range 36 | * 37 | * @return 0 on success, any other value on failure. 38 | */ 39 | int get_memory_ranges_proc_iomem(struct memory_range **range, int *ranges) 40 | { 41 | const char *iomem= "/proc/iomem"; 42 | int memory_ranges = 0; 43 | char line[MAX_LINE]; 44 | FILE *fp; 45 | 46 | fp = fopen(iomem, "r"); 47 | if (!fp) { 48 | fprintf(stderr, "Cannot open %s: %s\n", 49 | iomem, strerror(errno)); 50 | return -1; 51 | } 52 | 53 | while (fgets(line, sizeof(line), fp) != 0) { 54 | unsigned long long start, end; 55 | char *str; 56 | int type; 57 | int consumed; 58 | int count; 59 | if (memory_ranges >= MAX_MEMORY_RANGES) 60 | break; 61 | count = sscanf(line, "%llx-%llx : %n", 62 | &start, &end, &consumed); 63 | if (count != 2) 64 | continue; 65 | 66 | if (consumed >= sizeof(line)) { 67 | fprintf(stderr, 68 | "line too long, consider increase MAX_LINE\n"); 69 | fclose(fp); 70 | return -1; 71 | } 72 | 73 | str = line + consumed; 74 | 75 | /* printf("%016Lx-%016Lx : %s", start, end, str); */ 76 | 77 | if (memcmp(str, "System RAM\n", 11) == 0) { 78 | type = RANGE_RAM; 79 | } 80 | else if (memcmp(str, "reserved\n", 9) == 0) { 81 | type = RANGE_RESERVED; 82 | } 83 | else if (memcmp(str, "ACPI Tables\n", 12) == 0) { 84 | type = RANGE_ACPI; 85 | } 86 | else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) { 87 | type = RANGE_ACPI_NVS; 88 | } 89 | else if (memcmp(str, "Persistent Memory (legacy)\n", 27) == 0) { 90 | type = RANGE_PRAM; 91 | } 92 | else if (memcmp(str, "Persistent Memory\n", 18) == 0) { 93 | type = RANGE_PMEM; 94 | } 95 | else { 96 | continue; 97 | } 98 | memory_range[memory_ranges].start = start; 99 | memory_range[memory_ranges].end = end; 100 | memory_range[memory_ranges].type = type; 101 | 102 | /* printf("%016Lx-%016Lx : %x\n", start, end, type); */ 103 | 104 | memory_ranges++; 105 | } 106 | fclose(fp); 107 | *range = memory_range; 108 | *ranges = memory_ranges; 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /lib/iomem_parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * borrowed from kexec-tools 3 | * 4 | * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) 5 | * Copyright (C) 2018 Fengguang Wu 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation (version 2 of the License). 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | struct memory_range { 22 | unsigned long long start, end; 23 | unsigned type; 24 | 25 | #define RANGE_RAM 0 26 | #define RANGE_RESERVED 1 27 | #define RANGE_ACPI 2 28 | #define RANGE_ACPI_NVS 3 29 | #define RANGE_UNCACHED 4 30 | #define RANGE_PMEM 6 31 | #define RANGE_PRAM 11 32 | }; 33 | -------------------------------------------------------------------------------- /lib/memparse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * memparse() borrowed from linux/lib/cmdline.c 3 | * 4 | * This source code is licensed under the GNU General Public License, 5 | * Version 2. See the file COPYING for more details. 6 | * 7 | */ 8 | 9 | #include 10 | 11 | /** 12 | * memparse - parse a string with mem suffixes into a number 13 | * @ptr: Where parse begins 14 | * @retptr: (output) Optional pointer to next char after parse completes 15 | * 16 | * Parses a string into a number. The number stored at @ptr is 17 | * potentially suffixed with K, M, G, T, P, E. 18 | */ 19 | 20 | unsigned long long memparse(const char *ptr, char **retptr) 21 | { 22 | char *endptr; /* local pointer to end of parsed string */ 23 | 24 | unsigned long long ret = strtoull(ptr, &endptr, 0); 25 | 26 | switch (*endptr) { 27 | case 'E': 28 | case 'e': 29 | ret <<= 10; 30 | case 'P': 31 | case 'p': 32 | ret <<= 10; 33 | case 'T': 34 | case 't': 35 | ret <<= 10; 36 | case 'G': 37 | case 'g': 38 | ret <<= 10; 39 | case 'M': 40 | case 'm': 41 | ret <<= 10; 42 | case 'K': 43 | case 'k': 44 | ret <<= 10; 45 | endptr++; 46 | default: 47 | break; 48 | } 49 | 50 | if (retptr) 51 | *retptr = endptr; 52 | 53 | return ret; 54 | } 55 | -------------------------------------------------------------------------------- /lib/memparse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | unsigned long long memparse(const char *ptr, char **retptr); 10 | -------------------------------------------------------------------------------- /lib/page-types.c: -------------------------------------------------------------------------------- 1 | /* 2 | * page-types: Tool for querying page flags 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; version 2. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should find a copy of v2 of the GNU General Public License somewhere on 14 | * your Linux system; if not, write to the Free Software Foundation, Inc., 59 15 | * Temple Place, Suite 330, Boston, MA 02111-1307 USA. 16 | * 17 | * Copyright (C) 2009 Intel corporation 18 | * 19 | * Authors: Wu Fengguang 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 27 | 28 | #ifndef KPF_PGTABLE 29 | #define KPF_PGTABLE 26 30 | #endif 31 | 32 | /* kernel hacking assistances 33 | * WARNING: subject to change, never rely on them! 34 | */ 35 | #define KPF_RESERVED 32 36 | #define KPF_MLOCKED 33 37 | #define KPF_MAPPEDTODISK 34 38 | #define KPF_PRIVATE 35 39 | #define KPF_PRIVATE_2 36 40 | #define KPF_OWNER_PRIVATE 37 41 | #define KPF_ARCH 38 42 | #define KPF_UNCACHED 39 43 | #define KPF_SOFTDIRTY 40 44 | 45 | static const char * const page_flag_names[] = { 46 | [KPF_LOCKED] = "L:locked", 47 | [KPF_ERROR] = "E:error", 48 | [KPF_REFERENCED] = "R:referenced", 49 | [KPF_UPTODATE] = "U:uptodate", 50 | [KPF_DIRTY] = "D:dirty", 51 | [KPF_LRU] = "l:lru", 52 | [KPF_ACTIVE] = "A:active", 53 | [KPF_SLAB] = "S:slab", 54 | [KPF_WRITEBACK] = "W:writeback", 55 | [KPF_RECLAIM] = "I:reclaim", 56 | [KPF_BUDDY] = "B:buddy", 57 | 58 | [KPF_MMAP] = "M:mmap", 59 | [KPF_ANON] = "a:anonymous", 60 | [KPF_SWAPCACHE] = "s:swapcache", 61 | [KPF_SWAPBACKED] = "b:swapbacked", 62 | [KPF_COMPOUND_HEAD] = "H:compound_head", 63 | [KPF_COMPOUND_TAIL] = "T:compound_tail", 64 | [KPF_HUGE] = "G:huge", 65 | [KPF_UNEVICTABLE] = "u:unevictable", 66 | [KPF_HWPOISON] = "X:hwpoison", 67 | [KPF_NOPAGE] = "n:nopage", 68 | [KPF_KSM] = "x:ksm", 69 | [KPF_THP] = "t:thp", 70 | [KPF_BALLOON] = "o:balloon", 71 | [KPF_PGTABLE] = "g:pgtable", 72 | 73 | #ifdef KPF_ZERO_PAGE 74 | [KPF_ZERO_PAGE] = "z:zero_page", 75 | #endif 76 | #ifdef KPF_IDLE 77 | [KPF_IDLE] = "i:idle_page", 78 | #endif 79 | 80 | [KPF_RESERVED] = "r:reserved", 81 | [KPF_MLOCKED] = "m:mlocked", 82 | [KPF_MAPPEDTODISK] = "d:mappedtodisk", 83 | [KPF_PRIVATE] = "P:private", 84 | [KPF_PRIVATE_2] = "p:private_2", 85 | [KPF_OWNER_PRIVATE] = "O:owner_private", 86 | [KPF_ARCH] = "h:arch", 87 | [KPF_UNCACHED] = "c:uncached", 88 | [KPF_SOFTDIRTY] = "f:softdirty", 89 | 90 | }; 91 | 92 | 93 | char *page_flag_name(uint64_t flags) 94 | { 95 | static char buf[65]; 96 | int present; 97 | size_t i, j; 98 | 99 | for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) { 100 | present = (flags >> i) & 1; 101 | if (!page_flag_names[i]) { 102 | /* if (present) */ 103 | /* fatal("unknown flag bit %d\n", i); */ 104 | continue; 105 | } 106 | buf[j++] = present ? page_flag_names[i][0] : '_'; 107 | } 108 | 109 | return buf; 110 | } 111 | 112 | char *page_flag_longname(uint64_t flags) 113 | { 114 | static char buf[1024]; 115 | size_t i, n; 116 | 117 | for (i = 0, n = 0; i < ARRAY_SIZE(page_flag_names); i++) { 118 | if (!page_flag_names[i]) 119 | continue; 120 | if ((flags >> i) & 1) 121 | n += snprintf(buf + n, sizeof(buf) - n, "%s,", 122 | page_flag_names[i] + 2); 123 | } 124 | if (n) 125 | n--; 126 | buf[n] = '\0'; 127 | 128 | return buf; 129 | } 130 | 131 | -------------------------------------------------------------------------------- /lib/page-types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | 10 | char *page_flag_name(uint64_t flags); 11 | char *page_flag_longname(uint64_t flags); 12 | -------------------------------------------------------------------------------- /lib/stats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Yao Yuan 8 | */ 9 | 10 | #ifndef AEP_STATS_H 11 | #define AEP_STATS_H 12 | 13 | #include 14 | 15 | template 16 | void add_count(M& map, const K& key, V&& n) 17 | { 18 | auto search = map.find(key); 19 | 20 | if (search != map.end()) 21 | search->second += n; 22 | else 23 | map[key] = n; 24 | } 25 | 26 | template 27 | void inc_count(M& map, const K& key) 28 | { 29 | add_count(map, key, 1); 30 | } 31 | 32 | template 33 | int find_map(M& map, const K& key, V&& value) 34 | { 35 | auto search = map.find(key); 36 | if (search != map.end()) { 37 | value = search->second; 38 | return 1; 39 | } 40 | 41 | return 0; 42 | } 43 | 44 | template 45 | inline int percent(T n, T total) 46 | { 47 | return ((n + 1) * 100) / (total + 1); 48 | } 49 | 50 | static inline float tv_secs(struct timeval& t1, struct timeval& t2) 51 | { 52 | return (t2.tv_sec - t1.tv_sec) + 53 | (t2.tv_usec - t1.tv_usec) * 0.000001; 54 | } 55 | 56 | 57 | #endif 58 | // vim:set ts=2 sw=2 et: 59 | -------------------------------------------------------------------------------- /plot-page-refs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | refs_count_file=$1 11 | 12 | [[ $refs_count_file ]] || { 13 | # try default output file 14 | [[ -f refs_count ]] && refs_count_file=refs_count 15 | } 16 | 17 | [[ $refs_count_file ]] || { 18 | echo "Usage: $0 refs_count_file" 19 | exit 20 | } 21 | 22 | plot() { 23 | gnuplot <<-EOF 24 | 25 | set xlabel "refs" 26 | set ylabel "pages" 27 | 28 | set logscale y 29 | 30 | set size 1 31 | set terminal pngcairo size ${width:-1280}, 800 32 | set terminal pngcairo size ${width:-1000}, 600 33 | set terminal pngcairo size ${width:-1280}, ${height:-800} 34 | 35 | set output "$refs_count_file.png" 36 | 37 | plot "$refs_count_file" using 1:2 with linespoints pt 7 ps 2 lw 2 lc rgbcolor "red" title "4k pages", \ 38 | "$refs_count_file" using 1:3 with linespoints pt 5 ps 2 lw 2 lc rgbcolor "blue" title "2M pages", \ 39 | "$refs_count_file" using 1:4 with linespoints pt 3 ps 2 lw 2 lc rgbcolor "orange" title "if 2M pages" 40 | 41 | EOF 42 | } 43 | 44 | plot $refs_count_file 45 | echo "feh $refs_count_file.png" 46 | -------------------------------------------------------------------------------- /pmu-refs/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | hmd 4 | filter-test 5 | -------------------------------------------------------------------------------- /pmu-refs/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (c) 2019 Intel Corporation 5 | # 6 | # Authors: Huang Ying 7 | # Jin Yao 8 | # 9 | 10 | PREFIX := /usr/local 11 | BIN := ${PREFIX}/bin 12 | LIBJEVENTS_DIR = ../pmu-tools/jevents 13 | PMEM2DRAM_DIR = ../../pmem2dram 14 | CXXFLAGS := -g -Wall -O2 -std=c++11 -MMD -Wno-unused-result \ 15 | -I${LIBJEVENTS_DIR} -I${PMEM2DRAM_DIR} 16 | LDLIBS := -lnuma -lstdc++ -lm 17 | OBJ := hmd.o cmsk.o pmu.o migration.o hmd-common.o hmd-config.o \ 18 | ${PMEM2DRAM_DIR}/AddressRangeFilter.o ${PMEM2DRAM_DIR}/Numa.o 19 | DEPS := $(patsubst %.o,%.d,${OBJ}) 20 | LIBJEVENTS = ${LIBJEVENTS_DIR}/libjevents.a 21 | 22 | all: hmd 23 | 24 | install: hmd 25 | install -d ${BIN} 26 | install -m 755 hmd ${BIN} 27 | 28 | clean-jevents: 29 | make -C ${LIBJEVENTS_DIR} clean 30 | 31 | clean: clean-jevents 32 | rm -f *.o *.d ${OBJ} $(DEPS) hmd 33 | 34 | ${LIBJEVENTS}: 35 | make -C ${LIBJEVENTS_DIR} 36 | 37 | hmd: ${OBJ} ${LIBJEVENTS} 38 | 39 | -include ${DEPS} 40 | -------------------------------------------------------------------------------- /pmu-refs/cmsk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #ifndef __CMSK__H__ 11 | #define __CMSK__H__ 12 | 13 | #include 14 | #include 15 | 16 | typedef unsigned short cms_count_t; 17 | #define CMS_COUNT_MAX USHRT_MAX 18 | 19 | enum { 20 | CMSK_AGING_CLEAR, 21 | CMSK_AGING_HALF, 22 | }; 23 | 24 | struct cms 25 | { 26 | unsigned char width_order; 27 | unsigned int width_mask; 28 | unsigned int depth; 29 | unsigned int total; 30 | std::vector> matrix; 31 | }; 32 | 33 | #define ACHASH_SIZE_ORDER_MAX 23 34 | 35 | struct achash_item 36 | { 37 | unsigned long addr; 38 | unsigned long pid:24; 39 | unsigned long next:24; 40 | unsigned long count:16; 41 | }; 42 | 43 | /* Address/count Hash */ 44 | struct achash 45 | { 46 | bool hash_mode; 47 | unsigned int threshold; 48 | unsigned int size_order; 49 | unsigned int size; 50 | unsigned int mask; 51 | unsigned int len; 52 | unsigned int samples; 53 | std::vector buckets; 54 | std::vector items; 55 | std::vector hist; 56 | int hist_max; 57 | }; 58 | 59 | struct cmsk_stats 60 | { 61 | unsigned int nr_hot_page; 62 | }; 63 | 64 | /* Top-K frequent items with Cout-Min-Sketch */ 65 | struct cmsk 66 | { 67 | int aging_method; 68 | unsigned int interval; 69 | unsigned int no; 70 | struct cms cms; 71 | struct achash achash; 72 | struct cmsk_stats stats; 73 | }; 74 | 75 | int cmsk_init(struct cmsk *cmsk); 76 | void cmsk_fini(struct cmsk *cmsk); 77 | void cmsk_clear(struct cmsk *cmsk); 78 | bool cmsk_update(struct cmsk *cmsk, unsigned long item1, unsigned long item2); 79 | void cmsk_age(struct cmsk *cmsk); 80 | void cmsk_sort(struct cmsk *cmsk); 81 | void cmsk_print(struct cmsk *cmsk); 82 | struct achash_item *cmsk_hot_pages(struct cmsk *cmsk, unsigned int *n); 83 | void cmsk_sort_by_pid(struct achash_item *items, unsigned int n); 84 | 85 | #endif /* __CMSK__H__ */ 86 | -------------------------------------------------------------------------------- /pmu-refs/hmd-common.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "hmd-common.h" 16 | 17 | unsigned long rdclock(void) 18 | { 19 | struct timespec ts; 20 | clock_gettime(CLOCK_MONOTONIC, &ts); 21 | return ts.tv_sec * NS_PER_SEC + ts.tv_nsec; 22 | } 23 | 24 | unsigned long rdclock_diff(unsigned long prev, unsigned long cur) 25 | { 26 | return cur - prev; 27 | } 28 | 29 | int read_all(int fd, void *buf, size_t n) 30 | { 31 | int left = n; 32 | 33 | while (left) { 34 | int ret = read(fd, buf, left); 35 | 36 | if (ret < 0 && errno == EINTR) 37 | continue; 38 | if (ret <= 0) 39 | return ret; 40 | 41 | left -= ret; 42 | buf = static_cast(buf) + ret; 43 | } 44 | 45 | return n; 46 | } 47 | -------------------------------------------------------------------------------- /pmu-refs/hmd-common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #ifndef __HMD_COMMON_HH__ 11 | #define __HMD_COMMON_HH__ 12 | 13 | #include "common.h" 14 | 15 | unsigned long rdclock(void); 16 | int read_all(int fd, void *buf, size_t n); 17 | 18 | #endif /* __COMMON__HH__ */ 19 | -------------------------------------------------------------------------------- /pmu-refs/hmd-config.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #include 11 | 12 | #include "hmd-common.h" 13 | #include "hmd-config.h" 14 | #include "cmsk.h" 15 | 16 | struct hmd_config hmd_config = { 17 | .pmem_pmu_events = { 18 | "mem_trans_retired.load_latency_gt_16", 19 | }, 20 | .dram_pmu_events = { 21 | "mem_trans_retired.load_latency_gt_16", 22 | }, 23 | .imc_dram_read = "cas_count_read", 24 | .imc_dram_write = "cas_count_write", 25 | .target_pid = -1, 26 | .sample_period_min = 1000, 27 | .cpumask = NULL, 28 | .expected_samples = 20000, 29 | .expected_samples_margin_percent = 20, 30 | 31 | /* default granularity is page size */ 32 | .granularity_order = PAGE_SHIFT, 33 | .unit_interval_ms = 1000, 34 | .interval_max = 10, 35 | 36 | .cmsk_cms_width_order = 13, 37 | .cmsk_achash_size_order = 14, 38 | .cmsk_achash_threshold = 8, 39 | .aging_method = CMSK_AGING_CLEAR, 40 | .hash_mode = false, 41 | .imc_counting = false, 42 | 43 | .numa = { 44 | .numa_dram_list = "0", 45 | .numa_pmem_list = "1", 46 | .pmem_dram_map = "1->0", 47 | }, 48 | 49 | .dram_watermark_percent = 10, 50 | 51 | .move_pages_max = 1024, 52 | .dram_count_multiple = 10, 53 | 54 | .show_only = 0, 55 | .verbose = 0, 56 | .runtime = 0, 57 | .cmsk_achash_hist_max = 256, 58 | }; 59 | -------------------------------------------------------------------------------- /pmu-refs/hmd-config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #ifndef __HMD_CONFIG__HH__ 11 | #define __HMD_CONFIG__HH__ 12 | 13 | #include "Option.h" 14 | 15 | enum pmu_event_type { 16 | PET_LOCAL, 17 | PET_REMOTE, 18 | PET_NUMBER 19 | }; 20 | 21 | struct bitmask; 22 | 23 | struct addr_range { 24 | int pid; 25 | unsigned long start; 26 | unsigned long size; 27 | }; 28 | 29 | struct hmd_config { 30 | const char *pmem_pmu_events[PET_NUMBER]; 31 | const char *dram_pmu_events[PET_NUMBER]; 32 | const char *imc_dram_read; 33 | const char *imc_dram_write; 34 | int target_pid; 35 | int sample_period_min; 36 | struct bitmask *cpumask; 37 | int expected_samples; 38 | int expected_samples_margin_percent; 39 | 40 | int granularity_order; 41 | int unit_interval_ms; 42 | int interval_max; 43 | 44 | int cmsk_cms_width_order; 45 | int cmsk_achash_size_order; 46 | int cmsk_achash_threshold; 47 | int aging_method; 48 | bool hash_mode; 49 | bool imc_counting; 50 | 51 | NumaHWConfig numa; 52 | 53 | int dram_watermark_percent; 54 | 55 | int move_pages_max; 56 | int dram_count_multiple; 57 | 58 | int show_only; 59 | int verbose; 60 | int runtime; 61 | int cmsk_achash_hist_max; 62 | 63 | int arfilter_reset_intervals; 64 | 65 | /* 66 | * Should be defined at the end of struct, otherwise g++ reports 67 | * "non-trivial designated initializers not supported" 68 | */ 69 | std::vector arfilter; 70 | }; 71 | 72 | extern struct hmd_config hmd_config; 73 | 74 | #endif /* __HMD_CONFIG__HH__ */ 75 | -------------------------------------------------------------------------------- /pmu-refs/migration.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "hmd-common.h" 17 | #include "hmd-config.h" 18 | #include "migration.h" 19 | #include "cmsk.h" 20 | #include "Numa.h" 21 | #include "pmu.h" 22 | #include "AddressRangeFilter.h" 23 | 24 | static inline int do_move_pages(int pid, unsigned long count, 25 | std::vector& pages, 26 | const std::vector* nodes, 27 | std::vector& status, 28 | int flags) 29 | { 30 | int ret; 31 | 32 | ret = move_pages(pid, count, (void **)pages.data(), 33 | nodes ? nodes->data() : NULL, 34 | status.data(), flags); 35 | if (ret < 0) { 36 | /* 37 | * It's OK if the process has gone (ESRCH), 38 | * or process is kernel thread (EINVAL) 39 | */ 40 | if (errno != ESRCH && errno != EINVAL) { 41 | sys_err("failed to move pages"); 42 | } 43 | return ret; 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | void MigrationState::init(PmuState *pmu_state, 50 | NumaNodeCollection *numa_nodes, 51 | AddressRangeFilter *filter) 52 | { 53 | hot_pages_.resize(hmd_config.move_pages_max); 54 | sample_counts_.resize(hmd_config.move_pages_max); 55 | target_nodes_.resize(hmd_config.move_pages_max); 56 | migrate_status_.resize(hmd_config.move_pages_max); 57 | pmu_state_ = pmu_state; 58 | numa_nodes_ = numa_nodes; 59 | arfilter_ = filter; 60 | } 61 | 62 | int MigrationState::get_current_nodes() 63 | { 64 | return do_move_pages(pid_, len_, hot_pages_, nullptr, 65 | migrate_status_, MPOL_MF_MOVE_ALL); 66 | } 67 | 68 | void MigrationState::get_target_nodes() 69 | { 70 | int i, j = 0, nid; 71 | NumaNode *node; 72 | 73 | for (i = 0; i < len_; i++) { 74 | nid = migrate_status_[i]; 75 | if (!numa_nodes_->is_valid_nid(nid)) 76 | continue; 77 | node = numa_nodes_->get_node(nid); 78 | if (!node->is_pmem()) 79 | continue; 80 | hot_pages_[j] = hot_pages_[i]; 81 | sample_counts_[j] = sample_counts_[i]; 82 | target_nodes_[j] = node->promote_target->id(); 83 | j++; 84 | } 85 | len_ = j; 86 | } 87 | 88 | bool MigrationState::is_page_hot_in_target_dram_node( 89 | int sample_count, int target_node) 90 | { 91 | return sample_count * pmu_state_->get_pmem_sample_period() >= 92 | pmu_state_->get_node(target_node)->get_dram_count_avg() * 93 | hmd_config.dram_count_multiple; 94 | } 95 | 96 | void MigrationState::filter() 97 | { 98 | int i, j = 0, node, sample_count; 99 | 100 | for (i = 0; i < len_; i++) { 101 | node = target_nodes_[i]; 102 | sample_count = sample_counts_[i]; 103 | if (/* !numa_nodes_->get_node(node)->mem_watermark_ok && */ 104 | !is_page_hot_in_target_dram_node(sample_count, node)) 105 | continue; 106 | sample_counts_[j] = sample_count; 107 | hot_pages_[j] = hot_pages_[i]; 108 | target_nodes_[j] = target_nodes_[i]; 109 | j++; 110 | } 111 | len_ = j; 112 | } 113 | 114 | int MigrationState::__move_pages() 115 | { 116 | int i, ret, moved = 0; 117 | 118 | if (!pid_ || !len_) 119 | return 0; 120 | 121 | ret = get_current_nodes(); 122 | if (ret < 0) 123 | return 0; 124 | get_target_nodes(); 125 | filter(); 126 | if (!len_) 127 | return 0; 128 | 129 | ret = do_move_pages(pid_, len_, hot_pages_, &target_nodes_, 130 | migrate_status_, MPOL_MF_MOVE_ALL); 131 | if (ret < 0) 132 | return 0; 133 | 134 | for (i = 0; i < len_; i++) { 135 | if (migrate_status_[i] == target_nodes_[i]) 136 | moved++; 137 | else { 138 | arfilter_->insert_range(pid_, hot_pages_[i], PAGE_SIZE); 139 | if (hmd_config.verbose) 140 | printf("Failed to move page 0x%lx of pid %d\n", hot_pages_[i], pid_); 141 | } 142 | } 143 | 144 | return moved; 145 | } 146 | 147 | int MigrationState::move_pages(int pid, 148 | const std::vector& hot_pages, 149 | const std::vector& sample_counts) 150 | { 151 | int moved; 152 | 153 | pid_ = pid; 154 | len_ = hot_pages.size(); 155 | std::copy(hot_pages.begin(), hot_pages.end(), hot_pages_.begin()); 156 | std::copy(sample_counts.begin(), sample_counts.end(), sample_counts_.begin()); 157 | 158 | moved = __move_pages(); 159 | stats_.nr_moved_page += moved; 160 | return moved; 161 | } 162 | -------------------------------------------------------------------------------- /pmu-refs/migration.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #ifndef __MIGRATION__HH__ 11 | #define __MIGRATION__HH__ 12 | 13 | #include 14 | 15 | class PmuState; 16 | class NumaNodeCollection; 17 | struct cmsk; 18 | class AddressRangeFilter; 19 | 20 | struct migration_stats { 21 | unsigned int nr_moved_page; 22 | }; 23 | 24 | class MigrationState 25 | { 26 | int pid_; 27 | int len_; 28 | 29 | std::vector hot_pages_; 30 | std::vector sample_counts_; 31 | std::vector target_nodes_; 32 | std::vector migrate_status_; 33 | 34 | PmuState *pmu_state_; 35 | NumaNodeCollection *numa_nodes_; 36 | AddressRangeFilter *arfilter_; 37 | struct migration_stats stats_ {}; 38 | 39 | int get_current_nodes(); 40 | void get_target_nodes(); 41 | bool is_page_hot_in_target_dram_node(int sample_count, int target_node); 42 | void filter(); 43 | int __move_pages(); 44 | 45 | public: 46 | void init(PmuState *pmu_state, NumaNodeCollection *numa_nodes, 47 | AddressRangeFilter *filter); 48 | int move_pages(int pid, const std::vector& hot_pages, 49 | const std::vector& sample_counts); 50 | 51 | const migration_stats& get_stats(void) 52 | { 53 | return stats_; 54 | } 55 | 56 | void begin_interval(void) 57 | { 58 | memset(&stats_, 0, sizeof(stats_)); 59 | } 60 | }; 61 | 62 | #endif /* __MIGRATION__HH__ */ 63 | -------------------------------------------------------------------------------- /pmu-refs/tests/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (c) 2019 Intel Corporation 5 | # 6 | # Authors: Huang Ying 7 | # Jin Yao 8 | # 9 | 10 | PREFIX := /usr/local 11 | BIN := ${PREFIX}/bin 12 | CXXFLAGS := -g -Wall -O2 -std=c++11 -MMD -Wno-unused-result 13 | 14 | filter-test: ../../AddressRangeFilter.cc filter-test.cc 15 | $(CXX) $< filter-test.cc -o $@ $(CXXFLAGS) 16 | 17 | all: filter-test 18 | make filter-test 19 | 20 | clean: 21 | rm -f *.o *.d filter-test 22 | -------------------------------------------------------------------------------- /pmu-refs/tests/filter-test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Authors: Huang Ying 7 | * Jin Yao 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "../../AddressRangeFilter.h" 18 | 19 | int main(int argc, char *argv[]) 20 | { 21 | AddressRangeFilter filter; 22 | 23 | filter.clear(); 24 | 25 | filter.insert_range(1, 1000, 1000); 26 | filter.insert_range(1, 2000, 1000); 27 | filter.insert_range(1, 0000, 1000); 28 | filter.insert_range(2, 1000, 1000); 29 | filter.insert_range(2, 3000, 1000); 30 | filter.insert_range(2, 1000, 2000); 31 | filter.insert_range(3, 4000, 1000); 32 | filter.insert_range(3, 3000, 1000); 33 | filter.insert_range(1, 2000, 1999); 34 | 35 | filter.show(); 36 | 37 | if (!filter.search_address(1, 3999)) 38 | printf("PASSED: Can't find pid 1/address 3999\n"); 39 | else 40 | printf("FAILED: Find pid 1/address 3999\n"); 41 | 42 | if (filter.search_address(1, 3998)) 43 | printf("PASSED: Find pid 1/address 3998\n"); 44 | else 45 | printf("FAILED: Can't find pid 1/address 3998\n"); 46 | 47 | if (filter.search_address(3, 3091)) 48 | printf("PASSED: Find pid 3/address 3091\n"); 49 | else 50 | printf("FAILED: Can't find pid 3/address 3091\n"); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /show-vmstat.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #include 10 | 11 | #include "ProcVmstat.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | ProcVmstat vmstat; 16 | 17 | vmstat.load_vmstat(); 18 | vmstat.load_numa_vmstat(); 19 | std::cout << "nr_free_pages " << vmstat.vmstat("nr_free_pages") << std::endl; 20 | std::cout << "nr_free_pages 0 " << vmstat.vmstat(0, "nr_free_pages") << std::endl; 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /sys-refs.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Yao Yuan 7 | * Fengguang Wu 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "lib/debug.h" 23 | #include "Option.h" 24 | #include "ProcMaps.h" 25 | #include "ProcIdlePages.h" 26 | #include "EPTScan.h" 27 | #include "EPTMigrate.h" 28 | #include "GlobalScan.h" 29 | #include "version.h" 30 | #include "OptionParser.h" 31 | 32 | using namespace std; 33 | 34 | OptionParser option; 35 | GlobalScan gscan; 36 | 37 | int debug_level() 38 | { 39 | return option.debug_level; 40 | } 41 | 42 | static const struct option opts[] = { 43 | {"interval", required_argument, NULL, 'i'}, 44 | {"sleep", required_argument, NULL, 's'}, 45 | {"loop", required_argument, NULL, 'l'}, 46 | {"output", required_argument, NULL, 'o'}, 47 | {"dram", required_argument, NULL, 'd'}, 48 | {"migrate", required_argument, NULL, 'm'}, 49 | {"verbose", required_argument, NULL, 'v'}, 50 | {"config", required_argument, NULL, 'c'}, 51 | {"help", no_argument, NULL, 'h'}, 52 | {"version", no_argument, NULL, 'r'}, 53 | {NULL, 0, NULL, 0} 54 | }; 55 | 56 | static void usage(char *prog) 57 | { 58 | fprintf(stderr, 59 | "%s [option] ...\n" 60 | "Options (order matters, the latter takes effect):\n" 61 | " -h|--help Show this information\n" 62 | " -i|--interval The scan interval in seconds\n" 63 | " -s|--sleep Seconds to sleep between scan rounds\n" 64 | " -l|--loop The number of scan rounds\n" 65 | " -o|--output The output file, defaults to refs-count\n" 66 | " -d|--dram The DRAM percent, wrt. DRAM+PMEM total size\n" 67 | " -m|--migrate Migrate what: 0|none, 1|hot, 2|cold, 3|both\n" 68 | " -v|--verbose Show debug info\n" 69 | " -r|--version Show version info\n" 70 | " -c|--config config file path name\n", 71 | prog); 72 | 73 | exit(0); 74 | } 75 | 76 | static void parse_cmdline(int argc, char *argv[]) 77 | { 78 | int options_index = 0; 79 | int opt = 0; 80 | const char *optstr = "hvri:s:l:o:d:m:c:"; 81 | 82 | optind = 1; 83 | while ((opt = getopt_long(argc, argv, optstr, opts, &options_index)) != EOF) { 84 | switch (opt) { 85 | case 0: 86 | case 'c': 87 | option.parse_file(optarg); 88 | break; 89 | case 's': 90 | option.sleep_secs = atof(optarg); 91 | break; 92 | case 'i': 93 | option.interval = atof(optarg); 94 | break; 95 | case 'l': 96 | option.nr_loops = atoi(optarg); 97 | break; 98 | case 'o': 99 | option.output_file = optarg; 100 | break; 101 | case 'd': 102 | option.dram_percent = atoi(optarg); 103 | break; 104 | case 'm': 105 | option.migrate_what = Option::parse_migrate_name(optarg); 106 | break; 107 | case 'v': 108 | ++option.debug_level; 109 | break; 110 | case 'r': 111 | print_version(); 112 | exit(0); 113 | case 'h': 114 | case '?': 115 | default: 116 | usage(argv[0]); 117 | } 118 | } 119 | 120 | } 121 | 122 | void signal_handler(int sign_num) 123 | { 124 | gscan.request_reload_conf(); 125 | } 126 | 127 | int register_signal_handler() 128 | { 129 | if (SIG_ERR != signal(SIGUSR1, signal_handler)) 130 | return 0; 131 | return -1; 132 | } 133 | 134 | int main(int argc, char *argv[]) 135 | { 136 | setlocale(LC_NUMERIC, ""); 137 | 138 | parse_cmdline(argc, argv); 139 | register_signal_handler(); 140 | 141 | gscan.apply_option(); 142 | gscan.main_loop(); 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /task-maps.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "ProcMaps.h" 17 | 18 | int main(int argc, char *argv[]) 19 | { 20 | pid_t pid; 21 | ProcMaps proc_maps; 22 | 23 | if (argc == 1) 24 | pid = getpid(); 25 | else 26 | pid = atoi(argv[1]); 27 | 28 | auto maps = proc_maps.load(pid); 29 | proc_maps.show(maps); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /task-numa-maps.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | require_relative "ProcNumaMaps" 11 | 12 | proc_numa_maps = ProcNumaMaps.new 13 | proc_numa_maps.load(ARGV[0]) 14 | 15 | proc_numa_maps.show_numa_placement 16 | -------------------------------------------------------------------------------- /task-refs.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0 3 | * 4 | * Copyright (c) 2018 Intel Corporation 5 | * 6 | * Authors: Fengguang Wu 7 | * Peng Bo 8 | * Yao Yuan 9 | * Liu Jingqi 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "Option.h" 23 | #include "ProcMaps.h" 24 | #include "ProcIdlePages.h" 25 | #include "EPTScan.h" 26 | #include "EPTMigrate.h" 27 | #include "lib/debug.h" 28 | #include "version.h" 29 | 30 | using namespace std; 31 | 32 | Option option; 33 | 34 | int debug_level() 35 | { 36 | return option.debug_level; 37 | } 38 | 39 | static const struct option opts[] = { 40 | {"pid", required_argument, NULL, 'p'}, 41 | {"interval", required_argument, NULL, 'i'}, 42 | {"loop", required_argument, NULL, 'l'}, 43 | {"output", required_argument, NULL, 'o'}, 44 | {"dram", required_argument, NULL, 'd'}, 45 | {"hot-refs", required_argument, NULL, 'H'}, 46 | {"cold-refs", required_argument, NULL, 'c'}, 47 | {"migrate", required_argument, NULL, 'm'}, 48 | {"verbose", required_argument, NULL, 'v'}, 49 | {"help", no_argument, NULL, 'h'}, 50 | {"changes", no_argument, NULL, 'g'}, 51 | {"version", no_argument, NULL, 'r'}, 52 | {NULL, 0, NULL, 0} 53 | }; 54 | 55 | static void usage(char *prog) 56 | { 57 | fprintf(stderr, 58 | "%s [option] ...\n" 59 | " -h|--help Show this information\n" 60 | " -p|--pid The PID to scan\n" 61 | " -i|--interval The scan interval in seconds\n" 62 | " -l|--loop The number of times to scan\n" 63 | " -o|--output The output file, defaults to refs-count-PID\n" 64 | " -d|--dram The DRAM percent, wrt. DRAM+PMEM total size\n" 65 | " -H|--hot-refs min_refs threshold for hot pages\n" 66 | " -c|--cold-refs max_refs threshold for cold pages\n" 67 | " -m|--migrate Migrate what: 0|none, 1|hot, 2|cold, 3|both\n" 68 | " -v|--verbose Show debug info\n" 69 | " -r|--version Show version info\n", 70 | prog); 71 | 72 | exit(0); 73 | } 74 | 75 | static void parse_cmdline(int argc, char *argv[]) 76 | { 77 | int options_index = 0; 78 | int opt = 0; 79 | const char *optstr = "hvrp:i:l:o:d:H:c:m:"; 80 | 81 | while ((opt = getopt_long(argc, argv, optstr, opts, &options_index)) != EOF) { 82 | switch (opt) { 83 | case 0: 84 | break; 85 | case 'p': 86 | option.pid = atoi(optarg); 87 | break; 88 | case 'i': 89 | option.interval = atof(optarg); 90 | break; 91 | case 'l': 92 | option.nr_walks = atoi(optarg); 93 | break; 94 | case 'o': 95 | option.output_file = optarg; 96 | break; 97 | case 'd': 98 | option.dram_percent = atoi(optarg); 99 | break; 100 | case 'H': 101 | option.hot_min_refs = atoi(optarg); 102 | break; 103 | case 'c': 104 | option.cold_max_refs = atoi(optarg); 105 | break; 106 | case 'm': 107 | option.migrate_what = Option::parse_migrate_name(optarg); 108 | break; 109 | case 'v': 110 | ++option.debug_level; 111 | break; 112 | case 'r': 113 | print_version(); 114 | exit(0); 115 | case 'h': 116 | case '?': 117 | default: 118 | usage(argv[0]); 119 | } 120 | } 121 | 122 | if (!option.pid) 123 | usage(argv[0]); 124 | 125 | if (option.output_file.empty()) 126 | option.output_file = "refs-count-" + std::to_string(option.pid); 127 | } 128 | 129 | int account_refs(EPTMigrate& migration) 130 | { 131 | int err; 132 | 133 | err = migration.walk_multi(option.nr_walks, option.interval); 134 | if (err) 135 | return err; 136 | 137 | EPTScan::reset_sys_refs_count(migration.get_nr_walks()); 138 | migration.count_refs(); 139 | 140 | err = migration.save_counts(option.output_file); 141 | if (err) 142 | return err; 143 | 144 | return 0; 145 | } 146 | 147 | int migrate(EPTMigrate& migration) 148 | { 149 | int err = 0; 150 | 151 | err = migration.migrate(); 152 | 153 | return err; 154 | } 155 | 156 | int main(int argc, char *argv[]) 157 | { 158 | int err = 0; 159 | 160 | setlocale(LC_NUMERIC, ""); 161 | 162 | parse_cmdline(argc, argv); 163 | 164 | EPTMigrate migration; 165 | 166 | migration.set_pid(option.pid); 167 | 168 | err = account_refs(migration); 169 | if (err) { 170 | cout << "return err " << err; 171 | return err; 172 | } 173 | 174 | err = migrate(migration); 175 | 176 | return err; 177 | } 178 | -------------------------------------------------------------------------------- /test-migratepages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MEM=1g 4 | DRAM_NODE=2 5 | PMEM_NODE=8 6 | 7 | pid_file1=$(mktemp) 8 | pid_file2=$(mktemp) 9 | 10 | echo "Start usemem $MEM in DRAM:" 11 | numactl --membind=$DRAM_NODE usemem --detach --sleep 1000 --pid-file $pid_file1 $MEM 12 | 13 | echo 14 | echo "Start usemem $MEM in PMEM:" 15 | numactl --membind=$PMEM_NODE usemem --detach --sleep 1000 --pid-file $pid_file2 $MEM 16 | 17 | # double check page location 18 | #./task-numa-maps.rb $pid1 19 | 20 | pid1=$(<$pid_file1) 21 | pid2=$(<$pid_file2) 22 | 23 | echo 24 | echo "DRAM => PMEM:" 25 | time migratepages $pid1 $DRAM_NODE $PMEM_NODE 26 | echo 27 | echo "PMEM => DRAM:" 28 | time migratepages $pid1 $PMEM_NODE $DRAM_NODE 29 | time migratepages $pid2 $PMEM_NODE $DRAM_NODE 30 | echo 31 | echo "DRAM => PMEM:" 32 | time migratepages $pid2 $DRAM_NODE $PMEM_NODE 33 | 34 | rm $pid_file1 35 | rm $pid_file2 36 | kill $pid1 37 | kill $pid2 38 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/2lm-kunpeng-global.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Yuan Yao 8 | # 9 | # Test purpose: 10 | # For comparing 2LM and KUNPENG performance with sysbench global mode. 11 | # 12 | # Expected HW and system setup: 13 | # 14 | # DRAM and AEP: 15 | # 256GB AEP per physical NUMA socket at least 16 | # 64GB DRAM per physical NUMA socket at least 17 | # 18 | # NUMA toplogy: 19 | # Physical NUMA 0 -> 64GB DRAM 20 | # Physical NUMA 1 -> 64GB DRAM 21 | # Physical NUMA 2 -> 256GB AEP 22 | # Physical NUMA 3 -> 256GB AEP 23 | # 24 | # These tests will run on NUMA 1 and 3. 25 | # 26 | 27 | cd "$(dirname "$0")" 28 | 29 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-32t-global.yaml 30 | sleep 10 31 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-16t-global.yaml 32 | sleep 10 33 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-4t-global.yaml 34 | sleep 10 35 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-1t-global.yaml 36 | 37 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/2lm-kunpeng.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Yuan Yao 8 | # 9 | 10 | 11 | cd "$(dirname "$0")" 12 | 13 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-32t.yaml 14 | sleep 10 15 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-16t.yaml 16 | sleep 10 17 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-4t.yaml 18 | sleep 10 19 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-1t.yaml 20 | 21 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/2lm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Yuan Yao 8 | # 9 | 10 | cd "$(dirname "$0")" 11 | 12 | ../run-vm-tests.rb scheme-sysbench-memory-2lm-1t.yaml 13 | sleep 10 14 | ../run-vm-tests.rb scheme-sysbench-memory-2lm-4t.yaml 15 | sleep 10 16 | ../run-vm-tests.rb scheme-sysbench-memory-2lm-16t.yaml 17 | sleep 10 18 | ../run-vm-tests.rb scheme-sysbench-memory-2lm-32t.yaml 19 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/aepwatch_gnuplot.sh: -------------------------------------------------------------------------------- 1 | ../../aepwatch_gnuplot.sh -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/edp_gnuplot.sh: -------------------------------------------------------------------------------- 1 | ../../edp_gnuplot.sh -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/kvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yuan Yao 9 | # 10 | 11 | [[ $kernel ]] || kernel=$1 12 | [[ $kernel ]] || kernel=( 13 | /c/linux/arch/x86/boot/bzImage 14 | ) 15 | 16 | [[ $qemu_cmd ]] || qemu_cmd=qemu-system-x86_64 17 | [[ $qemu_smp ]] || qemu_smp='cpus=32' 18 | [[ $qemu_mem ]] || qemu_mem='128G' 19 | [[ $qemu_ssh ]] || qemu_ssh='2222' 20 | [[ $qemu_log ]] && qemu_log=file:$qemu_log 21 | [[ $qemu_log ]] || qemu_log=stdio 22 | [[ $interleave ]] && numactl="numactl --interleave=$interleave" 23 | [[ $qemu_numactl ]] && numactl="numactl $qemu_numactl" 24 | 25 | kvm=( 26 | $numactl 27 | $qemu_cmd 28 | -machine pc,nvdimm 29 | -cpu host 30 | -enable-kvm 31 | -kernel $kernel 32 | -smp $qemu_smp 33 | -m $qemu_mem 34 | -device virtio-net-pci,netdev=net0 35 | -netdev user,id=net0,hostfwd=tcp::$qemu_ssh-:22 36 | -no-reboot 37 | -serial $qemu_log 38 | -display none 39 | -monitor null 40 | ) 41 | 42 | append=( 43 | ip=dhcp 44 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 45 | root=/dev/nfs 46 | ignore_loglevel 47 | sysrq_always_enabled 48 | panic=10 49 | # prompt_ramdisk=0 50 | earlyprintk=ttyS0,115200 51 | console=ttyS0,115200 52 | console=tty0 53 | vga=normal 54 | # root=/dev/ram0 55 | rw 56 | ) 57 | 58 | exec "${kvm[@]}" --append "${append[*]}" 59 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/run-sysbench-memory-1t.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yuan Yao 9 | # 10 | 11 | [[ $interleave ]] && numactl="numactl --interleave=$interleave --" 12 | 13 | sysbench_cmd=( 14 | $numactl 15 | stdbuf -oL 16 | sysbench 17 | --time=$time 18 | memory 19 | --memory-block-size=$memory_block_size 20 | --memory-total-size=512G 21 | --memory-scope=$memory_scope 22 | --memory-oper=$memory_oper 23 | --memory-access-mode=rnd 24 | --rand-type=$rand_type 25 | --rand-pareto-h=0.1 26 | --threads=$threads 27 | run 28 | ) 29 | 30 | echo "${sysbench_cmd[@]}" 31 | time "${sysbench_cmd[@]}" & 32 | 33 | [[ $pid_file ]] && echo $! > $pid_file 34 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/run-sysbench-memory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yuan Yao 9 | # 10 | 11 | [[ $interleave ]] && numactl="numactl --interleave=$interleave --" 12 | 13 | sysbench_cmd=( 14 | $numactl 15 | stdbuf -oL 16 | sysbench 17 | --time=$time 18 | memory 19 | --memory-block-size=$memory_block_size 20 | --memory-total-size=1024T 21 | --memory-scope=$memory_scope 22 | --memory-oper=$memory_oper 23 | --memory-access-mode=rnd 24 | --rand-type=$rand_type 25 | --rand-pareto-h=0.1 26 | --threads=$threads 27 | run 28 | ) 29 | 30 | echo "${sysbench_cmd[@]}" 31 | time "${sysbench_cmd[@]}" & 32 | 33 | [[ $pid_file ]] && echo $! > $pid_file 34 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-16t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-39 -m 1" 13 | migrate_numactl: "-C 24-39 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | #- 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory.sh 22 | workload_params: 23 | - ¶ms 24 | time: 3600 25 | threads: 16 26 | memory_block_size: 244224M 27 | memory_scope: global 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-16t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-16t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-39 -m 1" 13 | migrate_numactl: "-C 24-39 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | #- 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory.sh 22 | workload_params: 23 | - ¶ms 24 | time: 3600 25 | threads: 16 26 | memory_block_size: 15264M 27 | memory_scope: local 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-16t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-1t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24 -m 1" 13 | migrate_numactl: "-C 24 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory-1t.sh 22 | workload_params: 23 | - ¶ms 24 | time: 60 25 | threads: 1 26 | memory_block_size: 244224M 27 | memory_scope: global 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-1t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-1t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24 -m 1" 13 | migrate_numactl: "-C 24 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory-1t.sh 22 | workload_params: 23 | - ¶ms 24 | time: 60 25 | threads: 1 26 | memory_block_size: 244224M 27 | memory_scope: local 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-1t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-32t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=32 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-47,72-79 -m 1" 13 | migrate_numactl: "-C 24-47,72-79 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory.sh 22 | workload_params: 23 | - ¶ms 24 | time: 3600 25 | threads: 32 26 | memory_block_size: 244224M 27 | memory_scope: global 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-32t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-32t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=32 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-47,72-79 -m 1" 13 | migrate_numactl: "-C 24-47,72-79 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory.sh 22 | workload_params: 23 | - ¶ms 24 | time: 3600 25 | threads: 32 26 | memory_block_size: 7632M 27 | memory_scope: local 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-32t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-4t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-27 -m 1" 13 | migrate_numactl: "-C 24-27 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory.sh 22 | workload_params: 23 | - ¶ms 24 | time: 3600 25 | threads: 4 26 | memory_block_size: 244224M 27 | memory_scope: global 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-4t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-2lm-4t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-27 -m 1" 13 | migrate_numactl: "-C 24-27 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | workload_script: run-sysbench-memory.sh 22 | workload_params: 23 | - ¶ms 24 | time: 3600 25 | threads: 4 26 | memory_block_size: 61056M 27 | memory_scope: local 28 | rand_type: gaussian 29 | memory_oper: read 30 | - <<: *params 31 | memory_oper: write 32 | migrate_cmd: sys-refs 33 | migrate_config: sys-refs-sysbench-memory-4t.yaml 34 | 35 | skip_baseline_run: true 36 | skip_migration_run: true 37 | #skip_dram_baseline_run: true 38 | # one_way_migrate: true 39 | no_eatmem: true 40 | 41 | emon_base_dir: "/opt/intel/sep" 42 | aepwatch_base_dir: "/opt/intel/ipmwatch" 43 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-16t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-39 -m 3" 13 | migrate_numactl: "-C 24-39 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | workload_params: 25 | - ¶ms 26 | time: 3600 27 | threads: 16 28 | memory_block_size: 244224M 29 | memory_scope: global 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-16t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-16t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-39 -m 3" 13 | migrate_numactl: "-C 24-39 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | workload_params: 25 | - ¶ms 26 | time: 3600 27 | threads: 16 28 | memory_block_size: 15264M 29 | memory_scope: local 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-16t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-1t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24 -m 3" 13 | migrate_numactl: "-C 24 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory-1t.sh 24 | workload_params: 25 | - ¶ms 26 | time: 60 27 | threads: 1 28 | memory_block_size: 244224M 29 | memory_scope: global 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-1t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-1t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24 -m 3" 13 | migrate_numactl: "-C 24 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory-1t.sh 24 | workload_params: 25 | - ¶ms 26 | time: 60 27 | threads: 1 28 | memory_block_size: 244224M 29 | memory_scope: local 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-1t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-32t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=32 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-47,72-79 -m 3" 13 | migrate_numactl: "-C 24-27,72-79 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | workload_params: 25 | - ¶ms 26 | time: 3600 27 | threads: 32 28 | memory_block_size: 244224M 29 | memory_scope: global 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-32t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-32t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=32 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-47,72-79 -m 3" 13 | migrate_numactl: "-C 24-27,72-79 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | workload_params: 25 | - ¶ms 26 | time: 3600 27 | threads: 32 28 | memory_block_size: 7632M 29 | memory_scope: local 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-32t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-4t-global.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-27 -m 3" 13 | migrate_numactl: "-C 24-27 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | workload_params: 25 | - ¶ms 26 | time: 3600 27 | threads: 4 28 | memory_block_size: 244224M 29 | memory_scope: global 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-4t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/scheme-sysbench-memory-kunpeng-4t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-27 -m 3" 13 | migrate_numactl: "-C 24-27 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | workload_params: 25 | - ¶ms 26 | time: 3600 27 | threads: 4 28 | memory_block_size: 61056M 29 | memory_scope: local 30 | rand_type: gaussian 31 | memory_oper: read 32 | - <<: *params 33 | memory_oper: write 34 | migrate_cmd: sys-refs 35 | migrate_config: sys-refs-sysbench-memory-4t.yaml 36 | 37 | skip_baseline_run: true 38 | skip_dram_baseline_run: true 39 | one_way_migrate: true 40 | emon_base_dir: "/opt/intel/sep" 41 | aepwatch_base_dir: "/opt/intel/ipmwatch" 42 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/sys-refs-sysbench-memory-16t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | max_threads: 4 4 | exit_on_exceeded: 1 5 | # exit_on_stabilized: 1 6 | policies: 7 | # - name: usemem 8 | # placement: dram 9 | # - name: sysbench 10 | # migration: hot 11 | # dump_distribution: true 12 | - name: qemu-system-x86 13 | migration: hot 14 | dump_distribution: true 15 | 16 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/sys-refs-sysbench-memory-1t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | initial_interval: 10 4 | max_threads: 1 5 | # interval: 10 6 | exit_on_exceeded: 1 7 | exit_on_stabilized: 1 8 | 9 | policies: 10 | # - name: usemem 11 | # placement: dram 12 | # - name: sysbench 13 | # migration: hot 14 | # dump_distribution: true 15 | - name: qemu-system-x86 16 | migration: hot 17 | dump_distribution: true 18 | 19 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/sys-refs-sysbench-memory-32t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | max_threads: 4 4 | exit_on_exceeded: 1 5 | # exit_on_stabilized: 1 6 | policies: 7 | # - name: usemem 8 | # placement: dram 9 | # - name: sysbench 10 | # migration: hot 11 | # dump_distribution: true 12 | - name: qemu-system-x86 13 | migration: hot 14 | dump_distribution: true 15 | 16 | -------------------------------------------------------------------------------- /tests/2lm-vs-kunpeng/sys-refs-sysbench-memory-4t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | # initial_interval: 5 4 | max_threads: 1 5 | # interval: 5 6 | exit_on_exceeded: 1 7 | # exit_on_stabilized: 1 8 | policies: 9 | # - name: usemem 10 | # placement: dram 11 | # - name: sysbench 12 | # migration: hot 13 | # dump_distribution: true 14 | - name: qemu-system-x86 15 | migration: hot 16 | dump_distribution: true 17 | 18 | -------------------------------------------------------------------------------- /tests/aepwatch_gnuplot.sh: -------------------------------------------------------------------------------- 1 | ../aepwatch_gnuplot.sh -------------------------------------------------------------------------------- /tests/edp_gnuplot.sh: -------------------------------------------------------------------------------- 1 | ../edp_gnuplot.sh -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/2lm-kunpeng.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Yuan Yao 8 | # 9 | 10 | 11 | cd "$(dirname "$0")" 12 | 13 | #../run-vm-tests.rb scheme-sysbench-memory-kunpeng-16t.yaml 14 | #sleep 10 15 | ../run-vm-tests.rb scheme-sysbench-memory-kunpeng-32t.yaml 16 | #sleep 10 17 | #../run-vm-tests.rb scheme-sysbench-memory-kunpeng-4t.yaml 18 | #sleep 10 19 | #../run-vm-tests.rb scheme-sysbench-memory-kunpeng-1t.yaml 20 | 21 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/2lm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Yuan Yao 8 | # 9 | 10 | cd "$(dirname "$0")" 11 | 12 | #../run-vm-tests.rb scheme-sysbench-memory-2lm-1t.yaml 13 | #sleep 10 14 | #../run-vm-tests.rb scheme-sysbench-memory-2lm-4t.yaml 15 | #sleep 10 16 | #../run-vm-tests.rb scheme-sysbench-memory-2lm-16t.yaml 17 | #sleep 10 18 | ../run-vm-tests.rb scheme-sysbench-memory-2lm-32t.yaml 19 | #sleep 10 20 | #../run-vm-tests.rb scheme-sysbench-memory-2lm-16t.yaml 21 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/kvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yuan Yao 9 | # 10 | 11 | [[ $kernel ]] || kernel=$1 12 | [[ $kernel ]] || kernel=( 13 | /c/linux/arch/x86/boot/bzImage 14 | ) 15 | 16 | [[ $qemu_cmd ]] || qemu_cmd=qemu-system-x86_64 17 | [[ $qemu_smp ]] || qemu_smp='cpus=32' 18 | [[ $qemu_mem ]] || qemu_mem='128G' 19 | [[ $qemu_ssh ]] || qemu_ssh='2222' 20 | [[ $qemu_log ]] && qemu_log=file:$qemu_log 21 | [[ $qemu_log ]] || qemu_log=stdio 22 | [[ $interleave ]] && numactl="numactl --interleave=$interleave" 23 | [[ $qemu_numactl ]] && numactl="numactl $qemu_numactl" 24 | 25 | kvm=( 26 | $numactl 27 | $qemu_cmd 28 | -machine pc,nvdimm 29 | -cpu host 30 | -enable-kvm 31 | -kernel $kernel 32 | -smp $qemu_smp 33 | -m $qemu_mem 34 | -device virtio-net-pci,netdev=net0 35 | -netdev user,id=net0,hostfwd=tcp::$qemu_ssh-:22 36 | -no-reboot 37 | -serial $qemu_log 38 | -display none 39 | -monitor null 40 | ) 41 | 42 | append=( 43 | ip=dhcp 44 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 45 | root=/dev/nfs 46 | ignore_loglevel 47 | sysrq_always_enabled 48 | panic=10 49 | # prompt_ramdisk=0 50 | earlyprintk=ttyS0,115200 51 | console=ttyS0,115200 52 | console=tty0 53 | vga=normal 54 | # root=/dev/ram0 55 | rw 56 | ) 57 | 58 | exec "${kvm[@]}" --append "${append[*]}" 59 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/run-sysbench-memory-1t.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yuan Yao 9 | # 10 | 11 | [[ $interleave ]] && numactl="numactl --interleave=$interleave --" 12 | 13 | sysbench_cmd=( 14 | $numactl 15 | stdbuf -oL 16 | sysbench 17 | --time=$time 18 | memory 19 | --memory-block-size=$memory_block_size 20 | --memory-total-size=512G 21 | --memory-scope=$memory_scope 22 | --memory-oper=$memory_oper 23 | --memory-access-mode=rnd 24 | --rand-type=$rand_type 25 | --rand-gaussian-stddev=$gaussian_stddev 26 | --rand-pareto-h=0.1 27 | --threads=$threads 28 | run 29 | ) 30 | 31 | echo "${sysbench_cmd[@]}" 32 | time "${sysbench_cmd[@]}" & 33 | 34 | [[ $pid_file ]] && echo $! > $pid_file 35 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/run-sysbench-memory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2019 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yuan Yao 9 | # 10 | 11 | [[ $interleave ]] && numactl="numactl --interleave=$interleave --" 12 | 13 | sysbench_cmd=( 14 | $numactl 15 | stdbuf -oL 16 | sysbench 17 | --time=$time 18 | memory 19 | --memory-block-size=$memory_block_size 20 | --memory-total-size=1024T 21 | --memory-scope=$memory_scope 22 | --memory-oper=$memory_oper 23 | --memory-access-mode=rnd 24 | --rand-type=$rand_type 25 | --rand-gaussian-stddev=$gaussian_stddev 26 | --rand-pareto-h=0.1 27 | --threads=$threads 28 | run 29 | ) 30 | 31 | echo "${sysbench_cmd[@]}" 32 | time "${sysbench_cmd[@]}" & 33 | 34 | [[ $pid_file ]] && echo $! > $pid_file 35 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-2lm-16t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-39 -m 1" 13 | migrate_numactl: "-C 24-39 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | #- 4 20 | # - 999 # pure PMEM 21 | 22 | 23 | workload_script: run-sysbench-memory.sh 24 | 25 | workload_params: 26 | - ¶ms 27 | time: 3600 28 | threads: 16 29 | memory_block_size: 244224M 30 | memory_scope: global 31 | rand_type: gaussian 32 | gaussian_stddev: 25.7 # 10% 33 | memory_oper: write 34 | - <<: *params 35 | gaussian_stddev: 12.8 # 20% 36 | - <<: *params 37 | gaussian_stddev: 4.28 # 60% 38 | 39 | # - <<: *params 40 | # gaussian_stddev: 4.28 # 60% 41 | # memory_oper: read 42 | 43 | #- <<: *params 44 | # gaussian_stddev: 6.43 # 40% 45 | # memory_oper: read 46 | #- <<: *params 47 | # gaussian_stddev: 4.28 # 60% 48 | # memory_oper: read 49 | #- <<: *params 50 | # gaussian_stddev: 25.7 51 | # memory_oper: write 52 | #- <<: *params 53 | # gaussian_stddev: 12.8 54 | # memory_oper: write 55 | #- <<: *params 56 | # gaussian_stddev: 6.43 57 | # memory_oper: write 58 | #- <<: *params 59 | # gaussian_stddev: 4.28 60 | # memory_oper: write 61 | 62 | migrate_cmd: sys-refs 63 | migrate_config: sys-refs-sysbench-memory-16t.yaml 64 | 65 | skip_baseline_run: true 66 | skip_migration_run: true 67 | #skip_dram_baseline_run: true 68 | # one_way_migrate: true 69 | no_eatmem: true 70 | 71 | emon_base_dir: "/opt/intel/sep" 72 | aepwatch_base_dir: "/opt/intel/ipmwatch" 73 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-2lm-1t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24 -m 1" 13 | migrate_numactl: "-C 24 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | 22 | 23 | workload_script: run-sysbench-memory-1t.sh 24 | 25 | workload_params: 26 | - ¶ms 27 | time: 60 28 | threads: 1 29 | memory_block_size: 244224M 30 | memory_scope: global 31 | rand_type: gaussian 32 | gaussian_stddev: 25.7 # 10% 33 | memory_oper: read 34 | - <<: *params 35 | gaussian_stddev: 12.8 # 20% 36 | memory_oper: read 37 | - <<: *params 38 | gaussian_stddev: 6.43 # 40% 39 | memory_oper: read 40 | - <<: *params 41 | gaussian_stddev: 4.28 # 60% 42 | memory_oper: read 43 | - <<: *params 44 | gaussian_stddev: 25.7 45 | memory_oper: write 46 | - <<: *params 47 | gaussian_stddev: 12.8 48 | memory_oper: write 49 | - <<: *params 50 | gaussian_stddev: 6.43 51 | memory_oper: write 52 | - <<: *params 53 | gaussian_stddev: 4.28 54 | memory_oper: write 55 | 56 | migrate_cmd: sys-refs 57 | migrate_config: sys-refs-sysbench-memory-1t.yaml 58 | 59 | skip_baseline_run: true 60 | skip_migration_run: true 61 | #skip_dram_baseline_run: true 62 | # one_way_migrate: true 63 | no_eatmem: true 64 | 65 | emon_base_dir: "/opt/intel/sep" 66 | aepwatch_base_dir: "/opt/intel/ipmwatch" 67 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-2lm-32t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=32 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | #qemu_numactl: "-C 24-47,72-79 -m 1" 13 | qemu_numactl: "-N 1 -m 1" 14 | 15 | #migrate_numactl: "-C 24-47,72-79 -m 1" 16 | 17 | ratios: # PMEM:DRAM 18 | #- 0 # pure DRAM 19 | - 1 20 | # - 2 # 2:1 PMEM:DRAM 21 | # - 4 22 | # - 999 # pure PMEM 23 | 24 | 25 | workload_script: run-sysbench-memory.sh 26 | 27 | workload_params: 28 | - ¶ms 29 | time: 3600 30 | threads: 32 31 | memory_block_size: 244224M 32 | memory_scope: global 33 | rand_type: gaussian 34 | gaussian_stddev: 12.8 # 20% 35 | memory_oper: write 36 | #- <<: *params 37 | #gaussian_stddev: 25.7 # 60% 38 | - <<: *params 39 | gaussian_stddev: 4.28 # 60% 40 | 41 | #memory_oper: read 42 | 43 | #- <<: *params 44 | # gaussian_stddev: 6.43 # 40% 45 | # memory_oper: read 46 | #- <<: *params 47 | # gaussian_stddev: 4.28 # 60% 48 | # memory_oper: read 49 | #- <<: *params 50 | # gaussian_stddev: 25.7 51 | # memory_oper: write 52 | #- <<: *params 53 | # gaussian_stddev: 12.8 54 | # memory_oper: write 55 | #- <<: *params 56 | # gaussian_stddev: 6.43 57 | # memory_oper: write 58 | #- <<: *params 59 | # gaussian_stddev: 4.28 60 | # memory_oper: write 61 | 62 | migrate_cmd: sys-refs 63 | migrate_config: sys-refs-sysbench-memory-32t.yaml 64 | 65 | skip_baseline_run: true 66 | skip_migration_run: true 67 | #skip_dram_baseline_run: true 68 | # one_way_migrate: true 69 | no_eatmem: true 70 | 71 | emon_base_dir: "/opt/intel/sep" 72 | aepwatch_base_dir: "/opt/intel/ipmwatch" 73 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-2lm-4t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-27 -m 1" 13 | migrate_numactl: "-C 24-27 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | - 1 18 | # - 2 # 2:1 PMEM:DRAM 19 | # - 4 20 | # - 999 # pure PMEM 21 | 22 | workload_script: run-sysbench-memory.sh 23 | 24 | 25 | workload_params: 26 | - ¶ms 27 | time: 3600 28 | threads: 4 29 | memory_block_size: 61056M 30 | memory_scope: global 31 | rand_type: gaussian 32 | gaussian_stddev: 25.7 # 10% 33 | memory_oper: read 34 | - <<: *params 35 | gaussian_stddev: 12.8 # 20% 36 | memory_oper: read 37 | - <<: *params 38 | gaussian_stddev: 6.43 # 40% 39 | memory_oper: read 40 | - <<: *params 41 | gaussian_stddev: 4.28 # 60% 42 | memory_oper: read 43 | - <<: *params 44 | gaussian_stddev: 25.7 45 | memory_oper: write 46 | - <<: *params 47 | gaussian_stddev: 12.8 48 | memory_oper: write 49 | - <<: *params 50 | gaussian_stddev: 6.43 51 | memory_oper: write 52 | - <<: *params 53 | gaussian_stddev: 4.28 54 | memory_oper: write 55 | 56 | migrate_cmd: sys-refs 57 | migrate_config: sys-refs-sysbench-memory-4t.yaml 58 | 59 | skip_baseline_run: true 60 | skip_migration_run: true 61 | #skip_dram_baseline_run: true 62 | # one_way_migrate: true 63 | no_eatmem: true 64 | 65 | emon_base_dir: "/opt/intel/sep" 66 | aepwatch_base_dir: "/opt/intel/ipmwatch" 67 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-kunpeng-16t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-39 -m 3" 13 | migrate_numactl: "-C 24-39 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | #- 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | 25 | workload_params: 26 | - ¶ms 27 | time: 3600 28 | threads: 16 29 | memory_block_size: 244224M 30 | memory_scope: global 31 | rand_type: gaussian 32 | gaussian_stddev: 25.7 # 10% 33 | memory_oper: write 34 | #- <<: *params 35 | #gaussian_stddev: 4.28 # 60% 36 | #memory_oper: write 37 | #- <<: *params 38 | #gaussian_stddev: 25.7 # 10% 39 | #memory_oper: write 40 | 41 | #- <<: *params 42 | # gaussian_stddev: 4.28 # 60% 43 | # memory_oper: read 44 | #- <<: *params 45 | # gaussian_stddev: 25.7 46 | # memory_oper: write 47 | #- <<: *params 48 | # gaussian_stddev: 12.8 49 | # memory_oper: write 50 | #- <<: *params 51 | # gaussian_stddev: 6.43 52 | # memory_oper: write 53 | #- <<: *params 54 | # gaussian_stddev: 4.28 55 | # memory_oper: write 56 | 57 | migrate_cmd: sys-refs 58 | migrate_config: sys-refs-sysbench-memory-16t.yaml 59 | 60 | skip_baseline_run: true 61 | skip_dram_baseline_run: true 62 | one_way_migrate: true 63 | 64 | emon_base_dir: "/opt/intel/sep" 65 | aepwatch_base_dir: "/opt/intel/ipmwatch" 66 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-kunpeng-1t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24 -m 3" 13 | migrate_numactl: "-C 24 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory-1t.sh 24 | 25 | workload_params: 26 | - ¶ms 27 | time: 60 28 | threads: 1 29 | memory_block_size: 244224M 30 | memory_scope: global 31 | rand_type: gaussian 32 | gaussian_stddev: 25.7 # 10% 33 | memory_oper: read 34 | - <<: *params 35 | gaussian_stddev: 12.8 # 20% 36 | memory_oper: read 37 | - <<: *params 38 | gaussian_stddev: 6.43 # 40% 39 | memory_oper: read 40 | - <<: *params 41 | gaussian_stddev: 4.28 # 60% 42 | memory_oper: read 43 | - <<: *params 44 | gaussian_stddev: 25.7 45 | memory_oper: write 46 | - <<: *params 47 | gaussian_stddev: 12.8 48 | memory_oper: write 49 | - <<: *params 50 | gaussian_stddev: 6.43 51 | memory_oper: write 52 | - <<: *params 53 | gaussian_stddev: 4.28 54 | memory_oper: write 55 | 56 | migrate_cmd: sys-refs 57 | migrate_config: sys-refs-sysbench-memory-1t.yaml 58 | 59 | skip_baseline_run: true 60 | skip_dram_baseline_run: true 61 | one_way_migrate: true 62 | emon_base_dir: "/opt/intel/sep" 63 | aepwatch_base_dir: "/opt/intel/ipmwatch" 64 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-kunpeng-32t.yaml: -------------------------------------------------------------------------------- 1 | OBqemu_script: kvm.sh 2 | qemu_smp: cpus=32 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-47,72-79 -m 3" 13 | migrate_numactl: "-C 24-27,72-79 -m 1" 14 | 15 | 16 | 17 | ratios: # PMEM:DRAM 18 | #- 0 # pure DRAM 19 | #- 1 20 | #- 2 # 2:1 PMEM:DRAM 21 | #- 3 22 | - 4 23 | # - 999 # pure PMEM 24 | 25 | workload_script: run-sysbench-memory.sh 26 | 27 | workload_params: 28 | - ¶ms 29 | time: 3600 30 | threads: 32 31 | memory_block_size: 244224M 32 | memory_scope: global 33 | rand_type: gaussian 34 | gaussian_stddev: 12.8 # 20% 35 | memory_oper: write 36 | #- <<: *params 37 | # gaussian_stddev: 4.28 # 60% 38 | # memory_oper: write 39 | #- <<: *params 40 | # gaussian_stddev: 12.8 # 20% 41 | # memory_oper: write 42 | # 43 | #- <<: *params 44 | # gaussian_stddev: 6.43 # 40% 45 | # memory_oper: read 46 | #- <<: *params 47 | # gaussian_stddev: 4.28 # 60% 48 | # memory_oper: read 49 | #- <<: *params 50 | # gaussian_stddev: 3.22 # 80% 51 | # memory_oper: read 52 | #- <<: *params 53 | # gaussian_stddev: 25.7 54 | # memory_oper: write 55 | #- <<: *params 56 | # gaussian_stddev: 12.8 57 | # memory_oper: write 58 | #- <<: *params 59 | # gaussian_stddev: 6.43 60 | # memory_oper: write 61 | #- <<: *params 62 | # gaussian_stddev: 4.28 63 | # memory_oper: write 64 | #- <<: *params 65 | # gaussian_stddev: 3.22 66 | # memory_oper: write 67 | 68 | migrate_cmd: sys-refs 69 | migrate_config: sys-refs-sysbench-memory-32t.yaml 70 | 71 | skip_baseline_run: true 72 | skip_dram_baseline_run: true 73 | one_way_migrate: true 74 | 75 | 76 | emon_base_dir: "/opt/intel/sep" 77 | aepwatch_base_dir: "/opt/intel/ipmwatch" 78 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/scheme-sysbench-memory-kunpeng-4t.yaml: -------------------------------------------------------------------------------- 1 | qemu_script: kvm.sh 2 | qemu_smp: cpus=24 3 | qemu_mem: 256G 4 | 5 | dram_nodes: 6 | #- 0 7 | - 1 8 | pmem_nodes: 9 | #- 2 10 | - 3 11 | 12 | qemu_numactl: "-C 24-27 -m 3" 13 | migrate_numactl: "-C 24-27 -m 1" 14 | 15 | ratios: # PMEM:DRAM 16 | #- 0 # pure DRAM 17 | #- 1 18 | #- 2 # 2:1 PMEM:DRAM 19 | - 3 20 | - 4 21 | # - 999 # pure PMEM 22 | 23 | workload_script: run-sysbench-memory.sh 24 | 25 | workload_params: 26 | - ¶ms 27 | time: 3600 28 | threads: 4 29 | memory_block_size: 61056M 30 | memory_scope: global 31 | rand_type: gaussian 32 | gaussian_stddev: 25.7 # 10% 33 | memory_oper: read 34 | - <<: *params 35 | gaussian_stddev: 12.8 # 20% 36 | memory_oper: read 37 | - <<: *params 38 | gaussian_stddev: 6.43 # 40% 39 | memory_oper: read 40 | - <<: *params 41 | gaussian_stddev: 4.28 # 60% 42 | memory_oper: read 43 | - <<: *params 44 | gaussian_stddev: 25.7 45 | memory_oper: write 46 | - <<: *params 47 | gaussian_stddev: 12.8 48 | memory_oper: write 49 | - <<: *params 50 | gaussian_stddev: 6.43 51 | memory_oper: write 52 | - <<: *params 53 | gaussian_stddev: 4.28 54 | memory_oper: write 55 | 56 | migrate_cmd: sys-refs 57 | migrate_config: sys-refs-sysbench-memory-4t.yaml 58 | 59 | skip_baseline_run: true 60 | skip_dram_baseline_run: true 61 | one_way_migrate: true 62 | 63 | emon_base_dir: "/opt/intel/sep" 64 | aepwatch_base_dir: "/opt/intel/ipmwatch" 65 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/sys-refs-sysbench-memory-16t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | max_threads: 4 4 | exit_on_exceeded: 1 5 | # exit_on_stabilized: 1 6 | policies: 7 | # - name: usemem 8 | # placement: dram 9 | # - name: sysbench 10 | # migration: hot 11 | # dump_distribution: true 12 | - name: qemu-system-x86 13 | migration: hot 14 | dump_distribution: true 15 | 16 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/sys-refs-sysbench-memory-1t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | initial_interval: 10 4 | max_threads: 1 5 | # interval: 10 6 | exit_on_exceeded: 1 7 | exit_on_stabilized: 1 8 | 9 | policies: 10 | # - name: usemem 11 | # placement: dram 12 | # - name: sysbench 13 | # migration: hot 14 | # dump_distribution: true 15 | - name: qemu-system-x86 16 | migration: hot 17 | dump_distribution: true 18 | 19 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/sys-refs-sysbench-memory-32t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | max_threads: 4 4 | exit_on_exceeded: 1 5 | # exit_on_stabilized: 1 6 | policies: 7 | # - name: usemem 8 | # placement: dram 9 | # - name: sysbench 10 | # migration: hot 11 | # dump_distribution: true 12 | - name: qemu-system-x86 13 | migration: hot 14 | dump_distribution: true 15 | 16 | -------------------------------------------------------------------------------- /tests/gaussian-stddev-global/sys-refs-sysbench-memory-4t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | # initial_interval: 5 4 | max_threads: 1 5 | # interval: 5 6 | exit_on_exceeded: 1 7 | # exit_on_stabilized: 1 8 | policies: 9 | # - name: usemem 10 | # placement: dram 11 | # - name: sysbench 12 | # migration: hot 13 | # dump_distribution: true 14 | - name: qemu-system-x86 15 | migration: hot 16 | dump_distribution: true 17 | 18 | -------------------------------------------------------------------------------- /tests/grc-conf.sys-refs: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (c) 2018 Intel Corporation 5 | # 6 | # Authors: Fengguang Wu 7 | # 8 | 9 | # Workload 10 | regexp= -- (.*) 11 | colours=default,bright_green 12 | - 13 | # sysbench 14 | regexp=^Running .* options:$ 15 | colours=bright_magenta 16 | - 17 | # sysbench 18 | regexp=[0-9.]+ MiB/sec 19 | colours=bright_blue 20 | - 21 | # migrator 22 | regexp=/c/[a-zA-Z0-9./ -]* 23 | colours=bright_cyan 24 | - 25 | # migrator 26 | regexp=.* anon .* 27 | colours=yellow 28 | - 29 | # sys-refs 30 | regexp=^(Scan result:|DRAM page distribution across 10 VMA slots:|Anonymous page distribution across NUMA nodes:) 31 | colours=on_blue bold white 32 | - 33 | # sys-refs 34 | regexp=^Starting page table scans: 35 | colours=on_red bold yellow 36 | - 37 | # sys-refs 38 | regexp=^Sleeping for ([0-9.]+) seconds 39 | colours=bold cyan,bold white 40 | - 41 | # sys-refs 42 | regexp=^ *\d+ +\d+% (\|#+)$ 43 | colours=default,on_magenta bold white 44 | -------------------------------------------------------------------------------- /tests/kernel-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # - nr_task 3 | # - runtime 4 | # - build_kconfig 5 | # - target 6 | 7 | ## Build linux kernel 8 | # 9 | # SPDX-License-Identifier: GPL-2.0 10 | # 11 | # Copyright (c) 2018 Intel Corporation 12 | # 13 | # Authors: Peng Bo 14 | # 15 | 16 | : ${nr_task:=8} 17 | : ${runtime:=300} 18 | : ${build_kconfig:=defconfig} 19 | : ${BENCHMARK_ROOT:=/root/linux} 20 | : ${dir_name:=/root/pengbo-workspace/kernel-build} 21 | : ${script_name:=script} 22 | 23 | [ -n "$target" ] || target=vmlinux 24 | log_echo() 25 | { 26 | date=$(date +'%F %T') 27 | echo "$date $@" 28 | } 29 | 30 | log_eval() 31 | { 32 | log_echo "$@" 33 | eval "$@" 34 | } 35 | 36 | log_cmd() 37 | { 38 | log_echo "$@" 39 | "$@" 40 | } 41 | 42 | setup_sys() 43 | { 44 | echo $thp > /sys/kernel/mm/transparent_hugepage/enabled 45 | echo 0 > /proc/sys/kernel/numa_balancing 46 | } 47 | 48 | numa_cmd() 49 | { 50 | local node=$1 51 | numactl -m $node 52 | } 53 | 54 | 55 | run_kbuild() 56 | { 57 | local node=$1 58 | start_time=$(date +%s) 59 | iterations=0 60 | while true; do 61 | echo 3 > /proc/sys/vm/drop_caches 62 | numactl -m $node make mrproper | tee -a ${log_file} 63 | numactl -m $node make $build_kconfig | tee -a ${log_file} 64 | numactl -m $node make -j $nr_task $target 2> /dev/null | tee -a ${log_file} 65 | iterations=$((iterations + 1)) 66 | now=$(date +%s) 67 | [ $((now - start_time)) -gt "$runtime" ] && break 68 | done 69 | echo "iterations: $iterations" 70 | echo "runtime: $((now - start_time))" 71 | } 72 | 73 | cd $BENCHMARK_ROOT || { 74 | echo "ERROR: no kernel source code in $BENCHMARK_ROOT" 1>&2 75 | exit 1 76 | } 77 | 78 | thp=always 79 | setup_sys 80 | run_kbuild 1 81 | -------------------------------------------------------------------------------- /tests/kvm-hugetlb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # Yao Yuan 9 | # 10 | 11 | [[ $kernel ]] || kernel=$1 12 | [[ $kernel ]] || kernel=( 13 | /c/linux/arch/x86/boot/bzImage 14 | ) 15 | 16 | [[ $qemu_cmd ]] || qemu_cmd=qemu-system-x86_64 17 | [[ $qemu_smp ]] || qemu_smp='cpus=32' 18 | [[ $qemu_mem ]] || qemu_mem='128G' 19 | [[ $qemu_ssh ]] || qemu_ssh='2222' 20 | [[ $qemu_log ]] && qemu_log=file:$qemu_log 21 | [[ $qemu_log ]] || qemu_log=stdio 22 | [[ $interleave ]] && numactl="numactl --interleave=$interleave" 23 | 24 | kvm=( 25 | $numactl 26 | $qemu_cmd 27 | -machine pc,nvdimm 28 | -cpu host 29 | -enable-kvm 30 | -kernel $kernel 31 | -smp $qemu_smp 32 | -numa node,nodeid=0,memdev=hgtlb0 33 | -m $qemu_mem 34 | -mem-prealloc 35 | -object memory-backend-file,id=hgtlb0,size=$qemu_mem,mem-path=/dev/hugepages 36 | -device virtio-net-pci,netdev=net0 37 | -netdev user,id=net0,hostfwd=tcp::$qemu_ssh-:22 38 | -no-reboot 39 | -serial $qemu_log 40 | -display none 41 | -monitor null 42 | ) 43 | 44 | append=( 45 | ip=dhcp 46 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 47 | root=/dev/nfs 48 | ignore_loglevel 49 | sysrq_always_enabled 50 | panic=10 51 | # prompt_ramdisk=0 52 | earlyprintk=ttyS0,115200 53 | console=ttyS0,115200 54 | console=tty0 55 | vga=normal 56 | # root=/dev/ram0 57 | rw 58 | ) 59 | 60 | echo "kvm=${kvm[@]}" 61 | exec "${kvm[@]}" --append "${append[*]}" 62 | -------------------------------------------------------------------------------- /tests/kvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | [[ $kernel ]] || kernel=$1 11 | [[ $kernel ]] || kernel=( 12 | /c/linux/arch/x86/boot/bzImage 13 | ) 14 | 15 | [[ $qemu_cmd ]] || qemu_cmd=qemu-system-x86_64 16 | [[ $qemu_smp ]] || qemu_smp='cpus=32' 17 | [[ $qemu_mem ]] || qemu_mem='128G' 18 | [[ $qemu_ssh ]] || qemu_ssh='2222' 19 | [[ $qemu_log ]] && qemu_log=file:$qemu_log 20 | [[ $qemu_log ]] || qemu_log=stdio 21 | [[ $interleave ]] && numactl="numactl --interleave=$interleave" 22 | [[ $qemu_numactl ]] && numactl="numactl $qemu_numactl" 23 | 24 | kvm=( 25 | $numactl 26 | $qemu_cmd 27 | -machine pc,nvdimm 28 | -cpu host 29 | -enable-kvm 30 | -kernel $kernel 31 | -smp $qemu_smp 32 | -m $qemu_mem 33 | -device virtio-net-pci,netdev=net0 34 | -netdev user,id=net0,hostfwd=tcp::$qemu_ssh-:22 35 | -no-reboot 36 | -serial $qemu_log 37 | -display none 38 | -monitor null 39 | ) 40 | 41 | append=( 42 | ip=dhcp 43 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 44 | root=/dev/nfs 45 | ignore_loglevel 46 | sysrq_always_enabled 47 | panic=10 48 | # prompt_ramdisk=0 49 | earlyprintk=ttyS0,115200 50 | console=ttyS0,115200 51 | console=tty0 52 | vga=normal 53 | # root=/dev/ram0 54 | rw 55 | ) 56 | 57 | exec "${kvm[@]}" --append "${append[*]}" 58 | -------------------------------------------------------------------------------- /tests/run-sysbench-memory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | [[ $interleave ]] && numactl="numactl --interleave=$interleave --" 11 | 12 | sysbench_cmd=( 13 | $numactl 14 | stdbuf -oL 15 | sysbench 16 | --time=$time 17 | memory 18 | --memory-block-size=$memory_block_size 19 | --memory-total-size=1024T 20 | --memory-scope=$memory_scope 21 | --memory-oper=$memory_oper 22 | --memory-access-mode=rnd 23 | --rand-type=$rand_type 24 | --rand-pareto-h=0.1 25 | --threads=$threads 26 | run 27 | ) 28 | 29 | echo "${sysbench_cmd[@]}" 30 | time "${sysbench_cmd[@]}" & 31 | 32 | [[ $pid_file ]] && echo $! > $pid_file 33 | -------------------------------------------------------------------------------- /tests/run-vm-tests.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | require_relative "../VMTest.rb" 11 | require_relative "../ExclusiveRun.rb" 12 | 13 | ExclusiveRun.new("/tmp/vm-tests.lock") do 14 | 15 | vm_test = VMTest.new 16 | vm_test.run_all(ARGV[0] || "scheme-sysbench-memory.yaml") 17 | 18 | end 19 | -------------------------------------------------------------------------------- /tests/scheme-sysbench-memory-32t-hugetlb.yaml: -------------------------------------------------------------------------------- 1 | # setup notes: 2 | # HW: 2S machine, 1 DRAM node and 1 PMEM node per socket. 3 | # GRUB: numa=fake=4U default_hugepagesz=2M hugepagesz=2M hugepages=256000 4 | 5 | dram_nodes: 6 | - 0 7 | - 1 8 | - 2 9 | - 3 10 | - 4 11 | - 5 12 | - 6 13 | - 7 14 | pmem_nodes: 15 | - 8 16 | - 9 17 | - 10 18 | - 11 19 | - 12 20 | - 13 21 | - 14 22 | - 15 23 | ratios: # PMEM:DRAM 24 | - 0 # pure DRAM 25 | - 1 26 | - 2 # 2:1 PMEM:DRAM 27 | - 4 28 | - 999 # pure PMEM 29 | qemu_script: kvm-hugetlb.sh 30 | qemu_ssh: 2225 31 | qemu_mem: 70G # workload size + safety margin 32 | #skip_baseline_run: true 33 | hugetlb: true 34 | one_way_migrate: 1 35 | #transparent_hugepage: always 36 | workload_script: run-sysbench-memory.sh 37 | workload_params: 38 | - ¶ms 39 | time: 1200 40 | threads: 32 41 | memory_block_size: 2G 42 | memory_scope: local 43 | #memory_block_size: 64G 44 | #memory_scope: global 45 | rand_type: gaussian 46 | memory_oper: read 47 | - <<: *params 48 | memory_oper: write 49 | migrate_cmd: sys-refs 50 | migrate_config: sys-refs-sysbench-memory-32t.yaml 51 | -------------------------------------------------------------------------------- /tests/scheme-sysbench-memory-32t.yaml: -------------------------------------------------------------------------------- 1 | # setup notes: 2 | # HW: 2S machine, 1 DRAM node and 1 PMEM node per socket. 3 | # GRUB: numa=fake=4U 4 | 5 | dram_nodes: 6 | - 0 7 | - 1 8 | - 2 9 | - 3 10 | - 4 11 | - 5 12 | - 6 13 | - 7 14 | pmem_nodes: 15 | - 8 16 | - 9 17 | - 10 18 | - 11 19 | - 12 20 | - 13 21 | - 14 22 | - 15 23 | ratios: # PMEM:DRAM 24 | - 0 # pure DRAM 25 | - 1 26 | - 2 # 2:1 PMEM:DRAM 27 | - 4 28 | - 999 # pure PMEM 29 | qemu_script: kvm.sh 30 | qemu_ssh: 2225 31 | #skip_baseline_run: true 32 | one_way_migrate: 1 33 | workload_script: run-sysbench-memory.sh 34 | workload_params: 35 | - ¶ms 36 | time: 1200 37 | threads: 32 38 | memory_block_size: 1G 39 | memory_scope: local 40 | rand_type: gaussian 41 | memory_oper: read 42 | - <<: *params 43 | memory_oper: write 44 | migrate_cmd: sys-refs 45 | migrate_config: sys-refs-sysbench-memory-32t.yaml 46 | -------------------------------------------------------------------------------- /tests/scheme-sysbench-memory.yaml: -------------------------------------------------------------------------------- 1 | dram_nodes: 2 | - 0 3 | # - 1 4 | pmem_nodes: 5 | # - 0 6 | - 1 7 | # - 2 8 | # - 3 9 | ratios: # PMEM:DRAM 10 | - 0 # pure DRAM 11 | - 1 12 | # - 2 # 2:1 PMEM:DRAM 13 | # - 4 14 | - 999 # pure PMEM 15 | workload_script: run-sysbench-memory.sh 16 | workload_params: 17 | - ¶ms 18 | time: 60 19 | threads: 1 20 | memory_block_size: 1G 21 | memory_scope: local 22 | rand_type: gaussian 23 | memory_oper: read 24 | - <<: *params 25 | memory_oper: write 26 | migrate_cmd: sys-refs 27 | migrate_config: test-sysbench/sysbench-memory.yaml 28 | -------------------------------------------------------------------------------- /tests/sys-refs-example-config.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | interval: 2 # interval between scans 3 | sleep: 3 # wait time between scan round 4 | loop: 4 # total scan round to run 5 | output: /tmp/date/.log # log file path name 6 | bandwidth_mbps: 1024 # migration speed throttler, Mb per second 7 | dram_percent: 51 # 100 * (DRAM / total memory) 8 | 9 | policies: 10 | - pid: 1024 # process id filter 11 | migration: cold # hot or both 12 | 13 | - name: zsh # process name filter 14 | placement: dram # or pmem 15 | -------------------------------------------------------------------------------- /tests/sys-refs-sysbench-memory-32t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: false 3 | exit_on_stabilized: 1 4 | initial_interval: 1 5 | 6 | policies: 7 | - name: usemem 8 | placement: dram 9 | - name: sysbench 10 | migration: hot 11 | dump_distribution: true 12 | - name: qemu-system-x86 13 | migration: hot 14 | -------------------------------------------------------------------------------- /tests/sysbench/config/sysbench-memory-hugetlb-1t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | hugetlb: 1 4 | initial_interval: 1 5 | exit_on_exceeded: 1 6 | policies: 7 | # - name: sysbench 8 | # migration: hot 9 | # dump_distribution: true 10 | - name: qemu-system-x86 11 | migration: hot 12 | dump_distribution: true 13 | -------------------------------------------------------------------------------- /tests/sysbench/config/sysbench-memory-hugetlb-32t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | hugetlb: 1 4 | max_threads: 32 5 | initial_interval: 1 6 | exit_on_exceeded: 1 7 | policies: 8 | # - name: sysbench 9 | # migration: hot 10 | # dump_distribution: true 11 | - name: qemu-system-x86 12 | migration: hot 13 | dump_distribution: true 14 | -------------------------------------------------------------------------------- /tests/sysbench/config/sysbench-memory-thp-1t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | thp: 1 4 | initial_interval: 1 5 | exit_on_exceeded: 1 6 | policies: 7 | # - name: sysbench 8 | # migration: hot 9 | # dump_distribution: true 10 | - name: qemu-system-x86 11 | migration: hot 12 | dump_distribution: true 13 | -------------------------------------------------------------------------------- /tests/sysbench/config/sysbench-memory-thp-32t.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: true 3 | thp: 1 4 | max_threads: 32 5 | initial_interval: 1 6 | exit_on_exceeded: 1 7 | split_rss_size: 1G 8 | policies: 9 | # - name: sysbench 10 | # migration: hot 11 | # dump_distribution: true 12 | - name: qemu-system-x86 13 | migration: hot 14 | dump_distribution: true 15 | -------------------------------------------------------------------------------- /tests/sysbench/run-sysbench-hugetlb.sh: -------------------------------------------------------------------------------- 1 | ../run-vm-tests.rb scheme-sysbench-memory-hugetlb-1t.yaml 2 | ../run-vm-tests.rb scheme-sysbench-memory-hugetlb-32t.yaml 3 | -------------------------------------------------------------------------------- /tests/sysbench/run-sysbench-memory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | [[ $interleave ]] && numactl="numactl --interleave=$interleave --" 11 | 12 | sysbench_cmd=( 13 | $numactl 14 | stdbuf -oL 15 | sysbench 16 | --time=$time 17 | memory 18 | --memory-block-size=$memory_block_size 19 | --memory-total-size=1024T 20 | --memory-scope=$memory_scope 21 | --memory-oper=$memory_oper 22 | --memory-access-mode=rnd 23 | --rand-type=$rand_type 24 | --rand-pareto-h=0.1 25 | --threads=$threads 26 | run 27 | ) 28 | 29 | echo "${sysbench_cmd[@]}" 30 | time "${sysbench_cmd[@]}" & 31 | 32 | [[ $pid_file ]] && echo $! > $pid_file 33 | -------------------------------------------------------------------------------- /tests/sysbench/run-sysbench-thp.sh: -------------------------------------------------------------------------------- 1 | ../run-vm-tests.rb scheme-sysbench-memory-thp-1t.yaml 2 | ../run-vm-tests.rb scheme-sysbench-memory-thp-32t.yaml 3 | -------------------------------------------------------------------------------- /tests/sysbench/scheme-sysbench-memory-hugetlb-1t.yaml: -------------------------------------------------------------------------------- 1 | dram_nodes: 2 | - 0 3 | - 1 4 | pmem_nodes: 5 | - 2 6 | - 3 7 | ratios: # PMEM:DRAM 8 | - 0 # pure DRAM 9 | - 1 10 | - 2 # 2:1 PMEM:DRAM 11 | - 4 12 | - 999 # pure PMEM 13 | one_way_migrate: true 14 | transparent_hugepage: always 15 | qemu_cmd: qemu-system-x86_64 16 | qemu_script: ../vm/kvm-hugetlb.sh 17 | workload_script: run-sysbench-memory.sh 18 | workload_params: 19 | - ¶ms 20 | time: 1200 21 | threads: 1 22 | memory_block_size: 8G 23 | memory_scope: local 24 | rand_type: gaussian 25 | memory_oper: read 26 | - <<: *params 27 | memory_oper: write 28 | migrate_cmd: sys-refs 29 | migrate_config: config/sysbench-memory-hugetlb-1t.yaml 30 | -------------------------------------------------------------------------------- /tests/sysbench/scheme-sysbench-memory-hugetlb-32t.yaml: -------------------------------------------------------------------------------- 1 | dram_nodes: 2 | - 0 3 | - 1 4 | pmem_nodes: 5 | - 2 6 | - 3 7 | ratios: # PMEM:DRAM 8 | - 0 # pure DRAM 9 | - 1 10 | - 2 # 2:1 PMEM:DRAM 11 | - 4 12 | - 999 # pure PMEM 13 | one_way_migrate: true 14 | transparent_hugepage: always 15 | qemu_cmd: qemu-system-x86_64 16 | qemu_script: ../vm/kvm-hugetlb.sh 17 | workload_script: run-sysbench-memory.sh 18 | workload_params: 19 | - ¶ms 20 | time: 1200 21 | threads: 32 22 | memory_block_size: 1G 23 | memory_scope: local 24 | rand_type: gaussian 25 | memory_oper: read 26 | - <<: *params 27 | memory_oper: write 28 | migrate_cmd: sys-refs 29 | migrate_config: config/sysbench-memory-hugetlb-32t.yaml 30 | -------------------------------------------------------------------------------- /tests/sysbench/scheme-sysbench-memory-thp-1t.yaml: -------------------------------------------------------------------------------- 1 | dram_nodes: 2 | - 0 3 | - 1 4 | pmem_nodes: 5 | - 2 6 | - 3 7 | ratios: # PMEM:DRAM 8 | - 0 # pure DRAM 9 | - 1 10 | - 2 # 2:1 PMEM:DRAM 11 | - 4 12 | - 999 # pure PMEM 13 | one_way_migrate: true 14 | transparent_hugepage: always 15 | qemu_cmd: qemu-system-x86_64 16 | qemu_script: ../vm/kvm-image.sh 17 | workload_script: run-sysbench-memory.sh 18 | workload_params: 19 | - ¶ms 20 | time: 1200 21 | threads: 1 22 | memory_block_size: 8G 23 | memory_scope: local 24 | rand_type: gaussian 25 | memory_oper: read 26 | - <<: *params 27 | memory_oper: write 28 | migrate_cmd: sys-refs 29 | migrate_config: config/sysbench-memory-thp-1t.yaml 30 | -------------------------------------------------------------------------------- /tests/sysbench/scheme-sysbench-memory-thp-32t.yaml: -------------------------------------------------------------------------------- 1 | dram_nodes: 2 | - 0 3 | - 1 4 | pmem_nodes: 5 | - 2 6 | - 3 7 | ratios: # PMEM:DRAM 8 | - 0 # pure DRAM 9 | - 1 10 | - 2 # 2:1 PMEM:DRAM 11 | - 4 12 | - 999 # pure PMEM 13 | one_way_migrate: true 14 | transparent_hugepage: always 15 | qemu_cmd: qemu-system-x86_64 16 | qemu_script: ../vm/kvm-image.sh 17 | workload_script: run-sysbench-memory.sh 18 | workload_params: 19 | - ¶ms 20 | time: 1200 21 | threads: 32 22 | memory_block_size: 1G 23 | memory_scope: local 24 | rand_type: gaussian 25 | memory_oper: read 26 | - <<: *params 27 | memory_oper: write 28 | migrate_cmd: sys-refs 29 | migrate_config: config/sysbench-memory-thp-32t.yaml 30 | -------------------------------------------------------------------------------- /tests/test-sysbench/kvm-dax.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | kernel=$1 11 | [[ $kernel ]] || kernel=( 12 | /c/linux/arch/x86/boot/bzImage 13 | ) 14 | 15 | kvm=( 16 | #numactl -m 0 -- taskset -c 0-25 17 | qemu-system-x86_64 18 | -machine pc,nvdimm 19 | -cpu host 20 | -enable-kvm 21 | -kernel $kernel 22 | -smp 64 23 | -m 256G 24 | -object memory-backend-file,size=128G,share=on,mem-path=/dev/shm/qemu_node0,id=tmpfs-node0 25 | -numa node,cpus=0-31,nodeid=0,memdev=tmpfs-node0 26 | -object memory-backend-file,size=128G,share=on,mem-path=/dev/dax1.0,align=128M,id=dax-node1 27 | -numa node,cpus=32-63,nodeid=1,memdev=dax-node1 28 | -net nic,vlan=0,macaddr=00:00:00:00:00:00,model=virtio 29 | -net user,vlan=0,hostfwd=tcp::2225-:22 30 | -boot order=nc 31 | -no-reboot 32 | -watchdog i6300esb 33 | -serial stdio 34 | -display none 35 | -monitor null 36 | ) 37 | 38 | append=( 39 | ip=dhcp 40 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 41 | root=/dev/nfs 42 | debug 43 | sched_debug 44 | apic=debug 45 | ignore_loglevel 46 | sysrq_always_enabled 47 | panic=10 48 | # prompt_ramdisk=0 49 | earlyprintk=ttyS0,115200 50 | console=ttyS0,115200 51 | console=tty0 52 | vga=normal 53 | # root=/dev/ram0 54 | rw 55 | ) 56 | 57 | "${kvm[@]}" --append "${append[*]}" 58 | -------------------------------------------------------------------------------- /tests/test-sysbench/kvm-dram.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | kernel=$1 11 | [[ $kernel ]] || kernel=( 12 | /c/linux/arch/x86/boot/bzImage 13 | # /os/ubuntu/boot/vmlinuz-4.18.0-00108-g33582b3a8f52 14 | ) 15 | 16 | kvm=( 17 | #numactl -m 0 -- taskset -c 0-25 18 | qemu-system-x86_64 19 | -machine pc,nvdimm 20 | -cpu host 21 | -enable-kvm 22 | -kernel $kernel 23 | -smp 88 24 | -m 200G 25 | -object memory-backend-file,size=100G,share=on,mem-path=/dev/shm/qemu_node0,id=tmpfs-node0 26 | -numa node,cpus=0-43,nodeid=0,memdev=tmpfs-node0 27 | -object memory-backend-file,size=100G,share=on,mem-path=/dev/shm/qemu_node1,id=tmpfs-node1 28 | -numa node,cpus=44-87,nodeid=1,memdev=tmpfs-node1 29 | -net nic,vlan=0,macaddr=00:00:00:00:00:00,model=virtio 30 | -net user,vlan=0,hostfwd=tcp::2225-:22 31 | -boot order=nc 32 | -no-reboot 33 | -watchdog i6300esb 34 | -serial stdio 35 | -display none 36 | -monitor null 37 | ) 38 | 39 | append=( 40 | ip=dhcp 41 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 42 | root=/dev/nfs 43 | debug 44 | sched_debug 45 | apic=debug 46 | ignore_loglevel 47 | sysrq_always_enabled 48 | panic=10 49 | # prompt_ramdisk=0 50 | earlyprintk=ttyS0,115200 51 | console=ttyS0,115200 52 | console=tty0 53 | vga=normal 54 | # root=/dev/ram0 55 | rw 56 | ) 57 | 58 | "${kvm[@]}" --append "${append[*]}" 59 | -------------------------------------------------------------------------------- /tests/test-sysbench/kvm-numa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | kernel=$1 11 | [[ $kernel ]] || kernel=( 12 | /c/linux/arch/x86/boot/bzImage 13 | ) 14 | 15 | [[ $qemu_smp ]] || qemu_smp='cpus=1' 16 | [[ $qemu_mem ]] || qemu_mem='256G' 17 | 18 | kvm=( 19 | #numactl -m 0 -- taskset -c 0-25 20 | qemu-system-x86_64 21 | -machine pc,nvdimm 22 | -cpu host 23 | -enable-kvm 24 | -kernel $kernel 25 | -smp $qemu_smp 26 | -m $qemu_mem 27 | # DRAM numa node 28 | -object memory-backend-ram,size=32G,host-nodes=0,policy=bind,id=node0 29 | -numa node,cpus=0-31,nodeid=0,memdev=node0 30 | # PMEM numa node 31 | -object memory-backend-ram,size=64G,host-nodes=2,policy=bind,id=node1 32 | -numa node,cpus=32-63,nodeid=1,memdev=node1 33 | -net nic,vlan=0,macaddr=00:00:00:00:00:00,model=virtio 34 | -net user,vlan=0,hostfwd=tcp::2225-:22 35 | -boot order=nc 36 | -no-reboot 37 | -watchdog i6300esb 38 | -serial stdio 39 | -display none 40 | -monitor null 41 | ) 42 | 43 | append=( 44 | ip=dhcp 45 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 46 | root=/dev/nfs 47 | debug 48 | sched_debug 49 | apic=debug 50 | ignore_loglevel 51 | sysrq_always_enabled 52 | panic=10 53 | # prompt_ramdisk=0 54 | earlyprintk=ttyS0,115200 55 | console=ttyS0,115200 56 | console=tty0 57 | vga=normal 58 | # root=/dev/ram0 59 | rw 60 | ) 61 | 62 | "${kvm[@]}" --append "${append[*]}" 63 | -------------------------------------------------------------------------------- /tests/test-sysbench/sysbench-memory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | script_path=$(realpath $0) 11 | script_dir=$(dirname $script_path) 12 | tests_dir=$(dirname $script_dir) 13 | project_dir=$(dirname $tests_dir) 14 | cd "$tests_dir" || exit 15 | 16 | nr_node=$(numactl -H | head -n1 | cut -f2 -d' ') 17 | 18 | DRAM_NODE=0 19 | PMEM_NODE=$((nr_node / 2)) 20 | 21 | setup_sys() 22 | { 23 | echo $thp > /sys/kernel/mm/transparent_hugepage/enabled 24 | echo 0 > /proc/sys/kernel/numa_balancing 25 | 26 | lsmod | grep -q kvm_ept_idle || { 27 | modprobe kvm 28 | insmod $tests_dir/kvm-ept-idle.ko 29 | } 30 | } 31 | 32 | run_migrations() 33 | { 34 | local what=$1 35 | local i 36 | 37 | for i in $(seq 300) 38 | do 39 | sleep 1 40 | grep -q "Threads started" $log_file && break 41 | done 42 | 43 | #numactl -a $sysbench_pid 44 | 45 | sys_refs_cmd=( 46 | #strace 47 | #schedtool -R -p 20 48 | nice -n-20 49 | stdbuf -oL 50 | $project_dir/sys-refs 51 | #-vv 52 | #-l 6 53 | #-s 10 54 | #-i $interval 55 | -d $dram_percent 56 | -m $what 57 | -c $script_dir/sysbench-memory.yaml 58 | #-p $sysbench_pid 59 | ) 60 | 61 | #cat /proc/$sysbench_pid/smaps 62 | 63 | #echo sysbench_pid: $sysbench_pid 64 | echo "${sys_refs_cmd[@]}" 65 | /usr/bin/time -v "${sys_refs_cmd[@]}" 66 | #| stdbuf -i0 -oL grcat $project_dir/tests/grc-conf.sys-refs 67 | } 68 | 69 | run_test() 70 | { 71 | local suffix=$1 72 | local mempolicy="$2" 73 | 74 | sysbench_cmd=( 75 | #numactl 76 | #--interleave=all 77 | 78 | # perf stat 79 | # -e dTLB-load-misses,iTLB-load-misses 80 | # -- 81 | 82 | #/usr/bin/time -v 83 | 84 | sysbench 85 | #/usr/local/bin/sysbench-stats 86 | --time=$time 87 | memory 88 | --memory-block-size=$memory_block_size 89 | --memory-total-size=1024T 90 | --memory-scope=$memory_scope 91 | --memory-oper=$memory_oper 92 | --memory-access-mode=rnd 93 | --rand-type=$rand_type 94 | --rand-pareto-h=0.1 95 | --threads=$threads 96 | run 97 | ) 98 | 99 | log_file=$log_dir/$memory_oper-$rand_type-$threads-$thp.$suffix 100 | exec > $log_file 2>&1 101 | 102 | echo numactl $mempolicy -- "${sysbench_cmd[@]}" 103 | time numactl $mempolicy -- "${sysbench_cmd[@]}" & 104 | #| stdbuf -i0 -oL grcat $project_dir/tests/grc-conf.sys-refs & 105 | 106 | local sysbench_pid=$! 107 | 108 | trap "kill $sysbench_pid; exit" SIGINT SIGQUIT 109 | 110 | if [[ $suffix == 'h' ]]; then 111 | run_migrations hot 112 | elif [[ $suffix == 'c' ]]; then 113 | run_migrations cold 114 | elif [[ $suffix == 'b' ]]; then 115 | run_migrations both 116 | else 117 | sleep 2 118 | fi 119 | 120 | grep active_anon /sys/devices/system/node/node*/vmstat 121 | 122 | wait 123 | } 124 | 125 | run_tests() 126 | { 127 | setup_sys 128 | 129 | run_test h "--preferred=$PMEM_NODE" 130 | #run_test h "-m$PMEM_NODE" 131 | run_test $DRAM_NODE "-m$DRAM_NODE" 132 | run_test $PMEM_NODE "-m$PMEM_NODE" 133 | # run_test i "-i all" 134 | #run_test c "-m$DRAM_NODE" 135 | } 136 | 137 | log_dir=$script_dir/$(basename $0)-$(date +'%Y%m%d_%H%M%S') 138 | mkdir -p $log_dir 139 | cp -a $script_path $log_dir/ 140 | echo "less $log_dir/*.?" 141 | echo "tail -f $log_dir/*.h | grcat $project_dir/tests/grc-conf.sys-refs" 142 | 143 | thp=always 144 | thp=never 145 | mem=2G 146 | mem=64M 147 | mem=256M 148 | mem=1G 149 | if ((1)); then 150 | memory_scope=global 151 | mem=128G 152 | mem=64G 153 | mem=1G 154 | mem=16G 155 | mem=32G 156 | mem=8G 157 | else 158 | mem=256M 159 | mem=1G 160 | memory_scope=local 161 | fi 162 | dram_percent=20 163 | 164 | # sysbench params 165 | time=1800 166 | time=600 167 | rand_type=pareto 168 | rand_type=gaussian 169 | memory_oper=read 170 | memory_block_size=$mem 171 | threads=1 172 | threads=8 173 | threads=16 174 | threads=32 175 | threads=64 176 | threads=1 177 | 178 | run_tests 179 | memory_oper=write 180 | run_tests 181 | # this requires adjusting interval 182 | # thp=always 183 | # run_tests 184 | # memory_oper=read 185 | # run_tests 186 | #rand_type=zipfian 187 | #run_tests 188 | -------------------------------------------------------------------------------- /tests/test-sysbench/sysbench-memory.yaml: -------------------------------------------------------------------------------- 1 | options: 2 | dump_options: false 3 | exit_on_exceeded: 1 4 | exit_on_stabilized: 1 5 | 6 | policies: 7 | - name: usemem 8 | placement: dram 9 | - name: sysbench 10 | migration: hot 11 | dump_distribution: true 12 | - name: qemu-system-x86 13 | migration: hot 14 | -------------------------------------------------------------------------------- /tests/vm/kvm-dax.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | kernel=$1 11 | [[ $kernel ]] || kernel=( 12 | /c/linux/arch/x86/boot/bzImage 13 | ) 14 | 15 | kvm=( 16 | #numactl -m 0 -- taskset -c 0-25 17 | qemu-system-x86_64 18 | -machine pc,nvdimm 19 | -cpu host 20 | -enable-kvm 21 | -kernel $kernel 22 | -smp 64 23 | -m 256G 24 | -object memory-backend-file,size=128G,share=on,mem-path=/dev/shm/qemu_node0,id=tmpfs-node0 25 | -numa node,cpus=0-31,nodeid=0,memdev=tmpfs-node0 26 | -object memory-backend-file,size=128G,share=on,mem-path=/dev/dax1.0,align=128M,id=dax-node1 27 | -numa node,cpus=32-63,nodeid=1,memdev=dax-node1 28 | -net nic,vlan=0,macaddr=00:00:00:00:00:00,model=virtio 29 | -net user,vlan=0,hostfwd=tcp::2225-:22 30 | -boot order=nc 31 | -no-reboot 32 | -watchdog i6300esb 33 | -serial stdio 34 | -display none 35 | -monitor null 36 | ) 37 | 38 | append=( 39 | ip=dhcp 40 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 41 | root=/dev/nfs 42 | debug 43 | sched_debug 44 | apic=debug 45 | ignore_loglevel 46 | sysrq_always_enabled 47 | panic=10 48 | # prompt_ramdisk=0 49 | earlyprintk=ttyS0,115200 50 | console=ttyS0,115200 51 | console=tty0 52 | vga=normal 53 | # root=/dev/ram0 54 | rw 55 | ) 56 | 57 | "${kvm[@]}" --append "${append[*]}" 58 | -------------------------------------------------------------------------------- /tests/vm/kvm-dram.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | kernel=$1 11 | [[ $kernel ]] || kernel=( 12 | /c/linux/arch/x86/boot/bzImage 13 | # /os/ubuntu/boot/vmlinuz-4.18.0-00108-g33582b3a8f52 14 | ) 15 | 16 | kvm=( 17 | #numactl -m 0 -- taskset -c 0-25 18 | qemu-system-x86_64 19 | -machine pc,nvdimm 20 | -cpu host 21 | -enable-kvm 22 | -kernel $kernel 23 | -smp 88 24 | -m 200G 25 | -object memory-backend-file,size=100G,share=on,mem-path=/dev/shm/qemu_node0,id=tmpfs-node0 26 | -numa node,cpus=0-43,nodeid=0,memdev=tmpfs-node0 27 | -object memory-backend-file,size=100G,share=on,mem-path=/dev/shm/qemu_node1,id=tmpfs-node1 28 | -numa node,cpus=44-87,nodeid=1,memdev=tmpfs-node1 29 | -net nic,vlan=0,macaddr=00:00:00:00:00:00,model=virtio 30 | -net user,vlan=0,hostfwd=tcp::2225-:22 31 | -boot order=nc 32 | -no-reboot 33 | -watchdog i6300esb 34 | -serial stdio 35 | -display none 36 | -monitor null 37 | ) 38 | 39 | append=( 40 | ip=dhcp 41 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 42 | root=/dev/nfs 43 | debug 44 | sched_debug 45 | apic=debug 46 | ignore_loglevel 47 | sysrq_always_enabled 48 | panic=10 49 | # prompt_ramdisk=0 50 | earlyprintk=ttyS0,115200 51 | console=ttyS0,115200 52 | console=tty0 53 | vga=normal 54 | # root=/dev/ram0 55 | rw 56 | ) 57 | 58 | "${kvm[@]}" --append "${append[*]}" 59 | -------------------------------------------------------------------------------- /tests/vm/kvm-hugetlb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Firstly, mount hugetlbfs, for example: 4 | # mount -t hugetlbfs none /dev/hugepages 5 | # Then reserve some huge pages as below: 6 | # echo "10000" > /proc/sys/vm/nr_hugepages 7 | 8 | [[ $qemu_cmd ]] || qemu_cmd=qemu-system-x86_64 9 | [[ $qemu_smp ]] || qemu_smp='cpus=32' 10 | [[ $qemu_mem ]] || qemu_mem='128G' 11 | [[ $qemu_ssh ]] || qemu_ssh='2222' 12 | [[ $qemu_log ]] && qemu_log=file:$qemu_log 13 | [[ $qemu_log ]] || qemu_log=stdio 14 | [[ $interleave ]] && numactl="numactl --interleave=$interleave" 15 | 16 | kvm=( 17 | $numactl 18 | $qemu_cmd 19 | -machine pc,nvdimm 20 | -cpu host 21 | -enable-kvm 22 | -smp $qemu_smp 23 | -numa node,nodeid=0,memdev=hgtlb0 24 | -m $qemu_mem 25 | -object memory-backend-file,id=hgtlb0,size=$qemu_mem,mem-path=/dev/hugepages 26 | -device e1000,netdev=net0 27 | -netdev user,id=net0,hostfwd=tcp::$qemu_ssh-:22 28 | -drive if=virtio,file=images/guest.raw,format=raw 29 | -serial $qemu_log 30 | -display none 31 | -monitor none 32 | ) 33 | #echo "kvm=${kvm[@]}" 34 | exec "${kvm[@]}" 35 | -------------------------------------------------------------------------------- /tests/vm/kvm-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ $qemu_cmd ]] || qemu_cmd=qemu-system-x86_64 3 | [[ $qemu_smp ]] || qemu_smp='cpus=32' 4 | [[ $qemu_mem ]] || qemu_mem='128G' 5 | [[ $qemu_ssh ]] || qemu_ssh='2222' 6 | [[ $qemu_log ]] && qemu_log=file:$qemu_log 7 | [[ $qemu_log ]] || qemu_log=stdio 8 | [[ $interleave ]] && numactl="numactl --interleave=$interleave" 9 | 10 | kvm=( 11 | $numactl 12 | $qemu_cmd 13 | -machine pc,nvdimm 14 | -cpu host 15 | -enable-kvm 16 | -smp $qemu_smp 17 | -m $qemu_mem 18 | -device e1000,netdev=net0 19 | -netdev user,id=net0,hostfwd=tcp::$qemu_ssh-:22 20 | -drive if=virtio,file=images/guest.raw,format=raw 21 | -display none 22 | -serial $qemu_log 23 | -monitor none 24 | -vnc :3 -k en-us 25 | ) 26 | #echo "${kvm[@]}" 27 | exec ${kvm[@]} 28 | 29 | -------------------------------------------------------------------------------- /tests/vm/kvm-numa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: GPL-2.0 4 | # 5 | # Copyright (c) 2018 Intel Corporation 6 | # 7 | # Authors: Fengguang Wu 8 | # 9 | 10 | kernel=$1 11 | [[ $kernel ]] || kernel=( 12 | /c/linux/arch/x86/boot/bzImage 13 | ) 14 | 15 | [[ $qemu_smp ]] || qemu_smp='cpus=1' 16 | [[ $qemu_mem ]] || qemu_mem='256G' 17 | 18 | kvm=( 19 | #numactl -m 0 -- taskset -c 0-25 20 | qemu-system-x86_64 21 | -machine pc,nvdimm 22 | -cpu host 23 | -enable-kvm 24 | -kernel $kernel 25 | -smp $qemu_smp 26 | -m $qemu_mem 27 | # DRAM numa node 28 | -object memory-backend-ram,size=32G,host-nodes=0,policy=bind,id=node0 29 | -numa node,cpus=0-31,nodeid=0,memdev=node0 30 | # PMEM numa node 31 | -object memory-backend-ram,size=64G,host-nodes=2,policy=bind,id=node1 32 | -numa node,cpus=32-63,nodeid=1,memdev=node1 33 | -net nic,vlan=0,macaddr=00:00:00:00:00:00,model=virtio 34 | -net user,vlan=0,hostfwd=tcp::2225-:22 35 | -boot order=nc 36 | -no-reboot 37 | -watchdog i6300esb 38 | -serial stdio 39 | -display none 40 | -monitor null 41 | ) 42 | 43 | append=( 44 | ip=dhcp 45 | nfsroot=10.0.2.2:/os/ubuntu,v3,tcp,intr,rsize=524288 46 | root=/dev/nfs 47 | debug 48 | sched_debug 49 | apic=debug 50 | ignore_loglevel 51 | sysrq_always_enabled 52 | panic=10 53 | # prompt_ramdisk=0 54 | earlyprintk=ttyS0,115200 55 | console=ttyS0,115200 56 | console=tty0 57 | vga=normal 58 | # root=/dev/ram0 59 | rw 60 | ) 61 | 62 | "${kvm[@]}" --append "${append[*]}" 63 | -------------------------------------------------------------------------------- /utility.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | 4 | #{ 5 | # :cmd => @emon_bin + " -v", 6 | # :out => output_file("emon-v.dat"), 7 | # :err => output_file("emon-v.err"), 8 | # :wait => true, 9 | # :pid => nil, 10 | # :cwd => str 11 | #}, 12 | def new_proc(item) 13 | return if item[:skip] 14 | 15 | item[:cwd] = FileUtils.getwd() unless item[:cwd] 16 | 17 | puts "Running cmd: #{item[:cmd]}" 18 | item[:pid] = Process.spawn(item[:cmd], 19 | :out => [item[:out], 'w'], 20 | :err => [item[:err], 'w'], 21 | :chdir => item[:cwd]) rescue nil 22 | 23 | Process.wait item[:pid] if item[:wait] and item[:pid] 24 | end 25 | --------------------------------------------------------------------------------