xref: /linux/drivers/iommu/intel/perf.c (revision c6fbb759)
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 = 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