diff options
| -rw-r--r-- | arch/x86/events/amd/iommu.c | 113 | ||||
| -rw-r--r-- | include/linux/perf_event.h | 7 |
2 files changed, 57 insertions, 63 deletions
diff --git a/arch/x86/events/amd/iommu.c b/arch/x86/events/amd/iommu.c index 7ac8138023cc..f0d94c8b382a 100644 --- a/arch/x86/events/amd/iommu.c +++ b/arch/x86/events/amd/iommu.c | |||
| @@ -23,17 +23,16 @@ | |||
| 23 | 23 | ||
| 24 | #define COUNTER_SHIFT 16 | 24 | #define COUNTER_SHIFT 16 |
| 25 | 25 | ||
| 26 | #define _GET_BANK(ev) ((u8)(ev->hw.extra_reg.reg >> 8)) | 26 | /* iommu pmu conf masks */ |
| 27 | #define _GET_CNTR(ev) ((u8)(ev->hw.extra_reg.reg)) | 27 | #define GET_CSOURCE(x) ((x)->conf & 0xFFULL) |
| 28 | 28 | #define GET_DEVID(x) (((x)->conf >> 8) & 0xFFFFULL) | |
| 29 | /* iommu pmu config masks */ | 29 | #define GET_DOMID(x) (((x)->conf >> 24) & 0xFFFFULL) |
| 30 | #define _GET_CSOURCE(ev) ((ev->hw.config & 0xFFULL)) | 30 | #define GET_PASID(x) (((x)->conf >> 40) & 0xFFFFFULL) |
| 31 | #define _GET_DEVID(ev) ((ev->hw.config >> 8) & 0xFFFFULL) | 31 | |
| 32 | #define _GET_PASID(ev) ((ev->hw.config >> 24) & 0xFFFFULL) | 32 | /* iommu pmu conf1 masks */ |
| 33 | #define _GET_DOMID(ev) ((ev->hw.config >> 40) & 0xFFFFULL) | 33 | #define GET_DEVID_MASK(x) ((x)->conf1 & 0xFFFFULL) |
| 34 | #define _GET_DEVID_MASK(ev) ((ev->hw.extra_reg.config) & 0xFFFFULL) | 34 | #define GET_DOMID_MASK(x) (((x)->conf1 >> 16) & 0xFFFFULL) |
| 35 | #define _GET_PASID_MASK(ev) ((ev->hw.extra_reg.config >> 16) & 0xFFFFULL) | 35 | #define GET_PASID_MASK(x) (((x)->conf1 >> 32) & 0xFFFFFULL) |
| 36 | #define _GET_DOMID_MASK(ev) ((ev->hw.extra_reg.config >> 32) & 0xFFFFULL) | ||
| 37 | 36 | ||
| 38 | static struct perf_amd_iommu __perf_iommu; | 37 | static struct perf_amd_iommu __perf_iommu; |
| 39 | 38 | ||
| @@ -50,11 +49,11 @@ struct perf_amd_iommu { | |||
| 50 | *---------------------------------------------*/ | 49 | *---------------------------------------------*/ |
| 51 | PMU_FORMAT_ATTR(csource, "config:0-7"); | 50 | PMU_FORMAT_ATTR(csource, "config:0-7"); |
| 52 | PMU_FORMAT_ATTR(devid, "config:8-23"); | 51 | PMU_FORMAT_ATTR(devid, "config:8-23"); |
| 53 | PMU_FORMAT_ATTR(pasid, "config:24-39"); | 52 | PMU_FORMAT_ATTR(domid, "config:24-39"); |
| 54 | PMU_FORMAT_ATTR(domid, "config:40-55"); | 53 | PMU_FORMAT_ATTR(pasid, "config:40-59"); |
| 55 | PMU_FORMAT_ATTR(devid_mask, "config1:0-15"); | 54 | PMU_FORMAT_ATTR(devid_mask, "config1:0-15"); |
| 56 | PMU_FORMAT_ATTR(pasid_mask, "config1:16-31"); | 55 | PMU_FORMAT_ATTR(domid_mask, "config1:16-31"); |
| 57 | PMU_FORMAT_ATTR(domid_mask, "config1:32-47"); | 56 | PMU_FORMAT_ATTR(pasid_mask, "config1:32-51"); |
| 58 | 57 | ||
| 59 | static struct attribute *iommu_format_attrs[] = { | 58 | static struct attribute *iommu_format_attrs[] = { |
| 60 | &format_attr_csource.attr, | 59 | &format_attr_csource.attr, |
| @@ -150,30 +149,34 @@ static struct attribute_group amd_iommu_cpumask_group = { | |||
| 150 | 149 | ||
| 151 | /*---------------------------------------------*/ | 150 | /*---------------------------------------------*/ |
| 152 | 151 | ||
| 153 | static int get_next_avail_iommu_bnk_cntr(struct perf_amd_iommu *perf_iommu) | 152 | static int get_next_avail_iommu_bnk_cntr(struct perf_event *event) |
| 154 | { | 153 | { |
| 154 | struct perf_amd_iommu *piommu = container_of(event->pmu, struct perf_amd_iommu, pmu); | ||
| 155 | int max_cntrs = piommu->max_counters; | ||
| 156 | int max_banks = piommu->max_banks; | ||
| 157 | u32 shift, bank, cntr; | ||
| 155 | unsigned long flags; | 158 | unsigned long flags; |
| 156 | int shift, bank, cntr, retval; | 159 | int retval; |
| 157 | int max_banks = perf_iommu->max_banks; | ||
| 158 | int max_cntrs = perf_iommu->max_counters; | ||
| 159 | 160 | ||
| 160 | raw_spin_lock_irqsave(&perf_iommu->lock, flags); | 161 | raw_spin_lock_irqsave(&piommu->lock, flags); |
| 161 | 162 | ||
| 162 | for (bank = 0, shift = 0; bank < max_banks; bank++) { | 163 | for (bank = 0, shift = 0; bank < max_banks; bank++) { |
| 163 | for (cntr = 0; cntr < max_cntrs; cntr++) { | 164 | for (cntr = 0; cntr < max_cntrs; cntr++) { |
| 164 | shift = bank + (bank*3) + cntr; | 165 | shift = bank + (bank*3) + cntr; |
| 165 | if (perf_iommu->cntr_assign_mask & BIT_ULL(shift)) { | 166 | if (piommu->cntr_assign_mask & BIT_ULL(shift)) { |
| 166 | continue; | 167 | continue; |
| 167 | } else { | 168 | } else { |
| 168 | perf_iommu->cntr_assign_mask |= BIT_ULL(shift); | 169 | piommu->cntr_assign_mask |= BIT_ULL(shift); |
| 169 | retval = ((bank & 0xFF) << 8) | (cntr & 0xFF); | 170 | event->hw.iommu_bank = bank; |
| 171 | event->hw.iommu_cntr = cntr; | ||
| 172 | retval = 0; | ||
| 170 | goto out; | 173 | goto out; |
| 171 | } | 174 | } |
| 172 | } | 175 | } |
| 173 | } | 176 | } |
| 174 | retval = -ENOSPC; | 177 | retval = -ENOSPC; |
| 175 | out: | 178 | out: |
| 176 | raw_spin_unlock_irqrestore(&perf_iommu->lock, flags); | 179 | raw_spin_unlock_irqrestore(&piommu->lock, flags); |
| 177 | return retval; | 180 | return retval; |
| 178 | } | 181 | } |
| 179 | 182 | ||
| @@ -202,8 +205,6 @@ static int clear_avail_iommu_bnk_cntr(struct perf_amd_iommu *perf_iommu, | |||
| 202 | static int perf_iommu_event_init(struct perf_event *event) | 205 | static int perf_iommu_event_init(struct perf_event *event) |
| 203 | { | 206 | { |
| 204 | struct hw_perf_event *hwc = &event->hw; | 207 | struct hw_perf_event *hwc = &event->hw; |
| 205 | struct perf_amd_iommu *perf_iommu; | ||
| 206 | u64 config, config1; | ||
| 207 | 208 | ||
| 208 | /* test the event attr type check for PMU enumeration */ | 209 | /* test the event attr type check for PMU enumeration */ |
| 209 | if (event->attr.type != event->pmu->type) | 210 | if (event->attr.type != event->pmu->type) |
| @@ -225,21 +226,9 @@ static int perf_iommu_event_init(struct perf_event *event) | |||
| 225 | if (event->cpu < 0) | 226 | if (event->cpu < 0) |
| 226 | return -EINVAL; | 227 | return -EINVAL; |
| 227 | 228 | ||
| 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 | /* update the hw_perf_event struct with the iommu config data */ | 229 | /* update the hw_perf_event struct with the iommu config data */ |
| 241 | hwc->config = config; | 230 | hwc->conf = event->attr.config; |
| 242 | hwc->extra_reg.config = config1; | 231 | hwc->conf1 = event->attr.config1; |
| 243 | 232 | ||
| 244 | return 0; | 233 | return 0; |
| 245 | } | 234 | } |
| @@ -247,26 +236,28 @@ static int perf_iommu_event_init(struct perf_event *event) | |||
| 247 | static void perf_iommu_enable_event(struct perf_event *ev) | 236 | static void perf_iommu_enable_event(struct perf_event *ev) |
| 248 | { | 237 | { |
| 249 | struct amd_iommu *iommu = get_amd_iommu(0); | 238 | struct amd_iommu *iommu = get_amd_iommu(0); |
| 250 | u8 csource = _GET_CSOURCE(ev); | 239 | struct hw_perf_event *hwc = &ev->hw; |
| 251 | u16 devid = _GET_DEVID(ev); | 240 | u8 bank = hwc->iommu_bank; |
| 252 | u8 bank = _GET_BANK(ev); | 241 | u8 cntr = hwc->iommu_cntr; |
| 253 | u8 cntr = _GET_CNTR(ev); | ||
| 254 | u64 reg = 0ULL; | 242 | u64 reg = 0ULL; |
| 255 | 243 | ||
| 256 | reg = csource; | 244 | reg = GET_CSOURCE(hwc); |
| 257 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_COUNTER_SRC_REG, ®); | 245 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_COUNTER_SRC_REG, ®); |
| 258 | 246 | ||
| 259 | reg = devid | (_GET_DEVID_MASK(ev) << 32); | 247 | reg = GET_DEVID_MASK(hwc); |
| 248 | reg = GET_DEVID(hwc) | (reg << 32); | ||
| 260 | if (reg) | 249 | if (reg) |
| 261 | reg |= BIT(31); | 250 | reg |= BIT(31); |
| 262 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DEVID_MATCH_REG, ®); | 251 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DEVID_MATCH_REG, ®); |
| 263 | 252 | ||
| 264 | reg = _GET_PASID(ev) | (_GET_PASID_MASK(ev) << 32); | 253 | reg = GET_PASID_MASK(hwc); |
| 254 | reg = GET_PASID(hwc) | (reg << 32); | ||
| 265 | if (reg) | 255 | if (reg) |
| 266 | reg |= BIT(31); | 256 | reg |= BIT(31); |
| 267 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_PASID_MATCH_REG, ®); | 257 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_PASID_MATCH_REG, ®); |
| 268 | 258 | ||
| 269 | reg = _GET_DOMID(ev) | (_GET_DOMID_MASK(ev) << 32); | 259 | reg = GET_DOMID_MASK(hwc); |
| 260 | reg = GET_DOMID(hwc) | (reg << 32); | ||
| 270 | if (reg) | 261 | if (reg) |
| 271 | reg |= BIT(31); | 262 | reg |= BIT(31); |
| 272 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DOMID_MATCH_REG, ®); | 263 | amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DOMID_MATCH_REG, ®); |
| @@ -275,16 +266,16 @@ static void perf_iommu_enable_event(struct perf_event *ev) | |||
| 275 | static void perf_iommu_disable_event(struct perf_event *event) | 266 | static void perf_iommu_disable_event(struct perf_event *event) |
| 276 | { | 267 | { |
| 277 | struct amd_iommu *iommu = get_amd_iommu(0); | 268 | struct amd_iommu *iommu = get_amd_iommu(0); |
| 269 | struct hw_perf_event *hwc = &event->hw; | ||
| 278 | u64 reg = 0ULL; | 270 | u64 reg = 0ULL; |
| 279 | 271 | ||
| 280 | amd_iommu_pc_set_reg(iommu, _GET_BANK(event), _GET_CNTR(event), | 272 | amd_iommu_pc_set_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr, |
| 281 | IOMMU_PC_COUNTER_SRC_REG, ®); | 273 | IOMMU_PC_COUNTER_SRC_REG, ®); |
| 282 | } | 274 | } |
| 283 | 275 | ||
| 284 | static void perf_iommu_start(struct perf_event *event, int flags) | 276 | static void perf_iommu_start(struct perf_event *event, int flags) |
| 285 | { | 277 | { |
| 286 | struct hw_perf_event *hwc = &event->hw; | 278 | struct hw_perf_event *hwc = &event->hw; |
| 287 | struct amd_iommu *iommu = get_amd_iommu(0); | ||
| 288 | 279 | ||
| 289 | if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) | 280 | if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) |
| 290 | return; | 281 | return; |
| @@ -293,8 +284,10 @@ static void perf_iommu_start(struct perf_event *event, int flags) | |||
| 293 | hwc->state = 0; | 284 | hwc->state = 0; |
| 294 | 285 | ||
| 295 | if (flags & PERF_EF_RELOAD) { | 286 | if (flags & PERF_EF_RELOAD) { |
| 296 | u64 prev_raw_count = local64_read(&hwc->prev_count); | 287 | u64 prev_raw_count = local64_read(&hwc->prev_count); |
| 297 | amd_iommu_pc_set_reg(iommu, _GET_BANK(event), _GET_CNTR(event), | 288 | struct amd_iommu *iommu = get_amd_iommu(0); |
| 289 | |||
| 290 | amd_iommu_pc_set_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr, | ||
| 298 | IOMMU_PC_COUNTER_REG, &prev_raw_count); | 291 | IOMMU_PC_COUNTER_REG, &prev_raw_count); |
| 299 | } | 292 | } |
| 300 | 293 | ||
| @@ -309,7 +302,7 @@ static void perf_iommu_read(struct perf_event *event) | |||
| 309 | struct hw_perf_event *hwc = &event->hw; | 302 | struct hw_perf_event *hwc = &event->hw; |
| 310 | struct amd_iommu *iommu = get_amd_iommu(0); | 303 | struct amd_iommu *iommu = get_amd_iommu(0); |
| 311 | 304 | ||
| 312 | if (amd_iommu_pc_get_reg(iommu, _GET_BANK(event), _GET_CNTR(event), | 305 | if (amd_iommu_pc_get_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr, |
| 313 | IOMMU_PC_COUNTER_REG, &count)) | 306 | IOMMU_PC_COUNTER_REG, &count)) |
| 314 | return; | 307 | return; |
| 315 | 308 | ||
| @@ -329,7 +322,6 @@ static void perf_iommu_read(struct perf_event *event) | |||
| 329 | static void perf_iommu_stop(struct perf_event *event, int flags) | 322 | static void perf_iommu_stop(struct perf_event *event, int flags) |
| 330 | { | 323 | { |
| 331 | struct hw_perf_event *hwc = &event->hw; | 324 | struct hw_perf_event *hwc = &event->hw; |
| 332 | u64 config; | ||
| 333 | 325 | ||
| 334 | if (hwc->state & PERF_HES_UPTODATE) | 326 | if (hwc->state & PERF_HES_UPTODATE) |
| 335 | return; | 327 | return; |
| @@ -341,7 +333,6 @@ static void perf_iommu_stop(struct perf_event *event, int flags) | |||
| 341 | if (hwc->state & PERF_HES_UPTODATE) | 333 | if (hwc->state & PERF_HES_UPTODATE) |
| 342 | return; | 334 | return; |
| 343 | 335 | ||
| 344 | config = hwc->config; | ||
| 345 | perf_iommu_read(event); | 336 | perf_iommu_read(event); |
| 346 | hwc->state |= PERF_HES_UPTODATE; | 337 | hwc->state |= PERF_HES_UPTODATE; |
| 347 | } | 338 | } |
| @@ -349,16 +340,12 @@ static void perf_iommu_stop(struct perf_event *event, int flags) | |||
| 349 | static int perf_iommu_add(struct perf_event *event, int flags) | 340 | static int perf_iommu_add(struct perf_event *event, int flags) |
| 350 | { | 341 | { |
| 351 | int retval; | 342 | int retval; |
| 352 | struct perf_amd_iommu *perf_iommu = | ||
| 353 | container_of(event->pmu, struct perf_amd_iommu, pmu); | ||
| 354 | 343 | ||
| 355 | event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED; | 344 | event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED; |
| 356 | 345 | ||
| 357 | /* request an iommu bank/counter */ | 346 | /* request an iommu bank/counter */ |
| 358 | retval = get_next_avail_iommu_bnk_cntr(perf_iommu); | 347 | retval = get_next_avail_iommu_bnk_cntr(event); |
| 359 | if (retval != -ENOSPC) | 348 | if (retval) |
| 360 | event->hw.extra_reg.reg = (u16)retval; | ||
| 361 | else | ||
| 362 | return retval; | 349 | return retval; |
| 363 | 350 | ||
| 364 | if (flags & PERF_EF_START) | 351 | if (flags & PERF_EF_START) |
| @@ -369,6 +356,7 @@ static int perf_iommu_add(struct perf_event *event, int flags) | |||
| 369 | 356 | ||
| 370 | static void perf_iommu_del(struct perf_event *event, int flags) | 357 | static void perf_iommu_del(struct perf_event *event, int flags) |
| 371 | { | 358 | { |
| 359 | struct hw_perf_event *hwc = &event->hw; | ||
| 372 | struct perf_amd_iommu *perf_iommu = | 360 | struct perf_amd_iommu *perf_iommu = |
| 373 | container_of(event->pmu, struct perf_amd_iommu, pmu); | 361 | container_of(event->pmu, struct perf_amd_iommu, pmu); |
| 374 | 362 | ||
| @@ -376,8 +364,7 @@ static void perf_iommu_del(struct perf_event *event, int flags) | |||
| 376 | 364 | ||
| 377 | /* clear the assigned iommu bank/counter */ | 365 | /* clear the assigned iommu bank/counter */ |
| 378 | clear_avail_iommu_bnk_cntr(perf_iommu, | 366 | clear_avail_iommu_bnk_cntr(perf_iommu, |
| 379 | _GET_BANK(event), | 367 | hwc->iommu_bank, hwc->iommu_cntr); |
| 380 | _GET_CNTR(event)); | ||
| 381 | 368 | ||
| 382 | perf_event_update_userpage(event); | 369 | perf_event_update_userpage(event); |
| 383 | } | 370 | } |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index b6e75c9d4791..24a635887f28 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -165,6 +165,13 @@ struct hw_perf_event { | |||
| 165 | struct list_head bp_list; | 165 | struct list_head bp_list; |
| 166 | }; | 166 | }; |
| 167 | #endif | 167 | #endif |
| 168 | struct { /* amd_iommu */ | ||
| 169 | u8 iommu_bank; | ||
| 170 | u8 iommu_cntr; | ||
| 171 | u16 padding; | ||
| 172 | u64 conf; | ||
| 173 | u64 conf1; | ||
| 174 | }; | ||
| 168 | }; | 175 | }; |
| 169 | /* | 176 | /* |
| 170 | * If the event is a per task event, this will point to the task in | 177 | * If the event is a per task event, this will point to the task in |
