162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * perf.c - performance monitor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2021 Intel Corporation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Lu Baolu <baolu.lu@linux.intel.com>
862306a36Sopenharmony_ci *         Fenghua Yu <fenghua.yu@intel.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/spinlock.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "iommu.h"
1462306a36Sopenharmony_ci#include "perf.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(latency_lock);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cibool dmar_latency_enabled(struct intel_iommu *iommu, enum latency_type type)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct latency_statistic *lstat = iommu->perf_statistic;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	return lstat && lstat[type].enabled;
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciint dmar_latency_enable(struct intel_iommu *iommu, enum latency_type type)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct latency_statistic *lstat;
2862306a36Sopenharmony_ci	unsigned long flags;
2962306a36Sopenharmony_ci	int ret = -EBUSY;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (dmar_latency_enabled(iommu, type))
3262306a36Sopenharmony_ci		return 0;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	spin_lock_irqsave(&latency_lock, flags);
3562306a36Sopenharmony_ci	if (!iommu->perf_statistic) {
3662306a36Sopenharmony_ci		iommu->perf_statistic = kzalloc(sizeof(*lstat) * DMAR_LATENCY_NUM,
3762306a36Sopenharmony_ci						GFP_ATOMIC);
3862306a36Sopenharmony_ci		if (!iommu->perf_statistic) {
3962306a36Sopenharmony_ci			ret = -ENOMEM;
4062306a36Sopenharmony_ci			goto unlock_out;
4162306a36Sopenharmony_ci		}
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	lstat = iommu->perf_statistic;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (!lstat[type].enabled) {
4762306a36Sopenharmony_ci		lstat[type].enabled = true;
4862306a36Sopenharmony_ci		lstat[type].counter[COUNTS_MIN] = UINT_MAX;
4962306a36Sopenharmony_ci		ret = 0;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ciunlock_out:
5262306a36Sopenharmony_ci	spin_unlock_irqrestore(&latency_lock, flags);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return ret;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_civoid dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct latency_statistic *lstat = iommu->perf_statistic;
6062306a36Sopenharmony_ci	unsigned long flags;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (!dmar_latency_enabled(iommu, type))
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	spin_lock_irqsave(&latency_lock, flags);
6662306a36Sopenharmony_ci	memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM);
6762306a36Sopenharmony_ci	spin_unlock_irqrestore(&latency_lock, flags);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_civoid dmar_latency_update(struct intel_iommu *iommu, enum latency_type type, u64 latency)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct latency_statistic *lstat = iommu->perf_statistic;
7362306a36Sopenharmony_ci	unsigned long flags;
7462306a36Sopenharmony_ci	u64 min, max;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!dmar_latency_enabled(iommu, type))
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	spin_lock_irqsave(&latency_lock, flags);
8062306a36Sopenharmony_ci	if (latency < 100)
8162306a36Sopenharmony_ci		lstat[type].counter[COUNTS_10e2]++;
8262306a36Sopenharmony_ci	else if (latency < 1000)
8362306a36Sopenharmony_ci		lstat[type].counter[COUNTS_10e3]++;
8462306a36Sopenharmony_ci	else if (latency < 10000)
8562306a36Sopenharmony_ci		lstat[type].counter[COUNTS_10e4]++;
8662306a36Sopenharmony_ci	else if (latency < 100000)
8762306a36Sopenharmony_ci		lstat[type].counter[COUNTS_10e5]++;
8862306a36Sopenharmony_ci	else if (latency < 1000000)
8962306a36Sopenharmony_ci		lstat[type].counter[COUNTS_10e6]++;
9062306a36Sopenharmony_ci	else if (latency < 10000000)
9162306a36Sopenharmony_ci		lstat[type].counter[COUNTS_10e7]++;
9262306a36Sopenharmony_ci	else
9362306a36Sopenharmony_ci		lstat[type].counter[COUNTS_10e8_plus]++;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	min = lstat[type].counter[COUNTS_MIN];
9662306a36Sopenharmony_ci	max = lstat[type].counter[COUNTS_MAX];
9762306a36Sopenharmony_ci	lstat[type].counter[COUNTS_MIN] = min_t(u64, min, latency);
9862306a36Sopenharmony_ci	lstat[type].counter[COUNTS_MAX] = max_t(u64, max, latency);
9962306a36Sopenharmony_ci	lstat[type].counter[COUNTS_SUM] += latency;
10062306a36Sopenharmony_ci	lstat[type].samples++;
10162306a36Sopenharmony_ci	spin_unlock_irqrestore(&latency_lock, flags);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic char *latency_counter_names[] = {
10562306a36Sopenharmony_ci	"                  <0.1us",
10662306a36Sopenharmony_ci	"   0.1us-1us", "    1us-10us", "  10us-100us",
10762306a36Sopenharmony_ci	"   100us-1ms", "    1ms-10ms", "      >=10ms",
10862306a36Sopenharmony_ci	"     min(us)", "     max(us)", " average(us)"
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic char *latency_type_names[] = {
11262306a36Sopenharmony_ci	"   inv_iotlb", "  inv_devtlb", "     inv_iec",
11362306a36Sopenharmony_ci	"     svm_prq"
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciint dmar_latency_snapshot(struct intel_iommu *iommu, char *str, size_t size)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct latency_statistic *lstat = iommu->perf_statistic;
11962306a36Sopenharmony_ci	unsigned long flags;
12062306a36Sopenharmony_ci	int bytes = 0, i, j;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	memset(str, 0, size);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	for (i = 0; i < COUNTS_NUM; i++)
12562306a36Sopenharmony_ci		bytes += snprintf(str + bytes, size - bytes,
12662306a36Sopenharmony_ci				  "%s", latency_counter_names[i]);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	spin_lock_irqsave(&latency_lock, flags);
12962306a36Sopenharmony_ci	for (i = 0; i < DMAR_LATENCY_NUM; i++) {
13062306a36Sopenharmony_ci		if (!dmar_latency_enabled(iommu, i))
13162306a36Sopenharmony_ci			continue;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		bytes += snprintf(str + bytes, size - bytes,
13462306a36Sopenharmony_ci				  "\n%s", latency_type_names[i]);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		for (j = 0; j < COUNTS_NUM; j++) {
13762306a36Sopenharmony_ci			u64 val = lstat[i].counter[j];
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			switch (j) {
14062306a36Sopenharmony_ci			case COUNTS_MIN:
14162306a36Sopenharmony_ci				if (val == UINT_MAX)
14262306a36Sopenharmony_ci					val = 0;
14362306a36Sopenharmony_ci				else
14462306a36Sopenharmony_ci					val = div_u64(val, 1000);
14562306a36Sopenharmony_ci				break;
14662306a36Sopenharmony_ci			case COUNTS_MAX:
14762306a36Sopenharmony_ci				val = div_u64(val, 1000);
14862306a36Sopenharmony_ci				break;
14962306a36Sopenharmony_ci			case COUNTS_SUM:
15062306a36Sopenharmony_ci				if (lstat[i].samples)
15162306a36Sopenharmony_ci					val = div_u64(val, (lstat[i].samples * 1000));
15262306a36Sopenharmony_ci				else
15362306a36Sopenharmony_ci					val = 0;
15462306a36Sopenharmony_ci				break;
15562306a36Sopenharmony_ci			default:
15662306a36Sopenharmony_ci				break;
15762306a36Sopenharmony_ci			}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci			bytes += snprintf(str + bytes, size - bytes,
16062306a36Sopenharmony_ci					  "%12lld", val);
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	spin_unlock_irqrestore(&latency_lock, flags);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return bytes;
16662306a36Sopenharmony_ci}
167