]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Tony Jones <tonyj@suse.de> |
2 | Subject: switch to ioctl interface for perfmon2 | |
3 | Patch-mainline: never | |
4 | ||
5 | Patch accepted from SGI (bnc#430298, FATE #303968) added 12 new syscalls. | |
6 | Since these have not been accepted upstream, we don't want to have to | |
7 | support them. Decision was made to switch to a ioctl based interface. | |
8 | ||
9 | Signed-off-by: Tony Jones <tonyj@suse.de> | |
10 | ||
11 | --- | |
12 | include/linux/syscalls.h | 23 --- | |
13 | perfmon/Makefile | 3 | |
14 | perfmon/perfmon_control.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++ | |
15 | perfmon/perfmon_init.c | 3 | |
16 | perfmon/perfmon_priv.h | 25 +++ | |
17 | 5 files changed, 340 insertions(+), 24 deletions(-) | |
18 | ||
19 | --- a/include/linux/syscalls.h | |
20 | +++ b/include/linux/syscalls.h | |
21 | @@ -697,27 +697,4 @@ asmlinkage long sys_pipe(int __user *); | |
22 | ||
23 | int kernel_execve(const char *filename, char *const argv[], char *const envp[]); | |
24 | ||
25 | -asmlinkage long sys_pfm_create_context(struct pfarg_ctx __user *ureq, | |
26 | - void __user *uarg, size_t smpl_size); | |
27 | -asmlinkage long sys_pfm_write_pmcs(int fd, struct pfarg_pmc __user *ureq, | |
28 | - int count); | |
29 | -asmlinkage long sys_pfm_write_pmds(int fd, struct pfarg_pmd __user *ureq, | |
30 | - int count); | |
31 | -asmlinkage long sys_pfm_read_pmds(int fd, struct pfarg_pmd __user *ureq, | |
32 | - int count); | |
33 | -asmlinkage long sys_pfm_restart(int fd); | |
34 | -asmlinkage long sys_pfm_stop(int fd); | |
35 | -asmlinkage long sys_pfm_start(int fd, struct pfarg_start __user *ureq); | |
36 | -asmlinkage long sys_pfm_load_context(int fd, struct pfarg_load __user *ureq); | |
37 | -asmlinkage long sys_pfm_unload_context(int fd); | |
38 | -asmlinkage long sys_pfm_delete_evtsets(int fd, | |
39 | - struct pfarg_setinfo __user *ureq, | |
40 | - int count); | |
41 | -asmlinkage long sys_pfm_create_evtsets(int fd, | |
42 | - struct pfarg_setdesc __user *ureq, | |
43 | - int count); | |
44 | -asmlinkage long sys_pfm_getinfo_evtsets(int fd, | |
45 | - struct pfarg_setinfo __user *ureq, | |
46 | - int count); | |
47 | - | |
48 | #endif | |
49 | --- a/perfmon/Makefile | |
50 | +++ b/perfmon/Makefile | |
51 | @@ -7,6 +7,7 @@ obj-y = perfmon_init.o perfmon_rw.o perf | |
52 | perfmon_file.o perfmon_ctxsw.o perfmon_intr.o \ | |
53 | perfmon_dfl_smpl.o perfmon_sets.o perfmon_hotplug.o \ | |
54 | perfmon_msg.o perfmon_smpl.o perfmon_attach.o \ | |
55 | - perfmon_activate.o perfmon_ctx.o perfmon_fmt.o | |
56 | + perfmon_activate.o perfmon_ctx.o perfmon_fmt.o \ | |
57 | + perfmon_control.o | |
58 | ||
59 | obj-$(CONFIG_PERFMON_DEBUG_FS) += perfmon_debugfs.o | |
60 | --- /dev/null | |
61 | +++ b/perfmon/perfmon_control.c | |
62 | @@ -0,0 +1,310 @@ | |
63 | +/* | |
64 | + * perfmon_control.c: perfmon2 ioctl interface | |
65 | + * | |
66 | + * This file implements an ioctl interface alternative replacing the | |
67 | + * following syscalls: | |
68 | + * | |
69 | + * sys_pfm_create_context | |
70 | + * sys_pfm_write_pmcs | |
71 | + * sys_pfm_write_pmds | |
72 | + * sys_pfm_read_pmds | |
73 | + * sys_pfm_load_context | |
74 | + * sys_pfm_start | |
75 | + * sys_pfm_stop | |
76 | + * sys_pfm_restart | |
77 | + * sys_pfm_create_evtsets | |
78 | + * sys_pfm_getinfo_evtsets | |
79 | + * sys_pfm_delete_evtsets | |
80 | + * sys_pfm_unload_context | |
81 | + * | |
82 | + * For SLES11 | |
83 | + * | |
84 | + * Tony Jones <tonyj@suse.de> | |
85 | + * | |
86 | + * Copyright (c) 2008 Novell Inc | |
87 | + * Contributed by Tony Jones <tonyj@suse.de> | |
88 | + * | |
89 | + * This program is free software; you can redistribute it and/or | |
90 | + * modify it under the terms of version 2 of the GNU General Public | |
91 | + * License as published by the Free Software Foundation. | |
92 | + * | |
93 | + * This program is distributed in the hope that it will be useful, | |
94 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
95 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
96 | + * General Public License for more details. | |
97 | + * | |
98 | + * You should have received a copy of the GNU General Public License | |
99 | + * along with this program; if not, write to the Free Software | |
100 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
101 | + * 02111-1307 USA | |
102 | + */ | |
103 | + | |
104 | +#include <linux/kernel.h> | |
105 | +#include <linux/module.h> | |
106 | +#include <linux/proc_fs.h> | |
107 | +#include <linux/uaccess.h> | |
108 | +#include <linux/miscdevice.h> | |
109 | +#include <linux/perfmon_kern.h> | |
110 | +#include "perfmon_priv.h" | |
111 | +#include <linux/device.h> | |
112 | +#include <linux/compat.h> | |
113 | + | |
114 | +/* elements arranged to ensure current padding for 32/64bit */ | |
115 | +struct pfm_control_init { | |
116 | + __u64 req; /* struct pfarg_ctx* */ | |
117 | + __u64 fmt_name; /* char* */ | |
118 | + __u64 fmt_arg; /* void* */ | |
119 | + __u64 fmt_size; /* size_t */ | |
120 | +}; | |
121 | + | |
122 | +struct pfm_control_arglist { | |
123 | + __s32 fd; /* int */ | |
124 | + __s32 count; /* int */ | |
125 | + __u64 req; /* void* */ | |
126 | +}; | |
127 | + | |
128 | +struct pfm_control_argptr { | |
129 | + __u64 req; /* void* */ | |
130 | + __s32 fd; /* int */ | |
131 | + __s32 _pad; | |
132 | +}; | |
133 | + | |
134 | +struct pfm_control_fd { | |
135 | + __s32 fd; /* int */ | |
136 | + __s32 _pad; | |
137 | +}; | |
138 | + | |
139 | +union pfm_control { | |
140 | + struct pfm_control_init init; | |
141 | + struct pfm_control_arglist arglist; | |
142 | + struct pfm_control_argptr argptr; | |
143 | + struct pfm_control_fd fd; | |
144 | +}; | |
145 | + | |
146 | +#ifdef CONFIG_COMPAT | |
147 | +#define _PTR(p) (compat ? compat_ptr(p) : (void*)p) | |
148 | +#else | |
149 | +#define _PTR(p) (unsigned long)(p) | |
150 | +#endif | |
151 | + | |
152 | +static long pfm_control_create_context(union pfm_control *cdata, int compat) | |
153 | +{ | |
154 | + struct pfm_control_init *d = &cdata->init; | |
155 | + | |
156 | + return sys_pfm_create_context((struct pfarg_ctx *)_PTR(d->req), | |
157 | + (char *)_PTR(d->fmt_name), | |
158 | + (void *)_PTR(d->fmt_arg), | |
159 | + (size_t)d->fmt_size); | |
160 | +} | |
161 | + | |
162 | +static long pfm_control_write_pmcs(union pfm_control *cdata, int compat) | |
163 | +{ | |
164 | + struct pfm_control_arglist *d = &cdata->arglist; | |
165 | + | |
166 | + return sys_pfm_write_pmcs(d->fd, | |
167 | + (struct pfarg_pmc __user *)_PTR(d->req), | |
168 | + d->count); | |
169 | +} | |
170 | + | |
171 | +static long pfm_control_write_pmds(union pfm_control *cdata, int compat) | |
172 | +{ | |
173 | + struct pfm_control_arglist *d = &cdata->arglist; | |
174 | + | |
175 | + return sys_pfm_write_pmds(d->fd, | |
176 | + (struct pfarg_pmd __user *)_PTR(d->req), | |
177 | + d->count); | |
178 | +} | |
179 | + | |
180 | +static long pfm_control_read_pmds(union pfm_control *cdata, int compat) | |
181 | +{ | |
182 | + struct pfm_control_arglist *d = &cdata->arglist; | |
183 | + | |
184 | + return sys_pfm_read_pmds(d->fd, | |
185 | + (struct pfarg_pmd __user *)_PTR(d->req), | |
186 | + d->count); | |
187 | +} | |
188 | + | |
189 | +static long pfm_control_load_context(union pfm_control *cdata, int compat) | |
190 | +{ | |
191 | + struct pfm_control_argptr *d = &cdata->argptr; | |
192 | + | |
193 | + return sys_pfm_load_context(d->fd, | |
194 | + (struct pfarg_load __user *)_PTR(d->req)); | |
195 | +} | |
196 | + | |
197 | +static long pfm_control_start(union pfm_control *cdata, int compat) | |
198 | +{ | |
199 | + struct pfm_control_argptr *d = &cdata->argptr; | |
200 | + | |
201 | + return sys_pfm_start(d->fd, (struct pfarg_start __user *)_PTR(d->req)); | |
202 | +} | |
203 | + | |
204 | +static long pfm_control_stop(union pfm_control *cdata, int compat) | |
205 | +{ | |
206 | + struct pfm_control_fd *d = &cdata->fd; | |
207 | + | |
208 | + return sys_pfm_stop(d->fd); | |
209 | +} | |
210 | + | |
211 | +static long pfm_control_restart(union pfm_control *cdata, int compat) | |
212 | +{ | |
213 | + struct pfm_control_fd *d = &cdata->fd; | |
214 | + | |
215 | + return sys_pfm_restart(d->fd); | |
216 | +} | |
217 | + | |
218 | +static long pfm_control_create_evtsets(union pfm_control *cdata, int compat) | |
219 | +{ | |
220 | + struct pfm_control_arglist *d = &cdata->arglist; | |
221 | + | |
222 | + return sys_pfm_create_evtsets(d->fd, | |
223 | + (struct pfarg_setdesc __user *)_PTR(d->req), | |
224 | + d->count); | |
225 | +} | |
226 | + | |
227 | +static long pfm_control_getinfo_evtsets(union pfm_control *cdata, int compat) | |
228 | +{ | |
229 | + struct pfm_control_arglist *d = &cdata->arglist; | |
230 | + | |
231 | + return sys_pfm_getinfo_evtsets(d->fd, | |
232 | + (struct pfarg_setinfo __user *)_PTR(d->req), | |
233 | + d->count); | |
234 | +} | |
235 | + | |
236 | +static long pfm_control_delete_evtsets(union pfm_control *cdata, int compat) | |
237 | +{ | |
238 | + struct pfm_control_arglist *d = &cdata->arglist; | |
239 | + | |
240 | + return sys_pfm_delete_evtsets(d->fd, | |
241 | + (struct pfarg_setinfo __user *)_PTR(d->req), | |
242 | + d->count); | |
243 | +} | |
244 | + | |
245 | +static long pfm_control_unload_context(union pfm_control *cdata, int compat) | |
246 | +{ | |
247 | + struct pfm_control_fd *d = &cdata->fd; | |
248 | + | |
249 | + return sys_pfm_unload_context(d->fd); | |
250 | +} | |
251 | + | |
252 | +#define PFM_CONTROL_COUNT ARRAY_SIZE(pfm_control_tab) | |
253 | +#define PFM_CMD(func, elem) {func, sizeof(struct elem)} | |
254 | + | |
255 | +struct pfm_control_elem { | |
256 | + long (*func)(union pfm_control *, int compat); | |
257 | + size_t size; | |
258 | +}; | |
259 | + | |
260 | +static struct pfm_control_elem pfm_control_tab[] = { | |
261 | + PFM_CMD(pfm_control_create_context, pfm_control_init), | |
262 | + PFM_CMD(pfm_control_write_pmcs, pfm_control_arglist), | |
263 | + PFM_CMD(pfm_control_write_pmds, pfm_control_arglist), | |
264 | + PFM_CMD(pfm_control_read_pmds, pfm_control_arglist), | |
265 | + PFM_CMD(pfm_control_load_context, pfm_control_argptr), | |
266 | + PFM_CMD(pfm_control_start, pfm_control_argptr), | |
267 | + PFM_CMD(pfm_control_stop, pfm_control_fd), | |
268 | + PFM_CMD(pfm_control_restart, pfm_control_fd), | |
269 | + PFM_CMD(pfm_control_create_evtsets, pfm_control_arglist), | |
270 | + PFM_CMD(pfm_control_getinfo_evtsets, pfm_control_arglist), | |
271 | + PFM_CMD(pfm_control_delete_evtsets, pfm_control_arglist), | |
272 | + PFM_CMD(pfm_control_unload_context, pfm_control_fd), | |
273 | +}; | |
274 | + | |
275 | +static int __pfm_control_ioctl(struct inode *inode, struct file *file, | |
276 | + unsigned int cmd, unsigned long arg, | |
277 | + int compat) | |
278 | +{ | |
279 | + union pfm_control cdata; | |
280 | + int rc, op; | |
281 | + | |
282 | + if (perfmon_disabled) | |
283 | + return -ENOSYS; | |
284 | + | |
285 | + op = _IOC_NR(cmd); | |
286 | + | |
287 | + if (unlikely(op < 0 || op >= PFM_CONTROL_COUNT || | |
288 | + pfm_control_tab[op].func == NULL)) { | |
289 | + PFM_ERR("Invalid control request %d", op); | |
290 | + return -EINVAL; | |
291 | + } | |
292 | + | |
293 | + if (_IOC_SIZE(cmd) != pfm_control_tab[op].size) { | |
294 | + PFM_ERR("Invalid control request %d, size %d, expected %lu\n", | |
295 | + op, _IOC_SIZE(cmd), | |
296 | + (unsigned long)pfm_control_tab[op].size); | |
297 | + return -EINVAL; | |
298 | + } | |
299 | + | |
300 | + if (_IOC_TYPE(cmd) != 0 || _IOC_DIR(cmd) != _IOC_WRITE) | |
301 | + return -EINVAL; | |
302 | + | |
303 | + if (copy_from_user(&cdata, (void*)arg, pfm_control_tab[op].size) != 0) | |
304 | + return -EFAULT; | |
305 | + | |
306 | + rc = pfm_control_tab[op].func(&cdata, compat); | |
307 | + return rc; | |
308 | +} | |
309 | + | |
310 | +static int pfm_control_ioctl(struct inode *inode, struct file *file, | |
311 | + unsigned int cmd, unsigned long arg) | |
312 | +{ | |
313 | + return __pfm_control_ioctl(inode, file, cmd, arg, 0); | |
314 | +} | |
315 | + | |
316 | +#ifdef CONFIG_COMPAT | |
317 | +static long compat_pfm_control_ioctl(struct file *file, | |
318 | + unsigned int cmd, unsigned long arg) | |
319 | +{ | |
320 | + return __pfm_control_ioctl(file->f_dentry->d_inode, file, cmd, arg, 1); | |
321 | +} | |
322 | +#endif | |
323 | + | |
324 | +static const struct file_operations pfm_control_operations = { | |
325 | + .owner = THIS_MODULE, | |
326 | + .ioctl = pfm_control_ioctl, | |
327 | +#ifdef CONFIG_COMPAT | |
328 | + .compat_ioctl = compat_pfm_control_ioctl, | |
329 | +#endif | |
330 | +}; | |
331 | + | |
332 | +#ifdef USE_MISC_REGISTER | |
333 | +static struct miscdevice pfm_misc_device = { | |
334 | + .minor = MISC_DYNAMIC_MINOR, | |
335 | + .name = "perfmonctl", | |
336 | + .fops = &pfm_control_operations | |
337 | +}; | |
338 | +#endif | |
339 | + | |
340 | +int __init pfm_init_control(void) | |
341 | +{ | |
342 | + int ret=0; | |
343 | +#ifndef USE_MISC_REGISTER | |
344 | + static struct class *pfm_class; | |
345 | + struct device *dev; | |
346 | + int major; | |
347 | +#endif | |
348 | + | |
349 | +#ifdef USE_MISC_REGISTER | |
350 | + ret = misc_register(&pfm_misc_device); | |
351 | + if (ret) { | |
352 | + PFM_ERR("Failed to create perfmon control file. Error %d\n", ret); | |
353 | + } | |
354 | +#else | |
355 | + major = register_chrdev(0, "perfmon", &pfm_control_operations); | |
356 | + if (major < 0) { | |
357 | + PFM_ERR("Failed to register_chardev %d\n", major); | |
358 | + return major; | |
359 | + } | |
360 | + pfm_class = class_create(THIS_MODULE, "perfmon"); | |
361 | + if (IS_ERR(pfm_class)) { | |
362 | + PFM_ERR("Failed to class_create %ld\n", PTR_ERR(pfm_class)); | |
363 | + return -ENOENT; | |
364 | + } | |
365 | + dev = device_create(pfm_class, NULL, MKDEV(major,0), NULL, "perfmonctl"); | |
366 | + if (IS_ERR(dev)) { | |
367 | + PFM_ERR("Failed to device_create %ld\n", PTR_ERR(dev)); | |
368 | + return -ENOENT; | |
369 | + } | |
370 | +#endif | |
371 | + return ret; | |
372 | +} | |
373 | --- a/perfmon/perfmon_init.c | |
374 | +++ b/perfmon/perfmon_init.c | |
375 | @@ -104,6 +104,9 @@ int __init pfm_init(void) | |
376 | if (pfm_init_sysfs()) | |
377 | goto error_disable; | |
378 | ||
379 | + if (pfm_init_control()) | |
380 | + goto error_disable; | |
381 | + | |
382 | /* not critical, so no error checking */ | |
383 | pfm_init_debugfs(); | |
384 | ||
385 | --- a/perfmon/perfmon_priv.h | |
386 | +++ b/perfmon/perfmon_priv.h | |
387 | @@ -158,6 +158,7 @@ static inline void pfm_check_save_prev_c | |
388 | ||
389 | ||
390 | int pfm_init_fs(void); | |
391 | +int pfm_init_control(void); | |
392 | ||
393 | int pfm_init_hotplug(void); | |
394 | ||
395 | @@ -177,6 +178,30 @@ static inline void pfm_post_work(struct | |
396 | #define PFM_PMC_STK_ARG PFM_ARCH_PMC_STK_ARG | |
397 | #define PFM_PMD_STK_ARG PFM_ARCH_PMD_STK_ARG | |
398 | ||
399 | +/* these used to be in linux/syscalls.h, now accessed via ioctl interface */ | |
400 | +asmlinkage long sys_pfm_create_context(struct pfarg_ctx __user *ureq, | |
401 | + char __user *fmt_name, | |
402 | + void __user *fmt_uarg, size_t fmt_size); | |
403 | +asmlinkage long sys_pfm_write_pmcs(int fd, struct pfarg_pmc __user *ureq, | |
404 | + int count); | |
405 | +asmlinkage long sys_pfm_write_pmds(int fd, struct pfarg_pmd __user *ureq, | |
406 | + int count); | |
407 | +asmlinkage long sys_pfm_read_pmds(int fd, struct pfarg_pmd __user *ureq, | |
408 | + int count); | |
409 | +asmlinkage long sys_pfm_restart(int fd); | |
410 | +asmlinkage long sys_pfm_stop(int fd); | |
411 | +asmlinkage long sys_pfm_start(int fd, struct pfarg_start __user *ureq); | |
412 | +asmlinkage long sys_pfm_load_context(int fd, struct pfarg_load __user *ureq); | |
413 | +asmlinkage long sys_pfm_unload_context(int fd); | |
414 | +asmlinkage long sys_pfm_delete_evtsets(int fd, | |
415 | + struct pfarg_setinfo __user *ureq, | |
416 | + int count); | |
417 | +asmlinkage long sys_pfm_create_evtsets(int fd, | |
418 | + struct pfarg_setdesc __user *ureq, | |
419 | + int count); | |
420 | +asmlinkage long sys_pfm_getinfo_evtsets(int fd, | |
421 | + struct pfarg_setinfo __user *ureq, | |
422 | + int count); | |
423 | #endif /* CONFIG_PERFMON */ | |
424 | ||
425 | #endif /* __PERFMON_PRIV_H__ */ |