GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / iommu / intel / perf.c
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(DMAR_LATENCY_NUM, 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(&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 }