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

drm/amdgpu: add pmu counters

adding perf event counters

Signed-off-by: Jonathan Kim <Jonathan.Kim@amd.com>
Reviewed-by: Felix Kuehling <Felix.Kuehling@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Jonathan Kim and committed by
Alex Deucher
9c7c85f7 e4cf4bf5

+324 -1
+1 -1
drivers/gpu/drm/amd/amdgpu/Makefile
··· 54 54 amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \ 55 55 amdgpu_vf_error.o amdgpu_sched.o amdgpu_debugfs.o amdgpu_ids.o \ 56 56 amdgpu_gmc.o amdgpu_xgmi.o amdgpu_csa.o amdgpu_ras.o amdgpu_vm_cpu.o \ 57 - amdgpu_vm_sdma.o 57 + amdgpu_vm_sdma.o amdgpu_pmu.o 58 58 59 59 # add asic specific block 60 60 amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o cik_ih.o kv_smc.o kv_dpm.o \
+6
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
··· 61 61 62 62 #include "amdgpu_xgmi.h" 63 63 #include "amdgpu_ras.h" 64 + #include "amdgpu_pmu.h" 64 65 65 66 MODULE_FIRMWARE("amdgpu/vega10_gpu_info.bin"); 66 67 MODULE_FIRMWARE("amdgpu/vega12_gpu_info.bin"); ··· 2747 2746 return r; 2748 2747 } 2749 2748 2749 + r = amdgpu_pmu_init(adev); 2750 + if (r) 2751 + dev_err(adev->dev, "amdgpu_pmu_init failed\n"); 2752 + 2750 2753 return 0; 2751 2754 2752 2755 failed: ··· 2819 2814 amdgpu_debugfs_regs_cleanup(adev); 2820 2815 device_remove_file(adev->dev, &dev_attr_pcie_replay_count); 2821 2816 amdgpu_ucode_sysfs_fini(adev); 2817 + amdgpu_pmu_fini(adev); 2822 2818 } 2823 2819 2824 2820
+280
drivers/gpu/drm/amd/amdgpu/amdgpu_pmu.c
··· 1 + /* 2 + * Copyright 2019 Advanced Micro Devices, Inc. 3 + * 4 + * Permission is hereby granted, free of charge, to any person obtaining a 5 + * copy of this software and associated documentation files (the "Software"), 6 + * to deal in the Software without restriction, including without limitation 7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 + * and/or sell copies of the Software, and to permit persons to whom the 9 + * Software is furnished to do so, subject to the following conditions: 10 + * 11 + * The above copyright notice and this permission notice shall be included in 12 + * all copies or substantial portions of the Software. 13 + * 14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 + * OTHER DEALINGS IN THE SOFTWARE. 21 + * 22 + * Author: Jonathan Kim <jonathan.kim@amd.com> 23 + * 24 + */ 25 + 26 + #include <linux/perf_event.h> 27 + #include <linux/init.h> 28 + #include "amdgpu.h" 29 + #include "amdgpu_pmu.h" 30 + #include "df_v3_6.h" 31 + 32 + #define PMU_NAME_SIZE 32 33 + 34 + /* record to keep track of pmu entry per pmu type per device */ 35 + struct amdgpu_pmu_entry { 36 + struct list_head entry; 37 + struct amdgpu_device *adev; 38 + struct pmu pmu; 39 + unsigned int pmu_perf_type; 40 + }; 41 + 42 + static LIST_HEAD(amdgpu_pmu_list); 43 + 44 + 45 + /* initialize perf counter */ 46 + static int amdgpu_perf_event_init(struct perf_event *event) 47 + { 48 + struct hw_perf_event *hwc = &event->hw; 49 + 50 + /* test the event attr type check for PMU enumeration */ 51 + if (event->attr.type != event->pmu->type) 52 + return -ENOENT; 53 + 54 + /* update the hw_perf_event struct with config data */ 55 + hwc->conf = event->attr.config; 56 + 57 + return 0; 58 + } 59 + 60 + /* start perf counter */ 61 + static void amdgpu_perf_start(struct perf_event *event, int flags) 62 + { 63 + struct hw_perf_event *hwc = &event->hw; 64 + struct amdgpu_pmu_entry *pe = container_of(event->pmu, 65 + struct amdgpu_pmu_entry, 66 + pmu); 67 + 68 + if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) 69 + return; 70 + 71 + WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); 72 + hwc->state = 0; 73 + 74 + switch (pe->pmu_perf_type) { 75 + case PERF_TYPE_AMDGPU_DF: 76 + if (!(flags & PERF_EF_RELOAD)) 77 + pe->adev->df_funcs->pmc_start(pe->adev, hwc->conf, 1); 78 + 79 + pe->adev->df_funcs->pmc_start(pe->adev, hwc->conf, 0); 80 + break; 81 + default: 82 + break; 83 + } 84 + 85 + perf_event_update_userpage(event); 86 + 87 + } 88 + 89 + /* read perf counter */ 90 + static void amdgpu_perf_read(struct perf_event *event) 91 + { 92 + struct hw_perf_event *hwc = &event->hw; 93 + struct amdgpu_pmu_entry *pe = container_of(event->pmu, 94 + struct amdgpu_pmu_entry, 95 + pmu); 96 + 97 + u64 count, prev; 98 + 99 + do { 100 + prev = local64_read(&hwc->prev_count); 101 + 102 + switch (pe->pmu_perf_type) { 103 + case PERF_TYPE_AMDGPU_DF: 104 + pe->adev->df_funcs->pmc_get_count(pe->adev, hwc->conf, 105 + &count); 106 + break; 107 + default: 108 + count = 0; 109 + break; 110 + }; 111 + } while (local64_cmpxchg(&hwc->prev_count, prev, count) != prev); 112 + 113 + local64_add(count - prev, &event->count); 114 + } 115 + 116 + /* stop perf counter */ 117 + static void amdgpu_perf_stop(struct perf_event *event, int flags) 118 + { 119 + struct hw_perf_event *hwc = &event->hw; 120 + struct amdgpu_pmu_entry *pe = container_of(event->pmu, 121 + struct amdgpu_pmu_entry, 122 + pmu); 123 + 124 + if (hwc->state & PERF_HES_UPTODATE) 125 + return; 126 + 127 + switch (pe->pmu_perf_type) { 128 + case PERF_TYPE_AMDGPU_DF: 129 + pe->adev->df_funcs->pmc_stop(pe->adev, hwc->conf, 0); 130 + break; 131 + default: 132 + break; 133 + }; 134 + 135 + WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); 136 + hwc->state |= PERF_HES_STOPPED; 137 + 138 + if (hwc->state & PERF_HES_UPTODATE) 139 + return; 140 + 141 + amdgpu_perf_read(event); 142 + hwc->state |= PERF_HES_UPTODATE; 143 + } 144 + 145 + /* add perf counter */ 146 + static int amdgpu_perf_add(struct perf_event *event, int flags) 147 + { 148 + struct hw_perf_event *hwc = &event->hw; 149 + int retval; 150 + 151 + struct amdgpu_pmu_entry *pe = container_of(event->pmu, 152 + struct amdgpu_pmu_entry, 153 + pmu); 154 + 155 + event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 156 + 157 + switch (pe->pmu_perf_type) { 158 + case PERF_TYPE_AMDGPU_DF: 159 + retval = pe->adev->df_funcs->pmc_start(pe->adev, hwc->conf, 1); 160 + break; 161 + default: 162 + return 0; 163 + }; 164 + 165 + if (retval) 166 + return retval; 167 + 168 + if (flags & PERF_EF_START) 169 + amdgpu_perf_start(event, PERF_EF_RELOAD); 170 + 171 + return retval; 172 + 173 + } 174 + 175 + /* delete perf counter */ 176 + static void amdgpu_perf_del(struct perf_event *event, int flags) 177 + { 178 + struct hw_perf_event *hwc = &event->hw; 179 + struct amdgpu_pmu_entry *pe = container_of(event->pmu, 180 + struct amdgpu_pmu_entry, 181 + pmu); 182 + 183 + amdgpu_perf_stop(event, PERF_EF_UPDATE); 184 + 185 + switch (pe->pmu_perf_type) { 186 + case PERF_TYPE_AMDGPU_DF: 187 + pe->adev->df_funcs->pmc_stop(pe->adev, hwc->conf, 1); 188 + break; 189 + default: 190 + break; 191 + }; 192 + 193 + perf_event_update_userpage(event); 194 + } 195 + 196 + /* vega20 pmus */ 197 + 198 + /* init pmu tracking per pmu type */ 199 + static int init_pmu_by_type(struct amdgpu_device *adev, 200 + const struct attribute_group *attr_groups[], 201 + char *pmu_type_name, char *pmu_file_prefix, 202 + unsigned int pmu_perf_type, 203 + unsigned int num_counters) 204 + { 205 + char pmu_name[PMU_NAME_SIZE]; 206 + struct amdgpu_pmu_entry *pmu_entry; 207 + int ret = 0; 208 + 209 + pmu_entry = kzalloc(sizeof(struct amdgpu_pmu_entry), GFP_KERNEL); 210 + 211 + if (!pmu_entry) 212 + return -ENOMEM; 213 + 214 + pmu_entry->adev = adev; 215 + pmu_entry->pmu = (struct pmu){ 216 + .event_init = amdgpu_perf_event_init, 217 + .add = amdgpu_perf_add, 218 + .del = amdgpu_perf_del, 219 + .start = amdgpu_perf_start, 220 + .stop = amdgpu_perf_stop, 221 + .read = amdgpu_perf_read, 222 + .task_ctx_nr = perf_invalid_context, 223 + }; 224 + 225 + pmu_entry->pmu.attr_groups = attr_groups; 226 + pmu_entry->pmu_perf_type = pmu_perf_type; 227 + snprintf(pmu_name, PMU_NAME_SIZE, "%s_%d", 228 + pmu_file_prefix, adev->ddev->primary->index); 229 + 230 + ret = perf_pmu_register(&pmu_entry->pmu, pmu_name, -1); 231 + 232 + if (ret) { 233 + kfree(pmu_entry); 234 + pr_warn("Error initializing AMDGPU %s PMUs.\n", pmu_type_name); 235 + return ret; 236 + } 237 + 238 + pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n", 239 + pmu_type_name, num_counters); 240 + 241 + list_add_tail(&pmu_entry->entry, &amdgpu_pmu_list); 242 + 243 + return 0; 244 + } 245 + 246 + /* init amdgpu_pmu */ 247 + int amdgpu_pmu_init(struct amdgpu_device *adev) 248 + { 249 + int ret = 0; 250 + 251 + switch (adev->asic_type) { 252 + case CHIP_VEGA20: 253 + /* init df */ 254 + ret = init_pmu_by_type(adev, df_v3_6_attr_groups, 255 + "DF", "amdgpu_df", PERF_TYPE_AMDGPU_DF, 256 + DF_V3_6_MAX_COUNTERS); 257 + 258 + /* other pmu types go here*/ 259 + break; 260 + default: 261 + return 0; 262 + } 263 + 264 + return 0; 265 + } 266 + 267 + 268 + /* destroy all pmu data associated with target device */ 269 + void amdgpu_pmu_fini(struct amdgpu_device *adev) 270 + { 271 + struct amdgpu_pmu_entry *pe, *temp; 272 + 273 + list_for_each_entry_safe(pe, temp, &amdgpu_pmu_list, entry) { 274 + if (pe->adev == adev) { 275 + list_del(&pe->entry); 276 + perf_pmu_unregister(&pe->pmu); 277 + kfree(pe); 278 + } 279 + } 280 + }
+37
drivers/gpu/drm/amd/amdgpu/amdgpu_pmu.h
··· 1 + /* 2 + * Copyright 2019 Advanced Micro Devices, Inc. 3 + * 4 + * Permission is hereby granted, free of charge, to any person obtaining a 5 + * copy of this software and associated documentation files (the "Software"), 6 + * to deal in the Software without restriction, including without limitation 7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 + * and/or sell copies of the Software, and to permit persons to whom the 9 + * Software is furnished to do so, subject to the following conditions: 10 + * 11 + * The above copyright notice and this permission notice shall be included in 12 + * all copies or substantial portions of the Software. 13 + * 14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 + * OTHER DEALINGS IN THE SOFTWARE. 21 + * 22 + * Author: Jonathan Kim <jonathan.kim@amd.com> 23 + * 24 + */ 25 + 26 + #ifndef _AMDGPU_PMU_H_ 27 + #define _AMDGPU_PMU_H_ 28 + 29 + enum amdgpu_pmu_perf_type { 30 + PERF_TYPE_AMDGPU_DF = 0, 31 + PERF_TYPE_AMDGPU_MAX 32 + }; 33 + 34 + int amdgpu_pmu_init(struct amdgpu_device *adev); 35 + void amdgpu_pmu_fini(struct amdgpu_device *adev); 36 + 37 + #endif /* _AMDGPU_PMU_H_ */