Pull pnpacpi into release branch
[linux-2.6] / arch / ia64 / kernel / perfmon_default_smpl.c
1 /*
2  * Copyright (C) 2002-2003 Hewlett-Packard Co
3  *               Stephane Eranian <eranian@hpl.hp.com>
4  *
5  * This file implements the default sampling buffer format
6  * for the Linux/ia64 perfmon-2 subsystem.
7  */
8 #include <linux/kernel.h>
9 #include <linux/types.h>
10 #include <linux/module.h>
11 #include <linux/config.h>
12 #include <linux/init.h>
13 #include <asm/delay.h>
14 #include <linux/smp.h>
15
16 #include <asm/perfmon.h>
17 #include <asm/perfmon_default_smpl.h>
18
19 MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
20 MODULE_DESCRIPTION("perfmon default sampling format");
21 MODULE_LICENSE("GPL");
22
23 #define DEFAULT_DEBUG 1
24
25 #ifdef DEFAULT_DEBUG
26 #define DPRINT(a) \
27         do { \
28                 if (unlikely(pfm_sysctl.debug >0)) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \
29         } while (0)
30
31 #define DPRINT_ovfl(a) \
32         do { \
33                 if (unlikely(pfm_sysctl.debug > 0 && pfm_sysctl.debug_ovfl >0)) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \
34         } while (0)
35
36 #else
37 #define DPRINT(a)
38 #define DPRINT_ovfl(a)
39 #endif
40
41 static int
42 default_validate(struct task_struct *task, unsigned int flags, int cpu, void *data)
43 {
44         pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t*)data;
45         int ret = 0;
46
47         if (data == NULL) {
48                 DPRINT(("[%d] no argument passed\n", task->pid));
49                 return -EINVAL;
50         }
51
52         DPRINT(("[%d] validate flags=0x%x CPU%d\n", task->pid, flags, cpu));
53
54         /*
55          * must hold at least the buffer header + one minimally sized entry
56          */
57         if (arg->buf_size < PFM_DEFAULT_SMPL_MIN_BUF_SIZE) return -EINVAL;
58
59         DPRINT(("buf_size=%lu\n", arg->buf_size));
60
61         return ret;
62 }
63
64 static int
65 default_get_size(struct task_struct *task, unsigned int flags, int cpu, void *data, unsigned long *size)
66 {
67         pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data;
68
69         /*
70          * size has been validated in default_validate
71          */
72         *size = arg->buf_size;
73
74         return 0;
75 }
76
77 static int
78 default_init(struct task_struct *task, void *buf, unsigned int flags, int cpu, void *data)
79 {
80         pfm_default_smpl_hdr_t *hdr;
81         pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data;
82
83         hdr = (pfm_default_smpl_hdr_t *)buf;
84
85         hdr->hdr_version      = PFM_DEFAULT_SMPL_VERSION;
86         hdr->hdr_buf_size     = arg->buf_size;
87         hdr->hdr_cur_offs     = sizeof(*hdr);
88         hdr->hdr_overflows    = 0UL;
89         hdr->hdr_count        = 0UL;
90
91         DPRINT(("[%d] buffer=%p buf_size=%lu hdr_size=%lu hdr_version=%u cur_offs=%lu\n",
92                 task->pid,
93                 buf,
94                 hdr->hdr_buf_size,
95                 sizeof(*hdr),
96                 hdr->hdr_version,
97                 hdr->hdr_cur_offs));
98
99         return 0;
100 }
101
102 static int
103 default_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, struct pt_regs *regs, unsigned long stamp)
104 {
105         pfm_default_smpl_hdr_t *hdr;
106         pfm_default_smpl_entry_t *ent;
107         void *cur, *last;
108         unsigned long *e, entry_size;
109         unsigned int npmds, i;
110         unsigned char ovfl_pmd;
111         unsigned char ovfl_notify;
112
113         if (unlikely(buf == NULL || arg == NULL|| regs == NULL || task == NULL)) {
114                 DPRINT(("[%d] invalid arguments buf=%p arg=%p\n", task->pid, buf, arg));
115                 return -EINVAL;
116         }
117
118         hdr         = (pfm_default_smpl_hdr_t *)buf;
119         cur         = buf+hdr->hdr_cur_offs;
120         last        = buf+hdr->hdr_buf_size;
121         ovfl_pmd    = arg->ovfl_pmd;
122         ovfl_notify = arg->ovfl_notify;
123
124         /*
125          * precheck for sanity
126          */
127         if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full;
128
129         npmds = hweight64(arg->smpl_pmds[0]);
130
131         ent = (pfm_default_smpl_entry_t *)cur;
132
133         prefetch(arg->smpl_pmds_values);
134
135         entry_size = sizeof(*ent) + (npmds << 3);
136
137         /* position for first pmd */
138         e = (unsigned long *)(ent+1);
139
140         hdr->hdr_count++;
141
142         DPRINT_ovfl(("[%d] count=%lu cur=%p last=%p free_bytes=%lu ovfl_pmd=%d ovfl_notify=%d npmds=%u\n",
143                         task->pid,
144                         hdr->hdr_count,
145                         cur, last,
146                         last-cur,
147                         ovfl_pmd,
148                         ovfl_notify, npmds));
149
150         /*
151          * current = task running at the time of the overflow.
152          *
153          * per-task mode:
154          *      - this is ususally the task being monitored.
155          *        Under certain conditions, it might be a different task
156          *
157          * system-wide:
158          *      - this is not necessarily the task controlling the session
159          */
160         ent->pid            = current->pid;
161         ent->ovfl_pmd       = ovfl_pmd;
162         ent->last_reset_val = arg->pmd_last_reset; //pmd[0].reg_last_reset_val;
163
164         /*
165          * where did the fault happen (includes slot number)
166          */
167         ent->ip = regs->cr_iip | ((regs->cr_ipsr >> 41) & 0x3);
168
169         ent->tstamp    = stamp;
170         ent->cpu       = smp_processor_id();
171         ent->set       = arg->active_set;
172         ent->tgid      = current->tgid;
173
174         /*
175          * selectively store PMDs in increasing index number
176          */
177         if (npmds) {
178                 unsigned long *val = arg->smpl_pmds_values;
179                 for(i=0; i < npmds; i++) {
180                         *e++ = *val++;
181                 }
182         }
183
184         /*
185          * update position for next entry
186          */
187         hdr->hdr_cur_offs += entry_size;
188         cur               += entry_size;
189
190         /*
191          * post check to avoid losing the last sample
192          */
193         if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full;
194
195         /*
196          * keep same ovfl_pmds, ovfl_notify
197          */
198         arg->ovfl_ctrl.bits.notify_user     = 0;
199         arg->ovfl_ctrl.bits.block_task      = 0;
200         arg->ovfl_ctrl.bits.mask_monitoring = 0;
201         arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; /* reset before returning from interrupt handler */
202
203         return 0;
204 full:
205         DPRINT_ovfl(("sampling buffer full free=%lu, count=%lu, ovfl_notify=%d\n", last-cur, hdr->hdr_count, ovfl_notify));
206
207         /*
208          * increment number of buffer overflow.
209          * important to detect duplicate set of samples.
210          */
211         hdr->hdr_overflows++;
212
213         /*
214          * if no notification requested, then we saturate the buffer
215          */
216         if (ovfl_notify == 0) {
217                 arg->ovfl_ctrl.bits.notify_user     = 0;
218                 arg->ovfl_ctrl.bits.block_task      = 0;
219                 arg->ovfl_ctrl.bits.mask_monitoring = 1;
220                 arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0;
221         } else {
222                 arg->ovfl_ctrl.bits.notify_user     = 1;
223                 arg->ovfl_ctrl.bits.block_task      = 1; /* ignored for non-blocking context */
224                 arg->ovfl_ctrl.bits.mask_monitoring = 1;
225                 arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0; /* no reset now */
226         }
227         return -1; /* we are full, sorry */
228 }
229
230 static int
231 default_restart(struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs)
232 {
233         pfm_default_smpl_hdr_t *hdr;
234
235         hdr = (pfm_default_smpl_hdr_t *)buf;
236
237         hdr->hdr_count    = 0UL;
238         hdr->hdr_cur_offs = sizeof(*hdr);
239
240         ctrl->bits.mask_monitoring = 0;
241         ctrl->bits.reset_ovfl_pmds = 1; /* uses long-reset values */
242
243         return 0;
244 }
245
246 static int
247 default_exit(struct task_struct *task, void *buf, struct pt_regs *regs)
248 {
249         DPRINT(("[%d] exit(%p)\n", task->pid, buf));
250         return 0;
251 }
252
253 static pfm_buffer_fmt_t default_fmt={
254         .fmt_name           = "default_format",
255         .fmt_uuid           = PFM_DEFAULT_SMPL_UUID,
256         .fmt_arg_size       = sizeof(pfm_default_smpl_arg_t),
257         .fmt_validate       = default_validate,
258         .fmt_getsize        = default_get_size,
259         .fmt_init           = default_init,
260         .fmt_handler        = default_handler,
261         .fmt_restart        = default_restart,
262         .fmt_restart_active = default_restart,
263         .fmt_exit           = default_exit,
264 };
265
266 static int __init
267 pfm_default_smpl_init_module(void)
268 {
269         int ret;
270
271         ret = pfm_register_buffer_fmt(&default_fmt);
272         if (ret == 0) {
273                 printk("perfmon_default_smpl: %s v%u.%u registered\n",
274                         default_fmt.fmt_name,
275                         PFM_DEFAULT_SMPL_VERSION_MAJ,
276                         PFM_DEFAULT_SMPL_VERSION_MIN);
277         } else {
278                 printk("perfmon_default_smpl: %s cannot register ret=%d\n",
279                         default_fmt.fmt_name,
280                         ret);
281         }
282
283         return ret;
284 }
285
286 static void __exit
287 pfm_default_smpl_cleanup_module(void)
288 {
289         int ret;
290         ret = pfm_unregister_buffer_fmt(default_fmt.fmt_uuid);
291
292         printk("perfmon_default_smpl: unregister %s=%d\n", default_fmt.fmt_name, ret);
293 }
294
295 module_init(pfm_default_smpl_init_module);
296 module_exit(pfm_default_smpl_cleanup_module);
297