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 / lock.c
5.8 kB 210 lines
1#define _GNU_SOURCE 2#include "internal.h" 3#include <stdlib.h> 4#include <sched.h> 5#include <unistd.h> 6 7static uint32_t compute_way_mask(unsigned num_ways, unsigned start_way) 8{ 9 uint32_t mask = 0; 10 for (unsigned i = 0; i < num_ways; i++) 11 mask |= (1U << (start_way + i)); 12 return mask; 13} 14 15static unsigned find_start_way(uint32_t default_mask, unsigned num_ways) 16{ 17 for (unsigned i = 0; i <= 32 - num_ways; i++) { 18 uint32_t candidate = compute_way_mask(num_ways, i); 19 if ((candidate & default_mask) == candidate) 20 return i; 21 } 22 return 0; 23} 24 25void prime_region(volatile char *ptr, size_t size, unsigned iterations) 26{ 27 for (unsigned iter = 0; iter < iterations; iter++) { 28 for (size_t offset = 0; offset < size; offset += CACHE_LINE_SIZE) { 29 (void)ptr[offset]; 30 } 31 } 32} 33 34int icepick_lock(icepick_topology_t *topo, const icepick_config_t *cfg, 35 icepick_region_t **region) 36{ 37 if (!topo || !cfg || !region) 38 return ICEPICK_E_INVALID; 39 40 if (cfg->clos_id == 0 || cfg->clos_id >= topo->max_clos) 41 return ICEPICK_E_INVALID; 42 43 unsigned ways_needed = (cfg->size + topo->way_size - 1) / topo->way_size; 44 if (ways_needed == 0) 45 ways_needed = 1; 46 47 if (ways_needed >= topo->l3_ways) 48 return ICEPICK_E_TOO_LARGE; 49 50 int ret = clos_init(topo); 51 if (ret < 0) 52 return ret; 53 54 unsigned start_way = find_start_way(topo->default_way_mask, ways_needed); 55 uint32_t way_mask = compute_way_mask(ways_needed, start_way); 56 57 ret = clos_allocate(cfg->clos_id, way_mask); 58 if (ret < 0) 59 return ret; 60 61 icepick_region_t *r = calloc(1, sizeof(*r)); 62 if (!r) { 63 clos_release(cfg->clos_id); 64 return ICEPICK_E_ALLOC; 65 } 66 67 r->clos_id = cfg->clos_id; 68 r->numa_node = cfg->numa_node; 69 r->way_mask = way_mask; 70 r->topo = topo; 71 r->size = ways_needed * topo->way_size; 72 r->monitor = NULL; 73 74 ret = region_alloc(r->size, cfg->numa_node, cfg->huge_pages, &r->ptr); 75 if (ret < 0) { 76 clos_release(cfg->clos_id); 77 free(r); 78 return ret; 79 } 80 81 int cpu = sched_getcpu(); 82 if (cpu < 0) 83 cpu = 0; 84 85 cpu_set_t old_affinity, new_affinity; 86 CPU_ZERO(&new_affinity); 87 CPU_SET(cpu, &new_affinity); 88 sched_getaffinity(0, sizeof(old_affinity), &old_affinity); 89 sched_setaffinity(0, sizeof(new_affinity), &new_affinity); 90 91 int msr_fd = msr_open(cpu); 92 if (msr_fd < 0) { 93 sched_setaffinity(0, sizeof(old_affinity), &old_affinity); 94 region_free(r->ptr, r->size); 95 clos_release(cfg->clos_id); 96 free(r); 97 return msr_fd; 98 } 99 100 ret = clos_configure_mask(msr_fd, 0, clos_table[0].way_mask); 101 if (ret < 0) 102 goto cleanup; 103 104 ret = clos_configure_mask(msr_fd, cfg->clos_id, way_mask); 105 if (ret < 0) 106 goto cleanup; 107 108 ret = clos_associate_thread(msr_fd, cfg->clos_id); 109 if (ret < 0) 110 goto cleanup; 111 112 prime_region(r->ptr, r->size, 3); 113 114 clos_associate_thread(msr_fd, 0); 115 116 msr_close(msr_fd); 117 sched_setaffinity(0, sizeof(old_affinity), &old_affinity); 118 119 if (cfg->verify) { 120 icepick_latency_stats_t stats; 121 ret = icepick_verify(r, &stats); 122 if (ret < 0) { 123 icepick_unlock(r); 124 return ret; 125 } 126 } 127 128 if (cfg->auto_monitor) { 129 icepick_monitor_t *mon; 130 ret = icepick_monitor_start_ex(r, cfg->pmu_poll_interval_ns, 131 cfg->probe_interval_ns, 132 cfg->miss_threshold, &mon); 133 if (ret < 0) { 134 icepick_unlock(r); 135 return ret; 136 } 137 r->monitor = mon; 138 } 139 140 *region = r; 141 return 0; 142 143cleanup: 144 msr_close(msr_fd); 145 sched_setaffinity(0, sizeof(old_affinity), &old_affinity); 146 region_free(r->ptr, r->size); 147 clos_release(cfg->clos_id); 148 free(r); 149 return ret; 150} 151 152int icepick_unlock(icepick_region_t *region) 153{ 154 if (!region) 155 return ICEPICK_E_INVALID; 156 157 if (region->monitor) 158 icepick_monitor_stop(region->monitor); 159 160 int cpu = sched_getcpu(); 161 if (cpu < 0) 162 cpu = 0; 163 164 int msr_fd = msr_open(cpu); 165 if (msr_fd >= 0) { 166 clos_release(region->clos_id); 167 clos_configure_mask(msr_fd, region->clos_id, 0); 168 clos_configure_mask(msr_fd, 0, clos_get_default_mask()); 169 msr_close(msr_fd); 170 } 171 172 region_free(region->ptr, region->size); 173 free(region); 174 175 return 0; 176} 177 178void *icepick_region_ptr(const icepick_region_t *region) 179{ 180 return region ? region->ptr : NULL; 181} 182 183size_t icepick_region_size(const icepick_region_t *region) 184{ 185 return region ? region->size : 0; 186} 187 188unsigned icepick_region_clos(const icepick_region_t *region) 189{ 190 return region ? region->clos_id : 0; 191} 192 193const char *icepick_strerror(int err) 194{ 195 switch (err) { 196 case 0: return "success"; 197 case ICEPICK_E_NO_CAT: return "cat not supported on this cpu"; 198 case ICEPICK_E_PERMISSION: return "permission denied (need root or cap_sys_rawio)"; 199 case ICEPICK_E_NO_CLOS: return "no clos available or already allocated"; 200 case ICEPICK_E_TOO_LARGE: return "requested size exceeds available cache ways"; 201 case ICEPICK_E_NUMA: return "invalid numa node or binding failed"; 202 case ICEPICK_E_HUGEPAGE: return "huge page allocation failed"; 203 case ICEPICK_E_VERIFY: return "verification failed - data may not be cache-resident"; 204 case ICEPICK_E_INVALID: return "invalid argument"; 205 case ICEPICK_E_ALLOC: return "memory allocation failed"; 206 case ICEPICK_E_MSR: return "msr operation failed"; 207 case ICEPICK_E_THREAD: return "monitor thread creation failed"; 208 default: return "unknown error"; 209 } 210}