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 | |
13 | #include "iommu.h" |
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 = kcalloc(n: DMAR_LATENCY_NUM, size: sizeof(*lstat), |
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(lock: &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(lock: &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(lock: &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(buf: str + bytes, size: size - bytes, |
126 | fmt: "%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, type: i)) |
131 | continue; |
132 | |
133 | bytes += snprintf(buf: str + bytes, size: size - bytes, |
134 | fmt: "\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(dividend: val, divisor: 1000); |
145 | break; |
146 | case COUNTS_MAX: |
147 | val = div_u64(dividend: val, divisor: 1000); |
148 | break; |
149 | case COUNTS_SUM: |
150 | if (lstat[i].samples) |
151 | val = div_u64(dividend: val, divisor: (lstat[i].samples * 1000)); |
152 | else |
153 | val = 0; |
154 | break; |
155 | default: |
156 | break; |
157 | } |
158 | |
159 | bytes += snprintf(buf: str + bytes, size: size - bytes, |
160 | fmt: "%12lld" , val); |
161 | } |
162 | } |
163 | spin_unlock_irqrestore(lock: &latency_lock, flags); |
164 | |
165 | return bytes; |
166 | } |
167 | |