1 // SPDX-License-Identifier: GPL-2.0 2 /** 3 * perf.c - performance monitor 4 * 5 * Copyright (C) 2021 Intel Corporation 6 * 7 * Author: Lu Baolu <baolu.lu@linux.intel.com> 8 * Fenghua Yu <fenghua.yu@intel.com> 9 */ 10 11 #include <linux/spinlock.h> 12 #include <linux/intel-iommu.h> 13 14 #include "perf.h" 15 16 static DEFINE_SPINLOCK(latency_lock); 17 18 bool dmar_latency_enabled(struct intel_iommu *iommu, enum latency_type type) 19 { 20 struct latency_statistic *lstat = iommu->perf_statistic; 21 22 return lstat && lstat[type].enabled; 23 } 24 25 int dmar_latency_enable(struct intel_iommu *iommu, enum latency_type type) 26 { 27 struct latency_statistic *lstat; 28 unsigned long flags; 29 int ret = -EBUSY; 30 31 if (dmar_latency_enabled(iommu, type)) 32 return 0; 33 34 spin_lock_irqsave(&latency_lock, flags); 35 if (!iommu->perf_statistic) { 36 iommu->perf_statistic = kzalloc(sizeof(*lstat) * DMAR_LATENCY_NUM, 37 GFP_ATOMIC); 38 if (!iommu->perf_statistic) { 39 ret = -ENOMEM; 40 goto unlock_out; 41 } 42 } 43 44 lstat = iommu->perf_statistic; 45 46 if (!lstat[type].enabled) { 47 lstat[type].enabled = true; 48 lstat[type].counter[COUNTS_MIN] = UINT_MAX; 49 ret = 0; 50 } 51 unlock_out: 52 spin_unlock_irqrestore(&latency_lock, flags); 53 54 return ret; 55 } 56 57 void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type) 58 { 59 struct latency_statistic *lstat = iommu->perf_statistic; 60 unsigned long flags; 61 62 if (!dmar_latency_enabled(iommu, type)) 63 return; 64 65 spin_lock_irqsave(&latency_lock, flags); 66 memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM); 67 spin_unlock_irqrestore(&latency_lock, flags); 68 } 69 70 void dmar_latency_update(struct intel_iommu *iommu, enum latency_type type, u64 latency) 71 { 72 struct latency_statistic *lstat = iommu->perf_statistic; 73 unsigned long flags; 74 u64 min, max; 75 76 if (!dmar_latency_enabled(iommu, type)) 77 return; 78 79 spin_lock_irqsave(&latency_lock, flags); 80 if (latency < 100) 81 lstat[type].counter[COUNTS_10e2]++; 82 else if (latency < 1000) 83 lstat[type].counter[COUNTS_10e3]++; 84 else if (latency < 10000) 85 lstat[type].counter[COUNTS_10e4]++; 86 else if (latency < 100000) 87 lstat[type].counter[COUNTS_10e5]++; 88 else if (latency < 1000000) 89 lstat[type].counter[COUNTS_10e6]++; 90 else if (latency < 10000000) 91 lstat[type].counter[COUNTS_10e7]++; 92 else 93 lstat[type].counter[COUNTS_10e8_plus]++; 94 95 min = lstat[type].counter[COUNTS_MIN]; 96 max = lstat[type].counter[COUNTS_MAX]; 97 lstat[type].counter[COUNTS_MIN] = min_t(u64, min, latency); 98 lstat[type].counter[COUNTS_MAX] = max_t(u64, max, latency); 99 lstat[type].counter[COUNTS_SUM] += latency; 100 lstat[type].samples++; 101 spin_unlock_irqrestore(&latency_lock, flags); 102 } 103 104 static char *latency_counter_names[] = { 105 " <0.1us", 106 " 0.1us-1us", " 1us-10us", " 10us-100us", 107 " 100us-1ms", " 1ms-10ms", " >=10ms", 108 " min(us)", " max(us)", " average(us)" 109 }; 110 111 static char *latency_type_names[] = { 112 " inv_iotlb", " inv_devtlb", " inv_iec", 113 " svm_prq" 114 }; 115 116 int dmar_latency_snapshot(struct intel_iommu *iommu, char *str, size_t size) 117 { 118 struct latency_statistic *lstat = iommu->perf_statistic; 119 unsigned long flags; 120 int bytes = 0, i, j; 121 122 memset(str, 0, size); 123 124 for (i = 0; i < COUNTS_NUM; i++) 125 bytes += snprintf(str + bytes, size - bytes, 126 "%s", latency_counter_names[i]); 127 128 spin_lock_irqsave(&latency_lock, flags); 129 for (i = 0; i < DMAR_LATENCY_NUM; i++) { 130 if (!dmar_latency_enabled(iommu, i)) 131 continue; 132 133 bytes += snprintf(str + bytes, size - bytes, 134 "\n%s", latency_type_names[i]); 135 136 for (j = 0; j < COUNTS_NUM; j++) { 137 u64 val = lstat[i].counter[j]; 138 139 switch (j) { 140 case COUNTS_MIN: 141 if (val == UINT_MAX) 142 val = 0; 143 else 144 val = div_u64(val, 1000); 145 break; 146 case COUNTS_MAX: 147 val = div_u64(val, 1000); 148 break; 149 case COUNTS_SUM: 150 if (lstat[i].samples) 151 val = div_u64(val, (lstat[i].samples * 1000)); 152 else 153 val = 0; 154 break; 155 default: 156 break; 157 } 158 159 bytes += snprintf(str + bytes, size - bytes, 160 "%12lld", val); 161 } 162 } 163 spin_unlock_irqrestore(&latency_lock, flags); 164 165 return bytes; 166 } 167