linux/arch/x86/events/amd/iommu.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 Advanced Micro Devices, Inc.
   3 *
   4 * Author: Steven Kinney <Steven.Kinney@amd.com>
   5 * Author: Suravee Suthikulpanit <Suraveee.Suthikulpanit@amd.com>
   6 *
   7 * Perf: amd_iommu - AMD IOMMU Performance Counter PMU implementation
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13
  14#include <linux/perf_event.h>
  15#include <linux/init.h>
  16#include <linux/cpumask.h>
  17#include <linux/slab.h>
  18
  19#include "../perf_event.h"
  20#include "iommu.h"
  21
  22#define COUNTER_SHIFT           16
  23
  24#define _GET_BANK(ev)       ((u8)(ev->hw.extra_reg.reg >> 8))
  25#define _GET_CNTR(ev)       ((u8)(ev->hw.extra_reg.reg))
  26
  27/* iommu pmu config masks */
  28#define _GET_CSOURCE(ev)    ((ev->hw.config & 0xFFULL))
  29#define _GET_DEVID(ev)      ((ev->hw.config >> 8)  & 0xFFFFULL)
  30#define _GET_PASID(ev)      ((ev->hw.config >> 24) & 0xFFFFULL)
  31#define _GET_DOMID(ev)      ((ev->hw.config >> 40) & 0xFFFFULL)
  32#define _GET_DEVID_MASK(ev) ((ev->hw.extra_reg.config)  & 0xFFFFULL)
  33#define _GET_PASID_MASK(ev) ((ev->hw.extra_reg.config >> 16) & 0xFFFFULL)
  34#define _GET_DOMID_MASK(ev) ((ev->hw.extra_reg.config >> 32) & 0xFFFFULL)
  35
  36static struct perf_amd_iommu __perf_iommu;
  37
  38struct perf_amd_iommu {
  39        struct pmu pmu;
  40        u8 max_banks;
  41        u8 max_counters;
  42        u64 cntr_assign_mask;
  43        raw_spinlock_t lock;
  44        const struct attribute_group *attr_groups[4];
  45};
  46
  47#define format_group    attr_groups[0]
  48#define cpumask_group   attr_groups[1]
  49#define events_group    attr_groups[2]
  50#define null_group      attr_groups[3]
  51
  52/*---------------------------------------------
  53 * sysfs format attributes
  54 *---------------------------------------------*/
  55PMU_FORMAT_ATTR(csource,    "config:0-7");
  56PMU_FORMAT_ATTR(devid,      "config:8-23");
  57PMU_FORMAT_ATTR(pasid,      "config:24-39");
  58PMU_FORMAT_ATTR(domid,      "config:40-55");
  59PMU_FORMAT_ATTR(devid_mask, "config1:0-15");
  60PMU_FORMAT_ATTR(pasid_mask, "config1:16-31");
  61PMU_FORMAT_ATTR(domid_mask, "config1:32-47");
  62
  63static struct attribute *iommu_format_attrs[] = {
  64        &format_attr_csource.attr,
  65        &format_attr_devid.attr,
  66        &format_attr_pasid.attr,
  67        &format_attr_domid.attr,
  68        &format_attr_devid_mask.attr,
  69        &format_attr_pasid_mask.attr,
  70        &format_attr_domid_mask.attr,
  71        NULL,
  72};
  73
  74static struct attribute_group amd_iommu_format_group = {
  75        .name = "format",
  76        .attrs = iommu_format_attrs,
  77};
  78
  79/*---------------------------------------------
  80 * sysfs events attributes
  81 *---------------------------------------------*/
  82struct amd_iommu_event_desc {
  83        struct kobj_attribute attr;
  84        const char *event;
  85};
  86
  87static ssize_t _iommu_event_show(struct kobject *kobj,
  88                                struct kobj_attribute *attr, char *buf)
  89{
  90        struct amd_iommu_event_desc *event =
  91                container_of(attr, struct amd_iommu_event_desc, attr);
  92        return sprintf(buf, "%s\n", event->event);
  93}
  94
  95#define AMD_IOMMU_EVENT_DESC(_name, _event)                     \
  96{                                                               \
  97        .attr  = __ATTR(_name, 0444, _iommu_event_show, NULL),  \
  98        .event = _event,                                        \
  99}
 100
 101static struct amd_iommu_event_desc amd_iommu_v2_event_descs[] = {
 102        AMD_IOMMU_EVENT_DESC(mem_pass_untrans,        "csource=0x01"),
 103        AMD_IOMMU_EVENT_DESC(mem_pass_pretrans,       "csource=0x02"),
 104        AMD_IOMMU_EVENT_DESC(mem_pass_excl,           "csource=0x03"),
 105        AMD_IOMMU_EVENT_DESC(mem_target_abort,        "csource=0x04"),
 106        AMD_IOMMU_EVENT_DESC(mem_trans_total,         "csource=0x05"),
 107        AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pte_hit,   "csource=0x06"),
 108        AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pte_mis,   "csource=0x07"),
 109        AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pde_hit,   "csource=0x08"),
 110        AMD_IOMMU_EVENT_DESC(mem_iommu_tlb_pde_mis,   "csource=0x09"),
 111        AMD_IOMMU_EVENT_DESC(mem_dte_hit,             "csource=0x0a"),
 112        AMD_IOMMU_EVENT_DESC(mem_dte_mis,             "csource=0x0b"),
 113        AMD_IOMMU_EVENT_DESC(page_tbl_read_tot,       "csource=0x0c"),
 114        AMD_IOMMU_EVENT_DESC(page_tbl_read_nst,       "csource=0x0d"),
 115        AMD_IOMMU_EVENT_DESC(page_tbl_read_gst,       "csource=0x0e"),
 116        AMD_IOMMU_EVENT_DESC(int_dte_hit,             "csource=0x0f"),
 117        AMD_IOMMU_EVENT_DESC(int_dte_mis,             "csource=0x10"),
 118        AMD_IOMMU_EVENT_DESC(cmd_processed,           "csource=0x11"),
 119        AMD_IOMMU_EVENT_DESC(cmd_processed_inv,       "csource=0x12"),
 120        AMD_IOMMU_EVENT_DESC(tlb_inv,                 "csource=0x13"),
 121        AMD_IOMMU_EVENT_DESC(ign_rd_wr_mmio_1ff8h,    "csource=0x14"),
 122        AMD_IOMMU_EVENT_DESC(vapic_int_non_guest,     "csource=0x15"),
 123        AMD_IOMMU_EVENT_DESC(vapic_int_guest,         "csource=0x16"),
 124        AMD_IOMMU_EVENT_DESC(smi_recv,                "csource=0x17"),
 125        AMD_IOMMU_EVENT_DESC(smi_blk,                 "csource=0x18"),
 126        { /* end: all zeroes */ },
 127};
 128
 129/*---------------------------------------------
 130 * sysfs cpumask attributes
 131 *---------------------------------------------*/
 132static cpumask_t iommu_cpumask;
 133
 134static ssize_t _iommu_cpumask_show(struct device *dev,
 135                                   struct device_attribute *attr,
 136                                   char *buf)
 137{
 138        return cpumap_print_to_pagebuf(true, buf, &iommu_cpumask);
 139}
 140static DEVICE_ATTR(cpumask, S_IRUGO, _iommu_cpumask_show, NULL);
 141
 142static struct attribute *iommu_cpumask_attrs[] = {
 143        &dev_attr_cpumask.attr,
 144        NULL,
 145};
 146
 147static struct attribute_group amd_iommu_cpumask_group = {
 148        .attrs = iommu_cpumask_attrs,
 149};
 150
 151/*---------------------------------------------*/
 152
 153static int get_next_avail_iommu_bnk_cntr(struct perf_amd_iommu *perf_iommu)
 154{
 155        unsigned long flags;
 156        int shift, bank, cntr, retval;
 157        int max_banks = perf_iommu->max_banks;
 158        int max_cntrs = perf_iommu->max_counters;
 159
 160        raw_spin_lock_irqsave(&perf_iommu->lock, flags);
 161
 162        for (bank = 0, shift = 0; bank < max_banks; bank++) {
 163                for (cntr = 0; cntr < max_cntrs; cntr++) {
 164                        shift = bank + (bank*3) + cntr;
 165                        if (perf_iommu->cntr_assign_mask & (1ULL<<shift)) {
 166                                continue;
 167                        } else {
 168                                perf_iommu->cntr_assign_mask |= (1ULL<<shift);
 169                                retval = ((u16)((u16)bank<<8) | (u8)(cntr));
 170                                goto out;
 171                        }
 172                }
 173        }
 174        retval = -ENOSPC;
 175out:
 176        raw_spin_unlock_irqrestore(&perf_iommu->lock, flags);
 177        return retval;
 178}
 179
 180static int clear_avail_iommu_bnk_cntr(struct perf_amd_iommu *perf_iommu,
 181                                        u8 bank, u8 cntr)
 182{
 183        unsigned long flags;
 184        int max_banks, max_cntrs;
 185        int shift = 0;
 186
 187        max_banks = perf_iommu->max_banks;
 188        max_cntrs = perf_iommu->max_counters;
 189
 190        if ((bank > max_banks) || (cntr > max_cntrs))
 191                return -EINVAL;
 192
 193        shift = bank + cntr + (bank*3);
 194
 195        raw_spin_lock_irqsave(&perf_iommu->lock, flags);
 196        perf_iommu->cntr_assign_mask &= ~(1ULL<<shift);
 197        raw_spin_unlock_irqrestore(&perf_iommu->lock, flags);
 198
 199        return 0;
 200}
 201
 202static int perf_iommu_event_init(struct perf_event *event)
 203{
 204        struct hw_perf_event *hwc = &event->hw;
 205        struct perf_amd_iommu *perf_iommu;
 206        u64 config, config1;
 207
 208        /* test the event attr type check for PMU enumeration */
 209        if (event->attr.type != event->pmu->type)
 210                return -ENOENT;
 211
 212        /*
 213         * IOMMU counters are shared across all cores.
 214         * Therefore, it does not support per-process mode.
 215         * Also, it does not support event sampling mode.
 216         */
 217        if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
 218                return -EINVAL;
 219
 220        /* IOMMU counters do not have usr/os/guest/host bits */
 221        if (event->attr.exclude_user || event->attr.exclude_kernel ||
 222            event->attr.exclude_host || event->attr.exclude_guest)
 223                return -EINVAL;
 224
 225        if (event->cpu < 0)
 226                return -EINVAL;
 227
 228        perf_iommu = &__perf_iommu;
 229
 230        if (event->pmu != &perf_iommu->pmu)
 231                return -ENOENT;
 232
 233        if (perf_iommu) {
 234                config = event->attr.config;
 235                config1 = event->attr.config1;
 236        } else {
 237                return -EINVAL;
 238        }
 239
 240        /* integrate with iommu base devid (0000), assume one iommu */
 241        perf_iommu->max_banks =
 242                amd_iommu_pc_get_max_banks(IOMMU_BASE_DEVID);
 243        perf_iommu->max_counters =
 244                amd_iommu_pc_get_max_counters(IOMMU_BASE_DEVID);
 245        if ((perf_iommu->max_banks == 0) || (perf_iommu->max_counters == 0))
 246                return -EINVAL;
 247
 248        /* update the hw_perf_event struct with the iommu config data */
 249        hwc->config = config;
 250        hwc->extra_reg.config = config1;
 251
 252        return 0;
 253}
 254
 255static void perf_iommu_enable_event(struct perf_event *ev)
 256{
 257        u8 csource = _GET_CSOURCE(ev);
 258        u16 devid = _GET_DEVID(ev);
 259        u64 reg = 0ULL;
 260
 261        reg = csource;
 262        amd_iommu_pc_get_set_reg_val(devid,
 263                        _GET_BANK(ev), _GET_CNTR(ev) ,
 264                         IOMMU_PC_COUNTER_SRC_REG, &reg, true);
 265
 266        reg = 0ULL | devid | (_GET_DEVID_MASK(ev) << 32);
 267        if (reg)
 268                reg |= (1UL << 31);
 269        amd_iommu_pc_get_set_reg_val(devid,
 270                        _GET_BANK(ev), _GET_CNTR(ev) ,
 271                         IOMMU_PC_DEVID_MATCH_REG, &reg, true);
 272
 273        reg = 0ULL | _GET_PASID(ev) | (_GET_PASID_MASK(ev) << 32);
 274        if (reg)
 275                reg |= (1UL << 31);
 276        amd_iommu_pc_get_set_reg_val(devid,
 277                        _GET_BANK(ev), _GET_CNTR(ev) ,
 278                         IOMMU_PC_PASID_MATCH_REG, &reg, true);
 279
 280        reg = 0ULL | _GET_DOMID(ev) | (_GET_DOMID_MASK(ev) << 32);
 281        if (reg)
 282                reg |= (1UL << 31);
 283        amd_iommu_pc_get_set_reg_val(devid,
 284                        _GET_BANK(ev), _GET_CNTR(ev) ,
 285                         IOMMU_PC_DOMID_MATCH_REG, &reg, true);
 286}
 287
 288static void perf_iommu_disable_event(struct perf_event *event)
 289{
 290        u64 reg = 0ULL;
 291
 292        amd_iommu_pc_get_set_reg_val(_GET_DEVID(event),
 293                        _GET_BANK(event), _GET_CNTR(event),
 294                        IOMMU_PC_COUNTER_SRC_REG, &reg, true);
 295}
 296
 297static void perf_iommu_start(struct perf_event *event, int flags)
 298{
 299        struct hw_perf_event *hwc = &event->hw;
 300
 301        pr_debug("perf: amd_iommu:perf_iommu_start\n");
 302        if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
 303                return;
 304
 305        WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
 306        hwc->state = 0;
 307
 308        if (flags & PERF_EF_RELOAD) {
 309                u64 prev_raw_count =  local64_read(&hwc->prev_count);
 310                amd_iommu_pc_get_set_reg_val(_GET_DEVID(event),
 311                                _GET_BANK(event), _GET_CNTR(event),
 312                                IOMMU_PC_COUNTER_REG, &prev_raw_count, true);
 313        }
 314
 315        perf_iommu_enable_event(event);
 316        perf_event_update_userpage(event);
 317
 318}
 319
 320static void perf_iommu_read(struct perf_event *event)
 321{
 322        u64 count = 0ULL;
 323        u64 prev_raw_count = 0ULL;
 324        u64 delta = 0ULL;
 325        struct hw_perf_event *hwc = &event->hw;
 326        pr_debug("perf: amd_iommu:perf_iommu_read\n");
 327
 328        amd_iommu_pc_get_set_reg_val(_GET_DEVID(event),
 329                                _GET_BANK(event), _GET_CNTR(event),
 330                                IOMMU_PC_COUNTER_REG, &count, false);
 331
 332        /* IOMMU pc counter register is only 48 bits */
 333        count &= 0xFFFFFFFFFFFFULL;
 334
 335        prev_raw_count =  local64_read(&hwc->prev_count);
 336        if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
 337                                        count) != prev_raw_count)
 338                return;
 339
 340        /* Handling 48-bit counter overflowing */
 341        delta = (count << COUNTER_SHIFT) - (prev_raw_count << COUNTER_SHIFT);
 342        delta >>= COUNTER_SHIFT;
 343        local64_add(delta, &event->count);
 344
 345}
 346
 347static void perf_iommu_stop(struct perf_event *event, int flags)
 348{
 349        struct hw_perf_event *hwc = &event->hw;
 350        u64 config;
 351
 352        pr_debug("perf: amd_iommu:perf_iommu_stop\n");
 353
 354        if (hwc->state & PERF_HES_UPTODATE)
 355                return;
 356
 357        perf_iommu_disable_event(event);
 358        WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
 359        hwc->state |= PERF_HES_STOPPED;
 360
 361        if (hwc->state & PERF_HES_UPTODATE)
 362                return;
 363
 364        config = hwc->config;
 365        perf_iommu_read(event);
 366        hwc->state |= PERF_HES_UPTODATE;
 367}
 368
 369static int perf_iommu_add(struct perf_event *event, int flags)
 370{
 371        int retval;
 372        struct perf_amd_iommu *perf_iommu =
 373                        container_of(event->pmu, struct perf_amd_iommu, pmu);
 374
 375        pr_debug("perf: amd_iommu:perf_iommu_add\n");
 376        event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
 377
 378        /* request an iommu bank/counter */
 379        retval = get_next_avail_iommu_bnk_cntr(perf_iommu);
 380        if (retval != -ENOSPC)
 381                event->hw.extra_reg.reg = (u16)retval;
 382        else
 383                return retval;
 384
 385        if (flags & PERF_EF_START)
 386                perf_iommu_start(event, PERF_EF_RELOAD);
 387
 388        return 0;
 389}
 390
 391static void perf_iommu_del(struct perf_event *event, int flags)
 392{
 393        struct perf_amd_iommu *perf_iommu =
 394                        container_of(event->pmu, struct perf_amd_iommu, pmu);
 395
 396        pr_debug("perf: amd_iommu:perf_iommu_del\n");
 397        perf_iommu_stop(event, PERF_EF_UPDATE);
 398
 399        /* clear the assigned iommu bank/counter */
 400        clear_avail_iommu_bnk_cntr(perf_iommu,
 401                                     _GET_BANK(event),
 402                                     _GET_CNTR(event));
 403
 404        perf_event_update_userpage(event);
 405}
 406
 407static __init int _init_events_attrs(struct perf_amd_iommu *perf_iommu)
 408{
 409        struct attribute **attrs;
 410        struct attribute_group *attr_group;
 411        int i = 0, j;
 412
 413        while (amd_iommu_v2_event_descs[i].attr.attr.name)
 414                i++;
 415
 416        attr_group = kzalloc(sizeof(struct attribute *)
 417                * (i + 1) + sizeof(*attr_group), GFP_KERNEL);
 418        if (!attr_group)
 419                return -ENOMEM;
 420
 421        attrs = (struct attribute **)(attr_group + 1);
 422        for (j = 0; j < i; j++)
 423                attrs[j] = &amd_iommu_v2_event_descs[j].attr.attr;
 424
 425        attr_group->name = "events";
 426        attr_group->attrs = attrs;
 427        perf_iommu->events_group = attr_group;
 428
 429        return 0;
 430}
 431
 432static __init void amd_iommu_pc_exit(void)
 433{
 434        if (__perf_iommu.events_group != NULL) {
 435                kfree(__perf_iommu.events_group);
 436                __perf_iommu.events_group = NULL;
 437        }
 438}
 439
 440static __init int _init_perf_amd_iommu(
 441        struct perf_amd_iommu *perf_iommu, char *name)
 442{
 443        int ret;
 444
 445        raw_spin_lock_init(&perf_iommu->lock);
 446
 447        /* Init format attributes */
 448        perf_iommu->format_group = &amd_iommu_format_group;
 449
 450        /* Init cpumask attributes to only core 0 */
 451        cpumask_set_cpu(0, &iommu_cpumask);
 452        perf_iommu->cpumask_group = &amd_iommu_cpumask_group;
 453
 454        /* Init events attributes */
 455        if (_init_events_attrs(perf_iommu) != 0)
 456                pr_err("perf: amd_iommu: Only support raw events.\n");
 457
 458        /* Init null attributes */
 459        perf_iommu->null_group = NULL;
 460        perf_iommu->pmu.attr_groups = perf_iommu->attr_groups;
 461
 462        ret = perf_pmu_register(&perf_iommu->pmu, name, -1);
 463        if (ret) {
 464                pr_err("perf: amd_iommu: Failed to initialized.\n");
 465                amd_iommu_pc_exit();
 466        } else {
 467                pr_info("perf: amd_iommu: Detected. (%d banks, %d counters/bank)\n",
 468                        amd_iommu_pc_get_max_banks(IOMMU_BASE_DEVID),
 469                        amd_iommu_pc_get_max_counters(IOMMU_BASE_DEVID));
 470        }
 471
 472        return ret;
 473}
 474
 475static struct perf_amd_iommu __perf_iommu = {
 476        .pmu = {
 477                .task_ctx_nr    = perf_invalid_context,
 478                .event_init     = perf_iommu_event_init,
 479                .add            = perf_iommu_add,
 480                .del            = perf_iommu_del,
 481                .start          = perf_iommu_start,
 482                .stop           = perf_iommu_stop,
 483                .read           = perf_iommu_read,
 484        },
 485        .max_banks              = 0x00,
 486        .max_counters           = 0x00,
 487        .cntr_assign_mask       = 0ULL,
 488        .format_group           = NULL,
 489        .cpumask_group          = NULL,
 490        .events_group           = NULL,
 491        .null_group             = NULL,
 492};
 493
 494static __init int amd_iommu_pc_init(void)
 495{
 496        /* Make sure the IOMMU PC resource is available */
 497        if (!amd_iommu_pc_supported())
 498                return -ENODEV;
 499
 500        _init_perf_amd_iommu(&__perf_iommu, "amd_iommu");
 501
 502        return 0;
 503}
 504
 505device_initcall(amd_iommu_pc_init);
 506