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

perf dso: Use container_of() to avoid a pointer in 'struct dso_data'

The dso pointer in 'struct dso_data' is necessary for reference count
checking to account for the dso_data forming a global list of open dso's
with references to the dso.

The dso pointer also allows for the indirection that reference count
checking needs. Outside of reference count checking the indirection
isn't needed and container_of() is more efficient and saves space.

The reference count won't be increased by placing items onto the global
list, matching how things were before the reference count checking
change, but we assert the dso is in dsos holding it live (and that the
set of open dsos is a subset of all dsos for the machine).

Update the DSO data tests so that they use a dsos struct to make the
invariant true.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Changbin Du <changbin.du@huawei.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Tiezhu Yang <yangtiezhu@loongson.cn>
Link: https://lore.kernel.org/r/20240506180104.485674-5-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
37862d6f 23106e31

+46 -32
+29 -31
tools/perf/tests/dso-data.c
··· 10 10 #include <sys/resource.h> 11 11 #include <api/fs/fs.h> 12 12 #include "dso.h" 13 + #include "dsos.h" 13 14 #include "machine.h" 14 15 #include "symbol.h" 15 16 #include "tests.h" ··· 124 123 TEST_ASSERT_VAL("No test file", file); 125 124 126 125 memset(&machine, 0, sizeof(machine)); 126 + dsos__init(&machine.dsos); 127 127 128 - dso = dso__new((const char *)file); 129 - 128 + dso = dso__new(file); 129 + TEST_ASSERT_VAL("Failed to add dso", !dsos__add(&machine.dsos, dso)); 130 130 TEST_ASSERT_VAL("Failed to access to dso", 131 131 dso__data_fd(dso, &machine) >= 0); 132 132 ··· 172 170 } 173 171 174 172 dso__put(dso); 173 + dsos__exit(&machine.dsos); 175 174 unlink(file); 176 175 return 0; 177 176 } ··· 202 199 return nr - 1; 203 200 } 204 201 205 - static struct dso **dsos; 206 - 207 - static int dsos__create(int cnt, int size) 202 + static int dsos__create(int cnt, int size, struct dsos *dsos) 208 203 { 209 204 int i; 210 205 211 - dsos = malloc(sizeof(*dsos) * cnt); 212 - TEST_ASSERT_VAL("failed to alloc dsos array", dsos); 206 + dsos__init(dsos); 213 207 214 208 for (i = 0; i < cnt; i++) { 215 - char *file; 209 + struct dso *dso; 210 + char *file = test_file(size); 216 211 217 - file = test_file(size); 218 212 TEST_ASSERT_VAL("failed to get dso file", file); 219 - 220 - dsos[i] = dso__new(file); 221 - TEST_ASSERT_VAL("failed to get dso", dsos[i]); 213 + dso = dso__new(file); 214 + TEST_ASSERT_VAL("failed to get dso", dso); 215 + TEST_ASSERT_VAL("failed to add dso", !dsos__add(dsos, dso)); 216 + dso__put(dso); 222 217 } 223 218 224 219 return 0; 225 220 } 226 221 227 - static void dsos__delete(int cnt) 222 + static void dsos__delete(struct dsos *dsos) 228 223 { 229 - int i; 230 - 231 - for (i = 0; i < cnt; i++) { 232 - struct dso *dso = dsos[i]; 224 + for (unsigned int i = 0; i < dsos->cnt; i++) { 225 + struct dso *dso = dsos->dsos[i]; 233 226 234 227 dso__data_close(dso); 235 228 unlink(dso__name(dso)); 236 - dso__put(dso); 237 229 } 238 - 239 - free(dsos); 230 + dsos__exit(dsos); 240 231 } 241 232 242 233 static int set_fd_limit(int n) ··· 264 267 /* and this is now our dso open FDs limit */ 265 268 dso_cnt = limit / 2; 266 269 TEST_ASSERT_VAL("failed to create dsos\n", 267 - !dsos__create(dso_cnt, TEST_FILE_SIZE)); 270 + !dsos__create(dso_cnt, TEST_FILE_SIZE, &machine.dsos)); 268 271 269 272 for (i = 0; i < (dso_cnt - 1); i++) { 270 - struct dso *dso = dsos[i]; 273 + struct dso *dso = machine.dsos.dsos[i]; 271 274 272 275 /* 273 276 * Open dsos via dso__data_fd(), it opens the data ··· 287 290 } 288 291 289 292 /* verify the first one is already open */ 290 - TEST_ASSERT_VAL("dsos[0] is not open", dso__data(dsos[0])->fd != -1); 293 + TEST_ASSERT_VAL("dsos[0] is not open", dso__data(machine.dsos.dsos[0])->fd != -1); 291 294 292 295 /* open +1 dso to reach the allowed limit */ 293 - fd = dso__data_fd(dsos[i], &machine); 296 + fd = dso__data_fd(machine.dsos.dsos[i], &machine); 294 297 TEST_ASSERT_VAL("failed to get fd", fd > 0); 295 298 296 299 /* should force the first one to be closed */ 297 - TEST_ASSERT_VAL("failed to close dsos[0]", dso__data(dsos[0])->fd == -1); 300 + TEST_ASSERT_VAL("failed to close dsos[0]", dso__data(machine.dsos.dsos[0])->fd == -1); 298 301 299 302 /* cleanup everything */ 300 - dsos__delete(dso_cnt); 303 + dsos__delete(&machine.dsos); 301 304 302 305 /* Make sure we did not leak any file descriptor. */ 303 306 nr_end = open_files_cnt(); ··· 322 325 long nr_end, nr = open_files_cnt(), lim = new_limit(3); 323 326 int fd, fd_extra; 324 327 325 - #define dso_0 (dsos[0]) 326 - #define dso_1 (dsos[1]) 327 - #define dso_2 (dsos[2]) 328 + #define dso_0 (machine.dsos.dsos[0]) 329 + #define dso_1 (machine.dsos.dsos[1]) 330 + #define dso_2 (machine.dsos.dsos[2]) 328 331 329 332 /* Rest the internal dso open counter limit. */ 330 333 reset_fd_limit(); ··· 344 347 TEST_ASSERT_VAL("failed to set file limit", 345 348 !set_fd_limit((lim))); 346 349 347 - TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE)); 350 + TEST_ASSERT_VAL("failed to create dsos\n", 351 + !dsos__create(3, TEST_FILE_SIZE, &machine.dsos)); 348 352 349 353 /* open dso_0 */ 350 354 fd = dso__data_fd(dso_0, &machine); ··· 384 386 385 387 /* cleanup everything */ 386 388 close(fd_extra); 387 - dsos__delete(3); 389 + dsos__delete(&machine.dsos); 388 390 389 391 /* Make sure we did not leak any file descriptor. */ 390 392 nr_end = open_files_cnt();
+15 -1
tools/perf/util/dso.c
··· 497 497 static void dso__list_add(struct dso *dso) 498 498 { 499 499 list_add_tail(&dso__data(dso)->open_entry, &dso__data_open); 500 + #ifdef REFCNT_CHECKING 500 501 dso__data(dso)->dso = dso__get(dso); 502 + #endif 503 + /* Assume the dso is part of dsos, hence the optional reference count above. */ 504 + assert(dso__dsos(dso)); 501 505 dso__data_open_cnt++; 502 506 } 503 507 504 508 static void dso__list_del(struct dso *dso) 505 509 { 506 510 list_del_init(&dso__data(dso)->open_entry); 511 + #ifdef REFCNT_CHECKING 507 512 dso__put(dso__data(dso)->dso); 513 + #endif 508 514 WARN_ONCE(dso__data_open_cnt <= 0, 509 515 "DSO data fd counter out of bounds."); 510 516 dso__data_open_cnt--; ··· 660 654 static void close_first_dso(void) 661 655 { 662 656 struct dso_data *dso_data; 657 + struct dso *dso; 663 658 664 659 dso_data = list_first_entry(&dso__data_open, struct dso_data, open_entry); 665 - close_dso(dso_data->dso); 660 + #ifdef REFCNT_CHECKING 661 + dso = dso_data->dso; 662 + #else 663 + dso = container_of(dso_data, struct dso, data); 664 + #endif 665 + close_dso(dso); 666 666 } 667 667 668 668 static rlim_t get_fd_limit(void) ··· 1461 1449 data->fd = -1; 1462 1450 data->status = DSO_DATA_STATUS_UNKNOWN; 1463 1451 INIT_LIST_HEAD(&data->open_entry); 1452 + #ifdef REFCNT_CHECKING 1464 1453 data->dso = NULL; /* Set when on the open_entry list. */ 1454 + #endif 1465 1455 } 1466 1456 return res; 1467 1457 }
+2
tools/perf/util/dso.h
··· 147 147 struct dso_data { 148 148 struct rb_root cache; 149 149 struct list_head open_entry; 150 + #ifdef REFCNT_CHECKING 150 151 struct dso *dso; 152 + #endif 151 153 int fd; 152 154 int status; 153 155 u32 status_seen;