cache line locking on AMD x86_64 utilising L3 CAT pseudo-locking
1#define _GNU_SOURCE
2#include "internal.h"
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <getopt.h>
7
8static void usage(const char *prog)
9{
10 fprintf(stderr,
11 "usage: %s <command> [options]\n\n"
12 "commands:\n"
13 " topology show cache topology and cat capabilities\n"
14 " lock lock a memory region into l3 cache\n"
15 " unlock release a locked region\n"
16 " status show current clos configuration\n"
17 " bench run latency benchmark\n\n"
18 "use '%s <command> --help' for command-specific options.\n",
19 prog, prog);
20}
21
22static size_t parse_size(const char *str)
23{
24 char *end;
25 size_t val = strtoul(str, &end, 10);
26 if (*end == 'K' || *end == 'k')
27 val *= 1024;
28 else if (*end == 'M' || *end == 'm')
29 val *= 1024 * 1024;
30 else if (*end == 'G' || *end == 'g')
31 val *= 1024 * 1024 * 1024;
32 return val;
33}
34
35static int cmd_topology(int argc, char **argv)
36{
37 (void)argc;
38 (void)argv;
39
40 icepick_topology_t *topo;
41 int ret = icepick_discover_topology(&topo);
42 if (ret < 0) {
43 fprintf(stderr, "failed to discover topology: %s\n",
44 icepick_strerror(ret));
45 return 1;
46 }
47
48 printf("l3 cache:\n");
49 printf(" total size: %zu mb\n",
50 icepick_topology_l3_size(topo) / (1024 * 1024));
51 printf(" ways: %u\n", icepick_topology_l3_ways(topo));
52 printf(" per-way size: %zu kb\n",
53 icepick_topology_way_size(topo) / 1024);
54 printf(" ccx count: %u\n\n", icepick_topology_ccx_count(topo));
55
56 printf("cat configuration:\n");
57 printf(" supported: %s\n",
58 icepick_topology_cat_supported(topo) ? "yes" : "no");
59 printf(" max clos: %u\n\n", icepick_topology_max_clos(topo));
60
61 size_t max_lockable = (icepick_topology_l3_ways(topo) - 1) *
62 icepick_topology_way_size(topo);
63 printf("maximum lockable: %zu mb (%u ways, reserving 1 for system)\n",
64 max_lockable / (1024 * 1024), icepick_topology_l3_ways(topo) - 1);
65
66 icepick_free_topology(topo);
67 return 0;
68}
69
70static int cmd_lock(int argc, char **argv)
71{
72 size_t size = 0;
73 unsigned clos = 1;
74 int numa = -1;
75 bool verify = false;
76 bool huge = true;
77
78 static struct option long_options[] = {
79 {"size", required_argument, 0, 's'},
80 {"clos", required_argument, 0, 'c'},
81 {"numa", required_argument, 0, 'n'},
82 {"verify", no_argument, 0, 'v'},
83 {"no-huge", no_argument, 0, 'H'},
84 {"help", no_argument, 0, 'h'},
85 {0, 0, 0, 0}
86 };
87
88 int opt;
89 while ((opt = getopt_long(argc, argv, "s:c:n:vHh", long_options, NULL)) != -1) {
90 switch (opt) {
91 case 's': size = parse_size(optarg); break;
92 case 'c': clos = atoi(optarg); break;
93 case 'n': numa = atoi(optarg); break;
94 case 'v': verify = true; break;
95 case 'H': huge = false; break;
96 case 'h':
97 printf("usage: icepick lock [options]\n\n"
98 "options:\n"
99 " -s, --size SIZE region size (e.g., 4M, 2048K)\n"
100 " -c, --clos ID clos id to use (1-15, default: 1)\n"
101 " -n, --numa NODE numa node (default: any)\n"
102 " -v, --verify verify lock after priming\n"
103 " -H, --no-huge don't use huge pages\n");
104 return 0;
105 default:
106 return 1;
107 }
108 }
109
110 if (size == 0) {
111 fprintf(stderr, "error: --size is required\n");
112 return 1;
113 }
114
115 icepick_topology_t *topo;
116 int ret = icepick_discover_topology(&topo);
117 if (ret < 0) {
118 fprintf(stderr, "failed to discover topology: %s\n",
119 icepick_strerror(ret));
120 return 1;
121 }
122
123 printf("allocating region:\n");
124 printf(" requested: %zu kb\n", size / 1024);
125
126 icepick_config_t cfg = {
127 .size = size,
128 .clos_id = clos,
129 .numa_node = numa,
130 .huge_pages = huge,
131 .verify = verify
132 };
133
134 icepick_region_t *region;
135 ret = icepick_lock(topo, &cfg, ®ion);
136 if (ret < 0) {
137 fprintf(stderr, "lock failed: %s\n", icepick_strerror(ret));
138 icepick_free_topology(topo);
139 return 1;
140 }
141
142 printf(" allocated: %zu kb\n", icepick_region_size(region) / 1024);
143 printf(" address: %p\n", icepick_region_ptr(region));
144 printf(" clos: %u\n\n", icepick_region_clos(region));
145
146 if (verify) {
147 icepick_latency_stats_t stats;
148 icepick_verify(region, &stats);
149 printf("verification:\n");
150 printf(" mean latency: %lu ns\n", stats.mean_ns);
151 printf(" p99 latency: %lu ns\n", stats.p99_ns);
152 printf(" status: %s\n\n",
153 stats.mean_ns <= 50 ? "locked" : "warning - latency high");
154 }
155
156 printf("region locked. press enter to unlock and exit...\n");
157 getchar();
158
159 icepick_unlock(region);
160 icepick_free_topology(topo);
161 printf("region unlocked.\n");
162
163 return 0;
164}
165
166static int cmd_status(int argc, char **argv)
167{
168 (void)argc;
169 (void)argv;
170
171 icepick_topology_t *topo;
172 int ret = icepick_discover_topology(&topo);
173 if (ret < 0) {
174 fprintf(stderr, "failed to discover topology: %s\n",
175 icepick_strerror(ret));
176 return 1;
177 }
178
179 printf("clos configuration:\n");
180 printf(" clos 0: 0x%04x (system default)\n", clos_get_default_mask());
181
182 for (unsigned i = 1; i < icepick_topology_max_clos(topo); i++) {
183 if (clos_table[i].allocated)
184 printf(" clos %u: 0x%04x (allocated)\n", i, clos_table[i].way_mask);
185 }
186
187 icepick_free_topology(topo);
188 return 0;
189}
190
191static int cmd_bench(int argc, char **argv)
192{
193 size_t size = 1024 * 1024;
194 size_t iterations = 1000000;
195
196 static struct option long_options[] = {
197 {"size", required_argument, 0, 's'},
198 {"iterations", required_argument, 0, 'i'},
199 {"help", no_argument, 0, 'h'},
200 {0, 0, 0, 0}
201 };
202
203 int opt;
204 while ((opt = getopt_long(argc, argv, "s:i:h", long_options, NULL)) != -1) {
205 switch (opt) {
206 case 's': size = parse_size(optarg); break;
207 case 'i': iterations = strtoul(optarg, NULL, 10); break;
208 case 'h':
209 printf("usage: icepick bench [options]\n\n"
210 "options:\n"
211 " -s, --size SIZE region size (default: 1M)\n"
212 " -i, --iterations NUM iteration count (default: 1000000)\n");
213 return 0;
214 default:
215 return 1;
216 }
217 }
218
219 icepick_topology_t *topo;
220 int ret = icepick_discover_topology(&topo);
221 if (ret < 0) {
222 fprintf(stderr, "failed to discover topology: %s\n",
223 icepick_strerror(ret));
224 return 1;
225 }
226
227 ret = bench_compare(topo, size, iterations);
228 icepick_free_topology(topo);
229 return ret < 0 ? 1 : 0;
230}
231
232int main(int argc, char **argv)
233{
234 if (argc < 2) {
235 usage(argv[0]);
236 return 1;
237 }
238
239 const char *cmd = argv[1];
240 argc--;
241 argv++;
242
243 if (strcmp(cmd, "topology") == 0)
244 return cmd_topology(argc, argv);
245 if (strcmp(cmd, "lock") == 0)
246 return cmd_lock(argc, argv);
247 if (strcmp(cmd, "status") == 0)
248 return cmd_status(argc, argv);
249 if (strcmp(cmd, "bench") == 0)
250 return cmd_bench(argc, argv);
251 if (strcmp(cmd, "--help") == 0 || strcmp(cmd, "-h") == 0) {
252 usage(argv[0]);
253 return 0;
254 }
255
256 fprintf(stderr, "unknown command: %s\n", cmd);
257 usage(argv[0]);
258 return 1;
259}