summaryrefslogtreecommitdiffstats
path: root/driver
diff options
context:
space:
mode:
Diffstat (limited to 'driver')
-rw-r--r--driver/LICENSE339
-rw-r--r--driver/Makefile45
-rw-r--r--driver/README_Streamline.txt81
-rw-r--r--driver/gator.h79
-rw-r--r--driver/gator_annotate.c166
-rw-r--r--driver/gator_backtrace.c68
-rw-r--r--driver/gator_cookies.c384
-rw-r--r--driver/gator_events.c40
-rw-r--r--driver/gator_events.h19
-rw-r--r--driver/gator_events.sh19
-rw-r--r--driver/gator_events_armv6.c224
-rw-r--r--driver/gator_events_armv7.c322
-rw-r--r--driver/gator_events_block.c171
-rw-r--r--driver/gator_events_irq.c171
-rw-r--r--driver/gator_events_meminfo.c195
-rw-r--r--driver/gator_events_mmaped.c196
-rw-r--r--driver/gator_events_net.c177
-rw-r--r--driver/gator_events_pl310.c176
-rw-r--r--driver/gator_events_sched.c114
-rw-r--r--driver/gator_events_scorpion.c661
-rw-r--r--driver/gator_fs.c284
-rw-r--r--driver/gator_main.c1057
-rw-r--r--driver/gator_trace.h26
-rw-r--r--driver/gator_trace_sched.c263
24 files changed, 5277 insertions, 0 deletions
diff --git a/driver/LICENSE b/driver/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/driver/LICENSE
@@ -0,0 +1,339 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12freedom to share and change it. By contrast, the GNU General Public
13License is intended to guarantee your freedom to share and change free
14software--to make sure the software is free for all its users. This
15General Public License applies to most of the Free Software
16Foundation's software and to any other program whose authors commit to
17using it. (Some other Free Software Foundation software is covered by
18the GNU Lesser General Public License instead.) You can apply it to
19your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22price. Our General Public Licenses are designed to make sure that you
23have the freedom to distribute copies of free software (and charge for
24this service if you wish), that you receive source code or can get it
25if you want it, that you can change the software or use pieces of it
26in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29anyone to deny you these rights or to ask you to surrender the rights.
30These restrictions translate to certain responsibilities for you if you
31distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34gratis or for a fee, you must give the recipients all the rights that
35you have. You must make sure that they, too, receive or can get the
36source code. And you must show them these terms so they know their
37rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40(2) offer you this license which gives you legal permission to copy,
41distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44that everyone understands that there is no warranty for this free
45software. If the software is modified by someone else and passed on, we
46want its recipients to know that what they have is not the original, so
47that any problems introduced by others will not reflect on the original
48authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51patents. We wish to avoid the danger that redistributors of a free
52program will individually obtain patent licenses, in effect making the
53program proprietary. To prevent this, we have made it clear that any
54patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63a notice placed by the copyright holder saying it may be distributed
64under the terms of this General Public License. The "Program", below,
65refers to any such program or work, and a "work based on the Program"
66means either the Program or any derivative work under copyright law:
67that is to say, a work containing the Program or a portion of it,
68either verbatim or with modifications and/or translated into another
69language. (Hereinafter, translation is included without limitation in
70the term "modification".) Each licensee is addressed as "you".
71
72Activities other than copying, distribution and modification are not
73covered by this License; they are outside its scope. The act of
74running the Program is not restricted, and the output from the Program
75is covered only if its contents constitute a work based on the
76Program (independent of having been made by running the Program).
77Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80source code as you receive it, in any medium, provided that you
81conspicuously and appropriately publish on each copy an appropriate
82copyright notice and disclaimer of warranty; keep intact all the
83notices that refer to this License and to the absence of any warranty;
84and give any other recipients of the Program a copy of this License
85along with the Program.
86
87You may charge a fee for the physical act of transferring a copy, and
88you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91of it, thus forming a work based on the Program, and copy and
92distribute such modifications or work under the terms of Section 1
93above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114These requirements apply to the modified work as a whole. If
115identifiable sections of that work are not derived from the Program,
116and can be reasonably considered independent and separate works in
117themselves, then this License, and its terms, do not apply to those
118sections when you distribute them as separate works. But when you
119distribute the same sections as part of a whole which is a work based
120on the Program, the distribution of the whole must be on the terms of
121this License, whose permissions for other licensees extend to the
122entire whole, and thus to each and every part regardless of who wrote it.
123
124Thus, it is not the intent of this section to claim rights or contest
125your rights to work written entirely by you; rather, the intent is to
126exercise the right to control the distribution of derivative or
127collective works based on the Program.
128
129In addition, mere aggregation of another work not based on the Program
130with the Program (or with a work based on the Program) on a volume of
131a storage or distribution medium does not bring the other work under
132the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135under Section 2) in object code or executable form under the terms of
136Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155The source code for a work means the preferred form of the work for
156making modifications to it. For an executable work, complete source
157code means all the source code for all modules it contains, plus any
158associated interface definition files, plus the scripts used to
159control compilation and installation of the executable. However, as a
160special exception, the source code distributed need not include
161anything that is normally distributed (in either source or binary
162form) with the major components (compiler, kernel, and so on) of the
163operating system on which the executable runs, unless that component
164itself accompanies the executable.
165
166If distribution of executable or object code is made by offering
167access to copy from a designated place, then offering equivalent
168access to copy the source code from the same place counts as
169distribution of the source code, even though third parties are not
170compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173except as expressly provided under this License. Any attempt
174otherwise to copy, modify, sublicense or distribute the Program is
175void, and will automatically terminate your rights under this License.
176However, parties who have received copies, or rights, from you under
177this License will not have their licenses terminated so long as such
178parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181signed it. However, nothing else grants you permission to modify or
182distribute the Program or its derivative works. These actions are
183prohibited by law if you do not accept this License. Therefore, by
184modifying or distributing the Program (or any work based on the
185Program), you indicate your acceptance of this License to do so, and
186all its terms and conditions for copying, distributing or modifying
187the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190Program), the recipient automatically receives a license from the
191original licensor to copy, distribute or modify the Program subject to
192these terms and conditions. You may not impose any further
193restrictions on the recipients' exercise of the rights granted herein.
194You are not responsible for enforcing compliance by third parties to
195this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198infringement or for any other reason (not limited to patent issues),
199conditions are imposed on you (whether by court order, agreement or
200otherwise) that contradict the conditions of this License, they do not
201excuse you from the conditions of this License. If you cannot
202distribute so as to satisfy simultaneously your obligations under this
203License and any other pertinent obligations, then as a consequence you
204may not distribute the Program at all. For example, if a patent
205license would not permit royalty-free redistribution of the Program by
206all those who receive copies directly or indirectly through you, then
207the only way you could satisfy both it and this License would be to
208refrain entirely from distribution of the Program.
209
210If any portion of this section is held invalid or unenforceable under
211any particular circumstance, the balance of the section is intended to
212apply and the section as a whole is intended to apply in other
213circumstances.
214
215It is not the purpose of this section to induce you to infringe any
216patents or other property right claims or to contest validity of any
217such claims; this section has the sole purpose of protecting the
218integrity of the free software distribution system, which is
219implemented by public license practices. Many people have made
220generous contributions to the wide range of software distributed
221through that system in reliance on consistent application of that
222system; it is up to the author/donor to decide if he or she is willing
223to distribute software through any other system and a licensee cannot
224impose that choice.
225
226This section is intended to make thoroughly clear what is believed to
227be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230certain countries either by patents or by copyrighted interfaces, the
231original copyright holder who places the Program under this License
232may add an explicit geographical distribution limitation excluding
233those countries, so that distribution is permitted only in or among
234countries not thus excluded. In such case, this License incorporates
235the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238of the General Public License from time to time. Such new versions will
239be similar in spirit to the present version, but may differ in detail to
240address new problems or concerns.
241
242Each version is given a distinguishing version number. If the Program
243specifies a version number of this License which applies to it and "any
244later version", you have the option of following the terms and conditions
245either of that version or of any later version published by the Free
246Software Foundation. If the Program does not specify a version number of
247this License, you may choose any version ever published by the Free Software
248Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251programs whose distribution conditions are different, write to the author
252to ask for permission. For software which is copyrighted by the Free
253Software Foundation, write to the Free Software Foundation; we sometimes
254make exceptions for this. Our decision will be guided by the two goals
255of preserving the free status of all derivatives of our free software and
256of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285possible use to the public, the best way to achieve this is to make it
286free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289to attach them to the start of each source file to most effectively
290convey the exclusion of warranty; and each file should have at least
291the "copyright" line and a pointer to where the full notice is found.
292
293 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) <year> <name of author>
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License along
307 with this program; if not, write to the Free Software Foundation, Inc.,
308 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
310Also add information on how to contact you by electronic and paper mail.
311
312If the program is interactive, make it output a short notice like this
313when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) year name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320The hypothetical commands `show w' and `show c' should show the appropriate
321parts of the General Public License. Of course, the commands you use may
322be called something other than `show w' and `show c'; they could even be
323mouse-clicks or menu items--whatever suits your program.
324
325You should also get your employer (if you work as a programmer) or your
326school, if any, to sign a "copyright disclaimer" for the program, if
327necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 <signature of Ty Coon>, 1 April 1989
333 Ty Coon, President of Vice
334
335This General Public License does not permit incorporating your program into
336proprietary programs. If your program is a subroutine library, you may
337consider it more useful to permit linking proprietary applications with the
338library. If this is what you want to do, use the GNU Lesser General
339Public License instead of this License.
diff --git a/driver/Makefile b/driver/Makefile
new file mode 100644
index 0000000..b3680e1
--- /dev/null
+++ b/driver/Makefile
@@ -0,0 +1,45 @@
1ifneq ($(KERNELRELEASE),)
2
3obj-m := gator.o
4
5gator-y := gator_main.o \
6 gator_events_irq.o \
7 gator_events_sched.o \
8 gator_events_net.o \
9 gator_events_block.o \
10 gator_events_meminfo.o
11
12gator-y += gator_events_mmaped.o
13
14ifneq ($(GATOR_WITH_MALI_SUPPORT),)
15gator-y += gator_events_mali.o
16endif
17
18gator-$(CONFIG_ARM) += gator_events_armv6.o \
19 gator_events_armv7.o \
20 gator_events_pl310.o
21
22$(obj)/gator_main.o: gator_events.h
23
24clean-files := gator_events.h
25
26 chk_events.h = :
27 quiet_chk_events.h = echo ' CHK $@'
28silent_chk_events.h = :
29gator_events.h: FORCE
30 @$($(quiet)chk_events.h)
31 $(Q)cd $(obj) ; $(CONFIG_SHELL) $(obj)/gator_events.sh $@
32
33else
34
35all:
36 @echo
37 @echo "usage:"
38 @echo " make -C <kernel_build_dir> M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules"
39 @echo
40 $(error)
41
42clean:
43 rm -f *.o modules.order Module.symvers gator.ko gator.mod.c
44
45endif
diff --git a/driver/README_Streamline.txt b/driver/README_Streamline.txt
new file mode 100644
index 0000000..4688474
--- /dev/null
+++ b/driver/README_Streamline.txt
@@ -0,0 +1,81 @@
1
2*** Purpose ***
3
4Instructions on setting up ARM Streamline on the target.
5The gator driver and gator daemon are required to run on the ARM linux target in order for ARM Streamline to operate.
6The driver should be built as a module and the daemon must run with root permissions on the target.
7
8*** Preparing and building the kernel ***
9
10cd into the root source dir of the linux kernel
11make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- <platform_defconfig> (choose the appropriate configuration for your board)
12make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig
13
14Required Kernel Changes (depending on the kernel version, the location of these configuration settings within menuconfig may be different)
15- General Setup
16 - [*] Profiling Support
17- Kernel hacking
18 - [*] Tracers
19 - [*] Trace process context switches and events
20- Kernel Features
21 - [*] High Resolution Timer Support
22
23The "context switches and events" option will not be available if other trace configurations are enabled. Other trace configurations being enabled is sufficient to turn on context switches and events.
24
25Optional Kernel Changes (depending on the kernel version, the location of these configuration settings within menuconfig may be different)
26Note: Configurations may not be supported on all targets
27- System Type
28 - [*] <SoC name> debugging peripherals (enable core performance counters on supported SoCs) /* kernels before 2.6.35 */
29
30make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage
31
32*** Building the gator module ***
33
34To create the gator.ko module,
35 cd /ds-5-install-directory/arm/src
36 tar xzf gator-driver.tar.gz
37 cd gator-driver
38 make -C <kernel_build_dir> M=`pwd` ARCH=arm CROSS_COMPILE=<...> modules
39for example
40 make -C /home/username/kernel_2.6.32/ M=`pwd` ARCH=arm CROSS_COMPILE=/home/username/CodeSourcery/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi- modules
41If successful, a gator.ko module should be generated
42
43*** Compiling an application or shared library ***
44
45Recommended compiler settings:
46 "-g": Debug symbols needed for best analysis results.
47 "-fno-inline": Speed improvement when processing the image files and most accurate analysis results.
48 "-fno-omit-frame-pointer": ARM EABI frame pointers (Code Sourcery cross compiler) allow the call stack to be recorded with each sample taken when in ARM state (i.e. not -mthumb).
49
50*** Running gator ***
51
52Load the kernel onto the target and copy gatord and gator.ko into the target's filesystem.
53gatord is located in <installdir>/arm/armv5t/.
54Ensure gatord has execute permissions
55 chmod +x gatord
56gator.ko must be located in the same directory as gatord on the target.
57With root privileges, run the daemon
58 sudo ./gatord &
59
60*** Profiling the kernel (optional) ***
61
62make ARCH=arm CROSS_COMPILE=$(CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig
63- Kernel Hacking
64 - [*] Compile the kernel with debug info
65
66make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage
67Use vmlinux as the image for debug symbols in Streamline.
68Drivers may be profiled using this method by statically linking the driver into the kernel image.
69Note that the gator driver does not perform kernel call stack recording.
70
71*** Automatically start gator on boot (optional) ***
72
73cd /etc/init.d
74vi rungator.sh
75 #!/bin/bash
76 /path/to/gatord &
77update-rc.d rungator.sh defaults
78
79*** GPL License ***
80
81For license information, please see the file LICENSE.
diff --git a/driver/gator.h b/driver/gator.h
new file mode 100644
index 0000000..9d802a3
--- /dev/null
+++ b/driver/gator.h
@@ -0,0 +1,79 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#ifndef GATOR_H_
10#define GATOR_H_
11
12#include <linux/version.h>
13#include <linux/fs.h>
14#include <linux/mm.h>
15#include <linux/list.h>
16
17/******************************************************************************
18 * Filesystem
19 ******************************************************************************/
20int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
21 char const *name, const struct file_operations *fops, int perm);
22
23struct dentry *gatorfs_mkdir(struct super_block *sb,
24 struct dentry *root, char const *name);
25
26int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
27 char const *name, unsigned long *val);
28
29int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
30 char const *name, unsigned long *val);
31
32void gator_op_create_files(struct super_block *sb, struct dentry *root);
33
34/******************************************************************************
35 * Tracepoints
36 ******************************************************************************/
37#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
38# error Kernels prior to 2.6.32 not supported
39#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
40# define GATOR_DEFINE_PROBE(probe_name, proto) \
41 static void probe_##probe_name(PARAMS(proto))
42# define GATOR_REGISTER_TRACE(probe_name) \
43 register_trace_##probe_name(probe_##probe_name)
44# define GATOR_UNREGISTER_TRACE(probe_name) \
45 unregister_trace_##probe_name(probe_##probe_name)
46#else
47# define GATOR_DEFINE_PROBE(probe_name, proto) \
48 static void probe_##probe_name(void *data, PARAMS(proto))
49# define GATOR_REGISTER_TRACE(probe_name) \
50 register_trace_##probe_name(probe_##probe_name, NULL)
51# define GATOR_UNREGISTER_TRACE(probe_name) \
52 unregister_trace_##probe_name(probe_##probe_name, NULL)
53#endif
54
55/******************************************************************************
56 * Events
57 ******************************************************************************/
58struct gator_interface {
59 int (*create_files)(struct super_block *sb, struct dentry *root);
60 int (*start)(void);
61 void (*stop)(void);
62 void (*online)(void);
63 void (*offline)(void);
64 int (*read)(int **buffer);
65 struct list_head list;
66};
67
68#define gator_events_init(initfn) \
69 static inline int __gator_events_init_test(void) \
70 { return initfn(); }
71
72int gator_events_install(struct gator_interface *interface);
73int gator_events_get_key(void);
74extern u32 gator_cpuid(void);
75
76extern unsigned long gator_net_traffic;
77
78
79#endif // GATOR_H_
diff --git a/driver/gator_annotate.c b/driver/gator_annotate.c
new file mode 100644
index 0000000..d8d86af
--- /dev/null
+++ b/driver/gator_annotate.c
@@ -0,0 +1,166 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/slab.h>
11#include <linux/fs.h>
12#include <linux/mm.h>
13#include <linux/sched.h>
14#include <asm/uaccess.h>
15#include <asm/current.h>
16#include <linux/spinlock.h>
17
18#define ANNOTATE_SIZE (16*1024)
19static DEFINE_SPINLOCK(annotate_lock);
20static char *annotateBuf;
21static char *annotateBuf0;
22static char *annotateBuf1;
23static int annotatePos;
24static int annotateSel;
25
26static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
27{
28 char tempBuffer[512];
29 int retval, remaining, size;
30
31 if (*offset)
32 return -EINVAL;
33
34 // determine size to capture
35 remaining = ANNOTATE_SIZE - annotatePos - 256; // pad for headers and release
36 size = count < sizeof(tempBuffer) ? count : sizeof(tempBuffer);
37 size = size < remaining ? size : remaining;
38 if (size <= 0) {
39 wake_up(&gator_buffer_wait);
40 return 0;
41 }
42
43 // copy from user space
44 retval = copy_from_user(tempBuffer, buf, size);
45 if (retval == 0) {
46 // synchronize shared variables annotateBuf and annotatePos
47 spin_lock(&annotate_lock);
48 if (annotateBuf) {
49 uint32_t tid = current->pid;
50 uint32_t tick = gator_master_tick;
51 uint64_t time = gator_get_time();
52 uint32_t cpuid = smp_processor_id();
53 int pos = annotatePos;
54 pos += gator_write_packed_int(&annotateBuf[pos], tid);
55 pos += gator_write_packed_int(&annotateBuf[pos], tick);
56 pos += gator_write_packed_int(&annotateBuf[pos], time);
57 pos += gator_write_packed_int(&annotateBuf[pos], time >> 32);
58 pos += gator_write_packed_int(&annotateBuf[pos], cpuid);
59 pos += gator_write_packed_int(&annotateBuf[pos], size);
60 memcpy(&annotateBuf[pos], tempBuffer, size);
61 annotatePos = pos + size;
62 }
63 spin_unlock(&annotate_lock);
64
65 // return the number of bytes written
66 retval = size;
67 } else {
68 retval = -EINVAL;
69 }
70
71 return retval;
72}
73
74static int annotate_release(struct inode *inode, struct file *file)
75{
76 int remaining = ANNOTATE_SIZE - annotatePos;
77 if (remaining < 16) {
78 return -EFAULT;
79 }
80
81 spin_lock(&annotate_lock);
82 if (annotateBuf) {
83 uint32_t tid = current->pid;
84 uint32_t tick = gator_master_tick;
85 int pos = annotatePos;
86 pos += gator_write_packed_int(&annotateBuf[pos], tid);
87 pos += gator_write_packed_int(&annotateBuf[pos], tick);
88 pos += gator_write_packed_int(&annotateBuf[pos], 0); // time
89 pos += gator_write_packed_int(&annotateBuf[pos], 0); // time
90 pos += gator_write_packed_int(&annotateBuf[pos], 0); // cpuid
91 pos += gator_write_packed_int(&annotateBuf[pos], 0); // size
92 annotatePos = pos;
93 }
94 spin_unlock(&annotate_lock);
95
96 return 0;
97}
98
99static const struct file_operations annotate_fops = {
100 .write = annotate_write,
101 .release = annotate_release
102};
103
104static int gator_annotate_create_files(struct super_block *sb, struct dentry *root)
105{
106 annotateBuf = NULL;
107 return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666);
108}
109
110static int gator_annotate_init(void)
111{
112 annotateBuf0 = kmalloc(ANNOTATE_SIZE, GFP_KERNEL);
113 annotateBuf1 = kmalloc(ANNOTATE_SIZE, GFP_KERNEL);
114 if (!annotateBuf0 || !annotateBuf1)
115 return -1;
116 return 0;
117}
118
119static int gator_annotate_start(void)
120{
121 annotatePos = annotateSel = 0;
122 annotateBuf = annotateBuf0;
123 return 0;
124}
125
126static void gator_annotate_stop(void)
127{
128 spin_lock(&annotate_lock);
129 annotateBuf = NULL;
130 spin_unlock(&annotate_lock);
131}
132
133static void gator_annotate_exit(void)
134{
135 spin_lock(&annotate_lock);
136 kfree(annotateBuf0);
137 kfree(annotateBuf1);
138 annotateBuf = annotateBuf0 = annotateBuf1 = NULL;
139 spin_unlock(&annotate_lock);
140}
141
142static int gator_annotate_ready(void)
143{
144 return annotatePos && annotateBuf;
145}
146
147static int gator_annotate_read(char **buffer)
148{
149 int len;
150
151 if (!gator_annotate_ready())
152 return 0;
153
154 annotateSel = !annotateSel;
155
156 if (buffer)
157 *buffer = annotateBuf;
158
159 spin_lock(&annotate_lock);
160 len = annotatePos;
161 annotatePos = 0;
162 annotateBuf = annotateSel ? annotateBuf1 : annotateBuf0;
163 spin_unlock(&annotate_lock);
164
165 return len;
166}
diff --git a/driver/gator_backtrace.c b/driver/gator_backtrace.c
new file mode 100644
index 0000000..628a18f
--- /dev/null
+++ b/driver/gator_backtrace.c
@@ -0,0 +1,68 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10/*
11 * EABI backtrace stores {fp,lr} on the stack.
12 */
13struct frame_tail_eabi {
14 unsigned long fp; // points to prev_lr
15 unsigned long lr;
16};
17
18static void arm_backtrace_eabi(int cpu, struct pt_regs * const regs, unsigned int depth)
19{
20#if defined(__arm__)
21 struct frame_tail_eabi *tail;
22 struct frame_tail_eabi *next;
23 struct frame_tail_eabi *ptrtail;
24 struct frame_tail_eabi buftail;
25 unsigned long fp = regs->ARM_fp;
26 unsigned long lr = regs->ARM_lr;
27 int is_user_mode = user_mode(regs);
28
29 if (!is_user_mode) {
30 return;
31 }
32
33 /* entry preamble may not have executed */
34 gator_add_trace(cpu, lr);
35
36 /* check tail is valid */
37 if (fp == 0) {
38 return;
39 }
40
41 tail = (struct frame_tail_eabi *)(fp - 4);
42
43 while (depth-- && tail && !((unsigned long) tail & 3)) {
44 /* Also check accessibility of one struct frame_tail beyond */
45 if (!access_ok(VERIFY_READ, tail, sizeof(struct frame_tail_eabi)))
46 return;
47 if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi)))
48 return;
49 ptrtail = &buftail;
50
51 lr = ptrtail[0].lr;
52 gator_add_trace(cpu, lr);
53
54 /* frame pointers should progress back up the stack, towards higher addresses */
55 next = (struct frame_tail_eabi *)(lr - 4);
56 if (tail >= next || lr == 0) {
57 fp = ptrtail[0].fp;
58 next = (struct frame_tail_eabi *)(fp - 4);
59 /* check tail is valid */
60 if (tail >= next || fp == 0) {
61 return;
62 }
63 }
64
65 tail = next;
66 }
67#endif
68}
diff --git a/driver/gator_cookies.c b/driver/gator_cookies.c
new file mode 100644
index 0000000..64be841
--- /dev/null
+++ b/driver/gator_cookies.c
@@ -0,0 +1,384 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */
11#define TRANSLATE_SIZE 256
12#define MAX_COLLISIONS 2
13
14static uint32_t *gator_crc32_table;
15static uint32_t translate_buffer_mask;
16
17static DEFINE_PER_CPU(char *, translate_text);
18static DEFINE_PER_CPU(uint32_t, cookie_next_key);
19static DEFINE_PER_CPU(uint64_t *, cookie_keys);
20static DEFINE_PER_CPU(uint32_t *, cookie_values);
21static DEFINE_PER_CPU(int, translate_buffer_read);
22static DEFINE_PER_CPU(int, translate_buffer_write);
23static DEFINE_PER_CPU(unsigned int *, translate_buffer);
24
25static inline uint32_t get_cookie(int cpu, struct task_struct *task, struct vm_area_struct *vma, struct module *mod);
26static void wq_cookie_handler(struct work_struct *unused);
27DECLARE_WORK(cookie_work, wq_cookie_handler);
28
29static uint32_t cookiemap_code(uint64_t value64) {
30 uint32_t value = (uint32_t)((value64 >> 32) + value64);
31 uint32_t cookiecode = (value >> 24) & 0xff;
32 cookiecode = cookiecode * 31 + ((value >> 16) & 0xff);
33 cookiecode = cookiecode * 31 + ((value >> 8) & 0xff);
34 cookiecode = cookiecode * 31 + ((value >> 0) & 0xff);
35 cookiecode &= (COOKIEMAP_ENTRIES-1);
36 return cookiecode * MAX_COLLISIONS;
37}
38
39static uint32_t gator_chksum_crc32(char *data)
40{
41 register unsigned long crc;
42 unsigned char *block = data;
43 int i, length = strlen(data);
44
45 crc = 0xFFFFFFFF;
46 for (i = 0; i < length; i++) {
47 crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF];
48 }
49
50 return (crc ^ 0xFFFFFFFF);
51}
52
53/*
54 * Exists
55 * Pre: [0][1][v][3]..[n-1]
56 * Post: [v][0][1][3]..[n-1]
57 */
58static uint32_t cookiemap_exists(uint64_t key) {
59 unsigned long x, flags, retval = 0;
60 int cpu = smp_processor_id();
61 uint32_t cookiecode = cookiemap_code(key);
62 uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
63 uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
64
65 // Can be called from interrupt handler or from work queue
66 local_irq_save(flags);
67 for (x = 0; x < MAX_COLLISIONS; x++) {
68 if (keys[x] == key) {
69 uint32_t value = values[x];
70 for (; x > 0; x--) {
71 keys[x] = keys[x-1];
72 values[x] = values[x-1];
73 }
74 keys[0] = key;
75 values[0] = value;
76 retval = value;
77 break;
78 }
79 }
80 local_irq_restore(flags);
81
82 return retval;
83}
84
85/*
86 * Add
87 * Pre: [0][1][2][3]..[n-1]
88 * Post: [v][0][1][2]..[n-2]
89 */
90static void cookiemap_add(uint64_t key, uint32_t value) {
91 int cpu = smp_processor_id();
92 int cookiecode = cookiemap_code(key);
93 uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
94 uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
95 int x;
96
97 for (x = MAX_COLLISIONS-1; x > 0; x--) {
98 keys[x] = keys[x-1];
99 values[x] = keys[x-1];
100 }
101 keys[0] = key;
102 values[0] = value;
103}
104
105static void translate_buffer_write_int(int cpu, unsigned int x)
106{
107 per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x;
108 per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask;
109}
110
111static unsigned int translate_buffer_read_int(int cpu)
112{
113 unsigned int value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++];
114 per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask;
115 return value;
116}
117
118static void wq_cookie_handler(struct work_struct *unused)
119{
120 struct task_struct *task;
121 struct vm_area_struct *vma;
122 int cpu = smp_processor_id();
123 unsigned int cookie, commit;
124
125 commit = per_cpu(translate_buffer_write, cpu);
126 while (per_cpu(translate_buffer_read, cpu) != commit) {
127 task = (struct task_struct *)translate_buffer_read_int(cpu);
128 vma = (struct vm_area_struct *)translate_buffer_read_int(cpu);
129 cookie = get_cookie(cpu, task, vma, NULL);
130 }
131}
132
133// Retrieve full name from proc/pid/cmdline for java processes on Android
134static int translate_app_process(char** text, int cpu, struct task_struct * task, struct vm_area_struct *vma)
135{
136 void *maddr;
137 unsigned int len;
138 unsigned long addr;
139 struct mm_struct *mm;
140 struct page *page = NULL;
141 struct vm_area_struct *page_vma;
142 int bytes, offset, retval = 0, ptr;
143 char * buf = per_cpu(translate_text, cpu);
144
145 // Push work into a work queue if in atomic context as the kernel functions below might sleep
146 if (in_irq()) {
147 // Check if already in buffer
148 ptr = per_cpu(translate_buffer_read, cpu);
149 while (ptr != per_cpu(translate_buffer_write, cpu)) {
150 if (per_cpu(translate_buffer, cpu)[ptr] == (int)task)
151 goto out;
152 ptr = (ptr + 2) & translate_buffer_mask;
153 }
154
155 translate_buffer_write_int(cpu, (unsigned int)task);
156 translate_buffer_write_int(cpu, (unsigned int)vma);
157 schedule_work(&cookie_work);
158 goto out;
159 }
160
161 mm = get_task_mm(task);
162 if (!mm)
163 goto out;
164 if (!mm->arg_end)
165 goto outmm;
166 addr = mm->arg_start;
167 len = mm->arg_end - mm->arg_start;
168
169 if (len > TRANSLATE_SIZE)
170 len = TRANSLATE_SIZE;
171
172 down_read(&mm->mmap_sem);
173 while (len) {
174 if (get_user_pages(task, mm, addr, 1, 0, 1, &page, &page_vma) <= 0)
175 goto outsem;
176
177 maddr = kmap(page);
178 offset = addr & (PAGE_SIZE-1);
179 bytes = len;
180 if (bytes > PAGE_SIZE - offset)
181 bytes = PAGE_SIZE - offset;
182
183 copy_from_user_page(page_vma, page, addr, buf, maddr + offset, bytes);
184
185 kunmap(page); // release page allocated by get_user_pages()
186 page_cache_release(page);
187
188 len -= bytes;
189 buf += bytes;
190 addr += bytes;
191
192 *text = per_cpu(translate_text, cpu);
193 retval = 1;
194 }
195
196 // On app_process startup, /proc/pid/cmdline is initially "zygote" then "<pre-initialized>" but changes after an initial startup period
197 if (strcmp(*text, "zygote") == 0 || strcmp(*text, "<pre-initialized>") == 0)
198 retval = 0;
199
200outsem:
201 up_read(&mm->mmap_sem);
202outmm:
203 mmput(mm);
204out:
205 return retval;
206}
207
208static inline uint32_t get_cookie(int cpu, struct task_struct *task, struct vm_area_struct *vma, struct module *mod)
209{
210 unsigned long flags, cookie;
211 struct path *path;
212 uint64_t key;
213 char *text;
214
215 if (mod) {
216 text = mod->name;
217 } else {
218 if (!vma || !vma->vm_file) {
219 return INVALID_COOKIE;
220 }
221 path = &vma->vm_file->f_path;
222 if (!path || !path->dentry) {
223 return INVALID_COOKIE;
224 }
225
226 text = (char*)path->dentry->d_name.name;
227 }
228
229 key = gator_chksum_crc32(text);
230 key = (key << 32) | (uint32_t)task->tgid;
231
232 cookie = cookiemap_exists(key);
233 if (cookie) {
234 return cookie;
235 }
236
237 if (strcmp(text, "app_process") == 0 && !mod) {
238 if (!translate_app_process(&text, cpu, task, vma))
239 return INVALID_COOKIE;
240 }
241
242 // Can be called from interrupt handler or from work queue
243 local_irq_save(flags);
244
245 cookie = per_cpu(cookie_next_key, cpu)+=nr_cpu_ids;
246 cookiemap_add(key, cookie);
247
248 gator_buffer_write_packed_int(cpu, PROTOCOL_COOKIE);
249 gator_buffer_write_packed_int(cpu, cookie);
250 gator_buffer_write_string(cpu, text);
251
252 local_irq_restore(flags);
253
254 return cookie;
255}
256
257static int get_exec_cookie(int cpu, struct task_struct *task)
258{
259 unsigned long cookie = NO_COOKIE;
260 struct mm_struct *mm = task->mm;
261 struct vm_area_struct *vma;
262
263 if (!mm)
264 return cookie;
265
266 for (vma = mm->mmap; vma; vma = vma->vm_next) {
267 if (!vma->vm_file)
268 continue;
269 if (!(vma->vm_flags & VM_EXECUTABLE))
270 continue;
271 cookie = get_cookie(cpu, task, vma, NULL);
272 break;
273 }
274
275 return cookie;
276}
277
278static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsigned long addr, off_t *offset)
279{
280 unsigned long cookie = NO_COOKIE;
281 struct mm_struct *mm = task->mm;
282 struct vm_area_struct *vma;
283
284 if (!mm)
285 return cookie;
286
287 for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
288 if (addr < vma->vm_start || addr >= vma->vm_end)
289 continue;
290
291 if (vma->vm_file) {
292 cookie = get_cookie(cpu, task, vma, NULL);
293 *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start;
294 } else {
295 /* must be an anonymous map */
296 *offset = addr;
297 }
298
299 break;
300 }
301
302 if (!vma)
303 cookie = INVALID_COOKIE;
304
305 return cookie;
306}
307
308static int cookies_initialize(void)
309{
310 uint32_t crc, poly;
311 int i, j, cpu, size, err = 0;
312
313 int translate_buffer_size = 512; // must be a power of 2
314 translate_buffer_mask = translate_buffer_size / sizeof(per_cpu(translate_buffer, 0)[0]) - 1;
315
316 for_each_present_cpu(cpu) {
317 per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu;
318
319 size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t);
320 per_cpu(cookie_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL);
321 memset(per_cpu(cookie_keys, cpu), 0, size);
322
323 size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t);
324 per_cpu(cookie_values, cpu) = (uint32_t*)kmalloc(size, GFP_KERNEL);
325 memset(per_cpu(cookie_values, cpu), 0, size);
326
327 per_cpu(translate_buffer, cpu) = (unsigned int *)kmalloc(translate_buffer_size, GFP_KERNEL);
328 if (!per_cpu(translate_buffer, cpu)) {
329 err = -ENOMEM;
330 goto cookie_setup_error;
331 }
332
333 per_cpu(translate_buffer_write, cpu) = 0;
334 per_cpu(translate_buffer_read, cpu) = 0;
335
336 per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_SIZE, GFP_KERNEL);
337 if (!per_cpu(translate_text, cpu)) {
338 err = -ENOMEM;
339 goto cookie_setup_error;
340 }
341 }
342
343 // build CRC32 table
344 poly = 0x04c11db7;
345 gator_crc32_table = (uint32_t*)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL);
346 for (i = 0; i < 256; i++) {
347 crc = i;
348 for (j = 8; j > 0; j--) {
349 if (crc & 1) {
350 crc = (crc >> 1) ^ poly;
351 } else {
352 crc >>= 1;
353 }
354 }
355 gator_crc32_table[i] = crc;
356 }
357
358cookie_setup_error:
359 return err;
360}
361
362static void cookies_release(void)
363{
364 int cpu;
365
366 for_each_present_cpu(cpu) {
367 kfree(per_cpu(cookie_keys, cpu));
368 per_cpu(cookie_keys, cpu) = NULL;
369
370 kfree(per_cpu(cookie_values, cpu));
371 per_cpu(cookie_values, cpu) = NULL;
372
373 kfree(per_cpu(translate_buffer, cpu));
374 per_cpu(translate_buffer, cpu) = NULL;
375 per_cpu(translate_buffer_read, cpu) = 0;
376 per_cpu(translate_buffer_write, cpu) = 0;
377
378 kfree(per_cpu(translate_text, cpu));
379 per_cpu(translate_text, cpu) = NULL;
380 }
381
382 kfree(gator_crc32_table);
383 gator_crc32_table = NULL;
384}
diff --git a/driver/gator_events.c b/driver/gator_events.c
new file mode 100644
index 0000000..d837257
--- /dev/null
+++ b/driver/gator_events.c
@@ -0,0 +1,40 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10/**
11 * This file is #included in gator_main.c
12 * Update this file and Makefile to add custom counters.
13 */
14
15extern int gator_events_armv6_install(gator_interface *gi);
16extern int gator_events_armv7_install(gator_interface *gi);
17extern int gator_events_irq_install(gator_interface *gi);
18extern int gator_events_sched_install(gator_interface *gi);
19extern int gator_events_block_install(gator_interface *gi);
20extern int gator_events_meminfo_install(gator_interface *gi);
21extern int gator_events_net_install(gator_interface *gi);
22
23static int gator_events_install(void)
24{
25 if (gator_event_install(gator_events_armv6_install))
26 return -1;
27 if (gator_event_install(gator_events_armv7_install))
28 return -1;
29 if (gator_event_install(gator_events_irq_install))
30 return -1;
31 if (gator_event_install(gator_events_sched_install))
32 return -1;
33 if (gator_event_install(gator_events_block_install))
34 return -1;
35 if (gator_event_install(gator_events_meminfo_install))
36 return -1;
37 if (gator_event_install(gator_events_net_install))
38 return -1;
39 return 0;
40}
diff --git a/driver/gator_events.h b/driver/gator_events.h
new file mode 100644
index 0000000..8237c1a
--- /dev/null
+++ b/driver/gator_events.h
@@ -0,0 +1,19 @@
1/**
2 * Copyright 2010 ARM, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9
10struct __gator_interface {
11 int (*create_files)(struct super_block *sb, struct dentry *root);
12 int (*init)(int *key);
13 int (*start)(void);
14 void (*stop)(void);
15 int (*read)(int **buffer);
16 struct __gator_interface *next;
17};
18
19typedef struct __gator_interface gator_interface;
diff --git a/driver/gator_events.sh b/driver/gator_events.sh
new file mode 100644
index 0000000..5467dd6
--- /dev/null
+++ b/driver/gator_events.sh
@@ -0,0 +1,19 @@
1#!/bin/sh
2
3EVENTS=`grep gator_events_init *.c | sed 's/.\+gator_events_init(\(.\+\)).\+/\1/'`
4
5(
6 echo /\* This file is auto generated \*/
7 echo
8 for EVENT in $EVENTS; do
9 echo __weak int $EVENT\(void\)\;
10 done
11 echo
12 echo static int \(*gator_events_list[]\)\(void\) = {
13 for EVENT in $EVENTS; do
14 echo \ $EVENT,
15 done
16 echo }\;
17) > $1.tmp
18
19cmp -s $1 $1.tmp && rm $1.tmp || mv $1.tmp $1
diff --git a/driver/gator_events_armv6.c b/driver/gator_events_armv6.c
new file mode 100644
index 0000000..7b1d875
--- /dev/null
+++ b/driver/gator_events_armv6.c
@@ -0,0 +1,224 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include "gator.h"
10
11#define ARM1136 0xb36
12#define ARM1156 0xb56
13#define ARM1176 0xb76
14
15static const char *pmnc_name;
16
17/*
18 * Per-CPU PMCR
19 */
20#define PMCR_E (1 << 0) /* Enable */
21#define PMCR_P (1 << 1) /* Count reset */
22#define PMCR_C (1 << 2) /* Cycle counter reset */
23#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */
24#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */
25#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */
26
27#define PMN0 0
28#define PMN1 1
29#define CCNT 2
30#define CNTMAX (CCNT+1)
31
32static int pmnc_count = 0;
33static unsigned long pmnc_enabled[CNTMAX];
34static unsigned long pmnc_event[CNTMAX];
35static unsigned long pmnc_key[CNTMAX];
36
37static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
38static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
39
40static inline void armv6_pmnc_write(u32 val)
41{
42 /* upper 4bits and 7, 11 are write-as-0 */
43 val &= 0x0ffff77f;
44 asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val));
45}
46
47static inline u32 armv6_pmnc_read(void)
48{
49 u32 val;
50 asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val));
51 return val;
52}
53
54static void armv6_pmnc_reset_counter(unsigned int cnt)
55{
56 u32 val = 0;
57 switch (cnt) {
58 case CCNT:
59 asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val));
60 break;
61 case PMN0:
62 asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val));
63 break;
64 case PMN1:
65 asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val));
66 break;
67 }
68}
69
70int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root)
71{
72 struct dentry *dir;
73 int i;
74
75 pmnc_count = 3;
76
77 for (i = PMN0; i <= CCNT; i++) {
78 char buf[40];
79 if (i == CCNT) {
80 snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
81 } else {
82 snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i);
83 }
84 dir = gatorfs_mkdir(sb, root, buf);
85 if (!dir) {
86 return -1;
87 }
88 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
89 if (i != CCNT) {
90 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
91 }
92 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
93 }
94
95 return 0;
96}
97
98static void gator_events_armv6_online(void)
99{
100 unsigned int cnt;
101 u32 pmnc;
102
103 if (armv6_pmnc_read() & PMCR_E) {
104 armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
105 }
106
107 /* initialize PMNC, reset overflow, D bit, C bit and P bit. */
108 armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
109 PMCR_C | PMCR_P);
110
111 /* configure control register */
112 for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
113 unsigned long event;
114
115 per_cpu(perfPrev, smp_processor_id())[cnt] = 0;
116
117 if (!pmnc_enabled[cnt])
118 continue;
119
120 event = pmnc_event[cnt] & 255;
121
122 /*
123 * Set event (if destined for PMNx counters)
124 */
125 if (cnt == PMN0) {
126 pmnc |= event << 20;
127 } else if (cnt == PMN1) {
128 pmnc |= event << 12;
129 }
130
131 /*
132 * Reset counter
133 */
134 armv6_pmnc_reset_counter(cnt);
135 }
136 armv6_pmnc_write(pmnc | PMCR_E);
137}
138
139static void gator_events_armv6_offline(void)
140{
141 unsigned int cnt;
142
143 armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
144 for (cnt = PMN0; cnt <= CCNT; cnt++) {
145 armv6_pmnc_reset_counter(cnt);
146 }
147}
148
149static void gator_events_armv6_stop(void)
150{
151 unsigned int cnt;
152
153 for (cnt = PMN0; cnt <= CCNT; cnt++) {
154 pmnc_enabled[cnt] = 0;
155 pmnc_event[cnt] = 0;
156 }
157}
158
159static int gator_events_armv6_read(int **buffer)
160{
161 int cnt, len = 0;
162 int cpu = smp_processor_id();
163
164 for (cnt = PMN0; cnt <= CCNT; cnt++) {
165 if (pmnc_enabled[cnt]) {
166 u32 value = 0;
167 switch (cnt) {
168 case CCNT:
169 asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value));
170 break;
171 case PMN0:
172 asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value));
173 break;
174 case PMN1:
175 asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value));
176 break;
177 }
178 armv6_pmnc_reset_counter(cnt);
179 if (value != per_cpu(perfPrev, cpu)[cnt]) {
180 per_cpu(perfPrev, cpu)[cnt] = value;
181 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
182 per_cpu(perfCnt, cpu)[len++] = value;
183 }
184 }
185 }
186
187 // update or discard
188 if (buffer)
189 *buffer = per_cpu(perfCnt, cpu);
190
191 return len;
192}
193
194static struct gator_interface gator_events_armv6_interface = {
195 .create_files = gator_events_armv6_create_files,
196 .stop = gator_events_armv6_stop,
197 .online = gator_events_armv6_online,
198 .offline = gator_events_armv6_offline,
199 .read = gator_events_armv6_read,
200};
201
202int gator_events_armv6_init(void)
203{
204 unsigned int cnt;
205
206 switch (gator_cpuid()) {
207 case ARM1136:
208 case ARM1156:
209 case ARM1176:
210 pmnc_name = "ARM11";
211 break;
212 default:
213 return -1;
214 }
215
216 for (cnt = PMN0; cnt <= CCNT; cnt++) {
217 pmnc_enabled[cnt] = 0;
218 pmnc_event[cnt] = 0;
219 pmnc_key[cnt] = gator_events_get_key();
220 }
221
222 return gator_events_install(&gator_events_armv6_interface);
223}
224gator_events_init(gator_events_armv6_init);
diff --git a/driver/gator_events_armv7.c b/driver/gator_events_armv7.c
new file mode 100644
index 0000000..0b84745
--- /dev/null
+++ b/driver/gator_events_armv7.c
@@ -0,0 +1,322 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include "gator.h"
10
11#define CORTEX_A5 0xc05
12#define CORTEX_A8 0xc08
13#define CORTEX_A9 0xc09
14#define CORTEX_A15 0xc0f
15
16static const char *pmnc_name;
17static int pmnc_count;
18
19// Per-CPU PMNC: config reg
20#define PMNC_E (1 << 0) /* Enable all counters */
21#define PMNC_P (1 << 1) /* Reset all counters */
22#define PMNC_C (1 << 2) /* Cycle counter reset */
23#define PMNC_MASK 0x3f /* Mask for writable bits */
24
25// ccnt reg
26#define CCNT_REG (1 << 31)
27
28#define CCNT 0
29#define CNT0 1
30#define CNTMAX (6+1)
31
32static unsigned long pmnc_enabled[CNTMAX];
33static unsigned long pmnc_event[CNTMAX];
34static unsigned long pmnc_key[CNTMAX];
35
36static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
37static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
38
39static inline void armv7_pmnc_write(u32 val)
40{
41 val &= PMNC_MASK;
42 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
43}
44
45static inline u32 armv7_pmnc_read(void)
46{
47 u32 val;
48 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
49 return val;
50}
51
52static inline u32 armv7_ccnt_read(void)
53{
54 u32 zero = 0;
55 u32 den = CCNT_REG;
56 u32 val;
57
58 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
59 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read
60 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (zero)); // zero
61 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
62
63 return val;
64}
65
66static inline u32 armv7_cntn_read(unsigned int cnt)
67{
68 u32 zero = 0;
69 u32 sel = (cnt - CNT0);
70 u32 den = 1 << sel;
71 u32 val;
72
73 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable
74 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select
75 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); // read
76 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (zero)); // zero
77 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable
78
79 return val;
80}
81
82static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
83{
84 u32 val;
85
86 if (cnt >= CNTMAX) {
87 pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
88 return -1;
89 }
90
91 if (cnt == CCNT)
92 val = CCNT_REG;
93 else
94 val = (1 << (cnt - CNT0));
95
96 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
97
98 return cnt;
99}
100
101static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
102{
103 u32 val;
104
105 if (cnt >= CNTMAX) {
106 pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
107 return -1;
108 }
109
110 if (cnt == CCNT)
111 val = CCNT_REG;
112 else
113 val = (1 << (cnt - CNT0));
114
115 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
116
117 return cnt;
118}
119
120static inline int armv7_pmnc_select_counter(unsigned int cnt)
121{
122 u32 val;
123
124 if ((cnt == CCNT) || (cnt >= CNTMAX)) {
125 pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt);
126 return -1;
127 }
128
129 val = (cnt - CNT0);
130 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
131
132 return cnt;
133}
134
135static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
136{
137 if (armv7_pmnc_select_counter(cnt) == cnt) {
138 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
139 }
140}
141
142static void armv7_pmnc_reset_counter(unsigned int cnt)
143{
144 u32 val = 0;
145
146 if (cnt == CCNT) {
147 armv7_pmnc_disable_counter(cnt);
148
149 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
150
151 if (pmnc_enabled[cnt] != 0)
152 armv7_pmnc_enable_counter(cnt);
153
154 } else if (cnt >= CNTMAX) {
155 pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt);
156 } else {
157 armv7_pmnc_disable_counter(cnt);
158
159 if (armv7_pmnc_select_counter(cnt) == cnt)
160 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
161
162 if (pmnc_enabled[cnt] != 0)
163 armv7_pmnc_enable_counter(cnt);
164 }
165}
166
167static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
168{
169 struct dentry *dir;
170 int i;
171
172 for (i = 0; i < pmnc_count; i++) {
173 char buf[40];
174 if (i == 0) {
175 snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
176 } else {
177 snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1);
178 }
179 dir = gatorfs_mkdir(sb, root, buf);
180 if (!dir) {
181 return -1;
182 }
183 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
184 if (i > 0) {
185 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
186 }
187 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
188 }
189
190 return 0;
191}
192
193static void gator_events_armv7_online(void)
194{
195 unsigned int cnt;
196 int cpu = smp_processor_id();
197
198 if (armv7_pmnc_read() & PMNC_E) {
199 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
200 }
201
202 // Initialize & Reset PMNC: C bit and P bit
203 armv7_pmnc_write(PMNC_P | PMNC_C);
204
205 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
206 unsigned long event;
207
208 per_cpu(perfPrev, cpu)[cnt] = 0;
209
210 if (!pmnc_enabled[cnt])
211 continue;
212
213 // Disable counter
214 armv7_pmnc_disable_counter(cnt);
215
216 event = pmnc_event[cnt] & 255;
217
218 // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count
219 if (cnt != CCNT)
220 armv7_pmnc_write_evtsel(cnt, event);
221
222 // Reset counter
223 armv7_pmnc_reset_counter(cnt);
224
225 // Enable counter, but do not enable interrupt for this counter
226 armv7_pmnc_enable_counter(cnt);
227 }
228
229 // enable
230 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
231}
232
233static void gator_events_armv7_offline(void)
234{
235 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
236}
237
238static void gator_events_armv7_stop(void)
239{
240 unsigned int cnt;
241
242 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
243 pmnc_enabled[cnt] = 0;
244 pmnc_event[cnt] = 0;
245 }
246}
247
248static int gator_events_armv7_read(int **buffer)
249{
250 int cnt, len = 0;
251 int cpu = smp_processor_id();
252
253 if (!pmnc_count)
254 return 0;
255
256 for (cnt = 0; cnt < pmnc_count; cnt++) {
257 if (pmnc_enabled[cnt]) {
258 int value;
259 if (cnt == CCNT) {
260 value = armv7_ccnt_read();
261 } else {
262 value = armv7_cntn_read(cnt);
263 }
264 if (value != per_cpu(perfPrev, cpu)[cnt]) {
265 per_cpu(perfPrev, cpu)[cnt] = value;
266 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
267 per_cpu(perfCnt, cpu)[len++] = value;
268 }
269 }
270 }
271
272 // update or discard
273 if (buffer)
274 *buffer = per_cpu(perfCnt, cpu);
275
276 return len;
277}
278
279static struct gator_interface gator_events_armv7_interface = {
280 .create_files = gator_events_armv7_create_files,
281 .stop = gator_events_armv7_stop,
282 .online = gator_events_armv7_online,
283 .offline = gator_events_armv7_offline,
284 .read = gator_events_armv7_read,
285};
286
287int gator_events_armv7_init(void)
288{
289 unsigned int cnt;
290
291 switch (gator_cpuid()) {
292 case CORTEX_A5:
293 pmnc_name = "Cortex-A5";
294 pmnc_count = 2;
295 break;
296 case CORTEX_A8:
297 pmnc_name = "Cortex-A8";
298 pmnc_count = 4;
299 break;
300 case CORTEX_A9:
301 pmnc_name = "Cortex-A9";
302 pmnc_count = 6;
303 break;
304 case CORTEX_A15:
305 pmnc_name = "Cortex-A15";
306 pmnc_count = 6;
307 break;
308 default:
309 return -1;
310 }
311
312 pmnc_count++; // CNT[n] + CCNT
313
314 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
315 pmnc_enabled[cnt] = 0;
316 pmnc_event[cnt] = 0;
317 pmnc_key[cnt] = gator_events_get_key();
318 }
319
320 return gator_events_install(&gator_events_armv7_interface);
321}
322gator_events_init(gator_events_armv7_init);
diff --git a/driver/gator_events_block.c b/driver/gator_events_block.c
new file mode 100644
index 0000000..cbfed86
--- /dev/null
+++ b/driver/gator_events_block.c
@@ -0,0 +1,171 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include "gator.h"
11#include <trace/events/block.h>
12
13#define BLOCK_RQ_WR 0
14#define BLOCK_RQ_RD 1
15
16#define BLOCK_TOTAL (BLOCK_RQ_RD+1)
17
18#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
19#define EVENTWRITE REQ_RW
20#else
21#define EVENTWRITE REQ_WRITE
22#endif
23
24static ulong block_rq_wr_enabled;
25static ulong block_rq_rd_enabled;
26static ulong block_rq_wr_key;
27static ulong block_rq_rd_key;
28static DEFINE_PER_CPU(int[BLOCK_TOTAL], blockCnt);
29static DEFINE_PER_CPU(int[BLOCK_TOTAL * 2], blockGet);
30static DEFINE_PER_CPU(bool, new_data_avail);
31
32GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq))
33{
34 unsigned long flags;
35 int write, size;
36 int cpu = smp_processor_id();
37
38 if (!rq)
39 return;
40
41 write = rq->cmd_flags & EVENTWRITE;
42 size = rq->resid_len;
43
44 if (!size)
45 return;
46
47 // disable interrupts to synchronize with gator_events_block_read()
48 // spinlocks not needed since percpu buffers are used
49 local_irq_save(flags);
50 if (write)
51 per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] += size;
52 else
53 per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] += size;
54 local_irq_restore(flags);
55
56 per_cpu(new_data_avail, cpu) = true;
57}
58
59static int gator_events_block_create_files(struct super_block *sb, struct dentry *root)
60{
61 struct dentry *dir;
62
63 /* block_complete_wr */
64 dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr");
65 if (!dir) {
66 return -1;
67 }
68 gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled);
69 gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key);
70
71 /* block_complete_rd */
72 dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd");
73 if (!dir) {
74 return -1;
75 }
76 gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled);
77 gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key);
78
79 return 0;
80}
81
82static int gator_events_block_start(void)
83{
84 int cpu;
85
86 for_each_present_cpu(cpu)
87 per_cpu(new_data_avail, cpu) = true;
88
89 // register tracepoints
90 if (block_rq_wr_enabled || block_rq_rd_enabled)
91 if (GATOR_REGISTER_TRACE(block_rq_complete))
92 goto fail_block_rq_exit;
93 pr_debug("gator: registered block event tracepoints\n");
94
95 return 0;
96
97 // unregister tracepoints on error
98fail_block_rq_exit:
99 pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
100
101 return -1;
102}
103
104static void gator_events_block_stop(void)
105{
106 if (block_rq_wr_enabled || block_rq_rd_enabled)
107 GATOR_UNREGISTER_TRACE(block_rq_complete);
108 pr_debug("gator: unregistered block event tracepoints\n");
109
110 block_rq_wr_enabled = 0;
111 block_rq_rd_enabled = 0;
112}
113
114static int gator_events_block_read(int **buffer)
115{
116 unsigned long flags;
117 int len, value, cpu, data = 0;
118 cpu = smp_processor_id();
119
120 if (per_cpu(new_data_avail, cpu) == false)
121 return 0;
122
123 per_cpu(new_data_avail, cpu) = false;
124
125 len = 0;
126 if (block_rq_wr_enabled) {
127 local_irq_save(flags);
128 value = per_cpu(blockCnt, cpu)[BLOCK_RQ_WR];
129 per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] = 0;
130 local_irq_restore(flags);
131 per_cpu(blockGet, cpu)[len++] = block_rq_wr_key;
132 per_cpu(blockGet, cpu)[len++] = value;
133 data += value;
134 }
135 if (block_rq_rd_enabled) {
136 local_irq_save(flags);
137 value = per_cpu(blockCnt, cpu)[BLOCK_RQ_RD];
138 per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] = 0;
139 local_irq_restore(flags);
140 per_cpu(blockGet, cpu)[len++] = block_rq_rd_key;
141 per_cpu(blockGet, cpu)[len++] = value;
142 data += value;
143 }
144
145 if (data != 0)
146 per_cpu(new_data_avail, cpu) = true;
147
148 if (buffer)
149 *buffer = per_cpu(blockGet, cpu);
150
151 return len;
152}
153
154static struct gator_interface gator_events_block_interface = {
155 .create_files = gator_events_block_create_files,
156 .start = gator_events_block_start,
157 .stop = gator_events_block_stop,
158 .read = gator_events_block_read,
159};
160
161int gator_events_block_init(void)
162{
163 block_rq_wr_enabled = 0;
164 block_rq_rd_enabled = 0;
165
166 block_rq_wr_key = gator_events_get_key();
167 block_rq_rd_key = gator_events_get_key();
168
169 return gator_events_install(&gator_events_block_interface);
170}
171gator_events_init(gator_events_block_init);
diff --git a/driver/gator_events_irq.c b/driver/gator_events_irq.c
new file mode 100644
index 0000000..36a6589
--- /dev/null
+++ b/driver/gator_events_irq.c
@@ -0,0 +1,171 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include "gator.h"
11#include <trace/events/irq.h>
12
13#define HARDIRQ 0
14#define SOFTIRQ 1
15#define TOTALIRQ (SOFTIRQ+1)
16
17static ulong hardirq_enabled;
18static ulong softirq_enabled;
19static ulong hardirq_key;
20static ulong softirq_key;
21static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt);
22static DEFINE_PER_CPU(int[TOTALIRQ], irqPrev);
23static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet);
24
25GATOR_DEFINE_PROBE(irq_handler_exit, TP_PROTO(int irq,
26 struct irqaction *action, int ret))
27{
28 unsigned long flags;
29
30 // disable interrupts to synchronize with gator_events_irq_read()
31 // spinlocks not needed since percpu buffers are used
32 local_irq_save(flags);
33 per_cpu(irqCnt, smp_processor_id())[HARDIRQ]++;
34 local_irq_restore(flags);
35}
36
37#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
38GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec))
39#else
40GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr))
41#endif
42{
43 unsigned long flags;
44
45 // disable interrupts to synchronize with gator_events_irq_read()
46 // spinlocks not needed since percpu buffers are used
47 local_irq_save(flags);
48 per_cpu(irqCnt, smp_processor_id())[SOFTIRQ]++;
49 local_irq_restore(flags);
50}
51
52static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root)
53{
54 struct dentry *dir;
55
56 /* irq */
57 dir = gatorfs_mkdir(sb, root, "Linux_irq_irq");
58 if (!dir) {
59 return -1;
60 }
61 gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled);
62 gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key);
63
64 /* soft irq */
65 dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq");
66 if (!dir) {
67 return -1;
68 }
69 gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled);
70 gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key);
71
72 return 0;
73}
74
75static int gator_events_irq_start(void)
76{
77 int cpu, i;
78
79 for_each_present_cpu(cpu) {
80 for (i = 0; i < TOTALIRQ; i++)
81 per_cpu(irqPrev, cpu)[i] = 0;
82 }
83
84 // register tracepoints
85 if (hardirq_enabled)
86 if (GATOR_REGISTER_TRACE(irq_handler_exit))
87 goto fail_hardirq_exit;
88 if (softirq_enabled)
89 if (GATOR_REGISTER_TRACE(softirq_exit))
90 goto fail_softirq_exit;
91 pr_debug("gator: registered irq tracepoints\n");
92
93 return 0;
94
95 // unregister tracepoints on error
96fail_softirq_exit:
97 if (hardirq_enabled)
98 GATOR_UNREGISTER_TRACE(irq_handler_exit);
99fail_hardirq_exit:
100 pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
101
102 return -1;
103}
104
105static void gator_events_irq_stop(void)
106{
107 if (hardirq_enabled)
108 GATOR_UNREGISTER_TRACE(irq_handler_exit);
109 if (softirq_enabled)
110 GATOR_UNREGISTER_TRACE(softirq_exit);
111 pr_debug("gator: unregistered irq tracepoints\n");
112
113 hardirq_enabled = 0;
114 softirq_enabled = 0;
115}
116
117static int gator_events_irq_read(int **buffer)
118{
119 unsigned long flags;
120 int len, value;
121 int cpu = smp_processor_id();
122
123 len = 0;
124 if (hardirq_enabled) {
125 local_irq_save(flags);
126 value = per_cpu(irqCnt, cpu)[HARDIRQ];
127 per_cpu(irqCnt, cpu)[HARDIRQ] = 0;
128 local_irq_restore(flags);
129 if (value != per_cpu(irqPrev, cpu)[HARDIRQ]) {
130 per_cpu(irqPrev, cpu)[HARDIRQ] = value;
131 per_cpu(irqGet, cpu)[len++] = hardirq_key;
132 per_cpu(irqGet, cpu)[len++] = value;
133 }
134 }
135
136 if (softirq_enabled) {
137 local_irq_save(flags);
138 value = per_cpu(irqCnt, cpu)[SOFTIRQ];
139 per_cpu(irqCnt, cpu)[SOFTIRQ] = 0;
140 local_irq_restore(flags);
141 if (value != per_cpu(irqPrev, cpu)[SOFTIRQ]) {
142 per_cpu(irqPrev, cpu)[SOFTIRQ] = value;
143 per_cpu(irqGet, cpu)[len++] = softirq_key;
144 per_cpu(irqGet, cpu)[len++] = value;
145 }
146 }
147
148 if (buffer)
149 *buffer = per_cpu(irqGet, cpu);
150
151 return len;
152}
153
154static struct gator_interface gator_events_irq_interface = {
155 .create_files = gator_events_irq_create_files,
156 .start = gator_events_irq_start,
157 .stop = gator_events_irq_stop,
158 .read = gator_events_irq_read,
159};
160
161int gator_events_irq_init(void)
162{
163 hardirq_key = gator_events_get_key();
164 softirq_key = gator_events_get_key();
165
166 hardirq_enabled = 0;
167 softirq_enabled = 0;
168
169 return gator_events_install(&gator_events_irq_interface);
170}
171gator_events_init(gator_events_irq_init);
diff --git a/driver/gator_events_meminfo.c b/driver/gator_events_meminfo.c
new file mode 100644
index 0000000..f1595bd
--- /dev/null
+++ b/driver/gator_events_meminfo.c
@@ -0,0 +1,195 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include "gator.h"
11#include <linux/workqueue.h>
12#include <trace/events/kmem.h>
13
14#define MEMINFO_MEMFREE 0
15#define MEMINFO_MEMUSED 1
16#define MEMINFO_BUFFERRAM 2
17#define MEMINFO_TOTAL 3
18
19static ulong meminfo_global_enabled;
20static ulong meminfo_enabled[MEMINFO_TOTAL];
21static ulong meminfo_key[MEMINFO_TOTAL];
22static int meminfo_buffer[MEMINFO_TOTAL * 2];
23static int meminfo_length = 0;
24static unsigned int mem_event = 0;
25static bool new_data_avail;
26
27static void wq_sched_handler(struct work_struct *wsptr);
28
29DECLARE_WORK(work, wq_sched_handler);
30
31GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) {
32 mem_event++;
33}
34
35GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) {
36 mem_event++;
37}
38
39GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) {
40 mem_event++;
41}
42
43static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root)
44{
45 struct dentry *dir;
46 int i;
47
48 for (i = 0; i < MEMINFO_TOTAL; i++) {
49 switch (i) {
50 case MEMINFO_MEMFREE:
51 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree");
52 break;
53 case MEMINFO_MEMUSED:
54 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused");
55 break;
56 case MEMINFO_BUFFERRAM:
57 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram");
58 break;
59 default:
60 return -1;
61 }
62 if (!dir) {
63 return -1;
64 }
65 gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]);
66 gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]);
67 }
68
69 return 0;
70}
71
72static int gator_events_meminfo_start(void)
73{
74 int i;
75
76 new_data_avail = true;
77 for (i = 0; i < MEMINFO_TOTAL; i++) {
78 if (meminfo_enabled[i]) {
79 meminfo_global_enabled = 1;
80 }
81 }
82
83 if (meminfo_global_enabled == 0)
84 return 0;
85
86 if (GATOR_REGISTER_TRACE(mm_page_free_direct))
87 goto mm_page_free_direct_exit;
88 if (GATOR_REGISTER_TRACE(mm_pagevec_free))
89 goto mm_pagevec_free_exit;
90 if (GATOR_REGISTER_TRACE(mm_page_alloc))
91 goto mm_page_alloc_exit;
92
93 return 0;
94
95mm_page_alloc_exit:
96 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
97mm_pagevec_free_exit:
98 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
99mm_page_free_direct_exit:
100 return -1;
101}
102
103static void gator_events_meminfo_stop(void)
104{
105 int i;
106
107 if (meminfo_global_enabled) {
108 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
109 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
110 GATOR_UNREGISTER_TRACE(mm_page_alloc);
111 }
112
113 meminfo_global_enabled = 0;
114 for (i = 0; i < MEMINFO_TOTAL; i++) {
115 meminfo_enabled[i] = 0;
116 }
117}
118
119// Must be run in a work queue as the kernel function si_meminfo() can sleep
120static void wq_sched_handler(struct work_struct *wsptr)
121{
122 struct sysinfo info;
123 int i, len, value;
124
125 meminfo_length = len = 0;
126
127 si_meminfo(&info);
128 for (i = 0; i < MEMINFO_TOTAL; i++) {
129 if (meminfo_enabled[i]) {
130 switch (i) {
131 case MEMINFO_MEMFREE:
132 value = info.freeram * PAGE_SIZE;
133 break;
134 case MEMINFO_MEMUSED:
135 value = (info.totalram - info.freeram) * PAGE_SIZE;
136 break;
137 case MEMINFO_BUFFERRAM:
138 value = info.bufferram * PAGE_SIZE;
139 break;
140 default:
141 value = 0;
142 break;
143 }
144 meminfo_buffer[len++] = meminfo_key[i];
145 meminfo_buffer[len++] = value;
146 }
147 }
148
149 meminfo_length = len;
150 new_data_avail = true;
151}
152
153static int gator_events_meminfo_read(int **buffer)
154{
155 static unsigned int last_mem_event = 0;
156
157 if (smp_processor_id() || !meminfo_global_enabled)
158 return 0;
159
160 if (last_mem_event != mem_event) {
161 last_mem_event = mem_event;
162 schedule_work(&work);
163 }
164
165 if (!new_data_avail)
166 return 0;
167
168 new_data_avail = false;
169
170 if (buffer)
171 *buffer = meminfo_buffer;
172
173 return meminfo_length;
174}
175
176static struct gator_interface gator_events_meminfo_interface = {
177 .create_files = gator_events_meminfo_create_files,
178 .start = gator_events_meminfo_start,
179 .stop = gator_events_meminfo_stop,
180 .read = gator_events_meminfo_read,
181};
182
183int gator_events_meminfo_init(void)
184{
185 int i;
186
187 meminfo_global_enabled = 0;
188 for (i = 0; i < MEMINFO_TOTAL; i++) {
189 meminfo_enabled[i] = 0;
190 meminfo_key[i] = gator_events_get_key();
191 }
192
193 return gator_events_install(&gator_events_meminfo_interface);
194}
195gator_events_init(gator_events_meminfo_init);
diff --git a/driver/gator_events_mmaped.c b/driver/gator_events_mmaped.c
new file mode 100644
index 0000000..6884684
--- /dev/null
+++ b/driver/gator_events_mmaped.c
@@ -0,0 +1,196 @@
1/*
2 * Example events provider
3 *
4 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Similar entires must be present in events.xml file:
11 *
12 * <counter_set name="mmaped_cntX">
13 * <counter name="mmaped_cnt0"/>
14 * <counter name="mmaped_cnt1"/>
15 * </counter_set>
16 * <category name="mmaped" counter_set="mmaped_cntX" per_cpu="no">
17 * <event event="0x0" title="Simulated" name="Sine" description="Sort-of-sine"/>
18 * <event event="0x1" title="Simulated" name="Triangle" description="Triangular wave"/>
19 * <event event="0x2" title="Simulated" name="PWM" description="PWM Signal"/>
20 * </category>
21 */
22
23#include <linux/init.h>
24#include <linux/io.h>
25#include <linux/ratelimit.h>
26
27#include "gator.h"
28
29#define MMAPED_COUNTERS_NUM 3
30
31static struct {
32 unsigned long enabled;
33 unsigned long event;
34 unsigned long key;
35} mmaped_counters[MMAPED_COUNTERS_NUM];
36
37static int mmaped_buffer[MMAPED_COUNTERS_NUM * 2];
38
39#ifdef TODO
40static void __iomem *mmaped_base;
41#endif
42
43/* Adds mmaped_cntX directories and enabled, event, and key files to /dev/gator/events */
44static int gator_events_mmaped_create_files(struct super_block *sb,
45 struct dentry *root)
46{
47 int i;
48
49 for (i = 0; i < MMAPED_COUNTERS_NUM; i++) {
50 char buf[16];
51 struct dentry *dir;
52
53 snprintf(buf, sizeof(buf), "mmaped_cnt%d", i);
54 dir = gatorfs_mkdir(sb, root, buf);
55 if (WARN_ON(!dir))
56 return -1;
57 gatorfs_create_ulong(sb, dir, "enabled",
58 &mmaped_counters[i].enabled);
59 gatorfs_create_ulong(sb, dir, "event",
60 &mmaped_counters[i].event);
61 gatorfs_create_ro_ulong(sb, dir, "key",
62 &mmaped_counters[i].key);
63 }
64
65 return 0;
66}
67
68static int gator_events_mmaped_start(void)
69{
70#ifdef TODO
71 for (i = 0; i < MMAPED_COUNTERS_NUM; i++)
72 writel(mmaped_counters[i].event,
73 mmaped_base + COUNTERS_CONFIG_OFFSET[i]);
74
75 writel(ENABLED, COUNTERS_CONTROL_OFFSET);
76#endif
77
78 return 0;
79}
80
81static void gator_events_mmaped_stop(void)
82{
83#ifdef TODO
84 writel(DISABLED, COUNTERS_CONTROL_OFFSET);
85#endif
86}
87
88#ifndef TODO
89/* This function "simulates" counters, generating values of fancy
90 * functions like sine or triangle... */
91static int mmaped_simulate(int counter)
92{
93 int result = 0;
94
95 switch (counter) {
96 case 0: /* sort-of-sine */
97 {
98 static int t;
99 int x;
100
101 if (t % 1024 < 512)
102 x = 512 - (t % 512);
103 else
104 x = t % 512;
105
106 result = 32 * x / 512;
107 result = result * result;
108
109 if (t < 1024)
110 result = 1922 - result;
111
112 t = (t + 1) % 2048;
113 }
114 break;
115 case 1: /* triangle */
116 {
117 static int v, d = 1;
118
119 v += d;
120 if (v % 2000 == 0)
121 d = -d;
122
123 result = v;
124 }
125 break;
126 case 2: /* PWM signal */
127 {
128 static int t, dc;
129
130 t = (t + 1) % 2000;
131 if (t % 100)
132 dc = (dc + 200) % 2000;
133
134 result = t < dc ? 0 : 2000;
135 }
136 break;
137 }
138
139 return result;
140}
141#endif
142
143static int gator_events_mmaped_read(int **buffer)
144{
145 int i;
146 int len = 0;
147
148 /* System wide counters - read from one core only */
149 if (smp_processor_id())
150 return 0;
151
152 for (i = 0; i < MMAPED_COUNTERS_NUM; i++) {
153 if (mmaped_counters[i].enabled) {
154 mmaped_buffer[len++] = mmaped_counters[i].key;
155#ifdef TODO
156 mmaped_buffer[len++] = readl(mmaped_base +
157 COUNTERS_VALUE_OFFSET[i]);
158#else
159 mmaped_buffer[len++] = mmaped_simulate(
160 mmaped_counters[i].event);
161#endif
162 }
163 }
164
165 if (buffer)
166 *buffer = mmaped_buffer;
167
168 return len;
169}
170
171static struct gator_interface gator_events_mmaped_interface = {
172 .create_files = gator_events_mmaped_create_files,
173 .start = gator_events_mmaped_start,
174 .stop = gator_events_mmaped_stop,
175 .read = gator_events_mmaped_read,
176};
177
178/* Must not be static! */
179int __init gator_events_mmaped_init(void)
180{
181 int i;
182
183#ifdef TODO
184 mmaped_base = ioremap(COUNTERS_PHYS_ADDR, SZ_4K);
185 if (!mmaped_base)
186 return -ENOMEM;
187#endif
188
189 for (i = 0; i < MMAPED_COUNTERS_NUM; i++) {
190 mmaped_counters[i].enabled = 0;
191 mmaped_counters[i].key = gator_events_get_key();
192 }
193
194 return gator_events_install(&gator_events_mmaped_interface);
195}
196gator_events_init(gator_events_mmaped_init);
diff --git a/driver/gator_events_net.c b/driver/gator_events_net.c
new file mode 100644
index 0000000..a921586
--- /dev/null
+++ b/driver/gator_events_net.c
@@ -0,0 +1,177 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include "gator.h"
11#include <linux/netdevice.h>
12
13#define NETRX 0
14#define NETTX 1
15#define NETDRV 2
16#define TOTALNET (NETDRV+1)
17
18static ulong netdrv_enabled;
19static ulong netrx_enabled;
20static ulong nettx_enabled;
21static ulong netdrv_key;
22static ulong netrx_key;
23static ulong nettx_key;
24static int rx_total, tx_total;
25static ulong netPrev[TOTALNET];
26static int netGet[TOTALNET * 2];
27
28static void get_network_stats(struct work_struct *wsptr) {
29 int rx = 0, tx = 0;
30 struct net_device *dev;
31
32 for_each_netdev(&init_net, dev) {
33#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
34 const struct net_device_stats *stats = dev_get_stats(dev);
35#else
36 struct rtnl_link_stats64 temp;
37 const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
38#endif
39 rx += stats->rx_bytes;
40 tx += stats->tx_bytes;
41 }
42 rx_total = rx;
43 tx_total = tx;
44}
45DECLARE_WORK(wq_get_stats, get_network_stats);
46
47static void calculate_delta(int *drv, int *rx, int *tx)
48{
49 int drv_calc, rx_calc, tx_calc;
50
51 drv_calc = gator_net_traffic - netPrev[NETDRV];
52 if (drv_calc > 0) {
53 netPrev[NETDRV] += drv_calc;
54 netPrev[NETTX] += drv_calc;
55 // remove tcp/ip header overhead
56 // approximation based on empirical measurement
57 netPrev[NETRX] += drv_calc / 42;
58 netPrev[NETTX] += drv_calc / 18;
59 }
60
61 rx_calc = (int)(rx_total - netPrev[NETRX]);
62 if (rx_calc < 0)
63 rx_calc = 0;
64 netPrev[NETRX] += rx_calc;
65
66 tx_calc = (int)(tx_total - netPrev[NETTX]);
67 if (tx_calc < 0)
68 tx_calc = 0;
69 netPrev[NETTX] += tx_calc;
70
71 *drv = drv_calc;
72 *rx = rx_calc;
73 *tx = tx_calc;
74}
75
76static int gator_events_net_create_files(struct super_block *sb, struct dentry *root)
77{
78 struct dentry *dir;
79
80 dir = gatorfs_mkdir(sb, root, "Linux_net_drv");
81 if (!dir) {
82 return -1;
83 }
84 gatorfs_create_ulong(sb, dir, "enabled", &netdrv_enabled);
85 gatorfs_create_ro_ulong(sb, dir, "key", &netdrv_key);
86
87 dir = gatorfs_mkdir(sb, root, "Linux_net_rx");
88 if (!dir) {
89 return -1;
90 }
91 gatorfs_create_ulong(sb, dir, "enabled", &netrx_enabled);
92 gatorfs_create_ro_ulong(sb, dir, "key", &netrx_key);
93
94 dir = gatorfs_mkdir(sb, root, "Linux_net_tx");
95 if (!dir) {
96 return -1;
97 }
98 gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled);
99 gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key);
100
101 return 0;
102}
103
104static int gator_events_net_start(void)
105{
106 get_network_stats(NULL);
107 netPrev[NETDRV] = 0;
108 netPrev[NETRX] = rx_total;
109 netPrev[NETTX] = tx_total;
110 return 0;
111}
112
113static void gator_events_net_stop(void)
114{
115 netdrv_enabled = 0;
116 netrx_enabled = 0;
117 nettx_enabled = 0;
118}
119
120static int gator_events_net_read(int **buffer)
121{
122 int len, drv_delta, rx_delta, tx_delta;
123 static int last_drv_delta = 0, last_rx_delta = 0, last_tx_delta = 0;
124
125 if (smp_processor_id() != 0)
126 return 0;
127
128 schedule_work(&wq_get_stats);
129 calculate_delta(&drv_delta, &rx_delta, &tx_delta);
130
131 len = 0;
132 if (netdrv_enabled && last_drv_delta != drv_delta) {
133 last_drv_delta = drv_delta;
134 netGet[len++] = netdrv_key;
135 netGet[len++] = drv_delta;
136 }
137
138 if (netrx_enabled && last_rx_delta != rx_delta) {
139 last_rx_delta = rx_delta;
140 netGet[len++] = netrx_key;
141 netGet[len++] = rx_delta;
142 }
143
144 if (nettx_enabled && last_tx_delta != tx_delta) {
145 last_tx_delta = tx_delta;
146 netGet[len++] = nettx_key;
147 netGet[len++] = tx_delta;
148 }
149
150 if (buffer)
151 *buffer = netGet;
152
153 return len;
154}
155
156static struct gator_interface gator_events_net_interface = {
157 .create_files = gator_events_net_create_files,
158 .start = gator_events_net_start,
159 .stop = gator_events_net_stop,
160 .read = gator_events_net_read,
161};
162
163int gator_events_net_init(void)
164{
165 gator_net_traffic++;
166
167 netdrv_key = gator_events_get_key();
168 netrx_key = gator_events_get_key();
169 nettx_key = gator_events_get_key();
170
171 netdrv_enabled = 0;
172 netrx_enabled = 0;
173 nettx_enabled = 0;
174
175 return gator_events_install(&gator_events_net_interface);
176}
177gator_events_init(gator_events_net_init);
diff --git a/driver/gator_events_pl310.c b/driver/gator_events_pl310.c
new file mode 100644
index 0000000..0ef0cf3
--- /dev/null
+++ b/driver/gator_events_pl310.c
@@ -0,0 +1,176 @@
1/**
2 * PL310 (L2 Cache Controller) event counters for gator
3 *
4 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/init.h>
12#include <linux/io.h>
13#include <asm/hardware/cache-l2x0.h>
14
15#include "gator.h"
16
17#define PL310_COUNTERS_NUM 2
18
19static struct {
20 unsigned long enabled;
21 unsigned long event;
22 unsigned long key;
23} pl310_counters[PL310_COUNTERS_NUM];
24
25static int pl310_buffer[PL310_COUNTERS_NUM * 2];
26
27static void __iomem *pl310_base;
28
29
30
31static void gator_events_pl310_reset_counters(void)
32{
33 u32 val = readl(pl310_base + L2X0_EVENT_CNT_CTRL);
34
35 val |= ((1 << PL310_COUNTERS_NUM) - 1) << 1;
36
37 writel(val, pl310_base + L2X0_EVENT_CNT_CTRL);
38}
39
40
41static int gator_events_pl310_create_files(struct super_block *sb,
42 struct dentry *root)
43{
44 int i;
45
46 for (i = 0; i < PL310_COUNTERS_NUM; i++) {
47 char buf[16];
48 struct dentry *dir;
49
50 snprintf(buf, sizeof(buf), "PL310_cnt%d", i);
51 dir = gatorfs_mkdir(sb, root, buf);
52 if (WARN_ON(!dir))
53 return -1;
54 gatorfs_create_ulong(sb, dir, "enabled",
55 &pl310_counters[i].enabled);
56 gatorfs_create_ulong(sb, dir, "event",
57 &pl310_counters[i].event);
58 gatorfs_create_ro_ulong(sb, dir, "key",
59 &pl310_counters[i].key);
60 }
61
62 return 0;
63}
64
65static int gator_events_pl310_start(void)
66{
67 static const unsigned long l2x0_event_cntx_cfg[PL310_COUNTERS_NUM] = {
68 L2X0_EVENT_CNT0_CFG,
69 L2X0_EVENT_CNT1_CFG,
70 };
71 int i;
72
73 /* Counter event sources */
74 for (i = 0; i < PL310_COUNTERS_NUM; i++)
75 writel((pl310_counters[i].event & 0xf) << 2,
76 pl310_base + l2x0_event_cntx_cfg[i]);
77
78 gator_events_pl310_reset_counters();
79
80 /* Event counter enable */
81 writel(1, pl310_base + L2X0_EVENT_CNT_CTRL);
82
83 return 0;
84}
85
86static void gator_events_pl310_stop(void)
87{
88 /* Event counter disable */
89 writel(0, pl310_base + L2X0_EVENT_CNT_CTRL);
90}
91
92static int gator_events_pl310_read(int **buffer)
93{
94 static const unsigned long l2x0_event_cntx_val[PL310_COUNTERS_NUM] = {
95 L2X0_EVENT_CNT0_VAL,
96 L2X0_EVENT_CNT1_VAL,
97 };
98 int i;
99 int len = 0;
100
101 if (smp_processor_id())
102 return 0;
103
104 for (i = 0; i < PL310_COUNTERS_NUM; i++) {
105 if (pl310_counters[i].enabled) {
106 pl310_buffer[len++] = pl310_counters[i].key;
107 pl310_buffer[len++] = readl(pl310_base +
108 l2x0_event_cntx_val[i]);
109 }
110 }
111
112 /* PL310 counters are saturating, not wrapping in case of overflow */
113 gator_events_pl310_reset_counters();
114
115 if (buffer)
116 *buffer = pl310_buffer;
117
118 return len;
119}
120
121static struct gator_interface gator_events_pl310_interface = {
122 .create_files = gator_events_pl310_create_files,
123 .start = gator_events_pl310_start,
124 .stop = gator_events_pl310_stop,
125 .read = gator_events_pl310_read,
126};
127
128static void __maybe_unused gator_events_pl310_probe(unsigned long phys)
129{
130 if (pl310_base)
131 return;
132
133 pl310_base = ioremap(phys, SZ_4K);
134 if (pl310_base) {
135 u32 cache_id = readl(pl310_base + L2X0_CACHE_ID);
136
137 if ((cache_id & 0xff0003c0) != 0x410000c0) {
138 iounmap(pl310_base);
139 pl310_base = NULL;
140 }
141 }
142}
143
144int gator_events_pl310_init(void)
145{
146 int i;
147
148#if defined(CONFIG_ARCH_EXYNOS4)
149 gator_events_pl310_probe(0xfe600000);
150#endif
151#if defined(CONFIG_ARCH_OMAP4)
152 gator_events_pl310_probe(0x48242000);
153#endif
154#if defined(CONFIG_ARCH_TEGRA)
155 gator_events_pl310_probe(0x50043000);
156#endif
157#if defined(CONFIG_ARCH_U8500)
158 gator_events_pl310_probe(0xa0412000);
159#endif
160#if defined(CONFIG_ARCH_VEXPRESS) && !defined(CONFIG_ARCH_VEXPRESS_CA15X4)
161 // A9x4 core tile (HBI-0191)
162 gator_events_pl310_probe(0x1e00a000);
163 // New memory map tiles
164 gator_events_pl310_probe(0x2c0f0000);
165#endif
166 if (!pl310_base)
167 return -1;
168
169 for (i = 0; i < PL310_COUNTERS_NUM; i++) {
170 pl310_counters[i].enabled = 0;
171 pl310_counters[i].key = gator_events_get_key();
172 }
173
174 return gator_events_install(&gator_events_pl310_interface);
175}
176gator_events_init(gator_events_pl310_init);
diff --git a/driver/gator_events_sched.c b/driver/gator_events_sched.c
new file mode 100644
index 0000000..7e9db60
--- /dev/null
+++ b/driver/gator_events_sched.c
@@ -0,0 +1,114 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include "gator.h"
11#include <trace/events/sched.h>
12
13#define SCHED_SWITCH 0
14#define SCHED_TOTAL (SCHED_SWITCH+1)
15
16static ulong sched_switch_enabled;
17static ulong sched_switch_key;
18static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt);
19static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet);
20
21#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
22GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
23#else
24GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
25#endif
26{
27 unsigned long flags;
28
29 // disable interrupts to synchronize with gator_events_sched_read()
30 // spinlocks not needed since percpu buffers are used
31 local_irq_save(flags);
32 per_cpu(schedCnt, smp_processor_id())[SCHED_SWITCH]++;
33 local_irq_restore(flags);
34}
35
36static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root)
37{
38 struct dentry *dir;
39
40 /* switch */
41 dir = gatorfs_mkdir(sb, root, "Linux_sched_switch");
42 if (!dir) {
43 return -1;
44 }
45 gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled);
46 gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key);
47
48 return 0;
49}
50
51static int gator_events_sched_start(void)
52{
53 // register tracepoints
54 if (sched_switch_enabled)
55 if (GATOR_REGISTER_TRACE(sched_switch))
56 goto sched_switch_exit;
57 pr_debug("gator: registered scheduler event tracepoints\n");
58
59 return 0;
60
61 // unregister tracepoints on error
62sched_switch_exit:
63 pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
64
65 return -1;
66}
67
68static void gator_events_sched_stop(void)
69{
70 if (sched_switch_enabled)
71 GATOR_UNREGISTER_TRACE(sched_switch);
72 pr_debug("gator: unregistered scheduler event tracepoints\n");
73
74 sched_switch_enabled = 0;
75}
76
77static int gator_events_sched_read(int **buffer)
78{
79 unsigned long flags;
80 int len, value;
81 int cpu = smp_processor_id();
82
83 len = 0;
84 if (sched_switch_enabled) {
85 local_irq_save(flags);
86 value = per_cpu(schedCnt, cpu)[SCHED_SWITCH];
87 per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0;
88 local_irq_restore(flags);
89 per_cpu(schedGet, cpu)[len++] = sched_switch_key;
90 per_cpu(schedGet, cpu)[len++] = value;
91 }
92
93 if (buffer)
94 *buffer = per_cpu(schedGet, cpu);
95
96 return len;
97}
98
99static struct gator_interface gator_events_sched_interface = {
100 .create_files = gator_events_sched_create_files,
101 .start = gator_events_sched_start,
102 .stop = gator_events_sched_stop,
103 .read = gator_events_sched_read,
104};
105
106int gator_events_sched_init(void)
107{
108 sched_switch_enabled = 0;
109
110 sched_switch_key = gator_events_get_key();
111
112 return gator_events_install(&gator_events_sched_interface);
113}
114gator_events_init(gator_events_sched_init);
diff --git a/driver/gator_events_scorpion.c b/driver/gator_events_scorpion.c
new file mode 100644
index 0000000..d831a50
--- /dev/null
+++ b/driver/gator_events_scorpion.c
@@ -0,0 +1,661 @@
1/**
2 * Copyright (C) ARM Limited 2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include "gator.h"
10
11#define SCORPION 0xf
12#define SCORPIONMP 0x2d
13
14static const char *pmnc_name;
15static int pmnc_count;
16
17// Per-CPU PMNC: config reg
18#define PMNC_E (1 << 0) /* Enable all counters */
19#define PMNC_P (1 << 1) /* Reset all counters */
20#define PMNC_C (1 << 2) /* Cycle counter reset */
21#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
22#define PMNC_X (1 << 4) /* Export to ETM */
23#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
24#define PMNC_MASK 0x3f /* Mask for writable bits */
25
26// ccnt reg
27#define CCNT_REG (1 << 31)
28
29#define CCNT 0
30#define CNT0 1
31#define CNTMAX (4+1)
32
33static unsigned long pmnc_enabled[CNTMAX];
34static unsigned long pmnc_event[CNTMAX];
35static unsigned long pmnc_key[CNTMAX];
36
37static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
38static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
39
40enum scorpion_perf_types {
41 SCORPION_ICACHE_EXPL_INV = 0x4c,
42 SCORPION_ICACHE_MISS = 0x4d,
43 SCORPION_ICACHE_ACCESS = 0x4e,
44 SCORPION_ICACHE_CACHEREQ_L2 = 0x4f,
45 SCORPION_ICACHE_NOCACHE_L2 = 0x50,
46 SCORPION_HIQUP_NOPED = 0x51,
47 SCORPION_DATA_ABORT = 0x52,
48 SCORPION_IRQ = 0x53,
49 SCORPION_FIQ = 0x54,
50 SCORPION_ALL_EXCPT = 0x55,
51 SCORPION_UNDEF = 0x56,
52 SCORPION_SVC = 0x57,
53 SCORPION_SMC = 0x58,
54 SCORPION_PREFETCH_ABORT = 0x59,
55 SCORPION_INDEX_CHECK = 0x5a,
56 SCORPION_NULL_CHECK = 0x5b,
57 SCORPION_EXPL_ICIALLU = 0x5c,
58 SCORPION_IMPL_ICIALLU = 0x5d,
59 SCORPION_NONICIALLU_BTAC_INV = 0x5e,
60 SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5f,
61 SCORPION_SPIPE_ONLY_CYCLES = 0x60,
62 SCORPION_XPIPE_ONLY_CYCLES = 0x61,
63 SCORPION_DUAL_CYCLES = 0x62,
64 SCORPION_DISPATCH_ANY_CYCLES = 0x63,
65 SCORPION_FIFO_FULLBLK_CMT = 0x64,
66 SCORPION_FAIL_COND_INST = 0x65,
67 SCORPION_PASS_COND_INST = 0x66,
68 SCORPION_ALLOW_VU_CLK = 0x67,
69 SCORPION_VU_IDLE = 0x68,
70 SCORPION_ALLOW_L2_CLK = 0x69,
71 SCORPION_L2_IDLE = 0x6a,
72 SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b,
73 SCORPION_DTLB_EXPL_INV = 0x6c,
74 SCORPION_DTLB_MISS = 0x6d,
75 SCORPION_DTLB_ACCESS = 0x6e,
76 SCORPION_ITLB_MISS = 0x6f,
77 SCORPION_ITLB_IMPL_INV = 0x70,
78 SCORPION_ITLB_EXPL_INV = 0x71,
79 SCORPION_UTLB_D_MISS = 0x72,
80 SCORPION_UTLB_D_ACCESS = 0x73,
81 SCORPION_UTLB_I_MISS = 0x74,
82 SCORPION_UTLB_I_ACCESS = 0x75,
83 SCORPION_UTLB_INV_ASID = 0x76,
84 SCORPION_UTLB_INV_MVA = 0x77,
85 SCORPION_UTLB_INV_ALL = 0x78,
86 SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79,
87 SCORPION_S2_HOLD = 0x7a,
88 SCORPION_S2_HOLD_DEV_OP = 0x7b,
89 SCORPION_S2_HOLD_ORDER = 0x7c,
90 SCORPION_S2_HOLD_BARRIER = 0x7d,
91 SCORPION_VIU_DUAL_CYCLE = 0x7e,
92 SCORPION_VIU_SINGLE_CYCLE = 0x7f,
93 SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80,
94 SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81,
95 SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82,
96 SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83,
97 SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84,
98 SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85,
99 SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86,
100 SCORPION_EXCEPTIONS_INV_OPERATION = 0x87,
101 SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88,
102 SCORPION_COND_INST_FAIL_VX_PIPE = 0x89,
103 SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a,
104 SCORPION_EXCEPTIONS_OVERFLOW = 0x8b,
105 SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c,
106 SCORPION_EXCEPTIONS_DENORM = 0x8d,
107#ifdef CONFIG_ARCH_MSM_SCORPIONMP
108 SCORPIONMP_NUM_BARRIERS = 0x8e,
109 SCORPIONMP_BARRIER_CYCLES = 0x8f,
110#else
111 SCORPION_BANK_AB_HIT = 0x8e,
112 SCORPION_BANK_AB_ACCESS = 0x8f,
113 SCORPION_BANK_CD_HIT = 0x90,
114 SCORPION_BANK_CD_ACCESS = 0x91,
115 SCORPION_BANK_AB_DSIDE_HIT = 0x92,
116 SCORPION_BANK_AB_DSIDE_ACCESS = 0x93,
117 SCORPION_BANK_CD_DSIDE_HIT = 0x94,
118 SCORPION_BANK_CD_DSIDE_ACCESS = 0x95,
119 SCORPION_BANK_AB_ISIDE_HIT = 0x96,
120 SCORPION_BANK_AB_ISIDE_ACCESS = 0x97,
121 SCORPION_BANK_CD_ISIDE_HIT = 0x98,
122 SCORPION_BANK_CD_ISIDE_ACCESS = 0x99,
123 SCORPION_ISIDE_RD_WAIT = 0x9a,
124 SCORPION_DSIDE_RD_WAIT = 0x9b,
125 SCORPION_BANK_BYPASS_WRITE = 0x9c,
126 SCORPION_BANK_AB_NON_CASTOUT = 0x9d,
127 SCORPION_BANK_AB_L2_CASTOUT = 0x9e,
128 SCORPION_BANK_CD_NON_CASTOUT = 0x9f,
129 SCORPION_BANK_CD_L2_CASTOUT = 0xa0,
130#endif
131 MSM_MAX_EVT
132};
133
134struct scorp_evt {
135 u32 evt_type;
136 u32 val;
137 u8 grp;
138 u32 evt_type_act;
139};
140
141static const struct scorp_evt sc_evt[] = {
142 {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d},
143 {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e},
144 {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f},
145 {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f},
146 {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f},
147 {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e},
148 {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c},
149 {SCORPION_IRQ, 0x80000a00, 0, 0x4d},
150 {SCORPION_FIQ, 0x800a0000, 0, 0x4e},
151 {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f},
152 {SCORPION_UNDEF, 0x8000000b, 0, 0x4c},
153 {SCORPION_SVC, 0x80000b00, 0, 0x4d},
154 {SCORPION_SMC, 0x800b0000, 0, 0x4e},
155 {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f},
156 {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c},
157 {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d},
158 {SCORPION_EXPL_ICIALLU, 0x8000000d, 0, 0x4c},
159 {SCORPION_IMPL_ICIALLU, 0x80000d00, 0, 0x4d},
160 {SCORPION_NONICIALLU_BTAC_INV, 0x800d0000, 0, 0x4e},
161 {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8d000000, 0, 0x4f},
162
163 {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51},
164 {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52},
165 {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53},
166 {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53},
167 {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50},
168 {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52},
169 {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53},
170 {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50},
171 {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51},
172 {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52},
173 {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53},
174
175 {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54},
176 {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55},
177 {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56},
178 {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57},
179 {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55},
180 {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56},
181 {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57},
182 {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54},
183 {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55},
184 {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56},
185 {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57},
186 {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55},
187 {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56},
188 {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57},
189 {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55},
190 {SCORPION_S2_HOLD, 0x88000000, 2, 0x57},
191 {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55},
192 {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56},
193 {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57},
194
195 {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c},
196 {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d},
197 {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c},
198 {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d},
199 {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e},
200 {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c},
201 {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c},
202 {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d},
203 {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e},
204 {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c},
205 {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d},
206 {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e},
207 {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f},
208 {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c},
209 {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d},
210 {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f},
211
212 {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58},
213 {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59},
214 {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a},
215 {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b},
216 {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58},
217 {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59},
218 {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a},
219 {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b},
220 {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58},
221 {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59},
222 {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a},
223 {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b},
224 {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58},
225 {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a},
226 {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58},
227 {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58},
228 {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59},
229 {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a},
230 {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b},
231};
232
233static inline void scorpion_pmnc_write(u32 val)
234{
235 val &= PMNC_MASK;
236 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
237}
238
239static inline u32 scorpion_pmnc_read(void)
240{
241 u32 val;
242 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
243 return val;
244}
245
246static inline u32 scorpion_ccnt_read(void)
247{
248 u32 val;
249 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
250 return val;
251}
252
253static inline u32 scorpion_cntn_read(void)
254{
255 u32 val;
256 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
257 return val;
258}
259
260static inline u32 scorpion_pmnc_enable_counter(unsigned int cnt)
261{
262 u32 val;
263
264 if (cnt >= CNTMAX) {
265 pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
266 return -1;
267 }
268
269 if (cnt == CCNT)
270 val = CCNT_REG;
271 else
272 val = (1 << (cnt - CNT0));
273
274 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
275
276 return cnt;
277}
278
279static inline u32 scorpion_pmnc_disable_counter(unsigned int cnt)
280{
281 u32 val;
282
283 if (cnt >= CNTMAX) {
284 pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
285 return -1;
286 }
287
288 if (cnt == CCNT)
289 val = CCNT_REG;
290 else
291 val = (1 << (cnt - CNT0));
292
293 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
294
295 return cnt;
296}
297
298static inline int scorpion_pmnc_select_counter(unsigned int cnt)
299{
300 u32 val;
301
302 if ((cnt == CCNT) || (cnt >= CNTMAX)) {
303 pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt);
304 return -1;
305 }
306
307 val = (cnt - CNT0);
308 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
309
310 return cnt;
311}
312
313static u32 scorpion_read_lpm0(void)
314{
315 u32 val;
316 asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val));
317 return val;
318}
319
320static void scorpion_write_lpm0(u32 val)
321{
322 asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val));
323}
324
325static u32 scorpion_read_lpm1(void)
326{
327 u32 val;
328 asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val));
329 return val;
330}
331
332static void scorpion_write_lpm1(u32 val)
333{
334 asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val));
335}
336
337static u32 scorpion_read_lpm2(void)
338{
339 u32 val;
340 asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val));
341 return val;
342}
343
344static void scorpion_write_lpm2(u32 val)
345{
346 asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val));
347}
348
349static u32 scorpion_read_l2lpm(void)
350{
351 u32 val;
352 asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val));
353 return val;
354}
355
356static void scorpion_write_l2lpm(u32 val)
357{
358 asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val));
359}
360
361static u32 scorpion_read_vlpm(void)
362{
363 u32 val;
364 asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val));
365 return val;
366}
367
368static void scorpion_write_vlpm(u32 val)
369{
370 asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val));
371}
372
373struct scorpion_access_funcs {
374 u32 (*read) (void);
375 void (*write) (u32);
376};
377
378struct scorpion_access_funcs scor_func[] = {
379 {scorpion_read_lpm0, scorpion_write_lpm0},
380 {scorpion_read_lpm1, scorpion_write_lpm1},
381 {scorpion_read_lpm2, scorpion_write_lpm2},
382 {scorpion_read_l2lpm, scorpion_write_l2lpm},
383 {scorpion_read_vlpm, scorpion_write_vlpm},
384};
385
386u32 venum_orig_val;
387u32 fp_orig_val;
388
389static void scorpion_pre_vlpm(void)
390{
391 u32 venum_new_val;
392 u32 fp_new_val;
393
394 /* CPACR Enable CP10 access*/
395 asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (venum_orig_val));
396 venum_new_val = venum_orig_val | 0x00300000;
397 asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_new_val));
398 /* Enable FPEXC */
399 asm volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (fp_orig_val));
400 fp_new_val = fp_orig_val | 0x40000000;
401 asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_new_val));
402}
403
404static void scorpion_post_vlpm(void)
405{
406 /* Restore FPEXC*/
407 asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_orig_val));
408 /* Restore CPACR*/
409 asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_orig_val));
410}
411
412#define COLMN0MASK 0x000000ff
413#define COLMN1MASK 0x0000ff00
414#define COLMN2MASK 0x00ff0000
415static u32 scorpion_get_columnmask(u32 setval)
416{
417 if (setval & COLMN0MASK)
418 return 0xffffff00;
419 else if (setval & COLMN1MASK)
420 return 0xffff00ff;
421 else if (setval & COLMN2MASK)
422 return 0xff00ffff;
423 else
424 return 0x80ffffff;
425}
426
427static void scorpion_evt_setup(u32 gr, u32 setval)
428{
429 u32 val;
430 if (gr == 4)
431 scorpion_pre_vlpm();
432 val = scorpion_get_columnmask(setval) & scor_func[gr].read();
433 val = val | setval;
434 scor_func[gr].write(val);
435 if (gr == 4)
436 scorpion_post_vlpm();
437}
438
439static int get_scorpion_evtinfo(unsigned int evt_type, struct scorp_evt *evtinfo)
440{
441 u32 idx;
442 if ((evt_type < 0x4c) || (evt_type >= MSM_MAX_EVT))
443 return 0;
444 idx = evt_type - 0x4c;
445 if (sc_evt[idx].evt_type == evt_type) {
446 evtinfo->val = sc_evt[idx].val;
447 evtinfo->grp = sc_evt[idx].grp;
448 evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
449 return 1;
450 }
451 return 0;
452}
453
454static inline void scorpion_pmnc_write_evtsel(unsigned int cnt, u32 val)
455{
456 if (scorpion_pmnc_select_counter(cnt) == cnt) {
457 if (val < 0x40) {
458 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
459 } else {
460 u32 zero = 0;
461 struct scorp_evt evtinfo;
462 // extract evtinfo.grp and evtinfo.tevt_type_act from val
463 if (get_scorpion_evtinfo(val, &evtinfo) == 0) return;
464 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (evtinfo.evt_type_act));
465 asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (zero));
466 scorpion_evt_setup(evtinfo.grp, val);
467 }
468 }
469}
470
471static void scorpion_pmnc_reset_counter(unsigned int cnt)
472{
473 u32 val = 0;
474
475 if (cnt == CCNT) {
476 scorpion_pmnc_disable_counter(cnt);
477
478 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
479
480 if (pmnc_enabled[cnt] != 0)
481 scorpion_pmnc_enable_counter(cnt);
482
483 } else if (cnt >= CNTMAX) {
484 pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt);
485 } else {
486 scorpion_pmnc_disable_counter(cnt);
487
488 if (scorpion_pmnc_select_counter(cnt) == cnt)
489 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
490
491 if (pmnc_enabled[cnt] != 0)
492 scorpion_pmnc_enable_counter(cnt);
493 }
494}
495
496static int gator_events_scorpion_create_files(struct super_block *sb, struct dentry *root)
497{
498 struct dentry *dir;
499 int i;
500
501 for (i = 0; i < pmnc_count; i++) {
502 char buf[40];
503 if (i == 0) {
504 snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name);
505 } else {
506 snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i-1);
507 }
508 dir = gatorfs_mkdir(sb, root, buf);
509 if (!dir) {
510 return -1;
511 }
512 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
513 if (i > 0) {
514 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
515 }
516 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
517 }
518
519 return 0;
520}
521
522static void gator_events_scorpion_online(void)
523{
524 unsigned int cnt;
525
526 if (scorpion_pmnc_read() & PMNC_E) {
527 scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E);
528 }
529
530 /* Initialize & Reset PMNC: C bit and P bit */
531 scorpion_pmnc_write(PMNC_P | PMNC_C);
532
533 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
534 unsigned long event;
535
536 per_cpu(perfPrev, smp_processor_id())[cnt] = 0;
537
538 if (!pmnc_enabled[cnt])
539 continue;
540
541 // disable counter
542 scorpion_pmnc_disable_counter(cnt);
543
544 event = pmnc_event[cnt] & 255;
545
546 // Set event (if destined for PMNx counters), We don't need to set the event if it's a cycle count
547 if (cnt != CCNT)
548 scorpion_pmnc_write_evtsel(cnt, event);
549
550 // reset counter
551 scorpion_pmnc_reset_counter(cnt);
552
553 // Enable counter, do not enable interrupt for this counter
554 scorpion_pmnc_enable_counter(cnt);
555 }
556
557 // enable
558 scorpion_pmnc_write(scorpion_pmnc_read() | PMNC_E);
559}
560
561static void gator_events_scorpion_offline(void)
562{
563 scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E);
564
565 // investigate: need to do the clearpmu() here on each counter?
566}
567
568static void gator_events_scorpion_stop(void)
569{
570 unsigned int cnt;
571
572 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
573 pmnc_enabled[cnt] = 0;
574 pmnc_event[cnt] = 0;
575 }
576}
577
578static int gator_events_scorpion_read(int **buffer)
579{
580 int cnt, len = 0;
581 int cpu = smp_processor_id();
582
583 if (!pmnc_count)
584 return 0;
585
586 for (cnt = 0; cnt < pmnc_count; cnt++) {
587 if (pmnc_enabled[cnt]) {
588 int value;
589 if (cnt == CCNT) {
590 value = scorpion_ccnt_read();
591 } else if (scorpion_pmnc_select_counter(cnt) == cnt) {
592 value = scorpion_cntn_read();
593 } else {
594 value = 0;
595 }
596 scorpion_pmnc_reset_counter(cnt);
597 if (value != per_cpu(perfPrev, cpu)[cnt]) {
598 per_cpu(perfPrev, cpu)[cnt] = value;
599 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
600 per_cpu(perfCnt, cpu)[len++] = value;
601 }
602 }
603 }
604
605 // update or discard
606 if (buffer)
607 *buffer = per_cpu(perfCnt, cpu);
608
609 return len;
610}
611
612static struct gator_interface gator_events_scorpion_interface = {
613 .create_files = gator_events_scorpion_create_files,
614 .stop = gator_events_scorpion_stop,
615 .online = gator_events_scorpion_online,
616 .offline = gator_events_scorpion_offline,
617 .read = gator_events_scorpion_read,
618};
619
620
621static void scorpion_clear_pmuregs(void)
622{
623 scorpion_write_lpm0(0);
624 scorpion_write_lpm1(0);
625 scorpion_write_lpm2(0);
626 scorpion_write_l2lpm(0);
627 scorpion_pre_vlpm();
628 scorpion_write_vlpm(0);
629 scorpion_post_vlpm();
630}
631
632int gator_events_scorpion_init(void)
633{
634 unsigned int cnt;
635
636 switch (gator_cpuid()) {
637 case SCORPION:
638 pmnc_name = "Scorpion";
639 pmnc_count = 4;
640 break;
641 case SCORPIONMP:
642 pmnc_name = "ScorpionMP";
643 pmnc_count = 4;
644 break;
645 default:
646 return -1;
647 }
648
649 pmnc_count++; // CNT[n] + CCNT
650
651 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
652 pmnc_enabled[cnt] = 0;
653 pmnc_event[cnt] = 0;
654 pmnc_key[cnt] = gator_events_get_key();
655 }
656
657 scorpion_clear_pmuregs();
658
659 return gator_events_install(&gator_events_scorpion_interface);
660}
661gator_events_init(gator_events_scorpion_init);
diff --git a/driver/gator_fs.c b/driver/gator_fs.c
new file mode 100644
index 0000000..8277c3a
--- /dev/null
+++ b/driver/gator_fs.c
@@ -0,0 +1,284 @@
1/**
2 * @file gatorfs.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon
8 *
9 * A simple filesystem for configuration and
10 * access of oprofile.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/pagemap.h>
17#include <asm/uaccess.h>
18
19#define gatorfs_MAGIC 0x24051020
20#define TMPBUFSIZE 50
21DEFINE_SPINLOCK(gatorfs_lock);
22
23static struct inode *gatorfs_get_inode(struct super_block *sb, int mode)
24{
25 struct inode *inode = new_inode(sb);
26
27 if (inode) {
28#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
29 inode->i_ino = get_next_ino();
30#endif
31 inode->i_mode = mode;
32 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
33 }
34 return inode;
35}
36
37static const struct super_operations s_ops = {
38 .statfs = simple_statfs,
39 .drop_inode = generic_delete_inode,
40};
41
42ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
43{
44 return simple_read_from_buffer(buf, count, offset, str, strlen(str));
45}
46
47ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
48{
49 char tmpbuf[TMPBUFSIZE];
50 size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
51 if (maxlen > TMPBUFSIZE)
52 maxlen = TMPBUFSIZE;
53 return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
54}
55
56int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
57{
58 char tmpbuf[TMPBUFSIZE];
59 unsigned long flags;
60
61 if (!count)
62 return 0;
63
64 if (count > TMPBUFSIZE - 1)
65 return -EINVAL;
66
67 memset(tmpbuf, 0x0, TMPBUFSIZE);
68
69 if (copy_from_user(tmpbuf, buf, count))
70 return -EFAULT;
71
72 spin_lock_irqsave(&gatorfs_lock, flags);
73 *val = simple_strtoul(tmpbuf, NULL, 0);
74 spin_unlock_irqrestore(&gatorfs_lock, flags);
75 return 0;
76}
77
78static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
79{
80 unsigned long *val = file->private_data;
81 return gatorfs_ulong_to_user(*val, buf, count, offset);
82}
83
84static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
85{
86 unsigned long *value = file->private_data;
87 int retval;
88
89 if (*offset)
90 return -EINVAL;
91
92 retval = gatorfs_ulong_from_user(value, buf, count);
93
94 if (retval)
95 return retval;
96 return count;
97}
98
99static int default_open(struct inode *inode, struct file *filp)
100{
101 if (inode->i_private)
102 filp->private_data = inode->i_private;
103 return 0;
104}
105
106static const struct file_operations ulong_fops = {
107 .read = ulong_read_file,
108 .write = ulong_write_file,
109 .open = default_open,
110};
111
112static const struct file_operations ulong_ro_fops = {
113 .read = ulong_read_file,
114 .open = default_open,
115};
116
117static struct dentry *__gatorfs_create_file(struct super_block *sb,
118 struct dentry *root, char const *name, const struct file_operations *fops,
119 int perm)
120{
121 struct dentry *dentry;
122 struct inode *inode;
123
124 dentry = d_alloc_name(root, name);
125 if (!dentry)
126 return NULL;
127 inode = gatorfs_get_inode(sb, S_IFREG | perm);
128 if (!inode) {
129 dput(dentry);
130 return NULL;
131 }
132 inode->i_fop = fops;
133 d_add(dentry, inode);
134 return dentry;
135}
136
137int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
138 char const *name, unsigned long *val)
139{
140 struct dentry *d = __gatorfs_create_file(sb, root, name,
141 &ulong_fops, 0644);
142 if (!d)
143 return -EFAULT;
144
145 d->d_inode->i_private = val;
146 return 0;
147}
148
149int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
150 char const *name, unsigned long *val)
151{
152 struct dentry *d = __gatorfs_create_file(sb, root, name,
153 &ulong_ro_fops, 0444);
154 if (!d)
155 return -EFAULT;
156
157 d->d_inode->i_private = val;
158 return 0;
159}
160
161static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
162{
163 atomic_t *val = file->private_data;
164 return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset);
165}
166
167static const struct file_operations atomic_ro_fops = {
168 .read = atomic_read_file,
169 .open = default_open,
170};
171
172int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root,
173 char const *name, atomic_t *val)
174{
175 struct dentry *d = __gatorfs_create_file(sb, root, name,
176 &atomic_ro_fops, 0444);
177 if (!d)
178 return -EFAULT;
179
180 d->d_inode->i_private = val;
181 return 0;
182}
183
184int gatorfs_create_file(struct super_block *sb, struct dentry *root,
185 char const *name, const struct file_operations *fops)
186{
187 if (!__gatorfs_create_file(sb, root, name, fops, 0644))
188 return -EFAULT;
189 return 0;
190}
191
192int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
193 char const *name, const struct file_operations *fops, int perm)
194{
195 if (!__gatorfs_create_file(sb, root, name, fops, perm))
196 return -EFAULT;
197 return 0;
198}
199
200struct dentry *gatorfs_mkdir(struct super_block *sb,
201 struct dentry *root, char const *name)
202{
203 struct dentry *dentry;
204 struct inode *inode;
205
206 dentry = d_alloc_name(root, name);
207 if (!dentry)
208 return NULL;
209 inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
210 if (!inode) {
211 dput(dentry);
212 return NULL;
213 }
214 inode->i_op = &simple_dir_inode_operations;
215 inode->i_fop = &simple_dir_operations;
216 d_add(dentry, inode);
217 return dentry;
218}
219
220static int gatorfs_fill_super(struct super_block *sb, void *data, int silent)
221{
222 struct inode *root_inode;
223 struct dentry *root_dentry;
224
225 sb->s_blocksize = PAGE_CACHE_SIZE;
226 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
227 sb->s_magic = gatorfs_MAGIC;
228 sb->s_op = &s_ops;
229 sb->s_time_gran = 1;
230
231 root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
232 if (!root_inode)
233 return -ENOMEM;
234 root_inode->i_op = &simple_dir_inode_operations;
235 root_inode->i_fop = &simple_dir_operations;
236 root_dentry = d_alloc_root(root_inode);
237 if (!root_dentry) {
238 iput(root_inode);
239 return -ENOMEM;
240 }
241
242 sb->s_root = root_dentry;
243
244 gator_op_create_files(sb, root_dentry);
245
246 // FIXME: verify kill_litter_super removes our dentries
247 return 0;
248}
249
250#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
251static int gatorfs_get_sb(struct file_system_type *fs_type,
252 int flags, const char *dev_name, void *data, struct vfsmount *mnt)
253{
254 return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt);
255}
256#else
257static struct dentry *gatorfs_mount(struct file_system_type *fs_type,
258 int flags, const char *dev_name, void *data)
259{
260 return mount_nodev(fs_type, flags, data, gatorfs_fill_super);
261}
262#endif
263
264static struct file_system_type gatorfs_type = {
265 .owner = THIS_MODULE,
266 .name = "gatorfs",
267#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
268 .get_sb = gatorfs_get_sb,
269#else
270 .mount = gatorfs_mount,
271#endif
272
273 .kill_sb = kill_litter_super,
274};
275
276int __init gatorfs_register(void)
277{
278 return register_filesystem(&gatorfs_type);
279}
280
281void gatorfs_unregister(void)
282{
283 unregister_filesystem(&gatorfs_type);
284}
diff --git a/driver/gator_main.c b/driver/gator_main.c
new file mode 100644
index 0000000..340756e
--- /dev/null
+++ b/driver/gator_main.c
@@ -0,0 +1,1057 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10static unsigned long gator_protocol_version = 5;
11
12#include <linux/slab.h>
13#include <linux/cpu.h>
14#include <linux/sched.h>
15#include <linux/irq.h>
16#include <linux/vmalloc.h>
17#include <linux/hardirq.h>
18#include <linux/highmem.h>
19#include <linux/pagemap.h>
20#include <asm/uaccess.h>
21
22#include "gator.h"
23#include "gator_events.h"
24
25#ifndef CONFIG_GENERIC_TRACER
26#ifndef CONFIG_TRACING
27#error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined
28#endif
29#endif
30
31#ifndef CONFIG_PROFILING
32#error gator requires the kernel to have CONFIG_PROFILING defined
33#endif
34
35#ifndef CONFIG_HIGH_RES_TIMERS
36#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined
37#endif
38
39#ifdef CONFIG_SMP
40#ifndef CONFIG_LOCAL_TIMERS
41#error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems
42#endif
43#endif
44
45#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
46#error kernels prior to 2.6.32 are not supported
47#endif
48
49/******************************************************************************
50 * DEFINES
51 ******************************************************************************/
52#define BUFFER_SIZE_DEFAULT (256*1024)
53#define SYNC_FREQ_DEFAULT 1000
54
55#define NO_COOKIE 0UL
56#define INVALID_COOKIE ~0UL
57
58#define PROTOCOL_FRAME ~0
59#define PROTOCOL_START_TICK 1
60#define PROTOCOL_END_TICK 3
61#define PROTOCOL_START_BACKTRACE 5
62#define PROTOCOL_END_BACKTRACE 7
63#define PROTOCOL_COOKIE 9
64#define PROTOCOL_SCHEDULER_TRACE 11
65#define PROTOCOL_COUNTERS 13
66#define PROTOCOL_ANNOTATE 15
67#define PROTOCOL_CPU_SYNC 17
68
69#if defined(__arm__)
70#define PC_REG regs->ARM_pc
71#else
72#define PC_REG regs->ip
73#endif
74
75/******************************************************************************
76 * PER CPU
77 ******************************************************************************/
78static unsigned long gator_cpu_cores;
79static unsigned long gator_buffer_size;
80static unsigned long gator_backtrace_depth;
81
82static unsigned long gator_started;
83static unsigned long gator_buffer_opened;
84static unsigned long gator_timer_count;
85static unsigned long gator_streaming;
86static int gator_master_tick;
87static DEFINE_MUTEX(start_mutex);
88static DEFINE_MUTEX(gator_buffer_mutex);
89
90unsigned long gator_net_traffic;
91
92#define COMMIT_SIZE 128
93#define COMMIT_MASK (COMMIT_SIZE-1)
94static DEFINE_SPINLOCK(gator_commit_lock);
95static int *gator_commit;
96static int gator_commit_read;
97static int gator_commit_write;
98
99static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait);
100static DEFINE_PER_CPU(int, gator_cpu_sync);
101static DEFINE_PER_CPU(int, gator_cpu_tick);
102static DEFINE_PER_CPU(int, gator_first_time);
103
104/******************************************************************************
105 * Prototypes
106 ******************************************************************************/
107static void gator_buffer_write_packed_int(int cpu, unsigned int x);
108static void gator_buffer_write_string(int cpu, char *x);
109static int gator_write_packed_int(char *buffer, unsigned int x);
110static void gator_add_trace(int cpu, unsigned int address);
111static uint64_t gator_get_time(void);
112
113/******************************************************************************
114 * Application Includes
115 ******************************************************************************/
116#include "gator_cookies.c"
117#include "gator_trace_sched.c"
118#include "gator_backtrace.c"
119#include "gator_annotate.c"
120#include "gator_fs.c"
121
122/******************************************************************************
123 * Misc
124 ******************************************************************************/
125#if defined(__arm__)
126u32 gator_cpuid(void)
127{
128 u32 val;
129 asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val));
130 return (val >> 4) & 0xfff;
131}
132#endif
133
134/******************************************************************************
135 * Commit interface
136 ******************************************************************************/
137static int buffer_commit_ready(void)
138{
139 return (gator_commit_read != gator_commit_write);
140}
141
142static void buffer_commit_read(int *cpu, int *readval, int *writeval)
143{
144 int read = gator_commit_read;
145 *cpu = gator_commit[read+0];
146 *readval = gator_commit[read+1];
147 *writeval = gator_commit[read+2];
148 gator_commit_read = (read + 4) & COMMIT_MASK;
149}
150
151static void buffer_commit_write(int cpu, int readval, int writeval) {
152 int write = gator_commit_write;
153 gator_commit[write+0] = cpu;
154 gator_commit[write+1] = readval;
155 gator_commit[write+2] = writeval;
156 gator_commit_write = (write + 4) & COMMIT_MASK;
157}
158
159/******************************************************************************
160 * Buffer management
161 ******************************************************************************/
162static uint32_t use_buffer_size;
163static uint32_t use_buffer_mask;
164static DEFINE_PER_CPU(int, use_buffer_seq);
165static DEFINE_PER_CPU(int, use_buffer_read);
166static DEFINE_PER_CPU(int, use_buffer_write);
167static DEFINE_PER_CPU(char *, use_buffer);
168
169static void gator_buffer_write_packed_int(int cpu, unsigned int x)
170{
171 uint32_t write = per_cpu(use_buffer_write, cpu);
172 uint32_t mask = use_buffer_mask;
173 char *buffer = per_cpu(use_buffer, cpu);
174 int write0 = (write + 0) & mask;
175 int write1 = (write + 1) & mask;
176 int write2 = (write + 2) & mask;
177 int write3 = (write + 3) & mask;
178 int write4 = (write + 4) & mask;
179 int write5 = (write + 5) & mask;
180
181 if ((x & 0xffffff80) == 0) {
182 buffer[write0] = x & 0x7f;
183 per_cpu(use_buffer_write, cpu) = write1;
184 } else if ((x & 0xffffc000) == 0) {
185 buffer[write0] = x | 0x80;
186 buffer[write1] = (x>>7) & 0x7f;
187 per_cpu(use_buffer_write, cpu) = write2;
188 } else if ((x & 0xffe00000) == 0) {
189 buffer[write0] = x | 0x80;
190 buffer[write1] = (x>>7) | 0x80;
191 buffer[write2] = (x>>14) & 0x7f;
192 per_cpu(use_buffer_write, cpu) = write3;
193 } else if ((x & 0xf0000000) == 0) {
194 buffer[write0] = x | 0x80;
195 buffer[write1] = (x>>7) | 0x80;
196 buffer[write2] = (x>>14) | 0x80;
197 buffer[write3] = (x>>21) & 0x7f;
198 per_cpu(use_buffer_write, cpu) = write4;
199 } else {
200 buffer[write0] = x | 0x80;
201 buffer[write1] = (x>>7) | 0x80;
202 buffer[write2] = (x>>14) | 0x80;
203 buffer[write3] = (x>>21) | 0x80;
204 buffer[write4] = (x>>28) & 0x0f;
205 per_cpu(use_buffer_write, cpu) = write5;
206 }
207}
208
209static int gator_write_packed_int(char *buffer, unsigned int x)
210{
211 if ((x & 0xffffff80) == 0) {
212 buffer[0] = x & 0x7f;
213 return 1;
214 } else if ((x & 0xffffc000) == 0) {
215 buffer[0] = x | 0x80;
216 buffer[1] = (x>>7) & 0x7f;
217 return 2;
218 } else if ((x & 0xffe00000) == 0) {
219 buffer[0] = x | 0x80;
220 buffer[1] = (x>>7) | 0x80;
221 buffer[2] = (x>>14) & 0x7f;
222 return 3;
223 } else if ((x & 0xf0000000) == 0) {
224 buffer[0] = x | 0x80;
225 buffer[1] = (x>>7) | 0x80;
226 buffer[2] = (x>>14) | 0x80;
227 buffer[3] = (x>>21) & 0x7f;
228 return 4;
229 } else {
230 buffer[0] = x | 0x80;
231 buffer[1] = (x>>7) | 0x80;
232 buffer[2] = (x>>14) | 0x80;
233 buffer[3] = (x>>21) | 0x80;
234 buffer[4] = (x>>28) & 0x0f;
235 return 5;
236 }
237}
238
239static void gator_buffer_write_bytes(int cpu, char *x, int len)
240{
241 uint32_t write = per_cpu(use_buffer_write, cpu);
242 uint32_t mask = use_buffer_mask;
243 char *buffer = per_cpu(use_buffer, cpu);
244 int i;
245
246 for (i = 0; i < len; i++) {
247 buffer[write] = x[i];
248 write = (write + 1) & mask;
249 }
250
251 per_cpu(use_buffer_write, cpu) = write;
252}
253
254static void gator_buffer_write_string(int cpu, char *x)
255{
256 int len = strlen(x);
257 gator_buffer_write_packed_int(cpu, len);
258 gator_buffer_write_bytes(cpu, x, len);
259}
260
261static void gator_buffer_header(int cpu)
262{
263 gator_buffer_write_packed_int(cpu, PROTOCOL_FRAME);
264 gator_buffer_write_packed_int(cpu, cpu);
265 gator_buffer_write_packed_int(cpu, per_cpu(use_buffer_seq, cpu));
266 per_cpu(use_buffer_seq, cpu)++;
267}
268
269static void gator_buffer_commit(int cpu)
270{
271 buffer_commit_write(cpu, per_cpu(use_buffer_read, cpu), per_cpu(use_buffer_write, cpu));
272 per_cpu(use_buffer_read, cpu) = per_cpu(use_buffer_write, cpu);
273 gator_buffer_header(cpu);
274 wake_up(&gator_buffer_wait);
275}
276
277static void gator_buffer_check(int cpu, int tick)
278{
279 if (!(tick % gator_timer_count)) {
280 int c, sync;
281 spin_lock(&gator_commit_lock);
282 // synchronize, if all online cpus have the same tick waypoint
283 sync = per_cpu(gator_cpu_sync, cpu) = per_cpu(gator_cpu_tick, cpu);
284 for_each_online_cpu(c) {
285 if (sync != per_cpu(gator_cpu_sync, c)) {
286 sync = 0;
287 break;
288 }
289 }
290 if (sync) {
291 gator_buffer_write_packed_int(cpu, PROTOCOL_CPU_SYNC);
292 }
293 gator_buffer_commit(cpu);
294 spin_unlock(&gator_commit_lock);
295 } else {
296 int available = per_cpu(use_buffer_write, cpu) - per_cpu(use_buffer_read, cpu);
297 if (available < 0) {
298 available += use_buffer_size;
299 }
300 if (available >= ((use_buffer_size * 3) / 4)) {
301 spin_lock(&gator_commit_lock);
302 gator_buffer_commit(cpu);
303 spin_unlock(&gator_commit_lock);
304 }
305 }
306}
307
308static void gator_add_trace(int cpu, unsigned int address)
309{
310 off_t offset = 0;
311 unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset);
312
313 if (cookie == NO_COOKIE || cookie == INVALID_COOKIE) {
314 offset = address;
315 }
316
317 gator_buffer_write_packed_int(cpu, offset & ~1);
318 gator_buffer_write_packed_int(cpu, cookie);
319}
320
321static void gator_add_sample(int cpu, struct pt_regs * const regs)
322{
323 struct module *mod;
324 unsigned int addr, cookie = 0;
325 int inKernel = regs ? !user_mode(regs) : 1;
326 unsigned long exec_cookie = !inKernel ? get_exec_cookie(cpu, current) : NO_COOKIE;
327
328 gator_buffer_write_packed_int(cpu, PROTOCOL_START_BACKTRACE);
329
330 // TGID::PID::inKernel
331 gator_buffer_write_packed_int(cpu, exec_cookie);
332 gator_buffer_write_packed_int(cpu, (unsigned int)current->tgid);
333 gator_buffer_write_packed_int(cpu, (unsigned int)current->pid);
334 gator_buffer_write_packed_int(cpu, inKernel);
335
336 // get_irq_regs() will return NULL outside of IRQ context (e.g. nested IRQ)
337 if (regs) {
338 if (inKernel) {
339 addr = PC_REG;
340 mod = __module_address(addr);
341 if (mod) {
342 cookie = get_cookie(cpu, current, NULL, mod);
343 addr = addr - (unsigned long)mod->module_core;
344 }
345 gator_buffer_write_packed_int(cpu, addr & ~1);
346 gator_buffer_write_packed_int(cpu, cookie);
347 } else {
348 // Cookie+PC
349 gator_add_trace(cpu, PC_REG);
350
351 // Backtrace
352 if (gator_backtrace_depth)
353 arm_backtrace_eabi(cpu, regs, gator_backtrace_depth);
354 }
355 }
356
357 gator_buffer_write_packed_int(cpu, PROTOCOL_END_BACKTRACE);
358}
359
360static void gator_write_packet(int cpu, int type, int len, int *buffer)
361{
362 int i;
363 gator_buffer_write_packed_int(cpu, type);
364 gator_buffer_write_packed_int(cpu, len);
365 for (i = 0; i < len; i++) {
366 gator_buffer_write_packed_int(cpu, buffer[i]);
367 }
368}
369
370/******************************************************************************
371 * Interrupt Processing
372 ******************************************************************************/
373static LIST_HEAD(gator_events);
374
375static void gator_timer_interrupt(void)
376{
377 struct pt_regs * const regs = get_irq_regs();
378 int cpu = smp_processor_id();
379 int *buffer, len, tick;
380 struct gator_interface *gi;
381
382 // check full backtrace has enough space, otherwise may
383 // have breaks between samples in the same callstack
384 if (per_cpu(gator_first_time, cpu)) {
385 per_cpu(gator_first_time, cpu) = 0;
386
387 list_for_each_entry(gi, &gator_events, list)
388 if (gi->read)
389 gi->read(NULL);
390
391 return;
392 }
393
394 // Header
395 gator_buffer_write_packed_int(cpu, PROTOCOL_START_TICK); // Escape
396
397 // Output scheduler
398 len = gator_trace_sched_read(&buffer);
399 if (len > 0) {
400 gator_write_packet(cpu, PROTOCOL_SCHEDULER_TRACE, len, buffer);
401 }
402
403 // Output counters
404 list_for_each_entry(gi, &gator_events, list) {
405 if (gi->read) {
406 len = gi->read(&buffer);
407 if (len > 0)
408 gator_write_packet(cpu, PROTOCOL_COUNTERS, len, buffer);
409 }
410 }
411
412 // Output backtrace
413 gator_add_sample(cpu, regs);
414
415 // Timer Tick
416 tick = per_cpu(gator_cpu_tick, cpu);
417 if (tick == gator_master_tick) {
418 tick++;
419 per_cpu(gator_cpu_tick, cpu) = gator_master_tick = tick;
420 } else {
421 per_cpu(gator_cpu_tick, cpu) = tick = gator_master_tick;
422 }
423 gator_write_packet(cpu, PROTOCOL_END_TICK, 1, &tick);
424
425 // Check and commit; generally, commit is set to occur once per second
426 gator_buffer_check(cpu, tick);
427}
428
429/******************************************************************************
430 * hrtimer
431 ******************************************************************************/
432DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer);
433DEFINE_PER_CPU(int, hrtimer_is_active);
434static int hrtimer_running;
435static ktime_t profiling_interval;
436
437static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer)
438{
439 hrtimer_forward_now(hrtimer, profiling_interval);
440 gator_timer_interrupt();
441 return HRTIMER_RESTART;
442}
443
444static int gator_timer_init(void)
445{
446 return 0;
447}
448
449static void __gator_timer_offline(void *unused)
450{
451 int cpu = smp_processor_id();
452 if (per_cpu(hrtimer_is_active, cpu)) {
453 struct gator_interface *gi;
454 struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu);
455 hrtimer_cancel(hrtimer);
456 per_cpu(hrtimer_is_active, cpu) = 0;
457 gator_buffer_commit(cpu);
458
459 // offline any events
460 list_for_each_entry(gi, &gator_events, list)
461 if (gi->offline)
462 gi->offline();
463 }
464}
465
466static void gator_timer_offline(void)
467{
468 if (hrtimer_running) {
469 hrtimer_running = 0;
470
471 on_each_cpu(__gator_timer_offline, NULL, 1);
472
473 // output a final sync point
474 gator_buffer_write_packed_int(0, PROTOCOL_CPU_SYNC);
475 gator_buffer_commit(0);
476 }
477}
478
479static void __gator_timer_online(void *unused)
480{
481 int cpu = smp_processor_id();
482 if (!per_cpu(hrtimer_is_active, cpu)) {
483 struct gator_interface *gi;
484 struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu);
485 hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
486 hrtimer->function = gator_hrtimer_notify;
487 hrtimer_start(hrtimer, profiling_interval, HRTIMER_MODE_REL_PINNED);
488 per_cpu(gator_cpu_tick, cpu) = 0;
489 per_cpu(gator_first_time, cpu) = 1;
490 per_cpu(hrtimer_is_active, cpu) = 1;
491
492 // online any events
493 list_for_each_entry(gi, &gator_events, list)
494 if (gi->online)
495 gi->online();
496 }
497}
498
499int gator_timer_online(unsigned long setup)
500{
501 if (!setup) {
502 pr_err("gator: cannot start due to a system tick value of zero");
503 return -1;
504 } else if (hrtimer_running) {
505 pr_notice("gator: high res timer already running");
506 return 0;
507 }
508
509 hrtimer_running = 1;
510
511 // calculate profiling interval
512 profiling_interval = ns_to_ktime(1000000000UL / setup);
513
514 // timer interrupt
515 gator_master_tick = 0;
516 on_each_cpu(__gator_timer_online, NULL, 1);
517
518 return 0;
519}
520
521static uint64_t gator_get_time(void)
522{
523 struct timespec ts;
524 uint64_t timestamp;
525
526 ktime_get_ts(&ts);
527 timestamp = timespec_to_ns(&ts);
528
529 return timestamp;
530}
531
532/******************************************************************************
533 * cpu online notifier
534 ******************************************************************************/
535static int __cpuinit gator_cpu_notify(struct notifier_block *self,
536 unsigned long action, void *hcpu)
537{
538 long cpu = (long)hcpu;
539
540 switch (action) {
541 case CPU_ONLINE:
542 case CPU_ONLINE_FROZEN:
543 smp_call_function_single(cpu, __gator_timer_online, NULL, 1);
544 break;
545 case CPU_DOWN_PREPARE:
546 case CPU_DOWN_PREPARE_FROZEN:
547 smp_call_function_single(cpu, __gator_timer_offline, NULL, 1);
548 break;
549 }
550
551 return NOTIFY_OK;
552}
553
554static struct notifier_block __refdata gator_cpu_notifier = {
555 .notifier_call = gator_cpu_notify,
556};
557
558static int gator_notifier_start(void)
559{
560 return register_hotcpu_notifier(&gator_cpu_notifier);
561}
562
563static void gator_notifier_stop(void)
564{
565 unregister_hotcpu_notifier(&gator_cpu_notifier);
566}
567
568/******************************************************************************
569 * Main
570 ******************************************************************************/
571int gator_events_install(struct gator_interface *interface)
572{
573 list_add_tail(&interface->list, &gator_events);
574
575 return 0;
576}
577
578int gator_events_get_key(void)
579{
580 static int key;
581
582 return key++;
583}
584
585static int gator_init(void)
586{
587 int i;
588
589 if (gator_timer_init())
590 return -1;
591 if (gator_trace_sched_init())
592 return -1;
593 if (gator_annotate_init())
594 return -1;
595
596 // events sources (gator_events.h, generated by gator_events.sh)
597 for (i = 0; i < ARRAY_SIZE(gator_events_list); i++)
598 if (gator_events_list[i])
599 gator_events_list[i]();
600
601 return 0;
602}
603
604static int gator_start(void)
605{
606 struct gator_interface *gi;
607
608 // start all events
609 list_for_each_entry(gi, &gator_events, list) {
610 if (gi->start && gi->start() != 0) {
611 struct list_head *ptr = gi->list.prev;
612
613 while (ptr != &gator_events) {
614 gi = list_entry(ptr, struct gator_interface,
615 list);
616
617 if (gi->stop)
618 gi->stop();
619
620 ptr = ptr->prev;
621 }
622 goto events_failure;
623 }
624 }
625
626 if (gator_annotate_start())
627 goto annotate_failure;
628 if (gator_trace_sched_start())
629 goto sched_failure;
630 if (gator_timer_online(gator_timer_count))
631 goto timer_failure;
632 if (gator_notifier_start())
633 goto notifier_failure;
634
635 return 0;
636
637notifier_failure:
638 gator_timer_offline();
639timer_failure:
640 gator_trace_sched_stop();
641sched_failure:
642 gator_annotate_stop();
643annotate_failure:
644events_failure:
645
646 return -1;
647}
648
649static void gator_stop(void)
650{
651 struct gator_interface *gi;
652
653 // stop all events
654 list_for_each_entry(gi, &gator_events, list)
655 if (gi->stop)
656 gi->stop();
657
658 gator_annotate_stop();
659 gator_trace_sched_stop();
660
661 // stop all interrupt callback reads before tearing down other interfaces
662 gator_timer_offline();
663 gator_notifier_stop();
664}
665
666static void gator_exit(void)
667{
668 gator_annotate_exit();
669}
670
671/******************************************************************************
672 * Filesystem
673 ******************************************************************************/
674/* fopen("buffer") */
675static int gator_op_setup(void)
676{
677 int err = 0;
678 int cpu;
679
680 mutex_lock(&start_mutex);
681
682 use_buffer_size = gator_buffer_size;
683 use_buffer_mask = use_buffer_size - 1;
684
685 // must be a power of 2
686 if (use_buffer_size & (use_buffer_size - 1)) {
687 err = -ENOEXEC;
688 goto setup_error;
689 }
690
691 gator_net_traffic = 0;
692
693 gator_commit_read = gator_commit_write = 0;
694 gator_commit = vmalloc(COMMIT_SIZE * sizeof(int));
695 if (!gator_commit) {
696 err = -ENOMEM;
697 goto setup_error;
698 }
699
700 for_each_present_cpu(cpu) {
701 per_cpu(use_buffer, cpu) = vmalloc(use_buffer_size);
702 if (!per_cpu(use_buffer, cpu)) {
703 err = -ENOMEM;
704 goto setup_error;
705 }
706
707 per_cpu(gator_cpu_sync, cpu) = 0;
708 per_cpu(gator_cpu_tick, cpu) = 0;
709
710 per_cpu(use_buffer_seq, cpu) = 0;
711 per_cpu(use_buffer_read, cpu) = 0;
712 per_cpu(use_buffer_write, cpu) = 0;
713 gator_buffer_header(cpu);
714 }
715
716setup_error:
717 mutex_unlock(&start_mutex);
718 return err;
719}
720
721/* Actually start profiling (echo 1>/dev/gator/enable) */
722static int gator_op_start(void)
723{
724 int err = 0;
725
726 mutex_lock(&start_mutex);
727
728 if (gator_started || gator_start() || cookies_initialize())
729 err = -EINVAL;
730 else
731 gator_started = 1;
732
733 mutex_unlock(&start_mutex);
734
735 return err;
736}
737
738/* echo 0>/dev/gator/enable */
739static void gator_op_stop(void)
740{
741 mutex_lock(&start_mutex);
742
743 if (gator_started) {
744 gator_stop();
745
746 mutex_lock(&gator_buffer_mutex);
747
748 gator_started = 0;
749 cookies_release();
750 wake_up(&gator_buffer_wait);
751
752 mutex_unlock(&gator_buffer_mutex);
753 }
754
755 mutex_unlock(&start_mutex);
756}
757
758static void gator_shutdown(void)
759{
760 int cpu;
761
762 mutex_lock(&start_mutex);
763
764 vfree(gator_commit);
765 gator_commit = NULL;
766
767 for_each_present_cpu(cpu) {
768 mutex_lock(&gator_buffer_mutex);
769 vfree(per_cpu(use_buffer, cpu));
770 per_cpu(use_buffer, cpu) = NULL;
771 per_cpu(use_buffer_seq, cpu) = 0;
772 per_cpu(use_buffer_read, cpu) = 0;
773 per_cpu(use_buffer_write, cpu) = 0;
774 mutex_unlock(&gator_buffer_mutex);
775 }
776
777 mutex_unlock(&start_mutex);
778}
779
780static int gator_set_backtrace(unsigned long val)
781{
782 int err = 0;
783
784 mutex_lock(&start_mutex);
785
786 if (gator_started)
787 err = -EBUSY;
788 else
789 gator_backtrace_depth = val;
790
791 mutex_unlock(&start_mutex);
792
793 return err;
794}
795
796static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
797{
798 return gatorfs_ulong_to_user(gator_started, buf, count, offset);
799}
800
801static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
802{
803 unsigned long val;
804 int retval;
805
806 if (*offset)
807 return -EINVAL;
808
809 retval = gatorfs_ulong_from_user(&val, buf, count);
810 if (retval)
811 return retval;
812
813 if (val)
814 retval = gator_op_start();
815 else
816 gator_op_stop();
817
818 if (retval)
819 return retval;
820 return count;
821}
822
823static const struct file_operations enable_fops = {
824 .read = enable_read,
825 .write = enable_write,
826};
827
828static int event_buffer_open(struct inode *inode, struct file *file)
829{
830 int err = -EPERM;
831
832 if (!capable(CAP_SYS_ADMIN))
833 return -EPERM;
834
835 if (test_and_set_bit_lock(0, &gator_buffer_opened))
836 return -EBUSY;
837
838 if ((err = gator_op_setup()))
839 goto fail;
840
841 /* NB: the actual start happens from userspace
842 * echo 1 >/dev/gator/enable
843 */
844
845 return 0;
846
847fail:
848 __clear_bit_unlock(0, &gator_buffer_opened);
849 return err;
850}
851
852static int event_buffer_release(struct inode *inode, struct file *file)
853{
854 gator_op_stop();
855 gator_shutdown();
856 __clear_bit_unlock(0, &gator_buffer_opened);
857 return 0;
858}
859
860static ssize_t event_buffer_read(struct file *file, char __user *buf,
861 size_t count, loff_t *offset)
862{
863 int retval = -EINVAL;
864 int commit, length1, length2, read;
865 char *buffer1, *buffer2;
866 char annotate_header[6];
867 int cpu;
868
869 /* do not handle partial reads */
870 if (count != use_buffer_size || *offset)
871 return -EINVAL;
872
873 // sleep until the condition is true or a signal is received
874 // the condition is checked each time gator_buffer_wait is woken up
875 wait_event_interruptible(gator_buffer_wait, buffer_commit_ready() || gator_annotate_ready() || !gator_started);
876
877 if (signal_pending(current))
878 return -EINTR;
879
880 retval = -EFAULT;
881
882 mutex_lock(&gator_buffer_mutex);
883
884 if (buffer_commit_ready()) {
885 buffer_commit_read(&cpu, &read, &commit);
886
887 /* May happen if the buffer is freed during pending reads. */
888 if (!per_cpu(use_buffer, cpu)) {
889 retval = -EFAULT;
890 goto out;
891 }
892
893 /* determine the size of two halves */
894 length1 = commit - read;
895 length2 = 0;
896 buffer1 = &(per_cpu(use_buffer, cpu)[read]);
897 buffer2 = &(per_cpu(use_buffer, cpu)[0]);
898 if (length1 < 0) {
899 length1 = use_buffer_size - read;
900 length2 = commit;
901 }
902 } else if (gator_annotate_ready()) {
903 length2 = gator_annotate_read(&buffer2);
904 if (!length2)
905 goto out;
906 annotate_header[0] = PROTOCOL_ANNOTATE;
907 length1 = gator_write_packed_int(&annotate_header[1], length2) + 1;
908 buffer1 = annotate_header;
909 } else {
910 retval = 0;
911 goto out;
912 }
913
914 /* start, middle or end */
915 if (length1 > 0) {
916 if (copy_to_user(&buf[0], buffer1, length1)) {
917 goto out;
918 }
919 }
920
921 /* possible wrap around */
922 if (length2 > 0) {
923 if (copy_to_user(&buf[length1], buffer2, length2)) {
924 goto out;
925 }
926 }
927
928 retval = length1 + length2;
929
930 /* kick just in case we've lost an SMP event */
931 wake_up(&gator_buffer_wait);
932
933out:
934 // only adjust network stats if in streaming mode
935 if (gator_streaming)
936 gator_net_traffic += retval;
937 mutex_unlock(&gator_buffer_mutex);
938 return retval;
939}
940
941const struct file_operations gator_event_buffer_fops = {
942 .open = event_buffer_open,
943 .release = event_buffer_release,
944 .read = event_buffer_read,
945};
946
947static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
948{
949 return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count,
950 offset);
951}
952
953static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
954{
955 unsigned long val;
956 int retval;
957
958 if (*offset)
959 return -EINVAL;
960
961 retval = gatorfs_ulong_from_user(&val, buf, count);
962 if (retval)
963 return retval;
964
965 retval = gator_set_backtrace(val);
966
967 if (retval)
968 return retval;
969 return count;
970}
971
972static const struct file_operations depth_fops = {
973 .read = depth_read,
974 .write = depth_write
975};
976
977static const char gator_cpu_type[] = "gator";
978
979static ssize_t cpu_type_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
980{
981 return gatorfs_str_to_user(gator_cpu_type, buf, count, offset);
982}
983
984static const struct file_operations cpu_type_fops = {
985 .read = cpu_type_read,
986};
987
988void gator_op_create_files(struct super_block *sb, struct dentry *root)
989{
990 struct dentry *dir;
991 struct gator_interface *gi;
992 int cpu;
993
994 /* reinitialize default values */
995 gator_cpu_cores = 0;
996 for_each_present_cpu(cpu) {
997 gator_cpu_cores++;
998 }
999 gator_buffer_size = BUFFER_SIZE_DEFAULT;
1000 gator_streaming = 1;
1001
1002 gatorfs_create_file(sb, root, "enable", &enable_fops);
1003 gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops);
1004 gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops);
1005 gatorfs_create_file(sb, root, "cpu_type", &cpu_type_fops);
1006 gatorfs_create_ulong(sb, root, "cpu_cores", &gator_cpu_cores);
1007 gatorfs_create_ulong(sb, root, "buffer_size", &gator_buffer_size);
1008 gatorfs_create_ulong(sb, root, "tick", &gator_timer_count);
1009 gatorfs_create_ulong(sb, root, "streaming", &gator_streaming);
1010 gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version);
1011
1012 // Annotate interface
1013 gator_annotate_create_files(sb, root);
1014
1015 // Linux Events
1016 dir = gatorfs_mkdir(sb, root, "events");
1017 list_for_each_entry(gi, &gator_events, list)
1018 if (gi->create_files)
1019 gi->create_files(sb, dir);
1020}
1021
1022/******************************************************************************
1023 * Module
1024 ******************************************************************************/
1025static int __init gator_module_init(void)
1026{
1027 if (gatorfs_register()) {
1028 return -1;
1029 }
1030
1031 if (gator_init()) {
1032 gatorfs_unregister();
1033 return -1;
1034 }
1035
1036#ifdef GATOR_DEBUG
1037 pr_err("gator_module_init");
1038#endif
1039 return 0;
1040}
1041
1042static void __exit gator_module_exit(void)
1043{
1044#ifdef GATOR_DEBUG
1045 pr_err("gator_module_exit");
1046#endif
1047 tracepoint_synchronize_unregister();
1048 gatorfs_unregister();
1049 gator_exit();
1050}
1051
1052module_init(gator_module_init);
1053module_exit(gator_module_exit);
1054
1055MODULE_LICENSE("GPL");
1056MODULE_AUTHOR("ARM Ltd");
1057MODULE_DESCRIPTION("Gator system profiler");
diff --git a/driver/gator_trace.h b/driver/gator_trace.h
new file mode 100644
index 0000000..446d37b
--- /dev/null
+++ b/driver/gator_trace.h
@@ -0,0 +1,26 @@
1/**
2 * Copyright 2010 ARM, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
11# error Kernels prior to 2.6.32 not supported
12#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
13# define GATOR_DEFINE_PROBE(probe_name, proto) \
14 static void probe_##probe_name(PARAMS(proto))
15# define GATOR_REGISTER_TRACE(probe_name) \
16 register_trace_##probe_name(probe_##probe_name)
17# define GATOR_UNREGISTER_TRACE(probe_name) \
18 unregister_trace_##probe_name(probe_##probe_name)
19#else
20# define GATOR_DEFINE_PROBE(probe_name, proto) \
21 static void probe_##probe_name(void *data, PARAMS(proto))
22# define GATOR_REGISTER_TRACE(probe_name) \
23 register_trace_##probe_name(probe_##probe_name, NULL)
24# define GATOR_UNREGISTER_TRACE(probe_name) \
25 unregister_trace_##probe_name(probe_##probe_name, NULL)
26#endif
diff --git a/driver/gator_trace_sched.c b/driver/gator_trace_sched.c
new file mode 100644
index 0000000..19d8d89
--- /dev/null
+++ b/driver/gator_trace_sched.c
@@ -0,0 +1,263 @@
1/**
2 * Copyright (C) ARM Limited 2010-2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <trace/events/sched.h>
11#include "gator.h"
12
13#define SCHED_TIMER_EVENT 0
14#define SCHED_WAIT_TASK 1
15#define SCHED_WAKEUP 2
16#define SCHED_WAKEUP_NEW 3
17#define SCHED_SWITCH 4
18#define SCHED_MIGRATE_TASK 5
19#define SCHED_PROCESS_FREE 6
20#define SCHED_PROCESS_EXIT 7
21#define SCHED_PROCESS_WAIT 8
22#define SCHED_PROCESS_FORK 9
23#define SCHED_OVERFLOW -1
24
25#define SCHEDSIZE (16*1024)
26
27static DEFINE_PER_CPU(int *[2], theSchedBuf);
28static DEFINE_PER_CPU(int, theSchedSel);
29static DEFINE_PER_CPU(int, theSchedPos);
30static DEFINE_PER_CPU(int, theSchedErr);
31
32static void probe_sched_write(int type, int param1, int param2, int param3)
33{
34 unsigned long flags;
35 int cpu = smp_processor_id();
36 uint64_t time = gator_get_time();
37 int *schedBuf;
38 int schedPos;
39
40 if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)])
41 return;
42
43 // disable interrupts to synchronize with gator_trace_sched_read(); spinlocks not needed since percpu buffers are used
44 local_irq_save(flags);
45
46 schedPos = per_cpu(theSchedPos, cpu);
47 schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)];
48
49 if (schedPos < (SCHEDSIZE-100)) {
50 // capture
51 schedBuf[schedPos+0] = type;
52 schedBuf[schedPos+1] = (int)time;
53 schedBuf[schedPos+2] = (int)(time >> 32);
54 schedBuf[schedPos+3] = param1;
55 schedBuf[schedPos+4] = param2;
56 schedBuf[schedPos+5] = param3;
57 per_cpu(theSchedPos, cpu) = schedPos + 6;
58 } else if (!per_cpu(theSchedErr, cpu)) {
59 per_cpu(theSchedErr, cpu) = 1;
60 schedBuf[schedPos+0] = SCHED_OVERFLOW;
61 schedBuf[schedPos+1] = 0;
62 schedBuf[schedPos+2] = 0;
63 schedBuf[schedPos+3] = 0;
64 schedBuf[schedPos+4] = 0;
65 schedBuf[schedPos+5] = 0;
66 per_cpu(theSchedPos, cpu) = schedPos + 6;
67 pr_debug("gator: tracepoint overflow\n");
68 }
69 local_irq_restore(flags);
70}
71
72#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
73GATOR_DEFINE_PROBE(sched_wait_task, TP_PROTO(struct rq *rq, struct task_struct *p))
74#else
75GATOR_DEFINE_PROBE(sched_wait_task, TP_PROTO(struct task_struct *p))
76#endif
77{
78 probe_sched_write(SCHED_WAIT_TASK, 0, p->pid, 0);
79}
80
81#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
82GATOR_DEFINE_PROBE(sched_wakeup, TP_PROTO(struct rq *rq, struct task_struct *p, int success))
83#else
84GATOR_DEFINE_PROBE(sched_wakeup, TP_PROTO(struct task_struct *p, int success))
85#endif
86{
87 if (success)
88 probe_sched_write(SCHED_WAKEUP, 0, p->pid, 0);
89}
90
91#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
92GATOR_DEFINE_PROBE(sched_wakeup_new, TP_PROTO(struct rq *rq, struct task_struct *p, int success))
93#else
94GATOR_DEFINE_PROBE(sched_wakeup_new, TP_PROTO(struct task_struct *p, int success))
95#endif
96{
97 if (success)
98 probe_sched_write(SCHED_WAKEUP_NEW, 0, p->tgid, p->pid);
99}
100
101#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
102GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
103#else
104GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
105#endif
106{
107 probe_sched_write(SCHED_SWITCH, (int)next, next->tgid, next->pid);
108}
109
110GATOR_DEFINE_PROBE(sched_migrate_task, TP_PROTO(struct task_struct *p, int dest_cpu))
111{
112 probe_sched_write(SCHED_MIGRATE_TASK, 0, dest_cpu, p->pid);
113}
114
115GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p))
116{
117 probe_sched_write(SCHED_PROCESS_FREE, 0, p->pid, 0);
118}
119
120GATOR_DEFINE_PROBE(sched_process_exit, TP_PROTO(struct task_struct *p))
121{
122 probe_sched_write(SCHED_PROCESS_EXIT, 0, p->pid, 0);
123}
124
125GATOR_DEFINE_PROBE(sched_process_wait, TP_PROTO(struct pid *pid))
126{
127 probe_sched_write(SCHED_PROCESS_WAIT, 0, pid_nr(pid), 0);
128}
129
130GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child))
131{
132 probe_sched_write(SCHED_PROCESS_FORK, (int)child, parent->pid, child->pid);
133}
134
135int gator_trace_sched_init(void)
136{
137 return 0;
138}
139
140int gator_trace_sched_start(void)
141{
142 int cpu;
143
144 for_each_present_cpu(cpu) {
145 per_cpu(theSchedSel, cpu) = 0;
146 per_cpu(theSchedPos, cpu) = 0;
147 per_cpu(theSchedErr, cpu) = 0;
148 per_cpu(theSchedBuf, cpu)[0] = kmalloc(SCHEDSIZE * sizeof(int), GFP_KERNEL);
149 per_cpu(theSchedBuf, cpu)[1] = kmalloc(SCHEDSIZE * sizeof(int), GFP_KERNEL);
150 if (!per_cpu(theSchedBuf, cpu))
151 return -1;
152 }
153
154 // register tracepoints
155 if (GATOR_REGISTER_TRACE(sched_wait_task))
156 goto fail_sched_wait_task;
157 if (GATOR_REGISTER_TRACE(sched_wakeup))
158 goto fail_sched_wakeup;
159 if (GATOR_REGISTER_TRACE(sched_wakeup_new))
160 goto fail_sched_wakeup_new;
161 if (GATOR_REGISTER_TRACE(sched_switch))
162 goto fail_sched_switch;
163 if (GATOR_REGISTER_TRACE(sched_migrate_task))
164 goto fail_sched_migrate_task;
165 if (GATOR_REGISTER_TRACE(sched_process_free))
166 goto fail_sched_process_free;
167 if (GATOR_REGISTER_TRACE(sched_process_exit))
168 goto fail_sched_process_exit;
169 if (GATOR_REGISTER_TRACE(sched_process_wait))
170 goto fail_sched_process_wait;
171 if (GATOR_REGISTER_TRACE(sched_process_fork))
172 goto fail_sched_process_fork;
173 pr_debug("gator: registered tracepoints\n");
174
175 return 0;
176
177 // unregister tracepoints on error
178fail_sched_process_fork:
179 GATOR_UNREGISTER_TRACE(sched_process_wait);
180fail_sched_process_wait:
181 GATOR_UNREGISTER_TRACE(sched_process_exit);
182fail_sched_process_exit:
183 GATOR_UNREGISTER_TRACE(sched_process_free);
184fail_sched_process_free:
185 GATOR_UNREGISTER_TRACE(sched_migrate_task);
186fail_sched_migrate_task:
187 GATOR_UNREGISTER_TRACE(sched_switch);
188fail_sched_switch:
189 GATOR_UNREGISTER_TRACE(sched_wakeup_new);
190fail_sched_wakeup_new:
191 GATOR_UNREGISTER_TRACE(sched_wakeup);
192fail_sched_wakeup:
193 GATOR_UNREGISTER_TRACE(sched_wait_task);
194fail_sched_wait_task:
195 pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
196
197 return -1;
198}
199
200void gator_trace_sched_stop(void)
201{
202 int cpu;
203 GATOR_UNREGISTER_TRACE(sched_wait_task);
204 GATOR_UNREGISTER_TRACE(sched_wakeup);
205 GATOR_UNREGISTER_TRACE(sched_wakeup_new);
206 GATOR_UNREGISTER_TRACE(sched_switch);
207 GATOR_UNREGISTER_TRACE(sched_migrate_task);
208 GATOR_UNREGISTER_TRACE(sched_process_free);
209 GATOR_UNREGISTER_TRACE(sched_process_exit);
210 GATOR_UNREGISTER_TRACE(sched_process_wait);
211 GATOR_UNREGISTER_TRACE(sched_process_fork);
212 pr_debug("gator: unregistered tracepoints\n");
213
214 for_each_present_cpu(cpu) {
215 kfree(per_cpu(theSchedBuf, cpu)[0]);
216 kfree(per_cpu(theSchedBuf, cpu)[1]);
217 per_cpu(theSchedBuf, cpu)[0] = NULL;
218 per_cpu(theSchedBuf, cpu)[1] = NULL;
219 }
220}
221
222int gator_trace_sched_read(int **buffer)
223{
224 uint64_t time = gator_get_time();
225 int cpu = smp_processor_id();
226 unsigned long flags;
227 int *schedBuf;
228 int schedPos;
229 int i;
230
231 if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)])
232 return 0;
233
234 local_irq_save(flags);
235
236 schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)];
237 schedPos = per_cpu(theSchedPos, cpu);
238
239 per_cpu(theSchedSel, cpu) = !per_cpu(theSchedSel, cpu);
240 per_cpu(theSchedPos, cpu) = 0;
241 per_cpu(theSchedErr, cpu) = 0;
242
243 local_irq_restore(flags);
244
245 // find mm and replace with cookies
246 for (i = 0; i < schedPos; i += 6) {
247 uint32_t cookie = schedBuf[i+3];
248 if (cookie) {
249 struct task_struct *task = (struct task_struct *)cookie;
250 schedBuf[i+3] = get_exec_cookie(cpu, task);
251 }
252 }
253
254 // timer/end event
255 schedBuf[schedPos++] = SCHED_TIMER_EVENT;
256 schedBuf[schedPos++] = (int)time;
257 schedBuf[schedPos++] = (int)(time >> 32);
258
259 if (buffer)
260 *buffer = schedBuf;
261
262 return schedPos;
263}