Root/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
43static struct profile_map profile_pm[PROFILE_NUM_MAPS];
44
45static struct profilenode *node = NULL;
46static int profile_first_packet = 1;
47
48static 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
64static int profile_sequence_num;
65
66/*
67 * make a packet full of sample data
68 */
69static 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
128static 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
163struct 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 */
176static char prof_pkt[PROFILE_MAX_PACKET_SIZE];
177static 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 */
233static 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
264static 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
282static const struct file_operations profile_fops = {
283    .open = profile_open,
284    .read = profile_read,
285    .release = profile_release,
286};
287
288static int page_aligned(void *x)
289{
290    return !((unsigned int)x & ((1 << PAGE_SHIFT) - 1));
291}
292
293static 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 */
431static 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
463static int profile_maps_release(struct inode *inode, struct file *filp)
464{
465    return 0;
466}
467
468static const struct file_operations profile_maps_fops = {
469    .open = profile_maps_open,
470    .read = profile_maps_read,
471    .release = profile_maps_release,
472};
473
474static 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
484static int profile_rate_open(struct inode *inode, struct file *filp)
485{
486    return single_open(filp, profile_rate_show, NULL);
487}
488
489static 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
495static 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
503int 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
546module_init(ubi32_profile_init_module);
547
548MODULE_AUTHOR("David Fotland");
549MODULE_LICENSE("GPL");
550

Archive Download this file



interactive