Commit 78d13166b189ff1741327d748a7f8d73e65d70bd
Committed by
Benjamin Herrenschmidt
1 parent
58a685c2d8
Exists in
master
and in
13 other branches
powerpc/perf/hv-24x7: Use (unsigned long) not (u32) values when calling plpar_hcall_norets()
Signed-off-by: Cody P Schafer <cody@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Showing 1 changed file with 16 additions and 4 deletions Inline Diff
arch/powerpc/perf/hv-24x7.c
1 | /* | 1 | /* |
2 | * Hypervisor supplied "24x7" performance counter support | 2 | * Hypervisor supplied "24x7" performance counter support |
3 | * | 3 | * |
4 | * Author: Cody P Schafer <cody@linux.vnet.ibm.com> | 4 | * Author: Cody P Schafer <cody@linux.vnet.ibm.com> |
5 | * Copyright 2014 IBM Corporation. | 5 | * Copyright 2014 IBM Corporation. |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License | 8 | * modify it under the terms of the GNU General Public License |
9 | * as published by the Free Software Foundation; either version | 9 | * as published by the Free Software Foundation; either version |
10 | * 2 of the License, or (at your option) any later version. | 10 | * 2 of the License, or (at your option) any later version. |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #define pr_fmt(fmt) "hv-24x7: " fmt | 13 | #define pr_fmt(fmt) "hv-24x7: " fmt |
14 | 14 | ||
15 | #include <linux/perf_event.h> | 15 | #include <linux/perf_event.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <asm/firmware.h> | 18 | #include <asm/firmware.h> |
19 | #include <asm/hvcall.h> | 19 | #include <asm/hvcall.h> |
20 | #include <asm/io.h> | 20 | #include <asm/io.h> |
21 | 21 | ||
22 | #include "hv-24x7.h" | 22 | #include "hv-24x7.h" |
23 | #include "hv-24x7-catalog.h" | 23 | #include "hv-24x7-catalog.h" |
24 | #include "hv-common.h" | 24 | #include "hv-common.h" |
25 | 25 | ||
26 | /* | 26 | /* |
27 | * TODO: Merging events: | 27 | * TODO: Merging events: |
28 | * - Think of the hcall as an interface to a 4d array of counters: | 28 | * - Think of the hcall as an interface to a 4d array of counters: |
29 | * - x = domains | 29 | * - x = domains |
30 | * - y = indexes in the domain (core, chip, vcpu, node, etc) | 30 | * - y = indexes in the domain (core, chip, vcpu, node, etc) |
31 | * - z = offset into the counter space | 31 | * - z = offset into the counter space |
32 | * - w = lpars (guest vms, "logical partitions") | 32 | * - w = lpars (guest vms, "logical partitions") |
33 | * - A single request is: x,y,y_last,z,z_last,w,w_last | 33 | * - A single request is: x,y,y_last,z,z_last,w,w_last |
34 | * - this means we can retrieve a rectangle of counters in y,z for a single x. | 34 | * - this means we can retrieve a rectangle of counters in y,z for a single x. |
35 | * | 35 | * |
36 | * - Things to consider (ignoring w): | 36 | * - Things to consider (ignoring w): |
37 | * - input cost_per_request = 16 | 37 | * - input cost_per_request = 16 |
38 | * - output cost_per_result(ys,zs) = 8 + 8 * ys + ys * zs | 38 | * - output cost_per_result(ys,zs) = 8 + 8 * ys + ys * zs |
39 | * - limited number of requests per hcall (must fit into 4K bytes) | 39 | * - limited number of requests per hcall (must fit into 4K bytes) |
40 | * - 4k = 16 [buffer header] - 16 [request size] * request_count | 40 | * - 4k = 16 [buffer header] - 16 [request size] * request_count |
41 | * - 255 requests per hcall | 41 | * - 255 requests per hcall |
42 | * - sometimes it will be more efficient to read extra data and discard | 42 | * - sometimes it will be more efficient to read extra data and discard |
43 | */ | 43 | */ |
44 | 44 | ||
45 | /* | 45 | /* |
46 | * Example usage: | 46 | * Example usage: |
47 | * perf stat -e 'hv_24x7/domain=2,offset=8,starting_index=0,lpar=0xffffffff/' | 47 | * perf stat -e 'hv_24x7/domain=2,offset=8,starting_index=0,lpar=0xffffffff/' |
48 | */ | 48 | */ |
49 | 49 | ||
50 | /* u3 0-6, one of HV_24X7_PERF_DOMAIN */ | 50 | /* u3 0-6, one of HV_24X7_PERF_DOMAIN */ |
51 | EVENT_DEFINE_RANGE_FORMAT(domain, config, 0, 3); | 51 | EVENT_DEFINE_RANGE_FORMAT(domain, config, 0, 3); |
52 | /* u16 */ | 52 | /* u16 */ |
53 | EVENT_DEFINE_RANGE_FORMAT(starting_index, config, 16, 31); | 53 | EVENT_DEFINE_RANGE_FORMAT(starting_index, config, 16, 31); |
54 | /* u32, see "data_offset" */ | 54 | /* u32, see "data_offset" */ |
55 | EVENT_DEFINE_RANGE_FORMAT(offset, config, 32, 63); | 55 | EVENT_DEFINE_RANGE_FORMAT(offset, config, 32, 63); |
56 | /* u16 */ | 56 | /* u16 */ |
57 | EVENT_DEFINE_RANGE_FORMAT(lpar, config1, 0, 15); | 57 | EVENT_DEFINE_RANGE_FORMAT(lpar, config1, 0, 15); |
58 | 58 | ||
59 | EVENT_DEFINE_RANGE(reserved1, config, 4, 15); | 59 | EVENT_DEFINE_RANGE(reserved1, config, 4, 15); |
60 | EVENT_DEFINE_RANGE(reserved2, config1, 16, 63); | 60 | EVENT_DEFINE_RANGE(reserved2, config1, 16, 63); |
61 | EVENT_DEFINE_RANGE(reserved3, config2, 0, 63); | 61 | EVENT_DEFINE_RANGE(reserved3, config2, 0, 63); |
62 | 62 | ||
63 | static struct attribute *format_attrs[] = { | 63 | static struct attribute *format_attrs[] = { |
64 | &format_attr_domain.attr, | 64 | &format_attr_domain.attr, |
65 | &format_attr_offset.attr, | 65 | &format_attr_offset.attr, |
66 | &format_attr_starting_index.attr, | 66 | &format_attr_starting_index.attr, |
67 | &format_attr_lpar.attr, | 67 | &format_attr_lpar.attr, |
68 | NULL, | 68 | NULL, |
69 | }; | 69 | }; |
70 | 70 | ||
71 | static struct attribute_group format_group = { | 71 | static struct attribute_group format_group = { |
72 | .name = "format", | 72 | .name = "format", |
73 | .attrs = format_attrs, | 73 | .attrs = format_attrs, |
74 | }; | 74 | }; |
75 | 75 | ||
76 | static struct kmem_cache *hv_page_cache; | 76 | static struct kmem_cache *hv_page_cache; |
77 | 77 | ||
78 | /* | 78 | /* |
79 | * read_offset_data - copy data from one buffer to another while treating the | 79 | * read_offset_data - copy data from one buffer to another while treating the |
80 | * source buffer as a small view on the total avaliable | 80 | * source buffer as a small view on the total avaliable |
81 | * source data. | 81 | * source data. |
82 | * | 82 | * |
83 | * @dest: buffer to copy into | 83 | * @dest: buffer to copy into |
84 | * @dest_len: length of @dest in bytes | 84 | * @dest_len: length of @dest in bytes |
85 | * @requested_offset: the offset within the source data we want. Must be > 0 | 85 | * @requested_offset: the offset within the source data we want. Must be > 0 |
86 | * @src: buffer to copy data from | 86 | * @src: buffer to copy data from |
87 | * @src_len: length of @src in bytes | 87 | * @src_len: length of @src in bytes |
88 | * @source_offset: the offset in the sorce data that (src,src_len) refers to. | 88 | * @source_offset: the offset in the sorce data that (src,src_len) refers to. |
89 | * Must be > 0 | 89 | * Must be > 0 |
90 | * | 90 | * |
91 | * returns the number of bytes copied. | 91 | * returns the number of bytes copied. |
92 | * | 92 | * |
93 | * The following ascii art shows the various buffer possitioning we need to | 93 | * The following ascii art shows the various buffer possitioning we need to |
94 | * handle, assigns some arbitrary varibles to points on the buffer, and then | 94 | * handle, assigns some arbitrary varibles to points on the buffer, and then |
95 | * shows how we fiddle with those values to get things we care about (copy | 95 | * shows how we fiddle with those values to get things we care about (copy |
96 | * start in src and copy len) | 96 | * start in src and copy len) |
97 | * | 97 | * |
98 | * s = @src buffer | 98 | * s = @src buffer |
99 | * d = @dest buffer | 99 | * d = @dest buffer |
100 | * '.' areas in d are written to. | 100 | * '.' areas in d are written to. |
101 | * | 101 | * |
102 | * u | 102 | * u |
103 | * x w v z | 103 | * x w v z |
104 | * d |.........| | 104 | * d |.........| |
105 | * s |----------------------| | 105 | * s |----------------------| |
106 | * | 106 | * |
107 | * u | 107 | * u |
108 | * x w z v | 108 | * x w z v |
109 | * d |........------| | 109 | * d |........------| |
110 | * s |------------------| | 110 | * s |------------------| |
111 | * | 111 | * |
112 | * x w u,z,v | 112 | * x w u,z,v |
113 | * d |........| | 113 | * d |........| |
114 | * s |------------------| | 114 | * s |------------------| |
115 | * | 115 | * |
116 | * x,w u,v,z | 116 | * x,w u,v,z |
117 | * d |..................| | 117 | * d |..................| |
118 | * s |------------------| | 118 | * s |------------------| |
119 | * | 119 | * |
120 | * x u | 120 | * x u |
121 | * w v z | 121 | * w v z |
122 | * d |........| | 122 | * d |........| |
123 | * s |------------------| | 123 | * s |------------------| |
124 | * | 124 | * |
125 | * x z w v | 125 | * x z w v |
126 | * d |------| | 126 | * d |------| |
127 | * s |------| | 127 | * s |------| |
128 | * | 128 | * |
129 | * x = source_offset | 129 | * x = source_offset |
130 | * w = requested_offset | 130 | * w = requested_offset |
131 | * z = source_offset + src_len | 131 | * z = source_offset + src_len |
132 | * v = requested_offset + dest_len | 132 | * v = requested_offset + dest_len |
133 | * | 133 | * |
134 | * w_offset_in_s = w - x = requested_offset - source_offset | 134 | * w_offset_in_s = w - x = requested_offset - source_offset |
135 | * z_offset_in_s = z - x = src_len | 135 | * z_offset_in_s = z - x = src_len |
136 | * v_offset_in_s = v - x = request_offset + dest_len - src_len | 136 | * v_offset_in_s = v - x = request_offset + dest_len - src_len |
137 | */ | 137 | */ |
138 | static ssize_t read_offset_data(void *dest, size_t dest_len, | 138 | static ssize_t read_offset_data(void *dest, size_t dest_len, |
139 | loff_t requested_offset, void *src, | 139 | loff_t requested_offset, void *src, |
140 | size_t src_len, loff_t source_offset) | 140 | size_t src_len, loff_t source_offset) |
141 | { | 141 | { |
142 | size_t w_offset_in_s = requested_offset - source_offset; | 142 | size_t w_offset_in_s = requested_offset - source_offset; |
143 | size_t z_offset_in_s = src_len; | 143 | size_t z_offset_in_s = src_len; |
144 | size_t v_offset_in_s = requested_offset + dest_len - src_len; | 144 | size_t v_offset_in_s = requested_offset + dest_len - src_len; |
145 | size_t u_offset_in_s = min(z_offset_in_s, v_offset_in_s); | 145 | size_t u_offset_in_s = min(z_offset_in_s, v_offset_in_s); |
146 | size_t copy_len = u_offset_in_s - w_offset_in_s; | 146 | size_t copy_len = u_offset_in_s - w_offset_in_s; |
147 | 147 | ||
148 | if (requested_offset < 0 || source_offset < 0) | 148 | if (requested_offset < 0 || source_offset < 0) |
149 | return -EINVAL; | 149 | return -EINVAL; |
150 | 150 | ||
151 | if (z_offset_in_s <= w_offset_in_s) | 151 | if (z_offset_in_s <= w_offset_in_s) |
152 | return 0; | 152 | return 0; |
153 | 153 | ||
154 | memcpy(dest, src + w_offset_in_s, copy_len); | 154 | memcpy(dest, src + w_offset_in_s, copy_len); |
155 | return copy_len; | 155 | return copy_len; |
156 | } | 156 | } |
157 | 157 | ||
158 | static unsigned long h_get_24x7_catalog_page(char page[static 4096], | 158 | static unsigned long h_get_24x7_catalog_page_(unsigned long phys_4096, |
159 | u32 version, u32 index) | 159 | unsigned long version, |
160 | unsigned long index) | ||
160 | { | 161 | { |
161 | WARN_ON(!IS_ALIGNED((unsigned long)page, 4096)); | 162 | pr_devel("h_get_24x7_catalog_page(0x%lx, %lu, %lu)", |
163 | phys_4096, | ||
164 | version, | ||
165 | index); | ||
166 | WARN_ON(!IS_ALIGNED(phys_4096, 4096)); | ||
162 | return plpar_hcall_norets(H_GET_24X7_CATALOG_PAGE, | 167 | return plpar_hcall_norets(H_GET_24X7_CATALOG_PAGE, |
163 | virt_to_phys(page), | 168 | phys_4096, |
164 | version, | 169 | version, |
165 | index); | 170 | index); |
171 | } | ||
172 | |||
173 | static unsigned long h_get_24x7_catalog_page(char page[static 4096], | ||
174 | u32 version, u32 index) | ||
175 | { | ||
176 | return h_get_24x7_catalog_page_(virt_to_phys(page), | ||
177 | version, index); | ||
166 | } | 178 | } |
167 | 179 | ||
168 | static ssize_t catalog_read(struct file *filp, struct kobject *kobj, | 180 | static ssize_t catalog_read(struct file *filp, struct kobject *kobj, |
169 | struct bin_attribute *bin_attr, char *buf, | 181 | struct bin_attribute *bin_attr, char *buf, |
170 | loff_t offset, size_t count) | 182 | loff_t offset, size_t count) |
171 | { | 183 | { |
172 | unsigned long hret; | 184 | unsigned long hret; |
173 | ssize_t ret = 0; | 185 | ssize_t ret = 0; |
174 | size_t catalog_len = 0, catalog_page_len = 0, page_count = 0; | 186 | size_t catalog_len = 0, catalog_page_len = 0, page_count = 0; |
175 | loff_t page_offset = 0; | 187 | loff_t page_offset = 0; |
176 | uint32_t catalog_version_num = 0; | 188 | uint32_t catalog_version_num = 0; |
177 | void *page = kmem_cache_alloc(hv_page_cache, GFP_USER); | 189 | void *page = kmem_cache_alloc(hv_page_cache, GFP_USER); |
178 | struct hv_24x7_catalog_page_0 *page_0 = page; | 190 | struct hv_24x7_catalog_page_0 *page_0 = page; |
179 | if (!page) | 191 | if (!page) |
180 | return -ENOMEM; | 192 | return -ENOMEM; |
181 | 193 | ||
182 | hret = h_get_24x7_catalog_page(page, 0, 0); | 194 | hret = h_get_24x7_catalog_page(page, 0, 0); |
183 | if (hret) { | 195 | if (hret) { |
184 | ret = -EIO; | 196 | ret = -EIO; |
185 | goto e_free; | 197 | goto e_free; |
186 | } | 198 | } |
187 | 199 | ||
188 | catalog_version_num = be32_to_cpu(page_0->version); | 200 | catalog_version_num = be32_to_cpu(page_0->version); |
189 | catalog_page_len = be32_to_cpu(page_0->length); | 201 | catalog_page_len = be32_to_cpu(page_0->length); |
190 | catalog_len = catalog_page_len * 4096; | 202 | catalog_len = catalog_page_len * 4096; |
191 | 203 | ||
192 | page_offset = offset / 4096; | 204 | page_offset = offset / 4096; |
193 | page_count = count / 4096; | 205 | page_count = count / 4096; |
194 | 206 | ||
195 | if (page_offset >= catalog_page_len) | 207 | if (page_offset >= catalog_page_len) |
196 | goto e_free; | 208 | goto e_free; |
197 | 209 | ||
198 | if (page_offset != 0) { | 210 | if (page_offset != 0) { |
199 | hret = h_get_24x7_catalog_page(page, catalog_version_num, | 211 | hret = h_get_24x7_catalog_page(page, catalog_version_num, |
200 | page_offset); | 212 | page_offset); |
201 | if (hret) { | 213 | if (hret) { |
202 | ret = -EIO; | 214 | ret = -EIO; |
203 | goto e_free; | 215 | goto e_free; |
204 | } | 216 | } |
205 | } | 217 | } |
206 | 218 | ||
207 | ret = read_offset_data(buf, count, offset, | 219 | ret = read_offset_data(buf, count, offset, |
208 | page, 4096, page_offset * 4096); | 220 | page, 4096, page_offset * 4096); |
209 | e_free: | 221 | e_free: |
210 | if (hret) | 222 | if (hret) |
211 | pr_err("h_get_24x7_catalog_page(ver=%d, page=%lld) failed: rc=%ld\n", | 223 | pr_err("h_get_24x7_catalog_page(ver=%d, page=%lld) failed: rc=%ld\n", |
212 | catalog_version_num, page_offset, hret); | 224 | catalog_version_num, page_offset, hret); |
213 | kfree(page); | 225 | kfree(page); |
214 | 226 | ||
215 | pr_devel("catalog_read: offset=%lld(%lld) count=%zu(%zu) catalog_len=%zu(%zu) => %zd\n", | 227 | pr_devel("catalog_read: offset=%lld(%lld) count=%zu(%zu) catalog_len=%zu(%zu) => %zd\n", |
216 | offset, page_offset, count, page_count, catalog_len, | 228 | offset, page_offset, count, page_count, catalog_len, |
217 | catalog_page_len, ret); | 229 | catalog_page_len, ret); |
218 | 230 | ||
219 | return ret; | 231 | return ret; |
220 | } | 232 | } |
221 | 233 | ||
222 | #define PAGE_0_ATTR(_name, _fmt, _expr) \ | 234 | #define PAGE_0_ATTR(_name, _fmt, _expr) \ |
223 | static ssize_t _name##_show(struct device *dev, \ | 235 | static ssize_t _name##_show(struct device *dev, \ |
224 | struct device_attribute *dev_attr, \ | 236 | struct device_attribute *dev_attr, \ |
225 | char *buf) \ | 237 | char *buf) \ |
226 | { \ | 238 | { \ |
227 | unsigned long hret; \ | 239 | unsigned long hret; \ |
228 | ssize_t ret = 0; \ | 240 | ssize_t ret = 0; \ |
229 | void *page = kmem_cache_alloc(hv_page_cache, GFP_USER); \ | 241 | void *page = kmem_cache_alloc(hv_page_cache, GFP_USER); \ |
230 | struct hv_24x7_catalog_page_0 *page_0 = page; \ | 242 | struct hv_24x7_catalog_page_0 *page_0 = page; \ |
231 | if (!page) \ | 243 | if (!page) \ |
232 | return -ENOMEM; \ | 244 | return -ENOMEM; \ |
233 | hret = h_get_24x7_catalog_page(page, 0, 0); \ | 245 | hret = h_get_24x7_catalog_page(page, 0, 0); \ |
234 | if (hret) { \ | 246 | if (hret) { \ |
235 | ret = -EIO; \ | 247 | ret = -EIO; \ |
236 | goto e_free; \ | 248 | goto e_free; \ |
237 | } \ | 249 | } \ |
238 | ret = sprintf(buf, _fmt, _expr); \ | 250 | ret = sprintf(buf, _fmt, _expr); \ |
239 | e_free: \ | 251 | e_free: \ |
240 | kfree(page); \ | 252 | kfree(page); \ |
241 | return ret; \ | 253 | return ret; \ |
242 | } \ | 254 | } \ |
243 | static DEVICE_ATTR_RO(_name) | 255 | static DEVICE_ATTR_RO(_name) |
244 | 256 | ||
245 | PAGE_0_ATTR(catalog_version, "%lld\n", | 257 | PAGE_0_ATTR(catalog_version, "%lld\n", |
246 | (unsigned long long)be32_to_cpu(page_0->version)); | 258 | (unsigned long long)be32_to_cpu(page_0->version)); |
247 | PAGE_0_ATTR(catalog_len, "%lld\n", | 259 | PAGE_0_ATTR(catalog_len, "%lld\n", |
248 | (unsigned long long)be32_to_cpu(page_0->length) * 4096); | 260 | (unsigned long long)be32_to_cpu(page_0->length) * 4096); |
249 | static BIN_ATTR_RO(catalog, 0/* real length varies */); | 261 | static BIN_ATTR_RO(catalog, 0/* real length varies */); |
250 | 262 | ||
251 | static struct bin_attribute *if_bin_attrs[] = { | 263 | static struct bin_attribute *if_bin_attrs[] = { |
252 | &bin_attr_catalog, | 264 | &bin_attr_catalog, |
253 | NULL, | 265 | NULL, |
254 | }; | 266 | }; |
255 | 267 | ||
256 | static struct attribute *if_attrs[] = { | 268 | static struct attribute *if_attrs[] = { |
257 | &dev_attr_catalog_len.attr, | 269 | &dev_attr_catalog_len.attr, |
258 | &dev_attr_catalog_version.attr, | 270 | &dev_attr_catalog_version.attr, |
259 | NULL, | 271 | NULL, |
260 | }; | 272 | }; |
261 | 273 | ||
262 | static struct attribute_group if_group = { | 274 | static struct attribute_group if_group = { |
263 | .name = "interface", | 275 | .name = "interface", |
264 | .bin_attrs = if_bin_attrs, | 276 | .bin_attrs = if_bin_attrs, |
265 | .attrs = if_attrs, | 277 | .attrs = if_attrs, |
266 | }; | 278 | }; |
267 | 279 | ||
268 | static const struct attribute_group *attr_groups[] = { | 280 | static const struct attribute_group *attr_groups[] = { |
269 | &format_group, | 281 | &format_group, |
270 | &if_group, | 282 | &if_group, |
271 | NULL, | 283 | NULL, |
272 | }; | 284 | }; |
273 | 285 | ||
274 | static bool is_physical_domain(int domain) | 286 | static bool is_physical_domain(int domain) |
275 | { | 287 | { |
276 | return domain == HV_24X7_PERF_DOMAIN_PHYSICAL_CHIP || | 288 | return domain == HV_24X7_PERF_DOMAIN_PHYSICAL_CHIP || |
277 | domain == HV_24X7_PERF_DOMAIN_PHYSICAL_CORE; | 289 | domain == HV_24X7_PERF_DOMAIN_PHYSICAL_CORE; |
278 | } | 290 | } |
279 | 291 | ||
280 | static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, | 292 | static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, |
281 | u16 lpar, u64 *res, | 293 | u16 lpar, u64 *res, |
282 | bool success_expected) | 294 | bool success_expected) |
283 | { | 295 | { |
284 | unsigned long ret; | 296 | unsigned long ret; |
285 | 297 | ||
286 | /* | 298 | /* |
287 | * request_buffer and result_buffer are not required to be 4k aligned, | 299 | * request_buffer and result_buffer are not required to be 4k aligned, |
288 | * but are not allowed to cross any 4k boundary. Aligning them to 4k is | 300 | * but are not allowed to cross any 4k boundary. Aligning them to 4k is |
289 | * the simplest way to ensure that. | 301 | * the simplest way to ensure that. |
290 | */ | 302 | */ |
291 | struct reqb { | 303 | struct reqb { |
292 | struct hv_24x7_request_buffer buf; | 304 | struct hv_24x7_request_buffer buf; |
293 | struct hv_24x7_request req; | 305 | struct hv_24x7_request req; |
294 | } __packed __aligned(4096) request_buffer = { | 306 | } __packed __aligned(4096) request_buffer = { |
295 | .buf = { | 307 | .buf = { |
296 | .interface_version = HV_24X7_IF_VERSION_CURRENT, | 308 | .interface_version = HV_24X7_IF_VERSION_CURRENT, |
297 | .num_requests = 1, | 309 | .num_requests = 1, |
298 | }, | 310 | }, |
299 | .req = { | 311 | .req = { |
300 | .performance_domain = domain, | 312 | .performance_domain = domain, |
301 | .data_size = cpu_to_be16(8), | 313 | .data_size = cpu_to_be16(8), |
302 | .data_offset = cpu_to_be32(offset), | 314 | .data_offset = cpu_to_be32(offset), |
303 | .starting_lpar_ix = cpu_to_be16(lpar), | 315 | .starting_lpar_ix = cpu_to_be16(lpar), |
304 | .max_num_lpars = cpu_to_be16(1), | 316 | .max_num_lpars = cpu_to_be16(1), |
305 | .starting_ix = cpu_to_be16(ix), | 317 | .starting_ix = cpu_to_be16(ix), |
306 | .max_ix = cpu_to_be16(1), | 318 | .max_ix = cpu_to_be16(1), |
307 | } | 319 | } |
308 | }; | 320 | }; |
309 | 321 | ||
310 | struct resb { | 322 | struct resb { |
311 | struct hv_24x7_data_result_buffer buf; | 323 | struct hv_24x7_data_result_buffer buf; |
312 | struct hv_24x7_result res; | 324 | struct hv_24x7_result res; |
313 | struct hv_24x7_result_element elem; | 325 | struct hv_24x7_result_element elem; |
314 | __be64 result; | 326 | __be64 result; |
315 | } __packed __aligned(4096) result_buffer = {}; | 327 | } __packed __aligned(4096) result_buffer = {}; |
316 | 328 | ||
317 | ret = plpar_hcall_norets(H_GET_24X7_DATA, | 329 | ret = plpar_hcall_norets(H_GET_24X7_DATA, |
318 | virt_to_phys(&request_buffer), sizeof(request_buffer), | 330 | virt_to_phys(&request_buffer), sizeof(request_buffer), |
319 | virt_to_phys(&result_buffer), sizeof(result_buffer)); | 331 | virt_to_phys(&result_buffer), sizeof(result_buffer)); |
320 | 332 | ||
321 | if (ret) { | 333 | if (ret) { |
322 | if (success_expected) | 334 | if (success_expected) |
323 | pr_err_ratelimited("hcall failed: %d %#x %#x %d => 0x%lx (%ld) detail=0x%x failing ix=%x\n", | 335 | pr_err_ratelimited("hcall failed: %d %#x %#x %d => 0x%lx (%ld) detail=0x%x failing ix=%x\n", |
324 | domain, offset, ix, lpar, | 336 | domain, offset, ix, lpar, |
325 | ret, ret, | 337 | ret, ret, |
326 | result_buffer.buf.detailed_rc, | 338 | result_buffer.buf.detailed_rc, |
327 | result_buffer.buf.failing_request_ix); | 339 | result_buffer.buf.failing_request_ix); |
328 | return ret; | 340 | return ret; |
329 | } | 341 | } |
330 | 342 | ||
331 | *res = be64_to_cpu(result_buffer.result); | 343 | *res = be64_to_cpu(result_buffer.result); |
332 | return ret; | 344 | return ret; |
333 | } | 345 | } |
334 | 346 | ||
335 | static unsigned long event_24x7_request(struct perf_event *event, u64 *res, | 347 | static unsigned long event_24x7_request(struct perf_event *event, u64 *res, |
336 | bool success_expected) | 348 | bool success_expected) |
337 | { | 349 | { |
338 | return single_24x7_request(event_get_domain(event), | 350 | return single_24x7_request(event_get_domain(event), |
339 | event_get_offset(event), | 351 | event_get_offset(event), |
340 | event_get_starting_index(event), | 352 | event_get_starting_index(event), |
341 | event_get_lpar(event), | 353 | event_get_lpar(event), |
342 | res, | 354 | res, |
343 | success_expected); | 355 | success_expected); |
344 | } | 356 | } |
345 | 357 | ||
346 | static int h_24x7_event_init(struct perf_event *event) | 358 | static int h_24x7_event_init(struct perf_event *event) |
347 | { | 359 | { |
348 | struct hv_perf_caps caps; | 360 | struct hv_perf_caps caps; |
349 | unsigned domain; | 361 | unsigned domain; |
350 | unsigned long hret; | 362 | unsigned long hret; |
351 | u64 ct; | 363 | u64 ct; |
352 | 364 | ||
353 | /* Not our event */ | 365 | /* Not our event */ |
354 | if (event->attr.type != event->pmu->type) | 366 | if (event->attr.type != event->pmu->type) |
355 | return -ENOENT; | 367 | return -ENOENT; |
356 | 368 | ||
357 | /* Unused areas must be 0 */ | 369 | /* Unused areas must be 0 */ |
358 | if (event_get_reserved1(event) || | 370 | if (event_get_reserved1(event) || |
359 | event_get_reserved2(event) || | 371 | event_get_reserved2(event) || |
360 | event_get_reserved3(event)) { | 372 | event_get_reserved3(event)) { |
361 | pr_devel("reserved set when forbidden 0x%llx(0x%llx) 0x%llx(0x%llx) 0x%llx(0x%llx)\n", | 373 | pr_devel("reserved set when forbidden 0x%llx(0x%llx) 0x%llx(0x%llx) 0x%llx(0x%llx)\n", |
362 | event->attr.config, | 374 | event->attr.config, |
363 | event_get_reserved1(event), | 375 | event_get_reserved1(event), |
364 | event->attr.config1, | 376 | event->attr.config1, |
365 | event_get_reserved2(event), | 377 | event_get_reserved2(event), |
366 | event->attr.config2, | 378 | event->attr.config2, |
367 | event_get_reserved3(event)); | 379 | event_get_reserved3(event)); |
368 | return -EINVAL; | 380 | return -EINVAL; |
369 | } | 381 | } |
370 | 382 | ||
371 | /* unsupported modes and filters */ | 383 | /* unsupported modes and filters */ |
372 | if (event->attr.exclude_user || | 384 | if (event->attr.exclude_user || |
373 | event->attr.exclude_kernel || | 385 | event->attr.exclude_kernel || |
374 | event->attr.exclude_hv || | 386 | event->attr.exclude_hv || |
375 | event->attr.exclude_idle || | 387 | event->attr.exclude_idle || |
376 | event->attr.exclude_host || | 388 | event->attr.exclude_host || |
377 | event->attr.exclude_guest || | 389 | event->attr.exclude_guest || |
378 | is_sampling_event(event)) /* no sampling */ | 390 | is_sampling_event(event)) /* no sampling */ |
379 | return -EINVAL; | 391 | return -EINVAL; |
380 | 392 | ||
381 | /* no branch sampling */ | 393 | /* no branch sampling */ |
382 | if (has_branch_stack(event)) | 394 | if (has_branch_stack(event)) |
383 | return -EOPNOTSUPP; | 395 | return -EOPNOTSUPP; |
384 | 396 | ||
385 | /* offset must be 8 byte aligned */ | 397 | /* offset must be 8 byte aligned */ |
386 | if (event_get_offset(event) % 8) { | 398 | if (event_get_offset(event) % 8) { |
387 | pr_devel("bad alignment\n"); | 399 | pr_devel("bad alignment\n"); |
388 | return -EINVAL; | 400 | return -EINVAL; |
389 | } | 401 | } |
390 | 402 | ||
391 | /* Domains above 6 are invalid */ | 403 | /* Domains above 6 are invalid */ |
392 | domain = event_get_domain(event); | 404 | domain = event_get_domain(event); |
393 | if (domain > 6) { | 405 | if (domain > 6) { |
394 | pr_devel("invalid domain %d\n", domain); | 406 | pr_devel("invalid domain %d\n", domain); |
395 | return -EINVAL; | 407 | return -EINVAL; |
396 | } | 408 | } |
397 | 409 | ||
398 | hret = hv_perf_caps_get(&caps); | 410 | hret = hv_perf_caps_get(&caps); |
399 | if (hret) { | 411 | if (hret) { |
400 | pr_devel("could not get capabilities: rc=%ld\n", hret); | 412 | pr_devel("could not get capabilities: rc=%ld\n", hret); |
401 | return -EIO; | 413 | return -EIO; |
402 | } | 414 | } |
403 | 415 | ||
404 | /* PHYSICAL domains & other lpars require extra capabilities */ | 416 | /* PHYSICAL domains & other lpars require extra capabilities */ |
405 | if (!caps.collect_privileged && (is_physical_domain(domain) || | 417 | if (!caps.collect_privileged && (is_physical_domain(domain) || |
406 | (event_get_lpar(event) != event_get_lpar_max()))) { | 418 | (event_get_lpar(event) != event_get_lpar_max()))) { |
407 | pr_devel("hv permisions disallow: is_physical_domain:%d, lpar=0x%llx\n", | 419 | pr_devel("hv permisions disallow: is_physical_domain:%d, lpar=0x%llx\n", |
408 | is_physical_domain(domain), | 420 | is_physical_domain(domain), |
409 | event_get_lpar(event)); | 421 | event_get_lpar(event)); |
410 | return -EACCES; | 422 | return -EACCES; |
411 | } | 423 | } |
412 | 424 | ||
413 | /* see if the event complains */ | 425 | /* see if the event complains */ |
414 | if (event_24x7_request(event, &ct, false)) { | 426 | if (event_24x7_request(event, &ct, false)) { |
415 | pr_devel("test hcall failed\n"); | 427 | pr_devel("test hcall failed\n"); |
416 | return -EIO; | 428 | return -EIO; |
417 | } | 429 | } |
418 | 430 | ||
419 | return 0; | 431 | return 0; |
420 | } | 432 | } |
421 | 433 | ||
422 | static u64 h_24x7_get_value(struct perf_event *event) | 434 | static u64 h_24x7_get_value(struct perf_event *event) |
423 | { | 435 | { |
424 | unsigned long ret; | 436 | unsigned long ret; |
425 | u64 ct; | 437 | u64 ct; |
426 | ret = event_24x7_request(event, &ct, true); | 438 | ret = event_24x7_request(event, &ct, true); |
427 | if (ret) | 439 | if (ret) |
428 | /* We checked this in event init, shouldn't fail here... */ | 440 | /* We checked this in event init, shouldn't fail here... */ |
429 | return 0; | 441 | return 0; |
430 | 442 | ||
431 | return ct; | 443 | return ct; |
432 | } | 444 | } |
433 | 445 | ||
434 | static void h_24x7_event_update(struct perf_event *event) | 446 | static void h_24x7_event_update(struct perf_event *event) |
435 | { | 447 | { |
436 | s64 prev; | 448 | s64 prev; |
437 | u64 now; | 449 | u64 now; |
438 | now = h_24x7_get_value(event); | 450 | now = h_24x7_get_value(event); |
439 | prev = local64_xchg(&event->hw.prev_count, now); | 451 | prev = local64_xchg(&event->hw.prev_count, now); |
440 | local64_add(now - prev, &event->count); | 452 | local64_add(now - prev, &event->count); |
441 | } | 453 | } |
442 | 454 | ||
443 | static void h_24x7_event_start(struct perf_event *event, int flags) | 455 | static void h_24x7_event_start(struct perf_event *event, int flags) |
444 | { | 456 | { |
445 | if (flags & PERF_EF_RELOAD) | 457 | if (flags & PERF_EF_RELOAD) |
446 | local64_set(&event->hw.prev_count, h_24x7_get_value(event)); | 458 | local64_set(&event->hw.prev_count, h_24x7_get_value(event)); |
447 | } | 459 | } |
448 | 460 | ||
449 | static void h_24x7_event_stop(struct perf_event *event, int flags) | 461 | static void h_24x7_event_stop(struct perf_event *event, int flags) |
450 | { | 462 | { |
451 | h_24x7_event_update(event); | 463 | h_24x7_event_update(event); |
452 | } | 464 | } |
453 | 465 | ||
454 | static int h_24x7_event_add(struct perf_event *event, int flags) | 466 | static int h_24x7_event_add(struct perf_event *event, int flags) |
455 | { | 467 | { |
456 | if (flags & PERF_EF_START) | 468 | if (flags & PERF_EF_START) |
457 | h_24x7_event_start(event, flags); | 469 | h_24x7_event_start(event, flags); |
458 | 470 | ||
459 | return 0; | 471 | return 0; |
460 | } | 472 | } |
461 | 473 | ||
462 | static int h_24x7_event_idx(struct perf_event *event) | 474 | static int h_24x7_event_idx(struct perf_event *event) |
463 | { | 475 | { |
464 | return 0; | 476 | return 0; |
465 | } | 477 | } |
466 | 478 | ||
467 | static struct pmu h_24x7_pmu = { | 479 | static struct pmu h_24x7_pmu = { |
468 | .task_ctx_nr = perf_invalid_context, | 480 | .task_ctx_nr = perf_invalid_context, |
469 | 481 | ||
470 | .name = "hv_24x7", | 482 | .name = "hv_24x7", |
471 | .attr_groups = attr_groups, | 483 | .attr_groups = attr_groups, |
472 | .event_init = h_24x7_event_init, | 484 | .event_init = h_24x7_event_init, |
473 | .add = h_24x7_event_add, | 485 | .add = h_24x7_event_add, |
474 | .del = h_24x7_event_stop, | 486 | .del = h_24x7_event_stop, |
475 | .start = h_24x7_event_start, | 487 | .start = h_24x7_event_start, |
476 | .stop = h_24x7_event_stop, | 488 | .stop = h_24x7_event_stop, |
477 | .read = h_24x7_event_update, | 489 | .read = h_24x7_event_update, |
478 | .event_idx = h_24x7_event_idx, | 490 | .event_idx = h_24x7_event_idx, |
479 | }; | 491 | }; |
480 | 492 | ||
481 | static int hv_24x7_init(void) | 493 | static int hv_24x7_init(void) |
482 | { | 494 | { |
483 | int r; | 495 | int r; |
484 | unsigned long hret; | 496 | unsigned long hret; |
485 | struct hv_perf_caps caps; | 497 | struct hv_perf_caps caps; |
486 | 498 | ||
487 | if (!firmware_has_feature(FW_FEATURE_LPAR)) { | 499 | if (!firmware_has_feature(FW_FEATURE_LPAR)) { |
488 | pr_debug("not a virtualized system, not enabling\n"); | 500 | pr_debug("not a virtualized system, not enabling\n"); |
489 | return -ENODEV; | 501 | return -ENODEV; |
490 | } | 502 | } |
491 | 503 | ||
492 | hret = hv_perf_caps_get(&caps); | 504 | hret = hv_perf_caps_get(&caps); |
493 | if (hret) { | 505 | if (hret) { |
494 | pr_debug("could not obtain capabilities, not enabling, rc=%ld\n", | 506 | pr_debug("could not obtain capabilities, not enabling, rc=%ld\n", |
495 | hret); | 507 | hret); |
496 | return -ENODEV; | 508 | return -ENODEV; |
497 | } | 509 | } |
498 | 510 | ||
499 | hv_page_cache = kmem_cache_create("hv-page-4096", 4096, 4096, 0, NULL); | 511 | hv_page_cache = kmem_cache_create("hv-page-4096", 4096, 4096, 0, NULL); |
500 | if (!hv_page_cache) | 512 | if (!hv_page_cache) |
501 | return -ENOMEM; | 513 | return -ENOMEM; |
502 | 514 | ||
503 | r = perf_pmu_register(&h_24x7_pmu, h_24x7_pmu.name, -1); | 515 | r = perf_pmu_register(&h_24x7_pmu, h_24x7_pmu.name, -1); |
504 | if (r) | 516 | if (r) |
505 | return r; | 517 | return r; |
506 | 518 | ||
507 | return 0; | 519 | return 0; |
508 | } | 520 | } |
509 | 521 | ||
510 | device_initcall(hv_24x7_init); | 522 | device_initcall(hv_24x7_init); |
511 | 523 |