Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf cpumap: Add range data encoding

Often cpumaps encode a range of all CPUs, add a compact encoding that
doesn't require a bit mask or list of all CPUs.

Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexey Bayduraev <alexey.v.bayduraev@linux.intel.com>
Cc: Athira Jajeev <atrajeev@linux.vnet.ibm.com>
Cc: Colin Ian King <colin.king@intel.com>
Cc: Dave Marchevsky <davemarchevsky@fb.com>
Cc: German Gomez <german.gomez@arm.com>
Cc: Gustavo A. R. Silva <gustavoars@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Kees Kook <keescook@chromium.org>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Riccardo Mancini <rickyman7@gmail.com>
Cc: Song Liu <songliubraving@fb.com>
Cc: Stephane Eranian <eranian@google.com>
Link: https://lore.kernel.org/r/20220614143353.1559597-7-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
c7202d20 d773c999

+166 -87
+14
tools/lib/perf/include/perf/event.h
··· 153 153 enum { 154 154 PERF_CPU_MAP__CPUS = 0, 155 155 PERF_CPU_MAP__MASK = 1, 156 + PERF_CPU_MAP__RANGE_CPUS = 2, 156 157 }; 157 158 158 159 /* ··· 196 195 #pragma GCC diagnostic ignored "-Wpacked" 197 196 #pragma GCC diagnostic ignored "-Wattributes" 198 197 198 + /* 199 + * An encoding of a CPU map for a range starting at start_cpu through to 200 + * end_cpu. If any_cpu is 1, an any CPU (-1) value (aka dummy value) is present. 201 + */ 202 + struct perf_record_range_cpu_map { 203 + __u8 any_cpu; 204 + __u8 __pad; 205 + __u16 start_cpu; 206 + __u16 end_cpu; 207 + }; 208 + 199 209 struct __packed perf_record_cpu_map_data { 200 210 __u16 type; 201 211 union { ··· 216 204 struct perf_record_mask_cpu_map32 mask32_data; 217 205 /* Used when type == PERF_CPU_MAP__MASK and long_size == 8. */ 218 206 struct perf_record_mask_cpu_map64 mask64_data; 207 + /* Used when type == PERF_CPU_MAP__RANGE_CPUS. */ 208 + struct perf_record_range_cpu_map range_cpu_data; 219 209 }; 220 210 }; 221 211
+43 -9
tools/perf/tests/cpumap.c
··· 19 19 struct perf_record_cpu_map *map_event = &event->cpu_map; 20 20 struct perf_record_cpu_map_data *data; 21 21 struct perf_cpu_map *map; 22 - int i; 23 22 unsigned int long_size; 24 23 25 24 data = &map_event->data; ··· 31 32 32 33 TEST_ASSERT_VAL("wrong nr", data->mask32_data.nr == 1); 33 34 34 - for (i = 0; i < 20; i++) { 35 + TEST_ASSERT_VAL("wrong cpu", perf_record_cpu_map_data__test_bit(0, data)); 36 + TEST_ASSERT_VAL("wrong cpu", !perf_record_cpu_map_data__test_bit(1, data)); 37 + for (int i = 2; i <= 20; i++) 35 38 TEST_ASSERT_VAL("wrong cpu", perf_record_cpu_map_data__test_bit(i, data)); 36 - } 37 39 38 40 map = cpu_map__new_data(data); 39 41 TEST_ASSERT_VAL("wrong nr", perf_cpu_map__nr(map) == 20); 40 42 41 - for (i = 0; i < 20; i++) { 42 - TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__cpu(map, i).cpu == i); 43 - } 43 + TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__cpu(map, 0).cpu == 0); 44 + for (int i = 2; i <= 20; i++) 45 + TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__cpu(map, i - 1).cpu == i); 44 46 45 47 perf_cpu_map__put(map); 46 48 return 0; ··· 73 73 return 0; 74 74 } 75 75 76 + static int process_event_range_cpus(struct perf_tool *tool __maybe_unused, 77 + union perf_event *event, 78 + struct perf_sample *sample __maybe_unused, 79 + struct machine *machine __maybe_unused) 80 + { 81 + struct perf_record_cpu_map *map_event = &event->cpu_map; 82 + struct perf_record_cpu_map_data *data; 83 + struct perf_cpu_map *map; 84 + 85 + data = &map_event->data; 86 + 87 + TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__RANGE_CPUS); 88 + 89 + TEST_ASSERT_VAL("wrong any_cpu", data->range_cpu_data.any_cpu == 0); 90 + TEST_ASSERT_VAL("wrong start_cpu", data->range_cpu_data.start_cpu == 1); 91 + TEST_ASSERT_VAL("wrong end_cpu", data->range_cpu_data.end_cpu == 256); 92 + 93 + map = cpu_map__new_data(data); 94 + TEST_ASSERT_VAL("wrong nr", perf_cpu_map__nr(map) == 256); 95 + TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__cpu(map, 0).cpu == 1); 96 + TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__max(map).cpu == 256); 97 + TEST_ASSERT_VAL("wrong refcnt", refcount_read(&map->refcnt) == 1); 98 + perf_cpu_map__put(map); 99 + return 0; 100 + } 101 + 76 102 77 103 static int test__cpu_map_synthesize(struct test_suite *test __maybe_unused, int subtest __maybe_unused) 78 104 { 79 105 struct perf_cpu_map *cpus; 80 106 81 - /* This one is better stores in mask. */ 82 - cpus = perf_cpu_map__new("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19"); 107 + /* This one is better stored in a mask. */ 108 + cpus = perf_cpu_map__new("0,2-20"); 83 109 84 110 TEST_ASSERT_VAL("failed to synthesize map", 85 111 !perf_event__synthesize_cpu_map(NULL, cpus, process_event_mask, NULL)); 86 112 87 113 perf_cpu_map__put(cpus); 88 114 89 - /* This one is better stores in cpu values. */ 115 + /* This one is better stored in cpu values. */ 90 116 cpus = perf_cpu_map__new("1,256"); 91 117 92 118 TEST_ASSERT_VAL("failed to synthesize map", 93 119 !perf_event__synthesize_cpu_map(NULL, cpus, process_event_cpus, NULL)); 120 + 121 + perf_cpu_map__put(cpus); 122 + 123 + /* This one is better stored as a range. */ 124 + cpus = perf_cpu_map__new("1-256"); 125 + 126 + TEST_ASSERT_VAL("failed to synthesize map", 127 + !perf_event__synthesize_cpu_map(NULL, cpus, process_event_range_cpus, NULL)); 94 128 95 129 perf_cpu_map__put(cpus); 96 130 return 0;
+29 -2
tools/perf/util/cpumap.c
··· 112 112 113 113 } 114 114 115 + static struct perf_cpu_map *cpu_map__from_range(const struct perf_record_cpu_map_data *data) 116 + { 117 + struct perf_cpu_map *map; 118 + unsigned int i = 0; 119 + 120 + map = perf_cpu_map__empty_new(data->range_cpu_data.end_cpu - 121 + data->range_cpu_data.start_cpu + 1 + data->range_cpu_data.any_cpu); 122 + if (!map) 123 + return NULL; 124 + 125 + if (data->range_cpu_data.any_cpu) 126 + map->map[i++].cpu = -1; 127 + 128 + for (int cpu = data->range_cpu_data.start_cpu; cpu <= data->range_cpu_data.end_cpu; 129 + i++, cpu++) 130 + map->map[i].cpu = cpu; 131 + 132 + return map; 133 + } 134 + 115 135 struct perf_cpu_map *cpu_map__new_data(const struct perf_record_cpu_map_data *data) 116 136 { 117 - if (data->type == PERF_CPU_MAP__CPUS) 137 + switch (data->type) { 138 + case PERF_CPU_MAP__CPUS: 118 139 return cpu_map__from_entries(data); 119 - else 140 + case PERF_CPU_MAP__MASK: 120 141 return cpu_map__from_mask(data); 142 + case PERF_CPU_MAP__RANGE_CPUS: 143 + return cpu_map__from_range(data); 144 + default: 145 + pr_err("cpu_map__new_data unknown type %d\n", data->type); 146 + return NULL; 147 + } 121 148 } 122 149 123 150 size_t cpu_map__fprintf(struct perf_cpu_map *map, FILE *fp)
+5
tools/perf/util/session.c
··· 943 943 default: 944 944 pr_err("cpu_map swap: unsupported long size\n"); 945 945 } 946 + break; 947 + case PERF_CPU_MAP__RANGE_CPUS: 948 + data->range_cpu_data.start_cpu = bswap_16(data->range_cpu_data.start_cpu); 949 + data->range_cpu_data.end_cpu = bswap_16(data->range_cpu_data.end_cpu); 950 + break; 946 951 default: 947 952 break; 948 953 }
+75 -76
tools/perf/util/synthetic-events.c
··· 1195 1195 return err; 1196 1196 } 1197 1197 1198 - static void synthesize_cpus(struct perf_record_cpu_map_data *data, 1199 - const struct perf_cpu_map *map) 1198 + struct synthesize_cpu_map_data { 1199 + const struct perf_cpu_map *map; 1200 + int nr; 1201 + int min_cpu; 1202 + int max_cpu; 1203 + int has_any_cpu; 1204 + int type; 1205 + size_t size; 1206 + struct perf_record_cpu_map_data *data; 1207 + }; 1208 + 1209 + static void synthesize_cpus(struct synthesize_cpu_map_data *data) 1200 1210 { 1201 - int i, map_nr = perf_cpu_map__nr(map); 1202 - 1203 - data->cpus_data.nr = map_nr; 1204 - 1205 - for (i = 0; i < map_nr; i++) 1206 - data->cpus_data.cpu[i] = perf_cpu_map__cpu(map, i).cpu; 1211 + data->data->type = PERF_CPU_MAP__CPUS; 1212 + data->data->cpus_data.nr = data->nr; 1213 + for (int i = 0; i < data->nr; i++) 1214 + data->data->cpus_data.cpu[i] = perf_cpu_map__cpu(data->map, i).cpu; 1207 1215 } 1208 1216 1209 - static void synthesize_mask(struct perf_record_cpu_map_data *data, 1210 - const struct perf_cpu_map *map, int max) 1217 + static void synthesize_mask(struct synthesize_cpu_map_data *data) 1211 1218 { 1212 1219 int idx; 1213 1220 struct perf_cpu cpu; 1214 1221 1215 1222 /* Due to padding, the 4bytes per entry mask variant is always smaller. */ 1216 - data->mask32_data.nr = BITS_TO_U32(max); 1217 - data->mask32_data.long_size = 4; 1223 + data->data->type = PERF_CPU_MAP__MASK; 1224 + data->data->mask32_data.nr = BITS_TO_U32(data->max_cpu); 1225 + data->data->mask32_data.long_size = 4; 1218 1226 1219 - perf_cpu_map__for_each_cpu(cpu, idx, map) { 1227 + perf_cpu_map__for_each_cpu(cpu, idx, data->map) { 1220 1228 int bit_word = cpu.cpu / 32; 1221 - __u32 bit_mask = 1U << (cpu.cpu & 31); 1229 + u32 bit_mask = 1U << (cpu.cpu & 31); 1222 1230 1223 - data->mask32_data.mask[bit_word] |= bit_mask; 1231 + data->data->mask32_data.mask[bit_word] |= bit_mask; 1224 1232 } 1225 1233 } 1226 1234 1227 - static size_t cpus_size(const struct perf_cpu_map *map) 1235 + static void synthesize_range_cpus(struct synthesize_cpu_map_data *data) 1228 1236 { 1229 - return sizeof(struct cpu_map_entries) + perf_cpu_map__nr(map) * sizeof(u16); 1237 + data->data->type = PERF_CPU_MAP__RANGE_CPUS; 1238 + data->data->range_cpu_data.any_cpu = data->has_any_cpu; 1239 + data->data->range_cpu_data.start_cpu = data->min_cpu; 1240 + data->data->range_cpu_data.end_cpu = data->max_cpu; 1230 1241 } 1231 1242 1232 - static size_t mask_size(const struct perf_cpu_map *map, int *max) 1233 - { 1234 - *max = perf_cpu_map__max(map).cpu; 1235 - return sizeof(struct perf_record_mask_cpu_map32) + BITS_TO_U32(*max) * sizeof(__u32); 1236 - } 1237 - 1238 - static void *cpu_map_data__alloc(const struct perf_cpu_map *map, size_t *size, 1239 - u16 *type, int *max) 1243 + static void *cpu_map_data__alloc(struct synthesize_cpu_map_data *syn_data, 1244 + size_t header_size) 1240 1245 { 1241 1246 size_t size_cpus, size_mask; 1242 - bool is_dummy = perf_cpu_map__empty(map); 1243 1247 1244 - /* 1245 - * Both array and mask data have variable size based 1246 - * on the number of cpus and their actual values. 1247 - * The size of the 'struct perf_record_cpu_map_data' is: 1248 - * 1249 - * array = size of 'struct cpu_map_entries' + 1250 - * number of cpus * sizeof(u64) 1251 - * 1252 - * mask = size of 'struct perf_record_record_cpu_map' + 1253 - * maximum cpu bit converted to size of longs 1254 - * 1255 - * and finally + the size of 'struct perf_record_cpu_map_data'. 1256 - */ 1257 - size_cpus = cpus_size(map); 1258 - size_mask = mask_size(map, max); 1248 + syn_data->nr = perf_cpu_map__nr(syn_data->map); 1249 + syn_data->has_any_cpu = (perf_cpu_map__cpu(syn_data->map, 0).cpu == -1) ? 1 : 0; 1259 1250 1260 - if (is_dummy || (size_cpus < size_mask)) { 1261 - *size += size_cpus; 1262 - *type = PERF_CPU_MAP__CPUS; 1263 - } else { 1264 - *size += size_mask; 1265 - *type = PERF_CPU_MAP__MASK; 1251 + syn_data->min_cpu = perf_cpu_map__cpu(syn_data->map, syn_data->has_any_cpu).cpu; 1252 + syn_data->max_cpu = perf_cpu_map__max(syn_data->map).cpu; 1253 + if (syn_data->max_cpu - syn_data->min_cpu + 1 == syn_data->nr - syn_data->has_any_cpu) { 1254 + /* A consecutive range of CPUs can be encoded using a range. */ 1255 + assert(sizeof(u16) + sizeof(struct perf_record_range_cpu_map) == sizeof(u64)); 1256 + syn_data->type = PERF_CPU_MAP__RANGE_CPUS; 1257 + syn_data->size = header_size + sizeof(u64); 1258 + return zalloc(syn_data->size); 1266 1259 } 1267 1260 1268 - *size += sizeof(__u16); /* For perf_record_cpu_map_data.type. */ 1269 - *size = PERF_ALIGN(*size, sizeof(u64)); 1270 - return zalloc(*size); 1261 + size_cpus = sizeof(u16) + sizeof(struct cpu_map_entries) + syn_data->nr * sizeof(u16); 1262 + /* Due to padding, the 4bytes per entry mask variant is always smaller. */ 1263 + size_mask = sizeof(u16) + sizeof(struct perf_record_mask_cpu_map32) + 1264 + BITS_TO_U32(syn_data->max_cpu) * sizeof(__u32); 1265 + if (syn_data->has_any_cpu || size_cpus < size_mask) { 1266 + /* Follow the CPU map encoding. */ 1267 + syn_data->type = PERF_CPU_MAP__CPUS; 1268 + syn_data->size = header_size + PERF_ALIGN(size_cpus, sizeof(u64)); 1269 + return zalloc(syn_data->size); 1270 + } 1271 + /* Encode using a bitmask. */ 1272 + syn_data->type = PERF_CPU_MAP__MASK; 1273 + syn_data->size = header_size + PERF_ALIGN(size_mask, sizeof(u64)); 1274 + return zalloc(syn_data->size); 1271 1275 } 1272 1276 1273 - static void cpu_map_data__synthesize(struct perf_record_cpu_map_data *data, 1274 - const struct perf_cpu_map *map, 1275 - u16 type, int max) 1277 + static void cpu_map_data__synthesize(struct synthesize_cpu_map_data *data) 1276 1278 { 1277 - data->type = type; 1278 - 1279 - switch (type) { 1279 + switch (data->type) { 1280 1280 case PERF_CPU_MAP__CPUS: 1281 - synthesize_cpus(data, map); 1281 + synthesize_cpus(data); 1282 1282 break; 1283 1283 case PERF_CPU_MAP__MASK: 1284 - synthesize_mask(data, map, max); 1284 + synthesize_mask(data); 1285 + break; 1286 + case PERF_CPU_MAP__RANGE_CPUS: 1287 + synthesize_range_cpus(data); 1288 + break; 1285 1289 default: 1286 1290 break; 1287 1291 } ··· 1293 1289 1294 1290 static struct perf_record_cpu_map *cpu_map_event__new(const struct perf_cpu_map *map) 1295 1291 { 1296 - size_t size = sizeof(struct perf_event_header); 1292 + struct synthesize_cpu_map_data syn_data = { .map = map }; 1297 1293 struct perf_record_cpu_map *event; 1298 - int max; 1299 - u16 type; 1300 1294 1301 - event = cpu_map_data__alloc(map, &size, &type, &max); 1295 + 1296 + event = cpu_map_data__alloc(&syn_data, sizeof(struct perf_event_header)); 1302 1297 if (!event) 1303 1298 return NULL; 1304 1299 1300 + syn_data.data = &event->data; 1305 1301 event->header.type = PERF_RECORD_CPU_MAP; 1306 - event->header.size = size; 1307 - event->data.type = type; 1308 - 1309 - cpu_map_data__synthesize(&event->data, map, type, max); 1302 + event->header.size = syn_data.size; 1303 + cpu_map_data__synthesize(&syn_data); 1310 1304 return event; 1311 1305 } 1306 + 1312 1307 1313 1308 int perf_event__synthesize_cpu_map(struct perf_tool *tool, 1314 1309 const struct perf_cpu_map *map, ··· 2001 1998 int perf_event__synthesize_event_update_cpus(struct perf_tool *tool, struct evsel *evsel, 2002 1999 perf_event__handler_t process) 2003 2000 { 2004 - size_t size = sizeof(struct perf_event_header) + sizeof(u64) + sizeof(u64); 2001 + struct synthesize_cpu_map_data syn_data = { .map = evsel->core.own_cpus }; 2005 2002 struct perf_record_event_update *ev; 2006 - int max, err; 2007 - u16 type; 2003 + int err; 2008 2004 2009 - if (!evsel->core.own_cpus) 2010 - return 0; 2011 - 2012 - ev = cpu_map_data__alloc(evsel->core.own_cpus, &size, &type, &max); 2005 + ev = cpu_map_data__alloc(&syn_data, sizeof(struct perf_event_header) + 2 * sizeof(u64)); 2013 2006 if (!ev) 2014 2007 return -ENOMEM; 2015 2008 2009 + syn_data.data = &ev->cpus.cpus; 2016 2010 ev->header.type = PERF_RECORD_EVENT_UPDATE; 2017 - ev->header.size = (u16)size; 2011 + ev->header.size = (u16)syn_data.size; 2018 2012 ev->type = PERF_EVENT_UPDATE__CPUS; 2019 2013 ev->id = evsel->core.id[0]; 2020 - 2021 - cpu_map_data__synthesize(&ev->cpus.cpus, evsel->core.own_cpus, type, max); 2014 + cpu_map_data__synthesize(&syn_data); 2022 2015 2023 2016 err = process(tool, (union perf_event *)ev, NULL, NULL); 2024 2017 free(ev);