disable sstrip when using musl
[lede.git] / target / linux / ubicom32 / files / arch / ubicom32 / mach-common / profile.c
1 /*
2  * arch/ubicom32/mach-common/profile.c
3  *   Implementation for Ubicom32 Profiler
4  *
5  * (C) Copyright 2009, Ubicom, Inc.
6  *
7  * This file is part of the Ubicom32 Linux Kernel Port.
8  *
9  * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10  * it and/or modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation, either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17  * the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with the Ubicom32 Linux Kernel Port.  If not,
21  * see <http://www.gnu.org/licenses/>.
22  */
23
24 #include <linux/platform_device.h>
25 #include "profile.h"
26 #include <linux/seq_file.h>
27 #include <linux/proc_fs.h>
28 #include <linux/mm.h>
29 #include <linux/mmzone.h>
30 #include <linux/fs.h>
31 #include <linux/page-flags.h>
32 #include <asm/uaccess.h>
33 #include <asm/devtree.h>
34 #include <asm/profilesample.h>
35 #include <asm/memory_map.h>
36 #include <asm/page.h>
37 #include <asm/ip5000.h>
38
39 /*
40  * spacs for all memory blocks so we can hold locks for short time when walking tables
41  */
42 #define PROFILE_NUM_MAPS 5000
43 static struct profile_map profile_pm[PROFILE_NUM_MAPS];
44
45 static struct profilenode *node = NULL;
46 static int profile_first_packet = 1;
47
48 static int profile_open(struct inode *inode, struct file *filp)
49 {
50         if (!node) {
51                 return -ENOENT;
52         }
53         node->busy = 1;
54         if (!node->enabled) {
55                 node->enabled = 1;
56                 node->busy = 0;
57                 profile_first_packet = 1;
58                 return 0;
59         }
60         node->busy = 0;
61         return -EBUSY;
62 }
63
64 static int profile_sequence_num;
65
66 /*
67  * make a packet full of sample data
68  */
69 static int profile_make_data_packet(char *buf, int count)
70 {
71         int samples;            /* number of samples requested */
72         int i;
73         struct profile_header ph;
74         char *ptr;
75
76         if (count < sizeof(struct profile_header) + sizeof(struct profile_sample)) {
77                 return -EINVAL;
78         }
79
80         /*
81          * fill in the packet header
82          */
83         memset(&ph, 0, sizeof(struct profile_header));
84         ph.magic = PROF_MAGIC + PROFILE_VERSION;
85         ph.header_size = sizeof(struct profile_header);
86         ph.clocks = node->clocks;
87         for (i = 0; i < PROFILE_MAX_THREADS; ++i) {
88                 ph.instruction_count[i] = node->inst_count[i];
89         }
90         ph.profile_instructions = 0;
91         ph.enabled = node->enabled_threads;
92         ph.hrt = node->hrt;
93         ph.high = 0;
94         ph.profiler_thread = node->profiler_thread;
95         ph.clock_freq = node->clock_freq;
96         ph.seq_num = profile_sequence_num++;
97         ph.cpu_id = node->cpu_id;
98         ph.perf_counters[0] = node->stats[0];
99         ph.perf_counters[1] = node->stats[1];
100         ph.perf_counters[2] = node->stats[2];
101         ph.perf_counters[3] = node->stats[3];
102         ph.ddr_freq = node->ddr_freq;
103
104         ptr = buf + sizeof(struct profile_header);
105
106         samples = (count - sizeof(struct profile_header)) / sizeof(struct profile_sample);
107         for (i = 0; i < samples && node->count; ++i) {
108                 if (copy_to_user(ptr, &node->samples[node->tail], sizeof(struct profile_sample)) != 0) {
109                         return -EFAULT;
110                 }
111                 node->count--;
112                 node->tail++;
113                 if (node->tail >= node->max_samples) {
114                         node->tail = 0;
115                 }
116                 ptr += sizeof(struct profile_sample);
117         }
118         ph.sample_count = i;
119         if (copy_to_user(buf, &ph, sizeof(struct profile_header)) != 0) {
120                 return -EFAULT;
121         }
122         if (ph.sample_count == 0)
123                 return 0;
124         else
125                 return sizeof(struct profile_header) + ph.sample_count * sizeof(struct profile_sample);
126 }
127
128 static void profile_get_memory_stats(unsigned int *total_free, unsigned int *max_free)
129 {
130         struct list_head *p;
131         struct zone *zone;
132         unsigned int size;
133
134         *total_free = 0;
135         *max_free = 0;
136
137         /*
138          * get all the free regions.  In each zone, the array of free_area lists contains the first page of each frame of size 1 << order
139          */
140         for_each_zone(zone) {
141                 unsigned long order, flags, i;
142
143                 if (!populated_zone(zone))
144                         continue;
145
146                 if (!is_normal(zone))
147                         continue;
148
149                 spin_lock_irqsave(&zone->lock, flags);
150                 for_each_migratetype_order(order, i) {
151                         size = ((1 << order) << PAGE_SHIFT) >> 10;
152                         list_for_each(p, &(zone->free_area[order].free_list[i])) {
153                                 if (size > *max_free) {
154                                         *max_free = size;
155                                 }
156                                 *total_free += size;
157                         }
158                 }
159                 spin_unlock_irqrestore(&zone->lock, flags);
160         }
161 }
162
163 struct profile_counter_pkt profile_builtin_stats[] =
164 {
165         {
166         "Free memory(KB)", 0
167         },
168         {
169         "Max free Block(KB)", 0
170         }
171 };
172
173 /*
174  * make a packet full of performance counters
175  */
176 static char prof_pkt[PROFILE_MAX_PACKET_SIZE];
177 static int profile_make_stats_packet(char *buf, int count)
178 {
179         char *ptr = prof_pkt;
180         struct profile_header_counters hdr;
181         int stat_count = 0;
182         int i;
183         unsigned int total_free, max_free;
184         int builtin_count = sizeof(profile_builtin_stats) / sizeof(struct profile_counter_pkt);
185
186         if (count > PROFILE_MAX_PACKET_SIZE) {
187                 count = PROFILE_MAX_PACKET_SIZE;
188         }
189         stat_count = (count - sizeof(struct profile_header_counters)) / sizeof (struct profile_counter_pkt);
190         stat_count -= builtin_count;
191
192         if (stat_count <= 0) {
193                 return 0;
194         }
195
196         if (stat_count > node->num_counters) {
197                 stat_count = node->num_counters;
198         }
199
200         hdr.magic = PROF_MAGIC_COUNTERS;
201         hdr.ultra_sample_time = node->clocks;
202         hdr.ultra_count = stat_count;
203         hdr.linux_sample_time = UBICOM32_IO_TIMER->sysval;
204         hdr.linux_count = builtin_count;
205         memcpy(ptr, (void *)&hdr, sizeof(struct profile_header_counters));
206         ptr += sizeof(struct profile_header_counters);
207
208
209         for (i = 0; i < stat_count; ++i) {
210                 memcpy(ptr, (void *)(&(node->counters[i])), sizeof(struct profile_counter));
211                 ptr += sizeof(struct profile_counter);
212         }
213
214         /*
215          * built in statistics
216          */
217         profile_get_memory_stats(&total_free, &max_free);
218         profile_builtin_stats[0].value = total_free;
219         profile_builtin_stats[1].value = max_free;
220         memcpy(ptr, (void *)profile_builtin_stats, sizeof(profile_builtin_stats));
221         ptr += sizeof(profile_builtin_stats);
222
223         if (copy_to_user(buf, prof_pkt, ptr - prof_pkt) != 0) {
224                 return -EFAULT;
225         }
226         return ptr - prof_pkt;
227 }
228
229 /*
230  * return a udp packet ready to send to the profiler tool
231  * when there are no packets left to make, return 0
232  */
233 static int profile_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
234 {
235         int result = 0;
236         if (!node) {
237                 return -ENOENT;
238         }
239         node->busy = 1;
240         if (!node->enabled) {
241                 node->busy = 0;
242                 return -EPERM;
243         }
244         if (!node->samples) {
245                 node->busy = 0;
246                 return -ENOMEM;
247         }
248
249         if (profile_first_packet) {
250                 result = profile_make_stats_packet(buf, count);
251                 profile_first_packet = 0;
252         }
253         if (result == 0) {
254                 result = profile_make_data_packet(buf, count);
255                 if (result == 0) {
256                         profile_first_packet = 1;
257                 }
258         }
259         node->busy = 0;
260         return result;
261
262 }
263
264 static int profile_release(struct inode *inode, struct file *filp)
265 {
266         if (!node) {
267                 return -ENOENT;
268         }
269         node->busy = 1;
270         if (node->enabled) {
271                 node->enabled = 0;
272                 node->count = 0;
273                 node->tail = node->head;
274                 node->busy = 0;
275                 return 0;
276         }
277         node->busy = 0;
278         profile_first_packet = 1;
279         return -EBADF;
280 }
281
282 static const struct file_operations profile_fops = {
283         .open           = profile_open,
284         .read           = profile_read,
285         .release        = profile_release,
286 };
287
288 static int page_aligned(void *x)
289 {
290         return !((unsigned int)x & ((1 << PAGE_SHIFT) - 1));
291 }
292
293 static int profile_maps_open(struct inode *inode, struct file *filp)
294 {
295         struct rb_node *rb;
296         int num = 0;
297         int slab_start;
298         struct vm_area_struct *vma;
299         int type = PROFILE_MAP_TYPE_UNKNOWN;
300         int flags, i;
301         struct list_head *p;
302         struct zone *zone;
303
304         /*
305          * get the slab data (first so dups will show up as vmas)
306          */
307         slab_start = num;
308         num += kmem_cache_block_info("size-512", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
309         num += kmem_cache_block_info("size-1024", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
310         num += kmem_cache_block_info("size-2048", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
311         num += kmem_cache_block_info("size-4096", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
312         num += kmem_cache_block_info("size-8192", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
313
314         for (i = slab_start; i < num; ++i) {
315                 profile_pm[i].type_size |= PROFILE_MAP_TYPE_SMALL << PROFILE_MAP_TYPE_SHIFT;
316         }
317
318         slab_start = num;
319         num += kmem_cache_block_info("dentry", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
320         num += kmem_cache_block_info("inode_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
321         num += kmem_cache_block_info("sysfs_dir_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
322         num += kmem_cache_block_info("proc_inode_cache", (struct kmem_cache_size_info *)&profile_pm[num], PROFILE_NUM_MAPS - num);
323
324         for (i = slab_start; i < num; ++i) {
325                 profile_pm[i].type_size |= PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT;
326         }
327
328         /*
329          * get all the vma regions (allocated by mmap, most likely
330          */
331 #if 0
332         down_read(&nommu_vma_sem);
333         for (rb = rb_first(&nommu_vma_tree); rb && num < PROFILE_NUM_MAPS; rb = rb_next(rb)) {
334                 vma = rb_entry(rb, struct vm_area_struct, vm_rb);
335                 profile_pm[num].start = (vma->vm_start - SDRAMSTART) >> PAGE_SHIFT;
336                 profile_pm[num].type_size = (vma->vm_end - vma->vm_start + (1 << PAGE_SHIFT) - 1) >> PAGE_SHIFT;
337                 flags = vma->vm_flags & 0xf;
338                 if (flags == (VM_READ | VM_EXEC)) {
339                         type = PROFILE_MAP_TYPE_TEXT;
340                 } else if (flags == (VM_READ | VM_WRITE | VM_EXEC)) {
341                         type = PROFILE_MAP_TYPE_STACK;
342                 } else if (flags == (VM_READ | VM_WRITE)) {
343                         type = PROFILE_MAP_TYPE_APP_DATA;
344                 }
345                 profile_pm[num].type_size |= type << PROFILE_MAP_TYPE_SHIFT;
346                 num++;
347         }
348         up_read(&nommu_vma_sem);
349         if (rb) {
350                 return -ENOMEM;
351         }
352 #endif
353
354         /*
355          * get all the free regions.  In each zone, the array of free_area lists contains the first page of each frame of size 1 << order
356          */
357         for_each_zone(zone) {
358                 unsigned long order, flags, i;
359                 struct page *page;
360
361                 if (!populated_zone(zone))
362                         continue;
363
364                 if (!is_normal(zone))
365                         continue;
366
367                 spin_lock_irqsave(&zone->lock, flags);
368                 for_each_migratetype_order(order, i) {
369                         list_for_each(p, &(zone->free_area[order].free_list[i])) {
370                                 page = list_entry(p, struct page, lru);
371                                 profile_pm[num].start = ((page_to_phys(page) - SDRAMSTART) >> PAGE_SHIFT) - 0x40;
372                                 profile_pm[num].type_size = (PROFILE_MAP_TYPE_FREE << PROFILE_MAP_TYPE_SHIFT) | order;
373                                 num++;
374                                 if (num >= PROFILE_NUM_MAPS) {
375                                         spin_unlock_irqrestore(&zone->lock, flags);
376                                         return -ENOMEM;
377                                 }
378                         }
379                 }
380                 spin_unlock_irqrestore(&zone->lock, flags);
381         }
382
383         /*
384          * get the filesystem inodes
385          */
386         list_for_each(p, &(super_blocks)) {
387                 struct super_block *sb;
388                 struct list_head *q;
389                 if (num >= PROFILE_NUM_MAPS)
390                         break;
391                 sb = list_entry(p, struct super_block, s_list);
392                 if (page_aligned(sb)) {
393                         profile_pm[num].start = ((unsigned int)sb - SDRAMSTART) >> PAGE_SHIFT;
394                         profile_pm[num].type_size = (PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT);
395                         num++;
396                 }
397                 list_for_each(q, &(sb->s_inodes)) {
398                         struct inode *in;
399                         if (num >= PROFILE_NUM_MAPS)
400                                 break;
401                         in = list_entry(q, struct inode, i_sb_list);
402                         if (page_aligned(in)) {
403                                 profile_pm[num].start = ((unsigned int)in - SDRAMSTART) >> PAGE_SHIFT;
404                                 profile_pm[num].type_size = (PROFILE_MAP_TYPE_FS << PROFILE_MAP_TYPE_SHIFT);
405                                 num++;
406                         }
407                 }
408         }
409
410         /*
411          * get the buffer cache pages
412          */
413         for (i = 0; i < num_physpages && num < PROFILE_NUM_MAPS; ++i) {
414                 if ((mem_map + i)->flags & (1 << PG_lru)) {
415                         int start = i;
416                         while ((mem_map + i)->flags & (1 << PG_lru) && i < num_physpages)
417                                 i++;
418                         profile_pm[num].start = start;
419                         profile_pm[num].type_size = (i - start) | (PROFILE_MAP_TYPE_CACHE << PROFILE_MAP_TYPE_SHIFT);
420                         num++;
421                 }
422         }
423
424         filp->private_data = (void *)num;
425         return 0;
426 }
427
428 /*
429  * return one packet of map data, or 0 if all maps have been returned already
430  */
431 static int profile_maps_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
432 {
433         struct profile_header_maps header;
434         char *p = buf + sizeof(header);
435         int total = (int)filp->private_data;
436
437         header.count = (count - sizeof(header)) / sizeof(struct profile_map);
438         if (header.count > PROFILE_MAX_MAPS) {
439                 header.count = PROFILE_MAX_MAPS;;
440         }
441         if (header.count > total - *f_pos) {
442                 header.count = total - *f_pos;
443         }
444
445         if (header.count == 0) {
446                 return 0;
447         }
448
449         header.magic = PROF_MAGIC_MAPS;
450         header.page_shift = PAGE_SHIFT;
451
452         if (copy_to_user(buf, &header, sizeof(header)) != 0) {
453                 return -EFAULT;
454         }
455         if (copy_to_user(p, (void *)&profile_pm[*f_pos], sizeof(struct profile_map) * header.count) != 0) {
456                 return -EFAULT;
457         }
458         *f_pos += header.count;
459
460         return sizeof(header) + sizeof(struct profile_map) * header.count;
461 }
462
463 static int profile_maps_release(struct inode *inode, struct file *filp)
464 {
465         return 0;
466 }
467
468 static const struct file_operations profile_maps_fops = {
469         .open           = profile_maps_open,
470         .read           = profile_maps_read,
471         .release        = profile_maps_release,
472 };
473
474 static int profile_rate_show(struct seq_file *m, void *v)
475 {
476         if (node) {
477                 seq_printf(m, "%d samples per second.  %d virtual counters.\n", node->rate, node->num_counters);
478         } else {
479                 seq_printf(m, "Profiler is not initialized.\n");
480         }
481         return 0;
482 }
483
484 static int profile_rate_open(struct inode *inode, struct file *filp)
485 {
486         return single_open(filp, profile_rate_show, NULL);
487 }
488
489 static int profile_rate_write(struct file *filp, const char *buf, size_t len, loff_t *off)
490 {
491         *off = 0;
492         return 0;
493 }
494
495 static const struct file_operations profile_rate_fops = {
496         .open           = profile_rate_open,
497         .read           = seq_read,
498         .llseek         = seq_lseek,
499         .release        = single_release,
500         .write          = profile_rate_write,
501 };
502
503 int ubi32_profile_init_module(void)
504 {
505         struct proc_dir_entry *pdir;
506
507         /*
508          * find the device
509          */
510         node = (struct profilenode *)devtree_find_node("profiler");
511         if (!node) {
512                 printk(KERN_INFO "Profiler does not exist.\n");
513                 return -ENODEV;
514         }
515
516         /*
517          * allocate the sample buffer
518          */
519         node->max_samples = PROFILE_MAX_SAMPLES;
520         node->samples = kmalloc(node->max_samples * sizeof(struct profile_sample), GFP_KERNEL);
521         if (!node->samples) {
522                 printk(KERN_INFO "Profiler sample buffer kmalloc failed.\n");
523                 return -ENOMEM;
524         }
525
526         /*
527          * connect to the file system
528          */
529         pdir = proc_mkdir("profile", NULL);
530         if (!pdir) {
531                 return -ENOMEM;
532         }
533         if (!proc_create("data", 0, pdir, &profile_fops)) {
534                 return -ENOMEM;
535         }
536         if (!proc_create("rate", 0, pdir, &profile_rate_fops)) {
537                 return -ENOMEM;
538         }
539         if (!proc_create("maps", 0, pdir, &profile_maps_fops)) {
540                 return -ENOMEM;
541         }
542         return 0;
543 }
544
545
546 module_init(ubi32_profile_init_module);
547
548 MODULE_AUTHOR("David Fotland");
549 MODULE_LICENSE("GPL");