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
16static DEFINE_SPINLOCK(latency_lock);
17
18bool 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
25int 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 }
51unlock_out:
52 spin_unlock_irqrestore(lock: &latency_lock, flags);
53
54 return ret;
55}
56
57void 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
70void 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
104static 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
111static char *latency_type_names[] = {
112 " inv_iotlb", " inv_devtlb", " inv_iec",
113 " svm_prq"
114};
115
116int 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

source code of linux/drivers/iommu/intel/perf.c