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