cache line locking on AMD x86_64 utilising L3 CAT pseudo-locking
26

Configure Feed

Select the types of activity you want to include in your feed.

icepick / src / monitor.c
6.2 kB 228 lines
1#define _GNU_SOURCE 2#include "internal.h" 3#include <stdlib.h> 4#include <string.h> 5#include <stdio.h> 6#include <unistd.h> 7#include <sched.h> 8#include <time.h> 9#include <sys/ioctl.h> 10#include <sys/syscall.h> 11#include <linux/perf_event.h> 12#include <linux/hw_breakpoint.h> 13#include <x86intrin.h> 14#include <cpuid.h> 15 16#define AMD_L3_MISS_EVENT 0x0104 17#define PERF_CACHE_CONFIG (PERF_COUNT_HW_CACHE_LL | \ 18 (PERF_COUNT_HW_CACHE_OP_READ << 8) | \ 19 (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)) 20 21static bool is_amd(void) 22{ 23 uint32_t eax, ebx, ecx, edx; 24 __cpuid(0, eax, ebx, ecx, edx); 25 return (ebx == 0x68747541 && edx == 0x69746E65 && ecx == 0x444D4163); 26} 27 28static int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, 29 int group_fd, unsigned long flags) 30{ 31 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); 32} 33 34int monitor_perf_open(int cpu, bool amd) 35{ 36 struct perf_event_attr pe; 37 memset(&pe, 0, sizeof(pe)); 38 pe.size = sizeof(pe); 39 pe.disabled = 1; 40 pe.exclude_kernel = 1; 41 pe.exclude_hv = 1; 42 43 if (amd) { 44 pe.type = PERF_TYPE_RAW; 45 pe.config = AMD_L3_MISS_EVENT; 46 } else { 47 pe.type = PERF_TYPE_HW_CACHE; 48 pe.config = PERF_CACHE_CONFIG; 49 } 50 51 int fd = perf_event_open(&pe, -1, cpu, -1, 0); 52 if (fd < 0) 53 return -1; 54 55 ioctl(fd, PERF_EVENT_IOC_RESET, 0); 56 ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); 57 58 return fd; 59} 60 61static void nanosleep_relative(uint64_t ns) 62{ 63 struct timespec ts; 64 ts.tv_sec = ns / 1000000000ULL; 65 ts.tv_nsec = ns % 1000000000ULL; 66 nanosleep(&ts, NULL); 67} 68 69static uint64_t probe_latency(volatile char *ptr, size_t size) 70{ 71 size_t offset = ((size_t)rdtsc_start() % (size / CACHE_LINE_SIZE)) * CACHE_LINE_SIZE; 72 _mm_lfence(); 73 uint64_t start = rdtsc_start(); 74 (void)ptr[offset]; 75 uint64_t end = rdtsc_end(); 76 return cycles_to_ns(end - start); 77} 78 79static void reprime_region(icepick_monitor_t *mon) 80{ 81 icepick_region_t *r = mon->region; 82 83 cpu_set_t old_affinity, new_affinity; 84 CPU_ZERO(&new_affinity); 85 CPU_SET(mon->cpu, &new_affinity); 86 sched_getaffinity(0, sizeof(old_affinity), &old_affinity); 87 sched_setaffinity(0, sizeof(new_affinity), &new_affinity); 88 89 clos_associate_thread(mon->msr_fd, r->clos_id); 90 prime_region(r->ptr, r->size, r->prime_iterations, 91 r->prime_strategy, r->access_pattern, r->stride_bytes); 92 clos_associate_thread(mon->msr_fd, 0); 93 94 sched_setaffinity(0, sizeof(old_affinity), &old_affinity); 95 96 atomic_fetch_add(&mon->reprime_count, 1); 97} 98 99static void *monitor_thread(void *arg) 100{ 101 icepick_monitor_t *mon = arg; 102 icepick_region_t *r = mon->region; 103 104 cpu_set_t affinity; 105 CPU_ZERO(&affinity); 106 CPU_SET(mon->cpu, &affinity); 107 sched_setaffinity(0, sizeof(affinity), &affinity); 108 109 uint64_t prev_count = 0; 110 if (mon->perf_fd >= 0) { 111 read(mon->perf_fd, &prev_count, sizeof(prev_count)); 112 } 113 114 while (atomic_load(&mon->running)) { 115 if (mon->perf_fd >= 0) { 116 nanosleep_relative(mon->pmu_poll_interval_ns); 117 118 uint64_t curr_count; 119 if (read(mon->perf_fd, &curr_count, sizeof(curr_count)) != sizeof(curr_count)) 120 continue; 121 122 uint64_t delta = curr_count - prev_count; 123 prev_count = curr_count; 124 125 if (delta < mon->miss_threshold) 126 continue; 127 128 atomic_fetch_add(&mon->eviction_count, delta); 129 } 130 131 unsigned fast_probes = 0; 132 while (fast_probes < MONITOR_PROBE_CONFIRM_COUNT && atomic_load(&mon->running)) { 133 nanosleep_relative(mon->probe_interval_ns); 134 135 uint64_t lat = probe_latency(r->ptr, r->size); 136 if (lat > MONITOR_L3_LATENCY_THRESH_NS) { 137 reprime_region(mon); 138 break; 139 } 140 fast_probes++; 141 } 142 } 143 144 return NULL; 145} 146 147static int find_ccx_cpu(icepick_region_t *region) 148{ 149 return find_cpu_for_core_type(region->topo, region->core_type, region->numa_node); 150} 151 152int icepick_monitor_start_ex(icepick_region_t *region, uint64_t pmu_poll_ns, 153 uint64_t probe_ns, uint32_t miss_thresh, 154 icepick_monitor_t **out) 155{ 156 if (!region || !out) 157 return ICEPICK_E_INVALID; 158 159 icepick_monitor_t *mon = calloc(1, sizeof(*mon)); 160 if (!mon) 161 return ICEPICK_E_ALLOC; 162 163 mon->region = region; 164 mon->pmu_poll_interval_ns = pmu_poll_ns ? pmu_poll_ns : MONITOR_DEFAULT_PMU_POLL_NS; 165 mon->probe_interval_ns = probe_ns ? probe_ns : MONITOR_DEFAULT_PROBE_NS; 166 mon->miss_threshold = miss_thresh ? miss_thresh : MONITOR_DEFAULT_MISS_THRESH; 167 168 mon->cpu = find_ccx_cpu(region); 169 mon->msr_fd = msr_open(mon->cpu); 170 if (mon->msr_fd < 0) { 171 free(mon); 172 return ICEPICK_E_MSR; 173 } 174 175 mon->perf_fd = monitor_perf_open(mon->cpu, is_amd()); 176 mon->degraded = (mon->perf_fd < 0); 177 178 atomic_store(&mon->running, true); 179 atomic_store(&mon->eviction_count, 0); 180 atomic_store(&mon->reprime_count, 0); 181 182 if (pthread_create(&mon->thread, NULL, monitor_thread, mon) != 0) { 183 if (mon->perf_fd >= 0) 184 close(mon->perf_fd); 185 msr_close(mon->msr_fd); 186 free(mon); 187 return ICEPICK_E_THREAD; 188 } 189 190 *out = mon; 191 return 0; 192} 193 194int icepick_monitor_start(icepick_region_t *region, icepick_monitor_t **out) 195{ 196 return icepick_monitor_start_ex(region, 0, 0, 0, out); 197} 198 199int icepick_monitor_stop(icepick_monitor_t *mon) 200{ 201 if (!mon) 202 return ICEPICK_E_INVALID; 203 204 atomic_store(&mon->running, false); 205 pthread_join(mon->thread, NULL); 206 207 if (mon->perf_fd >= 0) 208 close(mon->perf_fd); 209 msr_close(mon->msr_fd); 210 free(mon); 211 212 return 0; 213} 214 215uint64_t icepick_monitor_eviction_count(const icepick_monitor_t *mon) 216{ 217 return mon ? atomic_load(&mon->eviction_count) : 0; 218} 219 220uint64_t icepick_monitor_reprime_count(const icepick_monitor_t *mon) 221{ 222 return mon ? atomic_load(&mon->reprime_count) : 0; 223} 224 225bool icepick_monitor_is_degraded(const icepick_monitor_t *mon) 226{ 227 return mon ? mon->degraded : true; 228}