Root/target/linux/ubicom32/files/arch/ubicom32/oprofile/profile.c

1/*
2 * arch/ubicom32/oprofile/profile.c
3 * Oprofile support for arch Ubicom32
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)
13 * any later version.
14 *
15 * The Ubicom32 Linux Kernel Port is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with the Ubicom32 Linux Kernel Port. If not, see
22 * <http://www.gnu.org/licenses/>.
23 *
24 * Ubicom32 implementation derived from (with many thanks):
25 * arch/m68knommu
26 * arch/blackfin
27 * arch/parisc
28 */
29
30/**
31 * @file profile.c
32 *
33 * @remark Copyright 2002 OProfile authors
34 * @remark Read the file COPYING
35 *
36 * @author Hunyue Yau <hy@hy-research.com>
37 */
38
39#include <linux/oprofile.h>
40#include <linux/init.h>
41#include <linux/errno.h>
42#include <linux/interrupt.h>
43#include <linux/module.h>
44#include <linux/kernel.h>
45
46#include <asm/devtree.h>
47#include <asm/thread.h>
48
49/* For identifying userland vs kernel address */
50#include <asm/stacktrace.h>
51#include "ipProf.h"
52
53/* For communications with the backend */
54static struct profilenode *profile_node;
55
56/* Bitmask containing all Linux threads - as seen by the ROSR reg */
57static unsigned long th_all_mask;
58
59/* Lookup table to translate a hardware thread into a CPU identifier
60 * Table is indexed by the ROSR value which is assumed to be
61 * relatively small (0...15).
62 */
63unsigned int cpu_map[THREAD_ARCHITECTURAL_MAX];
64
65static struct pt_regs regs;
66
67/*
68 * For each sample returned, checked to see if they are relevant to
69 * us. This is necessary as the ubicom32 architecture has other software
70 * running outside of Linux. Only then, put the sample into the relevant
71 * cpu bins.
72 *
73 * To minimize overhead, a global mask with all possible threads of in
74 * interest to us is used as a first check. Then a second mask identifying
75 * the thread is used to obtain an identifier for that "CPU".
76 */
77
78/*
79 * ubicom32_build_cpu_th_mask()
80 *
81 * Build a lookup table for translation between hardware thread
82 * "ROSR" values and Linux CPU ids
83 *
84 * *** This gets executed on all CPUs at once! ***
85 */
86static void ubicom32_build_cpu_th_mask(void *mask)
87{
88    thread_t self = thread_get_self();
89    unsigned long *th_m = mask;
90
91    BUG_ON(self <= 0 || self >= THREAD_ARCHITECTURAL_MAX);
92    cpu_map[self] = smp_processor_id();
93
94    set_bit(self, th_m);
95}
96
97/*
98 * profile_interrupt()
99 *
100 * Process samples returned from the profiler backend. The backend
101 * may return samples that are irrelevant to us or may even return
102 * multiple samples for the same CPU. Note that the sames may be
103 * for ANY cpu. At this time, this is unique and to support this requires
104 * Oprofile to expose an interface to accept the CPU that the same came
105 * frome.
106 */
107static irqreturn_t profile_interrupt(int irq, void *arg)
108{
109    int i, buf_entry;
110    int is_kernel;
111    unsigned int bit_th;
112    unsigned int th;
113
114    if (!(profile_node->enabled) || profile_node->count < 0) {
115        printk(KERN_WARNING
116            "Unexpected interrupt, no samples or not enabled!\n");
117        return IRQ_HANDLED;
118    }
119
120    profile_node->busy = 1; /* Keep backend out */
121
122    for (i = 0; i < profile_node->count; i++) {
123        buf_entry = profile_node->tail;
124        profile_node->tail++;
125        profile_node->tail %= IPPROFILETIO_MAX_SAMPLES;
126
127        /* Note - the "thread" ID is only the lower 4 bits */
128        th = (0x0f & profile_node->samples[buf_entry].thread);
129        bit_th = (1 << th);
130
131        if ((bit_th & th_all_mask) == 0)
132            continue;
133
134        regs.pc = profile_node->samples[buf_entry].pc;
135
136        is_kernel = ubicom32_is_kernel(regs.pc);
137
138        oprofile_add_ext_sample_cpu(regs.pc, &regs, 0, is_kernel,
139                        cpu_map[th]);
140    }
141    profile_node->count = 0;
142    profile_node->busy = 0;
143
144    return IRQ_HANDLED;
145}
146
147/*
148 * profile_start()
149 *
150 * Notification from oprofile to start the profiler
151 */
152static int profile_start(void)
153{
154    if (!profile_node)
155        return -1;
156
157    profile_node->enabled = 1;
158
159    return 0;
160}
161
162/*
163 * profile_stop()
164 *
165 * Notification from oprofile to stop the profiler
166 */
167static void profile_stop(void)
168{
169    if (profile_node)
170        profile_node->enabled = 0;
171}
172
173/*
174 * oprofile_arch_init()
175 *
176 * Attach to Oprofile after qualify the availability of the backend
177 * profiler support.
178 */
179int __init oprofile_arch_init(struct oprofile_operations *ops)
180{
181    int r = -ENODEV;
182
183    profile_node = (struct profilenode *)devtree_find_node("profiler");
184
185    if (profile_node == NULL) {
186        printk(KERN_WARNING "Cannot find profiler node\n");
187        return r;
188    }
189
190    r = request_irq(profile_node->dn.recvirq, profile_interrupt,
191            IRQF_DISABLED, "profiler", NULL);
192
193    if (r < 0) {
194        profile_node = NULL;
195        printk(KERN_WARNING "Cannot get profiler IRQ\n");
196        return r;
197    }
198
199    ops->start = profile_start;
200    ops->stop = profile_stop;
201    ops->cpu_type = "timer";
202
203    memset(cpu_map, 0, sizeof(cpu_map));
204
205    on_each_cpu(ubicom32_build_cpu_th_mask, &th_all_mask, 1);
206
207    memset(&regs, 0, sizeof(regs));
208
209    return r;
210}
211
212/*
213 * oprofile_arch_exit()
214 *
215 * External call to take outselves out.
216 * Make sure backend is not running.
217 */
218void oprofile_arch_exit(void)
219{
220    BUG_ON(profile_node->enabled);
221}
222

Archive Download this file



interactive