forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
3.9 KiB
166 lines
3.9 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* perf.c - performance monitor |
|
* |
|
* Copyright (C) 2021 Intel Corporation |
|
* |
|
* Author: Lu Baolu <[email protected]> |
|
* Fenghua Yu <[email protected]> |
|
*/ |
|
|
|
#include <linux/spinlock.h> |
|
#include <linux/intel-iommu.h> |
|
|
|
#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; |
|
}
|
|
|