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

x86/events/amd/iommu: Enable support for multiple IOMMUs

Add support for multiple IOMMUs to perf by exposing an AMD IOMMU PMU for
each IOMMU found in the system via:

/bus/event_source/devices/amd_iommu_x

where x is the IOMMU index. This allows users to specify different
events to be programmed into the performance counters of each IOMMU.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
[ Improve readability, shorten names. ]
Signed-off-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Jörg Rödel <joro@8bytes.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Cc: iommu@lists.linux-foundation.org
Link: http://lkml.kernel.org/r/1490166162-10002-11-git-send-email-Suravee.Suthikulpanit@amd.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Suravee Suthikulpanit and committed by
Ingo Molnar
25df39f2 cf25f904

+71 -41
+71 -41
arch/x86/events/amd/iommu.c
··· 34 34 #define GET_DOMID_MASK(x) (((x)->conf1 >> 16) & 0xFFFFULL) 35 35 #define GET_PASID_MASK(x) (((x)->conf1 >> 32) & 0xFFFFFULL) 36 36 37 - static struct perf_amd_iommu __perf_iommu; 37 + #define IOMMU_NAME_SIZE 16 38 38 39 39 struct perf_amd_iommu { 40 + struct list_head list; 40 41 struct pmu pmu; 42 + struct amd_iommu *iommu; 43 + char name[IOMMU_NAME_SIZE]; 41 44 u8 max_banks; 42 45 u8 max_counters; 43 46 u64 cntr_assign_mask; 44 47 raw_spinlock_t lock; 45 48 }; 49 + 50 + static LIST_HEAD(perf_amd_iommu_list); 46 51 47 52 /*--------------------------------------------- 48 53 * sysfs format attributes ··· 238 233 return 0; 239 234 } 240 235 236 + static inline struct amd_iommu *perf_event_2_iommu(struct perf_event *ev) 237 + { 238 + return (container_of(ev->pmu, struct perf_amd_iommu, pmu))->iommu; 239 + } 240 + 241 241 static void perf_iommu_enable_event(struct perf_event *ev) 242 242 { 243 - struct amd_iommu *iommu = get_amd_iommu(0); 243 + struct amd_iommu *iommu = perf_event_2_iommu(ev); 244 244 struct hw_perf_event *hwc = &ev->hw; 245 245 u8 bank = hwc->iommu_bank; 246 246 u8 cntr = hwc->iommu_cntr; ··· 275 265 276 266 static void perf_iommu_disable_event(struct perf_event *event) 277 267 { 278 - struct amd_iommu *iommu = get_amd_iommu(0); 268 + struct amd_iommu *iommu = perf_event_2_iommu(event); 279 269 struct hw_perf_event *hwc = &event->hw; 280 270 u64 reg = 0ULL; 281 271 ··· 295 285 296 286 if (flags & PERF_EF_RELOAD) { 297 287 u64 prev_raw_count = local64_read(&hwc->prev_count); 298 - struct amd_iommu *iommu = get_amd_iommu(0); 288 + struct amd_iommu *iommu = perf_event_2_iommu(event); 299 289 300 290 amd_iommu_pc_set_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr, 301 291 IOMMU_PC_COUNTER_REG, &prev_raw_count); ··· 310 300 { 311 301 u64 count, prev, delta; 312 302 struct hw_perf_event *hwc = &event->hw; 313 - struct amd_iommu *iommu = get_amd_iommu(0); 303 + struct amd_iommu *iommu = perf_event_2_iommu(event); 314 304 315 305 if (amd_iommu_pc_get_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr, 316 306 IOMMU_PC_COUNTER_REG, &count)) ··· 398 388 return 0; 399 389 } 400 390 401 - static __init void amd_iommu_pc_exit(void) 402 - { 403 - kfree(amd_iommu_events_group.attrs); 404 - } 405 - 406 391 const struct attribute_group *amd_iommu_attr_groups[] = { 407 392 &amd_iommu_format_group, 408 393 &amd_iommu_cpumask_group, ··· 405 400 NULL, 406 401 }; 407 402 408 - static __init int 409 - _init_perf_amd_iommu(struct perf_amd_iommu *perf_iommu, char *name) 403 + static struct pmu iommu_pmu = { 404 + .event_init = perf_iommu_event_init, 405 + .add = perf_iommu_add, 406 + .del = perf_iommu_del, 407 + .start = perf_iommu_start, 408 + .stop = perf_iommu_stop, 409 + .read = perf_iommu_read, 410 + .task_ctx_nr = perf_invalid_context, 411 + .attr_groups = amd_iommu_attr_groups, 412 + }; 413 + 414 + static __init int init_one_iommu(unsigned int idx) 410 415 { 416 + struct perf_amd_iommu *perf_iommu; 411 417 int ret; 418 + 419 + perf_iommu = kzalloc(sizeof(struct perf_amd_iommu), GFP_KERNEL); 420 + if (!perf_iommu) 421 + return -ENOMEM; 412 422 413 423 raw_spin_lock_init(&perf_iommu->lock); 414 424 415 - /* Init cpumask attributes to only core 0 */ 416 - cpumask_set_cpu(0, &iommu_cpumask); 425 + perf_iommu->pmu = iommu_pmu; 426 + perf_iommu->iommu = get_amd_iommu(idx); 427 + perf_iommu->max_banks = amd_iommu_pc_get_max_banks(idx); 428 + perf_iommu->max_counters = amd_iommu_pc_get_max_counters(idx); 417 429 418 - perf_iommu->max_banks = amd_iommu_pc_get_max_banks(0); 419 - perf_iommu->max_counters = amd_iommu_pc_get_max_counters(0); 420 - if (!perf_iommu->max_banks || !perf_iommu->max_counters) 430 + if (!perf_iommu->iommu || 431 + !perf_iommu->max_banks || 432 + !perf_iommu->max_counters) { 433 + kfree(perf_iommu); 421 434 return -EINVAL; 435 + } 422 436 423 - perf_iommu->pmu.attr_groups = amd_iommu_attr_groups; 424 - ret = perf_pmu_register(&perf_iommu->pmu, name, -1); 425 - if (ret) 426 - pr_err("Error initializing AMD IOMMU perf counters.\n"); 427 - else 428 - pr_info("Detected AMD IOMMU (%d banks, %d counters/bank).\n", 429 - amd_iommu_pc_get_max_banks(0), 430 - amd_iommu_pc_get_max_counters(0)); 437 + snprintf(perf_iommu->name, IOMMU_NAME_SIZE, "amd_iommu_%u", idx); 438 + 439 + ret = perf_pmu_register(&perf_iommu->pmu, perf_iommu->name, -1); 440 + if (!ret) { 441 + pr_info("Detected AMD IOMMU #%d (%d banks, %d counters/bank).\n", 442 + idx, perf_iommu->max_banks, perf_iommu->max_counters); 443 + list_add_tail(&perf_iommu->list, &perf_amd_iommu_list); 444 + } else { 445 + pr_warn("Error initializing IOMMU %d.\n", idx); 446 + kfree(perf_iommu); 447 + } 431 448 return ret; 432 449 } 433 450 434 - static struct perf_amd_iommu __perf_iommu = { 435 - .pmu = { 436 - .task_ctx_nr = perf_invalid_context, 437 - .event_init = perf_iommu_event_init, 438 - .add = perf_iommu_add, 439 - .del = perf_iommu_del, 440 - .start = perf_iommu_start, 441 - .stop = perf_iommu_stop, 442 - .read = perf_iommu_read, 443 - }, 444 - }; 445 - 446 451 static __init int amd_iommu_pc_init(void) 447 452 { 453 + unsigned int i, cnt = 0; 448 454 int ret; 449 455 450 456 /* Make sure the IOMMU PC resource is available */ ··· 466 450 if (ret) 467 451 return ret; 468 452 469 - ret = _init_perf_amd_iommu(&__perf_iommu, "amd_iommu"); 470 - if (ret) 471 - amd_iommu_pc_exit(); 453 + /* 454 + * An IOMMU PMU is specific to an IOMMU, and can function independently. 455 + * So we go through all IOMMUs and ignore the one that fails init 456 + * unless all IOMMU are failing. 457 + */ 458 + for (i = 0; i < amd_iommu_get_num_iommus(); i++) { 459 + ret = init_one_iommu(i); 460 + if (!ret) 461 + cnt++; 462 + } 472 463 473 - return ret; 464 + if (!cnt) { 465 + kfree(amd_iommu_events_group.attrs); 466 + return -ENODEV; 467 + } 468 + 469 + /* Init cpumask attributes to only core 0 */ 470 + cpumask_set_cpu(0, &iommu_cpumask); 471 + return 0; 474 472 } 475 473 476 474 device_initcall(amd_iommu_pc_init);