// SPDX-License-Identifier: GPL-2.0 /* * perf.c - performance monitor * * Copyright (C) 2021 Intel Corporation * * Author: Lu Baolu * Fenghua Yu */ #include #include #include "perf.h" static DEFINE_SPINLOCK(latency_lock); bool dmar_latency_enabled(struct intel_iommu *iommu, enum latency_type type) { struct latency_statistic *lstat = iommu->perf_statistic; return lstat && lstat[type].enabled; } int dmar_latency_enable(struct intel_iommu *iommu, enum latency_type type) { struct latency_statistic *lstat; unsigned long flags; int ret = -EBUSY; if (dmar_latency_enabled(iommu, type)) return 0; spin_lock_irqsave(&latency_lock, flags); if (!iommu->perf_statistic) { iommu->perf_statistic = kzalloc(sizeof(*lstat) * DMAR_LATENCY_NUM, GFP_ATOMIC); if (!iommu->perf_statistic) { ret = -ENOMEM; goto unlock_out; } } lstat = iommu->perf_statistic; if (!lstat[type].enabled) { lstat[type].enabled = true; lstat[type].counter[COUNTS_MIN] = UINT_MAX; ret = 0; } unlock_out: spin_unlock_irqrestore(&latency_lock, flags); return ret; } void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type) { struct latency_statistic *lstat = iommu->perf_statistic; unsigned long flags; if (!dmar_latency_enabled(iommu, type)) return; spin_lock_irqsave(&latency_lock, flags); memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM); spin_unlock_irqrestore(&latency_lock, flags); } void dmar_latency_update(struct intel_iommu *iommu, enum latency_type type, u64 latency) { struct latency_statistic *lstat = iommu->perf_statistic; unsigned long flags; u64 min, max; if (!dmar_latency_enabled(iommu, type)) return; spin_lock_irqsave(&latency_lock, flags); if (latency < 100) lstat[type].counter[COUNTS_10e2]++; else if (latency < 1000) lstat[type].counter[COUNTS_10e3]++; else if (latency < 10000) lstat[type].counter[COUNTS_10e4]++; else if (latency < 100000) lstat[type].counter[COUNTS_10e5]++; else if (latency < 1000000) lstat[type].counter[COUNTS_10e6]++; else if (latency < 10000000) lstat[type].counter[COUNTS_10e7]++; else lstat[type].counter[COUNTS_10e8_plus]++; min = lstat[type].counter[COUNTS_MIN]; max = lstat[type].counter[COUNTS_MAX]; lstat[type].counter[COUNTS_MIN] = min_t(u64, min, latency); lstat[type].counter[COUNTS_MAX] = max_t(u64, max, latency); lstat[type].counter[COUNTS_SUM] += latency; lstat[type].samples++; spin_unlock_irqrestore(&latency_lock, flags); } static char *latency_counter_names[] = { " <0.1us", " 0.1us-1us", " 1us-10us", " 10us-100us", " 100us-1ms", " 1ms-10ms", " >=10ms", " min(us)", " max(us)", " average(us)" }; static char *latency_type_names[] = { " inv_iotlb", " inv_devtlb", " inv_iec", " svm_prq" }; int dmar_latency_snapshot(struct intel_iommu *iommu, char *str, size_t size) { struct latency_statistic *lstat = iommu->perf_statistic; unsigned long flags; int bytes = 0, i, j; memset(str, 0, size); for (i = 0; i < COUNTS_NUM; i++) bytes += snprintf(str + bytes, size - bytes, "%s", latency_counter_names[i]); spin_lock_irqsave(&latency_lock, flags); for (i = 0; i < DMAR_LATENCY_NUM; i++) { if (!dmar_latency_enabled(iommu, i)) continue; bytes += snprintf(str + bytes, size - bytes, "\n%s", latency_type_names[i]); for (j = 0; j < COUNTS_NUM; j++) { u64 val = lstat[i].counter[j]; switch (j) { case COUNTS_MIN: if (val == UINT_MAX) val = 0; else val = div_u64(val, 1000); break; case COUNTS_MAX: val = div_u64(val, 1000); break; case COUNTS_SUM: if (lstat[i].samples) val = div_u64(val, (lstat[i].samples * 1000)); else val = 0; break; default: break; } bytes += snprintf(str + bytes, size - bytes, "%12lld", val); } } spin_unlock_irqrestore(&latency_lock, flags); return bytes; }