Commit c00193f9f09f9b852249a66391985f585d066084
Exists in
master
and in
4 other branches
Merge branches 'oprofile-v2' and 'timers/hpet' into x86/core-v4
Showing 21 changed files Side-by-side Diff
- Documentation/00-INDEX
- Documentation/hpet.txt
- Documentation/timers/00-INDEX
- Documentation/timers/hpet.txt
- arch/Kconfig
- arch/x86/kernel/apic_32.c
- arch/x86/kernel/apic_64.c
- arch/x86/kernel/hpet.c
- arch/x86/kernel/quirks.c
- arch/x86/oprofile/Makefile
- arch/x86/oprofile/nmi_int.c
- arch/x86/oprofile/op_model_amd.c
- arch/x86/oprofile/op_model_athlon.c
- arch/x86/oprofile/op_x86_model.h
- arch/x86/pci/fixup.c
- drivers/char/hpet.c
- drivers/oprofile/buffer_sync.c
- drivers/oprofile/cpu_buffer.c
- drivers/oprofile/cpu_buffer.h
- include/linux/hpet.h
- include/linux/oprofile.h
Documentation/00-INDEX
... | ... | @@ -159,8 +159,6 @@ |
159 | 159 | - info on using the Hayes ESP serial driver. |
160 | 160 | highuid.txt |
161 | 161 | - notes on the change from 16 bit to 32 bit user/group IDs. |
162 | -hpet.txt | |
163 | - - High Precision Event Timer Driver for Linux. | |
164 | 162 | timers/ |
165 | 163 | - info on the timer related topics |
166 | 164 | hw_random.txt |
Documentation/hpet.txt
1 | - High Precision Event Timer Driver for Linux | |
2 | - | |
3 | -The High Precision Event Timer (HPET) hardware is the future replacement | |
4 | -for the 8254 and Real Time Clock (RTC) periodic timer functionality. | |
5 | -Each HPET can have up to 32 timers. It is possible to configure the | |
6 | -first two timers as legacy replacements for 8254 and RTC periodic timers. | |
7 | -A specification done by Intel and Microsoft can be found at | |
8 | -<http://www.intel.com/technology/architecture/hpetspec.htm>. | |
9 | - | |
10 | -The driver supports detection of HPET driver allocation and initialization | |
11 | -of the HPET before the driver module_init routine is called. This enables | |
12 | -platform code which uses timer 0 or 1 as the main timer to intercept HPET | |
13 | -initialization. An example of this initialization can be found in | |
14 | -arch/i386/kernel/time_hpet.c. | |
15 | - | |
16 | -The driver provides two APIs which are very similar to the API found in | |
17 | -the rtc.c driver. There is a user space API and a kernel space API. | |
18 | -An example user space program is provided below. | |
19 | - | |
20 | -#include <stdio.h> | |
21 | -#include <stdlib.h> | |
22 | -#include <unistd.h> | |
23 | -#include <fcntl.h> | |
24 | -#include <string.h> | |
25 | -#include <memory.h> | |
26 | -#include <malloc.h> | |
27 | -#include <time.h> | |
28 | -#include <ctype.h> | |
29 | -#include <sys/types.h> | |
30 | -#include <sys/wait.h> | |
31 | -#include <signal.h> | |
32 | -#include <fcntl.h> | |
33 | -#include <errno.h> | |
34 | -#include <sys/time.h> | |
35 | -#include <linux/hpet.h> | |
36 | - | |
37 | - | |
38 | -extern void hpet_open_close(int, const char **); | |
39 | -extern void hpet_info(int, const char **); | |
40 | -extern void hpet_poll(int, const char **); | |
41 | -extern void hpet_fasync(int, const char **); | |
42 | -extern void hpet_read(int, const char **); | |
43 | - | |
44 | -#include <sys/poll.h> | |
45 | -#include <sys/ioctl.h> | |
46 | -#include <signal.h> | |
47 | - | |
48 | -struct hpet_command { | |
49 | - char *command; | |
50 | - void (*func)(int argc, const char ** argv); | |
51 | -} hpet_command[] = { | |
52 | - { | |
53 | - "open-close", | |
54 | - hpet_open_close | |
55 | - }, | |
56 | - { | |
57 | - "info", | |
58 | - hpet_info | |
59 | - }, | |
60 | - { | |
61 | - "poll", | |
62 | - hpet_poll | |
63 | - }, | |
64 | - { | |
65 | - "fasync", | |
66 | - hpet_fasync | |
67 | - }, | |
68 | -}; | |
69 | - | |
70 | -int | |
71 | -main(int argc, const char ** argv) | |
72 | -{ | |
73 | - int i; | |
74 | - | |
75 | - argc--; | |
76 | - argv++; | |
77 | - | |
78 | - if (!argc) { | |
79 | - fprintf(stderr, "-hpet: requires command\n"); | |
80 | - return -1; | |
81 | - } | |
82 | - | |
83 | - | |
84 | - for (i = 0; i < (sizeof (hpet_command) / sizeof (hpet_command[0])); i++) | |
85 | - if (!strcmp(argv[0], hpet_command[i].command)) { | |
86 | - argc--; | |
87 | - argv++; | |
88 | - fprintf(stderr, "-hpet: executing %s\n", | |
89 | - hpet_command[i].command); | |
90 | - hpet_command[i].func(argc, argv); | |
91 | - return 0; | |
92 | - } | |
93 | - | |
94 | - fprintf(stderr, "do_hpet: command %s not implemented\n", argv[0]); | |
95 | - | |
96 | - return -1; | |
97 | -} | |
98 | - | |
99 | -void | |
100 | -hpet_open_close(int argc, const char **argv) | |
101 | -{ | |
102 | - int fd; | |
103 | - | |
104 | - if (argc != 1) { | |
105 | - fprintf(stderr, "hpet_open_close: device-name\n"); | |
106 | - return; | |
107 | - } | |
108 | - | |
109 | - fd = open(argv[0], O_RDONLY); | |
110 | - if (fd < 0) | |
111 | - fprintf(stderr, "hpet_open_close: open failed\n"); | |
112 | - else | |
113 | - close(fd); | |
114 | - | |
115 | - return; | |
116 | -} | |
117 | - | |
118 | -void | |
119 | -hpet_info(int argc, const char **argv) | |
120 | -{ | |
121 | -} | |
122 | - | |
123 | -void | |
124 | -hpet_poll(int argc, const char **argv) | |
125 | -{ | |
126 | - unsigned long freq; | |
127 | - int iterations, i, fd; | |
128 | - struct pollfd pfd; | |
129 | - struct hpet_info info; | |
130 | - struct timeval stv, etv; | |
131 | - struct timezone tz; | |
132 | - long usec; | |
133 | - | |
134 | - if (argc != 3) { | |
135 | - fprintf(stderr, "hpet_poll: device-name freq iterations\n"); | |
136 | - return; | |
137 | - } | |
138 | - | |
139 | - freq = atoi(argv[1]); | |
140 | - iterations = atoi(argv[2]); | |
141 | - | |
142 | - fd = open(argv[0], O_RDONLY); | |
143 | - | |
144 | - if (fd < 0) { | |
145 | - fprintf(stderr, "hpet_poll: open of %s failed\n", argv[0]); | |
146 | - return; | |
147 | - } | |
148 | - | |
149 | - if (ioctl(fd, HPET_IRQFREQ, freq) < 0) { | |
150 | - fprintf(stderr, "hpet_poll: HPET_IRQFREQ failed\n"); | |
151 | - goto out; | |
152 | - } | |
153 | - | |
154 | - if (ioctl(fd, HPET_INFO, &info) < 0) { | |
155 | - fprintf(stderr, "hpet_poll: failed to get info\n"); | |
156 | - goto out; | |
157 | - } | |
158 | - | |
159 | - fprintf(stderr, "hpet_poll: info.hi_flags 0x%lx\n", info.hi_flags); | |
160 | - | |
161 | - if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) { | |
162 | - fprintf(stderr, "hpet_poll: HPET_EPI failed\n"); | |
163 | - goto out; | |
164 | - } | |
165 | - | |
166 | - if (ioctl(fd, HPET_IE_ON, 0) < 0) { | |
167 | - fprintf(stderr, "hpet_poll, HPET_IE_ON failed\n"); | |
168 | - goto out; | |
169 | - } | |
170 | - | |
171 | - pfd.fd = fd; | |
172 | - pfd.events = POLLIN; | |
173 | - | |
174 | - for (i = 0; i < iterations; i++) { | |
175 | - pfd.revents = 0; | |
176 | - gettimeofday(&stv, &tz); | |
177 | - if (poll(&pfd, 1, -1) < 0) | |
178 | - fprintf(stderr, "hpet_poll: poll failed\n"); | |
179 | - else { | |
180 | - long data; | |
181 | - | |
182 | - gettimeofday(&etv, &tz); | |
183 | - usec = stv.tv_sec * 1000000 + stv.tv_usec; | |
184 | - usec = (etv.tv_sec * 1000000 + etv.tv_usec) - usec; | |
185 | - | |
186 | - fprintf(stderr, | |
187 | - "hpet_poll: expired time = 0x%lx\n", usec); | |
188 | - | |
189 | - fprintf(stderr, "hpet_poll: revents = 0x%x\n", | |
190 | - pfd.revents); | |
191 | - | |
192 | - if (read(fd, &data, sizeof(data)) != sizeof(data)) { | |
193 | - fprintf(stderr, "hpet_poll: read failed\n"); | |
194 | - } | |
195 | - else | |
196 | - fprintf(stderr, "hpet_poll: data 0x%lx\n", | |
197 | - data); | |
198 | - } | |
199 | - } | |
200 | - | |
201 | -out: | |
202 | - close(fd); | |
203 | - return; | |
204 | -} | |
205 | - | |
206 | -static int hpet_sigio_count; | |
207 | - | |
208 | -static void | |
209 | -hpet_sigio(int val) | |
210 | -{ | |
211 | - fprintf(stderr, "hpet_sigio: called\n"); | |
212 | - hpet_sigio_count++; | |
213 | -} | |
214 | - | |
215 | -void | |
216 | -hpet_fasync(int argc, const char **argv) | |
217 | -{ | |
218 | - unsigned long freq; | |
219 | - int iterations, i, fd, value; | |
220 | - sig_t oldsig; | |
221 | - struct hpet_info info; | |
222 | - | |
223 | - hpet_sigio_count = 0; | |
224 | - fd = -1; | |
225 | - | |
226 | - if ((oldsig = signal(SIGIO, hpet_sigio)) == SIG_ERR) { | |
227 | - fprintf(stderr, "hpet_fasync: failed to set signal handler\n"); | |
228 | - return; | |
229 | - } | |
230 | - | |
231 | - if (argc != 3) { | |
232 | - fprintf(stderr, "hpet_fasync: device-name freq iterations\n"); | |
233 | - goto out; | |
234 | - } | |
235 | - | |
236 | - fd = open(argv[0], O_RDONLY); | |
237 | - | |
238 | - if (fd < 0) { | |
239 | - fprintf(stderr, "hpet_fasync: failed to open %s\n", argv[0]); | |
240 | - return; | |
241 | - } | |
242 | - | |
243 | - | |
244 | - if ((fcntl(fd, F_SETOWN, getpid()) == 1) || | |
245 | - ((value = fcntl(fd, F_GETFL)) == 1) || | |
246 | - (fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) { | |
247 | - fprintf(stderr, "hpet_fasync: fcntl failed\n"); | |
248 | - goto out; | |
249 | - } | |
250 | - | |
251 | - freq = atoi(argv[1]); | |
252 | - iterations = atoi(argv[2]); | |
253 | - | |
254 | - if (ioctl(fd, HPET_IRQFREQ, freq) < 0) { | |
255 | - fprintf(stderr, "hpet_fasync: HPET_IRQFREQ failed\n"); | |
256 | - goto out; | |
257 | - } | |
258 | - | |
259 | - if (ioctl(fd, HPET_INFO, &info) < 0) { | |
260 | - fprintf(stderr, "hpet_fasync: failed to get info\n"); | |
261 | - goto out; | |
262 | - } | |
263 | - | |
264 | - fprintf(stderr, "hpet_fasync: info.hi_flags 0x%lx\n", info.hi_flags); | |
265 | - | |
266 | - if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) { | |
267 | - fprintf(stderr, "hpet_fasync: HPET_EPI failed\n"); | |
268 | - goto out; | |
269 | - } | |
270 | - | |
271 | - if (ioctl(fd, HPET_IE_ON, 0) < 0) { | |
272 | - fprintf(stderr, "hpet_fasync, HPET_IE_ON failed\n"); | |
273 | - goto out; | |
274 | - } | |
275 | - | |
276 | - for (i = 0; i < iterations; i++) { | |
277 | - (void) pause(); | |
278 | - fprintf(stderr, "hpet_fasync: count = %d\n", hpet_sigio_count); | |
279 | - } | |
280 | - | |
281 | -out: | |
282 | - signal(SIGIO, oldsig); | |
283 | - | |
284 | - if (fd >= 0) | |
285 | - close(fd); | |
286 | - | |
287 | - return; | |
288 | -} | |
289 | - | |
290 | -The kernel API has three interfaces exported from the driver: | |
291 | - | |
292 | - hpet_register(struct hpet_task *tp, int periodic) | |
293 | - hpet_unregister(struct hpet_task *tp) | |
294 | - hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg) | |
295 | - | |
296 | -The kernel module using this interface fills in the ht_func and ht_data | |
297 | -members of the hpet_task structure before calling hpet_register. | |
298 | -hpet_control simply vectors to the hpet_ioctl routine and has the same | |
299 | -commands and respective arguments as the user API. hpet_unregister | |
300 | -is used to terminate usage of the HPET timer reserved by hpet_register. |
Documentation/timers/00-INDEX
Documentation/timers/hpet.txt
1 | + High Precision Event Timer Driver for Linux | |
2 | + | |
3 | +The High Precision Event Timer (HPET) hardware follows a specification | |
4 | +by Intel and Microsoft which can be found at | |
5 | + | |
6 | + http://www.intel.com/technology/architecture/hpetspec.htm | |
7 | + | |
8 | +Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision") | |
9 | +and up to 32 comparators. Normally three or more comparators are provided, | |
10 | +each of which can generate oneshot interupts and at least one of which has | |
11 | +additional hardware to support periodic interrupts. The comparators are | |
12 | +also called "timers", which can be misleading since usually timers are | |
13 | +independent of each other ... these share a counter, complicating resets. | |
14 | + | |
15 | +HPET devices can support two interrupt routing modes. In one mode, the | |
16 | +comparators are additional interrupt sources with no particular system | |
17 | +role. Many x86 BIOS writers don't route HPET interrupts at all, which | |
18 | +prevents use of that mode. They support the other "legacy replacement" | |
19 | +mode where the first two comparators block interrupts from 8254 timers | |
20 | +and from the RTC. | |
21 | + | |
22 | +The driver supports detection of HPET driver allocation and initialization | |
23 | +of the HPET before the driver module_init routine is called. This enables | |
24 | +platform code which uses timer 0 or 1 as the main timer to intercept HPET | |
25 | +initialization. An example of this initialization can be found in | |
26 | +arch/x86/kernel/hpet.c. | |
27 | + | |
28 | +The driver provides a userspace API which resembles the API found in the | |
29 | +RTC driver framework. An example user space program is provided below. | |
30 | + | |
31 | +#include <stdio.h> | |
32 | +#include <stdlib.h> | |
33 | +#include <unistd.h> | |
34 | +#include <fcntl.h> | |
35 | +#include <string.h> | |
36 | +#include <memory.h> | |
37 | +#include <malloc.h> | |
38 | +#include <time.h> | |
39 | +#include <ctype.h> | |
40 | +#include <sys/types.h> | |
41 | +#include <sys/wait.h> | |
42 | +#include <signal.h> | |
43 | +#include <fcntl.h> | |
44 | +#include <errno.h> | |
45 | +#include <sys/time.h> | |
46 | +#include <linux/hpet.h> | |
47 | + | |
48 | + | |
49 | +extern void hpet_open_close(int, const char **); | |
50 | +extern void hpet_info(int, const char **); | |
51 | +extern void hpet_poll(int, const char **); | |
52 | +extern void hpet_fasync(int, const char **); | |
53 | +extern void hpet_read(int, const char **); | |
54 | + | |
55 | +#include <sys/poll.h> | |
56 | +#include <sys/ioctl.h> | |
57 | +#include <signal.h> | |
58 | + | |
59 | +struct hpet_command { | |
60 | + char *command; | |
61 | + void (*func)(int argc, const char ** argv); | |
62 | +} hpet_command[] = { | |
63 | + { | |
64 | + "open-close", | |
65 | + hpet_open_close | |
66 | + }, | |
67 | + { | |
68 | + "info", | |
69 | + hpet_info | |
70 | + }, | |
71 | + { | |
72 | + "poll", | |
73 | + hpet_poll | |
74 | + }, | |
75 | + { | |
76 | + "fasync", | |
77 | + hpet_fasync | |
78 | + }, | |
79 | +}; | |
80 | + | |
81 | +int | |
82 | +main(int argc, const char ** argv) | |
83 | +{ | |
84 | + int i; | |
85 | + | |
86 | + argc--; | |
87 | + argv++; | |
88 | + | |
89 | + if (!argc) { | |
90 | + fprintf(stderr, "-hpet: requires command\n"); | |
91 | + return -1; | |
92 | + } | |
93 | + | |
94 | + | |
95 | + for (i = 0; i < (sizeof (hpet_command) / sizeof (hpet_command[0])); i++) | |
96 | + if (!strcmp(argv[0], hpet_command[i].command)) { | |
97 | + argc--; | |
98 | + argv++; | |
99 | + fprintf(stderr, "-hpet: executing %s\n", | |
100 | + hpet_command[i].command); | |
101 | + hpet_command[i].func(argc, argv); | |
102 | + return 0; | |
103 | + } | |
104 | + | |
105 | + fprintf(stderr, "do_hpet: command %s not implemented\n", argv[0]); | |
106 | + | |
107 | + return -1; | |
108 | +} | |
109 | + | |
110 | +void | |
111 | +hpet_open_close(int argc, const char **argv) | |
112 | +{ | |
113 | + int fd; | |
114 | + | |
115 | + if (argc != 1) { | |
116 | + fprintf(stderr, "hpet_open_close: device-name\n"); | |
117 | + return; | |
118 | + } | |
119 | + | |
120 | + fd = open(argv[0], O_RDONLY); | |
121 | + if (fd < 0) | |
122 | + fprintf(stderr, "hpet_open_close: open failed\n"); | |
123 | + else | |
124 | + close(fd); | |
125 | + | |
126 | + return; | |
127 | +} | |
128 | + | |
129 | +void | |
130 | +hpet_info(int argc, const char **argv) | |
131 | +{ | |
132 | +} | |
133 | + | |
134 | +void | |
135 | +hpet_poll(int argc, const char **argv) | |
136 | +{ | |
137 | + unsigned long freq; | |
138 | + int iterations, i, fd; | |
139 | + struct pollfd pfd; | |
140 | + struct hpet_info info; | |
141 | + struct timeval stv, etv; | |
142 | + struct timezone tz; | |
143 | + long usec; | |
144 | + | |
145 | + if (argc != 3) { | |
146 | + fprintf(stderr, "hpet_poll: device-name freq iterations\n"); | |
147 | + return; | |
148 | + } | |
149 | + | |
150 | + freq = atoi(argv[1]); | |
151 | + iterations = atoi(argv[2]); | |
152 | + | |
153 | + fd = open(argv[0], O_RDONLY); | |
154 | + | |
155 | + if (fd < 0) { | |
156 | + fprintf(stderr, "hpet_poll: open of %s failed\n", argv[0]); | |
157 | + return; | |
158 | + } | |
159 | + | |
160 | + if (ioctl(fd, HPET_IRQFREQ, freq) < 0) { | |
161 | + fprintf(stderr, "hpet_poll: HPET_IRQFREQ failed\n"); | |
162 | + goto out; | |
163 | + } | |
164 | + | |
165 | + if (ioctl(fd, HPET_INFO, &info) < 0) { | |
166 | + fprintf(stderr, "hpet_poll: failed to get info\n"); | |
167 | + goto out; | |
168 | + } | |
169 | + | |
170 | + fprintf(stderr, "hpet_poll: info.hi_flags 0x%lx\n", info.hi_flags); | |
171 | + | |
172 | + if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) { | |
173 | + fprintf(stderr, "hpet_poll: HPET_EPI failed\n"); | |
174 | + goto out; | |
175 | + } | |
176 | + | |
177 | + if (ioctl(fd, HPET_IE_ON, 0) < 0) { | |
178 | + fprintf(stderr, "hpet_poll, HPET_IE_ON failed\n"); | |
179 | + goto out; | |
180 | + } | |
181 | + | |
182 | + pfd.fd = fd; | |
183 | + pfd.events = POLLIN; | |
184 | + | |
185 | + for (i = 0; i < iterations; i++) { | |
186 | + pfd.revents = 0; | |
187 | + gettimeofday(&stv, &tz); | |
188 | + if (poll(&pfd, 1, -1) < 0) | |
189 | + fprintf(stderr, "hpet_poll: poll failed\n"); | |
190 | + else { | |
191 | + long data; | |
192 | + | |
193 | + gettimeofday(&etv, &tz); | |
194 | + usec = stv.tv_sec * 1000000 + stv.tv_usec; | |
195 | + usec = (etv.tv_sec * 1000000 + etv.tv_usec) - usec; | |
196 | + | |
197 | + fprintf(stderr, | |
198 | + "hpet_poll: expired time = 0x%lx\n", usec); | |
199 | + | |
200 | + fprintf(stderr, "hpet_poll: revents = 0x%x\n", | |
201 | + pfd.revents); | |
202 | + | |
203 | + if (read(fd, &data, sizeof(data)) != sizeof(data)) { | |
204 | + fprintf(stderr, "hpet_poll: read failed\n"); | |
205 | + } | |
206 | + else | |
207 | + fprintf(stderr, "hpet_poll: data 0x%lx\n", | |
208 | + data); | |
209 | + } | |
210 | + } | |
211 | + | |
212 | +out: | |
213 | + close(fd); | |
214 | + return; | |
215 | +} | |
216 | + | |
217 | +static int hpet_sigio_count; | |
218 | + | |
219 | +static void | |
220 | +hpet_sigio(int val) | |
221 | +{ | |
222 | + fprintf(stderr, "hpet_sigio: called\n"); | |
223 | + hpet_sigio_count++; | |
224 | +} | |
225 | + | |
226 | +void | |
227 | +hpet_fasync(int argc, const char **argv) | |
228 | +{ | |
229 | + unsigned long freq; | |
230 | + int iterations, i, fd, value; | |
231 | + sig_t oldsig; | |
232 | + struct hpet_info info; | |
233 | + | |
234 | + hpet_sigio_count = 0; | |
235 | + fd = -1; | |
236 | + | |
237 | + if ((oldsig = signal(SIGIO, hpet_sigio)) == SIG_ERR) { | |
238 | + fprintf(stderr, "hpet_fasync: failed to set signal handler\n"); | |
239 | + return; | |
240 | + } | |
241 | + | |
242 | + if (argc != 3) { | |
243 | + fprintf(stderr, "hpet_fasync: device-name freq iterations\n"); | |
244 | + goto out; | |
245 | + } | |
246 | + | |
247 | + fd = open(argv[0], O_RDONLY); | |
248 | + | |
249 | + if (fd < 0) { | |
250 | + fprintf(stderr, "hpet_fasync: failed to open %s\n", argv[0]); | |
251 | + return; | |
252 | + } | |
253 | + | |
254 | + | |
255 | + if ((fcntl(fd, F_SETOWN, getpid()) == 1) || | |
256 | + ((value = fcntl(fd, F_GETFL)) == 1) || | |
257 | + (fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) { | |
258 | + fprintf(stderr, "hpet_fasync: fcntl failed\n"); | |
259 | + goto out; | |
260 | + } | |
261 | + | |
262 | + freq = atoi(argv[1]); | |
263 | + iterations = atoi(argv[2]); | |
264 | + | |
265 | + if (ioctl(fd, HPET_IRQFREQ, freq) < 0) { | |
266 | + fprintf(stderr, "hpet_fasync: HPET_IRQFREQ failed\n"); | |
267 | + goto out; | |
268 | + } | |
269 | + | |
270 | + if (ioctl(fd, HPET_INFO, &info) < 0) { | |
271 | + fprintf(stderr, "hpet_fasync: failed to get info\n"); | |
272 | + goto out; | |
273 | + } | |
274 | + | |
275 | + fprintf(stderr, "hpet_fasync: info.hi_flags 0x%lx\n", info.hi_flags); | |
276 | + | |
277 | + if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) { | |
278 | + fprintf(stderr, "hpet_fasync: HPET_EPI failed\n"); | |
279 | + goto out; | |
280 | + } | |
281 | + | |
282 | + if (ioctl(fd, HPET_IE_ON, 0) < 0) { | |
283 | + fprintf(stderr, "hpet_fasync, HPET_IE_ON failed\n"); | |
284 | + goto out; | |
285 | + } | |
286 | + | |
287 | + for (i = 0; i < iterations; i++) { | |
288 | + (void) pause(); | |
289 | + fprintf(stderr, "hpet_fasync: count = %d\n", hpet_sigio_count); | |
290 | + } | |
291 | + | |
292 | +out: | |
293 | + signal(SIGIO, oldsig); | |
294 | + | |
295 | + if (fd >= 0) | |
296 | + close(fd); | |
297 | + | |
298 | + return; | |
299 | +} |
arch/Kconfig
... | ... | @@ -13,6 +13,20 @@ |
13 | 13 | |
14 | 14 | If unsure, say N. |
15 | 15 | |
16 | +config OPROFILE_IBS | |
17 | + bool "OProfile AMD IBS support (EXPERIMENTAL)" | |
18 | + default n | |
19 | + depends on OPROFILE && SMP && X86 | |
20 | + help | |
21 | + Instruction-Based Sampling (IBS) is a new profiling | |
22 | + technique that provides rich, precise program performance | |
23 | + information. IBS is introduced by AMD Family10h processors | |
24 | + (AMD Opteron Quad-Core processor โBarcelonaโ) to overcome | |
25 | + the limitations of conventional performance counter | |
26 | + sampling. | |
27 | + | |
28 | + If unsure, say N. | |
29 | + | |
16 | 30 | config HAVE_OPROFILE |
17 | 31 | def_bool n |
18 | 32 |
arch/x86/kernel/apic_32.c
... | ... | @@ -295,6 +295,9 @@ |
295 | 295 | * |
296 | 296 | * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and |
297 | 297 | * MCE interrupts are supported. Thus MCE offset must be set to 0. |
298 | + * | |
299 | + * If mask=1, the LVT entry does not generate interrupts while mask=0 | |
300 | + * enables the vector. See also the BKDGs. | |
298 | 301 | */ |
299 | 302 | |
300 | 303 | #define APIC_EILVT_LVTOFF_MCE 0 |
... | ... | @@ -319,6 +322,7 @@ |
319 | 322 | setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); |
320 | 323 | return APIC_EILVT_LVTOFF_IBS; |
321 | 324 | } |
325 | +EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs); | |
322 | 326 | |
323 | 327 | /* |
324 | 328 | * Program the next event, relative to now |
arch/x86/kernel/apic_64.c
... | ... | @@ -307,6 +307,9 @@ |
307 | 307 | * |
308 | 308 | * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and |
309 | 309 | * MCE interrupts are supported. Thus MCE offset must be set to 0. |
310 | + * | |
311 | + * If mask=1, the LVT entry does not generate interrupts while mask=0 | |
312 | + * enables the vector. See also the BKDGs. | |
310 | 313 | */ |
311 | 314 | |
312 | 315 | #define APIC_EILVT_LVTOFF_MCE 0 |
... | ... | @@ -331,6 +334,7 @@ |
331 | 334 | setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); |
332 | 335 | return APIC_EILVT_LVTOFF_IBS; |
333 | 336 | } |
337 | +EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs); | |
334 | 338 | |
335 | 339 | /* |
336 | 340 | * Program the next event, relative to now |
arch/x86/kernel/hpet.c
... | ... | @@ -115,13 +115,17 @@ |
115 | 115 | hd.hd_phys_address = hpet_address; |
116 | 116 | hd.hd_address = hpet; |
117 | 117 | hd.hd_nirqs = nrtimers; |
118 | - hd.hd_flags = HPET_DATA_PLATFORM; | |
119 | 118 | hpet_reserve_timer(&hd, 0); |
120 | 119 | |
121 | 120 | #ifdef CONFIG_HPET_EMULATE_RTC |
122 | 121 | hpet_reserve_timer(&hd, 1); |
123 | 122 | #endif |
124 | 123 | |
124 | + /* | |
125 | + * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254 | |
126 | + * is wrong for i8259!) not the output IRQ. Many BIOS writers | |
127 | + * don't bother configuring *any* comparator interrupts. | |
128 | + */ | |
125 | 129 | hd.hd_irq[0] = HPET_LEGACY_8254; |
126 | 130 | hd.hd_irq[1] = HPET_LEGACY_RTC; |
127 | 131 |
arch/x86/kernel/quirks.c
... | ... | @@ -354,9 +354,27 @@ |
354 | 354 | printk(KERN_DEBUG "Force enabled HPET at resume\n"); |
355 | 355 | } |
356 | 356 | |
357 | +static u32 ati_ixp4x0_rev(struct pci_dev *dev) | |
358 | +{ | |
359 | + u32 d; | |
360 | + u8 b; | |
361 | + | |
362 | + pci_read_config_byte(dev, 0xac, &b); | |
363 | + b &= ~(1<<5); | |
364 | + pci_write_config_byte(dev, 0xac, b); | |
365 | + pci_read_config_dword(dev, 0x70, &d); | |
366 | + d |= 1<<8; | |
367 | + pci_write_config_dword(dev, 0x70, d); | |
368 | + pci_read_config_dword(dev, 0x8, &d); | |
369 | + d &= 0xff; | |
370 | + dev_printk(KERN_DEBUG, &dev->dev, "SB4X0 revision 0x%x\n", d); | |
371 | + return d; | |
372 | +} | |
373 | + | |
357 | 374 | static void ati_force_enable_hpet(struct pci_dev *dev) |
358 | 375 | { |
359 | - u32 uninitialized_var(val); | |
376 | + u32 d, val; | |
377 | + u8 b; | |
360 | 378 | |
361 | 379 | if (hpet_address || force_hpet_address) |
362 | 380 | return; |
363 | 381 | |
364 | 382 | |
... | ... | @@ -366,14 +384,33 @@ |
366 | 384 | return; |
367 | 385 | } |
368 | 386 | |
387 | + d = ati_ixp4x0_rev(dev); | |
388 | + if (d < 0x82) | |
389 | + return; | |
390 | + | |
391 | + /* base address */ | |
369 | 392 | pci_write_config_dword(dev, 0x14, 0xfed00000); |
370 | 393 | pci_read_config_dword(dev, 0x14, &val); |
394 | + | |
395 | + /* enable interrupt */ | |
396 | + outb(0x72, 0xcd6); b = inb(0xcd7); | |
397 | + b |= 0x1; | |
398 | + outb(0x72, 0xcd6); outb(b, 0xcd7); | |
399 | + outb(0x72, 0xcd6); b = inb(0xcd7); | |
400 | + if (!(b & 0x1)) | |
401 | + return; | |
402 | + pci_read_config_dword(dev, 0x64, &d); | |
403 | + d |= (1<<10); | |
404 | + pci_write_config_dword(dev, 0x64, d); | |
405 | + pci_read_config_dword(dev, 0x64, &d); | |
406 | + if (!(d & (1<<10))) | |
407 | + return; | |
408 | + | |
371 | 409 | force_hpet_address = val; |
372 | 410 | force_hpet_resume_type = ATI_FORCE_HPET_RESUME; |
373 | 411 | dev_printk(KERN_DEBUG, &dev->dev, "Force enabled HPET at 0x%lx\n", |
374 | 412 | force_hpet_address); |
375 | 413 | cached_dev = dev; |
376 | - return; | |
377 | 414 | } |
378 | 415 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS, |
379 | 416 | ati_force_enable_hpet); |
arch/x86/oprofile/Makefile
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | timer_int.o ) |
8 | 8 | |
9 | 9 | oprofile-y := $(DRIVER_OBJS) init.o backtrace.o |
10 | -oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o \ | |
10 | +oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \ | |
11 | 11 | op_model_ppro.o op_model_p4.o |
12 | 12 | oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o |
arch/x86/oprofile/nmi_int.c
1 | 1 | /** |
2 | 2 | * @file nmi_int.c |
3 | 3 | * |
4 | - * @remark Copyright 2002 OProfile authors | |
4 | + * @remark Copyright 2002-2008 OProfile authors | |
5 | 5 | * @remark Read the file COPYING |
6 | 6 | * |
7 | 7 | * @author John Levon <levon@movementarian.org> |
8 | + * @author Robert Richter <robert.richter@amd.com> | |
8 | 9 | */ |
9 | 10 | |
10 | 11 | #include <linux/init.h> |
... | ... | @@ -439,6 +440,7 @@ |
439 | 440 | __u8 vendor = boot_cpu_data.x86_vendor; |
440 | 441 | __u8 family = boot_cpu_data.x86; |
441 | 442 | char *cpu_type; |
443 | + int ret = 0; | |
442 | 444 | |
443 | 445 | if (!cpu_has_apic) |
444 | 446 | return -ENODEV; |
445 | 447 | |
446 | 448 | |
447 | 449 | |
... | ... | @@ -451,19 +453,23 @@ |
451 | 453 | default: |
452 | 454 | return -ENODEV; |
453 | 455 | case 6: |
454 | - model = &op_athlon_spec; | |
456 | + model = &op_amd_spec; | |
455 | 457 | cpu_type = "i386/athlon"; |
456 | 458 | break; |
457 | 459 | case 0xf: |
458 | - model = &op_athlon_spec; | |
460 | + model = &op_amd_spec; | |
459 | 461 | /* Actually it could be i386/hammer too, but give |
460 | 462 | user space an consistent name. */ |
461 | 463 | cpu_type = "x86-64/hammer"; |
462 | 464 | break; |
463 | 465 | case 0x10: |
464 | - model = &op_athlon_spec; | |
466 | + model = &op_amd_spec; | |
465 | 467 | cpu_type = "x86-64/family10"; |
466 | 468 | break; |
469 | + case 0x11: | |
470 | + model = &op_amd_spec; | |
471 | + cpu_type = "x86-64/family11h"; | |
472 | + break; | |
467 | 473 | } |
468 | 474 | break; |
469 | 475 | |
470 | 476 | |
471 | 477 | |
... | ... | @@ -490,17 +496,24 @@ |
490 | 496 | return -ENODEV; |
491 | 497 | } |
492 | 498 | |
493 | - init_sysfs(); | |
494 | 499 | #ifdef CONFIG_SMP |
495 | 500 | register_cpu_notifier(&oprofile_cpu_nb); |
496 | 501 | #endif |
497 | - using_nmi = 1; | |
502 | + /* default values, can be overwritten by model */ | |
498 | 503 | ops->create_files = nmi_create_files; |
499 | 504 | ops->setup = nmi_setup; |
500 | 505 | ops->shutdown = nmi_shutdown; |
501 | 506 | ops->start = nmi_start; |
502 | 507 | ops->stop = nmi_stop; |
503 | 508 | ops->cpu_type = cpu_type; |
509 | + | |
510 | + if (model->init) | |
511 | + ret = model->init(ops); | |
512 | + if (ret) | |
513 | + return ret; | |
514 | + | |
515 | + init_sysfs(); | |
516 | + using_nmi = 1; | |
504 | 517 | printk(KERN_INFO "oprofile: using NMI interrupt.\n"); |
505 | 518 | return 0; |
506 | 519 | } |
... | ... | @@ -513,5 +526,7 @@ |
513 | 526 | unregister_cpu_notifier(&oprofile_cpu_nb); |
514 | 527 | #endif |
515 | 528 | } |
529 | + if (model->exit) | |
530 | + model->exit(); | |
516 | 531 | } |
arch/x86/oprofile/op_model_amd.c
1 | +/* | |
2 | + * @file op_model_amd.c | |
3 | + * athlon / K7 / K8 / Family 10h model-specific MSR operations | |
4 | + * | |
5 | + * @remark Copyright 2002-2008 OProfile authors | |
6 | + * @remark Read the file COPYING | |
7 | + * | |
8 | + * @author John Levon | |
9 | + * @author Philippe Elie | |
10 | + * @author Graydon Hoare | |
11 | + * @author Robert Richter <robert.richter@amd.com> | |
12 | + * @author Barry Kasindorf | |
13 | +*/ | |
14 | + | |
15 | +#include <linux/oprofile.h> | |
16 | +#include <linux/device.h> | |
17 | +#include <linux/pci.h> | |
18 | + | |
19 | +#include <asm/ptrace.h> | |
20 | +#include <asm/msr.h> | |
21 | +#include <asm/nmi.h> | |
22 | + | |
23 | +#include "op_x86_model.h" | |
24 | +#include "op_counter.h" | |
25 | + | |
26 | +#define NUM_COUNTERS 4 | |
27 | +#define NUM_CONTROLS 4 | |
28 | + | |
29 | +#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0) | |
30 | +#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0) | |
31 | +#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0) | |
32 | +#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) | |
33 | + | |
34 | +#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0) | |
35 | +#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) | |
36 | +#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) | |
37 | +#define CTRL_SET_ACTIVE(n) (n |= (1<<22)) | |
38 | +#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) | |
39 | +#define CTRL_CLEAR_LO(x) (x &= (1<<21)) | |
40 | +#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0) | |
41 | +#define CTRL_SET_ENABLE(val) (val |= 1<<20) | |
42 | +#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) | |
43 | +#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) | |
44 | +#define CTRL_SET_UM(val, m) (val |= (m << 8)) | |
45 | +#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff)) | |
46 | +#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf)) | |
47 | +#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) | |
48 | +#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) | |
49 | + | |
50 | +static unsigned long reset_value[NUM_COUNTERS]; | |
51 | + | |
52 | +#ifdef CONFIG_OPROFILE_IBS | |
53 | + | |
54 | +/* IbsFetchCtl bits/masks */ | |
55 | +#define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */ | |
56 | +#define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */ | |
57 | +#define IBS_FETCH_LOW_MAX_CNT_MASK 0x0000FFFFUL /* MaxCnt mask */ | |
58 | + | |
59 | +/*IbsOpCtl bits */ | |
60 | +#define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */ | |
61 | +#define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */ | |
62 | + | |
63 | +/* Codes used in cpu_buffer.c */ | |
64 | +/* This produces duplicate code, need to be fixed */ | |
65 | +#define IBS_FETCH_BEGIN 3 | |
66 | +#define IBS_OP_BEGIN 4 | |
67 | + | |
68 | +/* The function interface needs to be fixed, something like add | |
69 | + data. Should then be added to linux/oprofile.h. */ | |
70 | +extern void oprofile_add_ibs_sample(struct pt_regs *const regs, | |
71 | + unsigned int * const ibs_sample, u8 code); | |
72 | + | |
73 | +struct ibs_fetch_sample { | |
74 | + /* MSRC001_1031 IBS Fetch Linear Address Register */ | |
75 | + unsigned int ibs_fetch_lin_addr_low; | |
76 | + unsigned int ibs_fetch_lin_addr_high; | |
77 | + /* MSRC001_1030 IBS Fetch Control Register */ | |
78 | + unsigned int ibs_fetch_ctl_low; | |
79 | + unsigned int ibs_fetch_ctl_high; | |
80 | + /* MSRC001_1032 IBS Fetch Physical Address Register */ | |
81 | + unsigned int ibs_fetch_phys_addr_low; | |
82 | + unsigned int ibs_fetch_phys_addr_high; | |
83 | +}; | |
84 | + | |
85 | +struct ibs_op_sample { | |
86 | + /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */ | |
87 | + unsigned int ibs_op_rip_low; | |
88 | + unsigned int ibs_op_rip_high; | |
89 | + /* MSRC001_1035 IBS Op Data Register */ | |
90 | + unsigned int ibs_op_data1_low; | |
91 | + unsigned int ibs_op_data1_high; | |
92 | + /* MSRC001_1036 IBS Op Data 2 Register */ | |
93 | + unsigned int ibs_op_data2_low; | |
94 | + unsigned int ibs_op_data2_high; | |
95 | + /* MSRC001_1037 IBS Op Data 3 Register */ | |
96 | + unsigned int ibs_op_data3_low; | |
97 | + unsigned int ibs_op_data3_high; | |
98 | + /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */ | |
99 | + unsigned int ibs_dc_linear_low; | |
100 | + unsigned int ibs_dc_linear_high; | |
101 | + /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */ | |
102 | + unsigned int ibs_dc_phys_low; | |
103 | + unsigned int ibs_dc_phys_high; | |
104 | +}; | |
105 | + | |
106 | +/* | |
107 | + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+ | |
108 | +*/ | |
109 | +static void clear_ibs_nmi(void); | |
110 | + | |
111 | +static int ibs_allowed; /* AMD Family10h and later */ | |
112 | + | |
113 | +struct op_ibs_config { | |
114 | + unsigned long op_enabled; | |
115 | + unsigned long fetch_enabled; | |
116 | + unsigned long max_cnt_fetch; | |
117 | + unsigned long max_cnt_op; | |
118 | + unsigned long rand_en; | |
119 | + unsigned long dispatched_ops; | |
120 | +}; | |
121 | + | |
122 | +static struct op_ibs_config ibs_config; | |
123 | + | |
124 | +#endif | |
125 | + | |
126 | +/* functions for op_amd_spec */ | |
127 | + | |
128 | +static void op_amd_fill_in_addresses(struct op_msrs * const msrs) | |
129 | +{ | |
130 | + int i; | |
131 | + | |
132 | + for (i = 0; i < NUM_COUNTERS; i++) { | |
133 | + if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) | |
134 | + msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; | |
135 | + else | |
136 | + msrs->counters[i].addr = 0; | |
137 | + } | |
138 | + | |
139 | + for (i = 0; i < NUM_CONTROLS; i++) { | |
140 | + if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) | |
141 | + msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; | |
142 | + else | |
143 | + msrs->controls[i].addr = 0; | |
144 | + } | |
145 | +} | |
146 | + | |
147 | + | |
148 | +static void op_amd_setup_ctrs(struct op_msrs const * const msrs) | |
149 | +{ | |
150 | + unsigned int low, high; | |
151 | + int i; | |
152 | + | |
153 | + /* clear all counters */ | |
154 | + for (i = 0 ; i < NUM_CONTROLS; ++i) { | |
155 | + if (unlikely(!CTRL_IS_RESERVED(msrs, i))) | |
156 | + continue; | |
157 | + CTRL_READ(low, high, msrs, i); | |
158 | + CTRL_CLEAR_LO(low); | |
159 | + CTRL_CLEAR_HI(high); | |
160 | + CTRL_WRITE(low, high, msrs, i); | |
161 | + } | |
162 | + | |
163 | + /* avoid a false detection of ctr overflows in NMI handler */ | |
164 | + for (i = 0; i < NUM_COUNTERS; ++i) { | |
165 | + if (unlikely(!CTR_IS_RESERVED(msrs, i))) | |
166 | + continue; | |
167 | + CTR_WRITE(1, msrs, i); | |
168 | + } | |
169 | + | |
170 | + /* enable active counters */ | |
171 | + for (i = 0; i < NUM_COUNTERS; ++i) { | |
172 | + if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { | |
173 | + reset_value[i] = counter_config[i].count; | |
174 | + | |
175 | + CTR_WRITE(counter_config[i].count, msrs, i); | |
176 | + | |
177 | + CTRL_READ(low, high, msrs, i); | |
178 | + CTRL_CLEAR_LO(low); | |
179 | + CTRL_CLEAR_HI(high); | |
180 | + CTRL_SET_ENABLE(low); | |
181 | + CTRL_SET_USR(low, counter_config[i].user); | |
182 | + CTRL_SET_KERN(low, counter_config[i].kernel); | |
183 | + CTRL_SET_UM(low, counter_config[i].unit_mask); | |
184 | + CTRL_SET_EVENT_LOW(low, counter_config[i].event); | |
185 | + CTRL_SET_EVENT_HIGH(high, counter_config[i].event); | |
186 | + CTRL_SET_HOST_ONLY(high, 0); | |
187 | + CTRL_SET_GUEST_ONLY(high, 0); | |
188 | + | |
189 | + CTRL_WRITE(low, high, msrs, i); | |
190 | + } else { | |
191 | + reset_value[i] = 0; | |
192 | + } | |
193 | + } | |
194 | +} | |
195 | + | |
196 | +#ifdef CONFIG_OPROFILE_IBS | |
197 | + | |
198 | +static inline int | |
199 | +op_amd_handle_ibs(struct pt_regs * const regs, | |
200 | + struct op_msrs const * const msrs) | |
201 | +{ | |
202 | + unsigned int low, high; | |
203 | + struct ibs_fetch_sample ibs_fetch; | |
204 | + struct ibs_op_sample ibs_op; | |
205 | + | |
206 | + if (!ibs_allowed) | |
207 | + return 1; | |
208 | + | |
209 | + if (ibs_config.fetch_enabled) { | |
210 | + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); | |
211 | + if (high & IBS_FETCH_HIGH_VALID_BIT) { | |
212 | + ibs_fetch.ibs_fetch_ctl_high = high; | |
213 | + ibs_fetch.ibs_fetch_ctl_low = low; | |
214 | + rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high); | |
215 | + ibs_fetch.ibs_fetch_lin_addr_high = high; | |
216 | + ibs_fetch.ibs_fetch_lin_addr_low = low; | |
217 | + rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high); | |
218 | + ibs_fetch.ibs_fetch_phys_addr_high = high; | |
219 | + ibs_fetch.ibs_fetch_phys_addr_low = low; | |
220 | + | |
221 | + oprofile_add_ibs_sample(regs, | |
222 | + (unsigned int *)&ibs_fetch, | |
223 | + IBS_FETCH_BEGIN); | |
224 | + | |
225 | + /*reenable the IRQ */ | |
226 | + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); | |
227 | + high &= ~IBS_FETCH_HIGH_VALID_BIT; | |
228 | + high |= IBS_FETCH_HIGH_ENABLE; | |
229 | + low &= IBS_FETCH_LOW_MAX_CNT_MASK; | |
230 | + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); | |
231 | + } | |
232 | + } | |
233 | + | |
234 | + if (ibs_config.op_enabled) { | |
235 | + rdmsr(MSR_AMD64_IBSOPCTL, low, high); | |
236 | + if (low & IBS_OP_LOW_VALID_BIT) { | |
237 | + rdmsr(MSR_AMD64_IBSOPRIP, low, high); | |
238 | + ibs_op.ibs_op_rip_low = low; | |
239 | + ibs_op.ibs_op_rip_high = high; | |
240 | + rdmsr(MSR_AMD64_IBSOPDATA, low, high); | |
241 | + ibs_op.ibs_op_data1_low = low; | |
242 | + ibs_op.ibs_op_data1_high = high; | |
243 | + rdmsr(MSR_AMD64_IBSOPDATA2, low, high); | |
244 | + ibs_op.ibs_op_data2_low = low; | |
245 | + ibs_op.ibs_op_data2_high = high; | |
246 | + rdmsr(MSR_AMD64_IBSOPDATA3, low, high); | |
247 | + ibs_op.ibs_op_data3_low = low; | |
248 | + ibs_op.ibs_op_data3_high = high; | |
249 | + rdmsr(MSR_AMD64_IBSDCLINAD, low, high); | |
250 | + ibs_op.ibs_dc_linear_low = low; | |
251 | + ibs_op.ibs_dc_linear_high = high; | |
252 | + rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high); | |
253 | + ibs_op.ibs_dc_phys_low = low; | |
254 | + ibs_op.ibs_dc_phys_high = high; | |
255 | + | |
256 | + /* reenable the IRQ */ | |
257 | + oprofile_add_ibs_sample(regs, | |
258 | + (unsigned int *)&ibs_op, | |
259 | + IBS_OP_BEGIN); | |
260 | + rdmsr(MSR_AMD64_IBSOPCTL, low, high); | |
261 | + high = 0; | |
262 | + low &= ~IBS_OP_LOW_VALID_BIT; | |
263 | + low |= IBS_OP_LOW_ENABLE; | |
264 | + wrmsr(MSR_AMD64_IBSOPCTL, low, high); | |
265 | + } | |
266 | + } | |
267 | + | |
268 | + return 1; | |
269 | +} | |
270 | + | |
271 | +#endif | |
272 | + | |
273 | +static int op_amd_check_ctrs(struct pt_regs * const regs, | |
274 | + struct op_msrs const * const msrs) | |
275 | +{ | |
276 | + unsigned int low, high; | |
277 | + int i; | |
278 | + | |
279 | + for (i = 0 ; i < NUM_COUNTERS; ++i) { | |
280 | + if (!reset_value[i]) | |
281 | + continue; | |
282 | + CTR_READ(low, high, msrs, i); | |
283 | + if (CTR_OVERFLOWED(low)) { | |
284 | + oprofile_add_sample(regs, i); | |
285 | + CTR_WRITE(reset_value[i], msrs, i); | |
286 | + } | |
287 | + } | |
288 | + | |
289 | +#ifdef CONFIG_OPROFILE_IBS | |
290 | + op_amd_handle_ibs(regs, msrs); | |
291 | +#endif | |
292 | + | |
293 | + /* See op_model_ppro.c */ | |
294 | + return 1; | |
295 | +} | |
296 | + | |
297 | +static void op_amd_start(struct op_msrs const * const msrs) | |
298 | +{ | |
299 | + unsigned int low, high; | |
300 | + int i; | |
301 | + for (i = 0 ; i < NUM_COUNTERS ; ++i) { | |
302 | + if (reset_value[i]) { | |
303 | + CTRL_READ(low, high, msrs, i); | |
304 | + CTRL_SET_ACTIVE(low); | |
305 | + CTRL_WRITE(low, high, msrs, i); | |
306 | + } | |
307 | + } | |
308 | + | |
309 | +#ifdef CONFIG_OPROFILE_IBS | |
310 | + if (ibs_allowed && ibs_config.fetch_enabled) { | |
311 | + low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; | |
312 | + high = IBS_FETCH_HIGH_ENABLE; | |
313 | + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); | |
314 | + } | |
315 | + | |
316 | + if (ibs_allowed && ibs_config.op_enabled) { | |
317 | + low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) + IBS_OP_LOW_ENABLE; | |
318 | + high = 0; | |
319 | + wrmsr(MSR_AMD64_IBSOPCTL, low, high); | |
320 | + } | |
321 | +#endif | |
322 | +} | |
323 | + | |
324 | + | |
325 | +static void op_amd_stop(struct op_msrs const * const msrs) | |
326 | +{ | |
327 | + unsigned int low, high; | |
328 | + int i; | |
329 | + | |
330 | + /* Subtle: stop on all counters to avoid race with | |
331 | + * setting our pm callback */ | |
332 | + for (i = 0 ; i < NUM_COUNTERS ; ++i) { | |
333 | + if (!reset_value[i]) | |
334 | + continue; | |
335 | + CTRL_READ(low, high, msrs, i); | |
336 | + CTRL_SET_INACTIVE(low); | |
337 | + CTRL_WRITE(low, high, msrs, i); | |
338 | + } | |
339 | + | |
340 | +#ifdef CONFIG_OPROFILE_IBS | |
341 | + if (ibs_allowed && ibs_config.fetch_enabled) { | |
342 | + low = 0; /* clear max count and enable */ | |
343 | + high = 0; | |
344 | + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); | |
345 | + } | |
346 | + | |
347 | + if (ibs_allowed && ibs_config.op_enabled) { | |
348 | + low = 0; /* clear max count and enable */ | |
349 | + high = 0; | |
350 | + wrmsr(MSR_AMD64_IBSOPCTL, low, high); | |
351 | + } | |
352 | +#endif | |
353 | +} | |
354 | + | |
355 | +static void op_amd_shutdown(struct op_msrs const * const msrs) | |
356 | +{ | |
357 | + int i; | |
358 | + | |
359 | + for (i = 0 ; i < NUM_COUNTERS ; ++i) { | |
360 | + if (CTR_IS_RESERVED(msrs, i)) | |
361 | + release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
362 | + } | |
363 | + for (i = 0 ; i < NUM_CONTROLS ; ++i) { | |
364 | + if (CTRL_IS_RESERVED(msrs, i)) | |
365 | + release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | |
366 | + } | |
367 | +} | |
368 | + | |
369 | +#ifndef CONFIG_OPROFILE_IBS | |
370 | + | |
371 | +/* no IBS support */ | |
372 | + | |
373 | +static int op_amd_init(struct oprofile_operations *ops) | |
374 | +{ | |
375 | + return 0; | |
376 | +} | |
377 | + | |
378 | +static void op_amd_exit(void) {} | |
379 | + | |
380 | +#else | |
381 | + | |
382 | +static u8 ibs_eilvt_off; | |
383 | + | |
384 | +static inline void apic_init_ibs_nmi_per_cpu(void *arg) | |
385 | +{ | |
386 | + ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); | |
387 | +} | |
388 | + | |
389 | +static inline void apic_clear_ibs_nmi_per_cpu(void *arg) | |
390 | +{ | |
391 | + setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | |
392 | +} | |
393 | + | |
394 | +static int pfm_amd64_setup_eilvt(void) | |
395 | +{ | |
396 | +#define IBSCTL_LVTOFFSETVAL (1 << 8) | |
397 | +#define IBSCTL 0x1cc | |
398 | + struct pci_dev *cpu_cfg; | |
399 | + int nodes; | |
400 | + u32 value = 0; | |
401 | + | |
402 | + /* per CPU setup */ | |
403 | + on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1); | |
404 | + | |
405 | + nodes = 0; | |
406 | + cpu_cfg = NULL; | |
407 | + do { | |
408 | + cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | |
409 | + PCI_DEVICE_ID_AMD_10H_NB_MISC, | |
410 | + cpu_cfg); | |
411 | + if (!cpu_cfg) | |
412 | + break; | |
413 | + ++nodes; | |
414 | + pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | |
415 | + | IBSCTL_LVTOFFSETVAL); | |
416 | + pci_read_config_dword(cpu_cfg, IBSCTL, &value); | |
417 | + if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { | |
418 | + printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | |
419 | + "IBSCTL = 0x%08x", value); | |
420 | + return 1; | |
421 | + } | |
422 | + } while (1); | |
423 | + | |
424 | + if (!nodes) { | |
425 | + printk(KERN_DEBUG "No CPU node configured for IBS"); | |
426 | + return 1; | |
427 | + } | |
428 | + | |
429 | +#ifdef CONFIG_NUMA | |
430 | + /* Sanity check */ | |
431 | + /* Works only for 64bit with proper numa implementation. */ | |
432 | + if (nodes != num_possible_nodes()) { | |
433 | + printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, " | |
434 | + "found: %d, expected %d", | |
435 | + nodes, num_possible_nodes()); | |
436 | + return 1; | |
437 | + } | |
438 | +#endif | |
439 | + return 0; | |
440 | +} | |
441 | + | |
442 | +/* | |
443 | + * initialize the APIC for the IBS interrupts | |
444 | + * if available (AMD Family10h rev B0 and later) | |
445 | + */ | |
446 | +static void setup_ibs(void) | |
447 | +{ | |
448 | + ibs_allowed = boot_cpu_has(X86_FEATURE_IBS); | |
449 | + | |
450 | + if (!ibs_allowed) | |
451 | + return; | |
452 | + | |
453 | + if (pfm_amd64_setup_eilvt()) { | |
454 | + ibs_allowed = 0; | |
455 | + return; | |
456 | + } | |
457 | + | |
458 | + printk(KERN_INFO "oprofile: AMD IBS detected\n"); | |
459 | +} | |
460 | + | |
461 | + | |
462 | +/* | |
463 | + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h | |
464 | + * rev B0 and later */ | |
465 | +static void clear_ibs_nmi(void) | |
466 | +{ | |
467 | + if (ibs_allowed) | |
468 | + on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); | |
469 | +} | |
470 | + | |
471 | +static int (*create_arch_files)(struct super_block * sb, struct dentry * root); | |
472 | + | |
473 | +static int setup_ibs_files(struct super_block * sb, struct dentry * root) | |
474 | +{ | |
475 | + char buf[12]; | |
476 | + struct dentry *dir; | |
477 | + int ret = 0; | |
478 | + | |
479 | + /* architecture specific files */ | |
480 | + if (create_arch_files) | |
481 | + ret = create_arch_files(sb, root); | |
482 | + | |
483 | + if (ret) | |
484 | + return ret; | |
485 | + | |
486 | + if (!ibs_allowed) | |
487 | + return ret; | |
488 | + | |
489 | + /* model specific files */ | |
490 | + | |
491 | + /* setup some reasonable defaults */ | |
492 | + ibs_config.max_cnt_fetch = 250000; | |
493 | + ibs_config.fetch_enabled = 0; | |
494 | + ibs_config.max_cnt_op = 250000; | |
495 | + ibs_config.op_enabled = 0; | |
496 | + ibs_config.dispatched_ops = 1; | |
497 | + snprintf(buf, sizeof(buf), "ibs_fetch"); | |
498 | + dir = oprofilefs_mkdir(sb, root, buf); | |
499 | + oprofilefs_create_ulong(sb, dir, "rand_enable", | |
500 | + &ibs_config.rand_en); | |
501 | + oprofilefs_create_ulong(sb, dir, "enable", | |
502 | + &ibs_config.fetch_enabled); | |
503 | + oprofilefs_create_ulong(sb, dir, "max_count", | |
504 | + &ibs_config.max_cnt_fetch); | |
505 | + snprintf(buf, sizeof(buf), "ibs_uops"); | |
506 | + dir = oprofilefs_mkdir(sb, root, buf); | |
507 | + oprofilefs_create_ulong(sb, dir, "enable", | |
508 | + &ibs_config.op_enabled); | |
509 | + oprofilefs_create_ulong(sb, dir, "max_count", | |
510 | + &ibs_config.max_cnt_op); | |
511 | + oprofilefs_create_ulong(sb, dir, "dispatched_ops", | |
512 | + &ibs_config.dispatched_ops); | |
513 | + | |
514 | + return 0; | |
515 | +} | |
516 | + | |
517 | +static int op_amd_init(struct oprofile_operations *ops) | |
518 | +{ | |
519 | + setup_ibs(); | |
520 | + create_arch_files = ops->create_files; | |
521 | + ops->create_files = setup_ibs_files; | |
522 | + return 0; | |
523 | +} | |
524 | + | |
525 | +static void op_amd_exit(void) | |
526 | +{ | |
527 | + clear_ibs_nmi(); | |
528 | +} | |
529 | + | |
530 | +#endif | |
531 | + | |
532 | +struct op_x86_model_spec const op_amd_spec = { | |
533 | + .init = op_amd_init, | |
534 | + .exit = op_amd_exit, | |
535 | + .num_counters = NUM_COUNTERS, | |
536 | + .num_controls = NUM_CONTROLS, | |
537 | + .fill_in_addresses = &op_amd_fill_in_addresses, | |
538 | + .setup_ctrs = &op_amd_setup_ctrs, | |
539 | + .check_ctrs = &op_amd_check_ctrs, | |
540 | + .start = &op_amd_start, | |
541 | + .stop = &op_amd_stop, | |
542 | + .shutdown = &op_amd_shutdown | |
543 | +}; |
arch/x86/oprofile/op_model_athlon.c
1 | -/* | |
2 | - * @file op_model_athlon.h | |
3 | - * athlon / K7 / K8 / Family 10h model-specific MSR operations | |
4 | - * | |
5 | - * @remark Copyright 2002 OProfile authors | |
6 | - * @remark Read the file COPYING | |
7 | - * | |
8 | - * @author John Levon | |
9 | - * @author Philippe Elie | |
10 | - * @author Graydon Hoare | |
11 | - */ | |
12 | - | |
13 | -#include <linux/oprofile.h> | |
14 | -#include <asm/ptrace.h> | |
15 | -#include <asm/msr.h> | |
16 | -#include <asm/nmi.h> | |
17 | - | |
18 | -#include "op_x86_model.h" | |
19 | -#include "op_counter.h" | |
20 | - | |
21 | -#define NUM_COUNTERS 4 | |
22 | -#define NUM_CONTROLS 4 | |
23 | - | |
24 | -#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0) | |
25 | -#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0) | |
26 | -#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0) | |
27 | -#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) | |
28 | - | |
29 | -#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0) | |
30 | -#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) | |
31 | -#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) | |
32 | -#define CTRL_SET_ACTIVE(n) (n |= (1<<22)) | |
33 | -#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) | |
34 | -#define CTRL_CLEAR_LO(x) (x &= (1<<21)) | |
35 | -#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0) | |
36 | -#define CTRL_SET_ENABLE(val) (val |= 1<<20) | |
37 | -#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) | |
38 | -#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) | |
39 | -#define CTRL_SET_UM(val, m) (val |= (m << 8)) | |
40 | -#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff)) | |
41 | -#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf)) | |
42 | -#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) | |
43 | -#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) | |
44 | - | |
45 | -static unsigned long reset_value[NUM_COUNTERS]; | |
46 | - | |
47 | -static void athlon_fill_in_addresses(struct op_msrs * const msrs) | |
48 | -{ | |
49 | - int i; | |
50 | - | |
51 | - for (i = 0; i < NUM_COUNTERS; i++) { | |
52 | - if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) | |
53 | - msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; | |
54 | - else | |
55 | - msrs->counters[i].addr = 0; | |
56 | - } | |
57 | - | |
58 | - for (i = 0; i < NUM_CONTROLS; i++) { | |
59 | - if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) | |
60 | - msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; | |
61 | - else | |
62 | - msrs->controls[i].addr = 0; | |
63 | - } | |
64 | -} | |
65 | - | |
66 | - | |
67 | -static void athlon_setup_ctrs(struct op_msrs const * const msrs) | |
68 | -{ | |
69 | - unsigned int low, high; | |
70 | - int i; | |
71 | - | |
72 | - /* clear all counters */ | |
73 | - for (i = 0 ; i < NUM_CONTROLS; ++i) { | |
74 | - if (unlikely(!CTRL_IS_RESERVED(msrs, i))) | |
75 | - continue; | |
76 | - CTRL_READ(low, high, msrs, i); | |
77 | - CTRL_CLEAR_LO(low); | |
78 | - CTRL_CLEAR_HI(high); | |
79 | - CTRL_WRITE(low, high, msrs, i); | |
80 | - } | |
81 | - | |
82 | - /* avoid a false detection of ctr overflows in NMI handler */ | |
83 | - for (i = 0; i < NUM_COUNTERS; ++i) { | |
84 | - if (unlikely(!CTR_IS_RESERVED(msrs, i))) | |
85 | - continue; | |
86 | - CTR_WRITE(1, msrs, i); | |
87 | - } | |
88 | - | |
89 | - /* enable active counters */ | |
90 | - for (i = 0; i < NUM_COUNTERS; ++i) { | |
91 | - if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { | |
92 | - reset_value[i] = counter_config[i].count; | |
93 | - | |
94 | - CTR_WRITE(counter_config[i].count, msrs, i); | |
95 | - | |
96 | - CTRL_READ(low, high, msrs, i); | |
97 | - CTRL_CLEAR_LO(low); | |
98 | - CTRL_CLEAR_HI(high); | |
99 | - CTRL_SET_ENABLE(low); | |
100 | - CTRL_SET_USR(low, counter_config[i].user); | |
101 | - CTRL_SET_KERN(low, counter_config[i].kernel); | |
102 | - CTRL_SET_UM(low, counter_config[i].unit_mask); | |
103 | - CTRL_SET_EVENT_LOW(low, counter_config[i].event); | |
104 | - CTRL_SET_EVENT_HIGH(high, counter_config[i].event); | |
105 | - CTRL_SET_HOST_ONLY(high, 0); | |
106 | - CTRL_SET_GUEST_ONLY(high, 0); | |
107 | - | |
108 | - CTRL_WRITE(low, high, msrs, i); | |
109 | - } else { | |
110 | - reset_value[i] = 0; | |
111 | - } | |
112 | - } | |
113 | -} | |
114 | - | |
115 | - | |
116 | -static int athlon_check_ctrs(struct pt_regs * const regs, | |
117 | - struct op_msrs const * const msrs) | |
118 | -{ | |
119 | - unsigned int low, high; | |
120 | - int i; | |
121 | - | |
122 | - for (i = 0 ; i < NUM_COUNTERS; ++i) { | |
123 | - if (!reset_value[i]) | |
124 | - continue; | |
125 | - CTR_READ(low, high, msrs, i); | |
126 | - if (CTR_OVERFLOWED(low)) { | |
127 | - oprofile_add_sample(regs, i); | |
128 | - CTR_WRITE(reset_value[i], msrs, i); | |
129 | - } | |
130 | - } | |
131 | - | |
132 | - /* See op_model_ppro.c */ | |
133 | - return 1; | |
134 | -} | |
135 | - | |
136 | - | |
137 | -static void athlon_start(struct op_msrs const * const msrs) | |
138 | -{ | |
139 | - unsigned int low, high; | |
140 | - int i; | |
141 | - for (i = 0 ; i < NUM_COUNTERS ; ++i) { | |
142 | - if (reset_value[i]) { | |
143 | - CTRL_READ(low, high, msrs, i); | |
144 | - CTRL_SET_ACTIVE(low); | |
145 | - CTRL_WRITE(low, high, msrs, i); | |
146 | - } | |
147 | - } | |
148 | -} | |
149 | - | |
150 | - | |
151 | -static void athlon_stop(struct op_msrs const * const msrs) | |
152 | -{ | |
153 | - unsigned int low, high; | |
154 | - int i; | |
155 | - | |
156 | - /* Subtle: stop on all counters to avoid race with | |
157 | - * setting our pm callback */ | |
158 | - for (i = 0 ; i < NUM_COUNTERS ; ++i) { | |
159 | - if (!reset_value[i]) | |
160 | - continue; | |
161 | - CTRL_READ(low, high, msrs, i); | |
162 | - CTRL_SET_INACTIVE(low); | |
163 | - CTRL_WRITE(low, high, msrs, i); | |
164 | - } | |
165 | -} | |
166 | - | |
167 | -static void athlon_shutdown(struct op_msrs const * const msrs) | |
168 | -{ | |
169 | - int i; | |
170 | - | |
171 | - for (i = 0 ; i < NUM_COUNTERS ; ++i) { | |
172 | - if (CTR_IS_RESERVED(msrs, i)) | |
173 | - release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
174 | - } | |
175 | - for (i = 0 ; i < NUM_CONTROLS ; ++i) { | |
176 | - if (CTRL_IS_RESERVED(msrs, i)) | |
177 | - release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | |
178 | - } | |
179 | -} | |
180 | - | |
181 | -struct op_x86_model_spec const op_athlon_spec = { | |
182 | - .num_counters = NUM_COUNTERS, | |
183 | - .num_controls = NUM_CONTROLS, | |
184 | - .fill_in_addresses = &athlon_fill_in_addresses, | |
185 | - .setup_ctrs = &athlon_setup_ctrs, | |
186 | - .check_ctrs = &athlon_check_ctrs, | |
187 | - .start = &athlon_start, | |
188 | - .stop = &athlon_stop, | |
189 | - .shutdown = &athlon_shutdown | |
190 | -}; |
arch/x86/oprofile/op_x86_model.h
... | ... | @@ -32,6 +32,8 @@ |
32 | 32 | * various x86 CPU models' perfctr support. |
33 | 33 | */ |
34 | 34 | struct op_x86_model_spec { |
35 | + int (*init)(struct oprofile_operations *ops); | |
36 | + void (*exit)(void); | |
35 | 37 | unsigned int const num_counters; |
36 | 38 | unsigned int const num_controls; |
37 | 39 | void (*fill_in_addresses)(struct op_msrs * const msrs); |
... | ... | @@ -46,7 +48,7 @@ |
46 | 48 | extern struct op_x86_model_spec const op_ppro_spec; |
47 | 49 | extern struct op_x86_model_spec const op_p4_spec; |
48 | 50 | extern struct op_x86_model_spec const op_p4_ht2_spec; |
49 | -extern struct op_x86_model_spec const op_athlon_spec; | |
51 | +extern struct op_x86_model_spec const op_amd_spec; | |
50 | 52 | |
51 | 53 | #endif /* OP_X86_MODEL_H */ |
arch/x86/pci/fixup.c
... | ... | @@ -511,4 +511,32 @@ |
511 | 511 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, fam10h_pci_cfg_space_size); |
512 | 512 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, fam10h_pci_cfg_space_size); |
513 | 513 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, fam10h_pci_cfg_space_size); |
514 | + | |
515 | +/* | |
516 | + * SB600: Disable BAR1 on device 14.0 to avoid HPET resources from | |
517 | + * confusing the PCI engine: | |
518 | + */ | |
519 | +static void sb600_disable_hpet_bar(struct pci_dev *dev) | |
520 | +{ | |
521 | + u8 val; | |
522 | + | |
523 | + /* | |
524 | + * The SB600 and SB700 both share the same device | |
525 | + * ID, but the PM register 0x55 does something different | |
526 | + * for the SB700, so make sure we are dealing with the | |
527 | + * SB600 before touching the bit: | |
528 | + */ | |
529 | + | |
530 | + pci_read_config_byte(dev, 0x08, &val); | |
531 | + | |
532 | + if (val < 0x2F) { | |
533 | + outb(0x55, 0xCD6); | |
534 | + val = inb(0xCD7); | |
535 | + | |
536 | + /* Set bit 7 in PM register 0x55 */ | |
537 | + outb(0x55, 0xCD6); | |
538 | + outb(val | 0x80, 0xCD7); | |
539 | + } | |
540 | +} | |
541 | +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x4385, sb600_disable_hpet_bar); |
drivers/char/hpet.c
... | ... | @@ -53,6 +53,11 @@ |
53 | 53 | |
54 | 54 | #define HPET_RANGE_SIZE 1024 /* from HPET spec */ |
55 | 55 | |
56 | + | |
57 | +/* WARNING -- don't get confused. These macros are never used | |
58 | + * to write the (single) counter, and rarely to read it. | |
59 | + * They're badly named; to fix, someday. | |
60 | + */ | |
56 | 61 | #if BITS_PER_LONG == 64 |
57 | 62 | #define write_counter(V, MC) writeq(V, MC) |
58 | 63 | #define read_counter(MC) readq(MC) |
... | ... | @@ -77,7 +82,7 @@ |
77 | 82 | .rating = 250, |
78 | 83 | .read = read_hpet, |
79 | 84 | .mask = CLOCKSOURCE_MASK(64), |
80 | - .mult = 0, /*to be caluclated*/ | |
85 | + .mult = 0, /* to be calculated */ | |
81 | 86 | .shift = 10, |
82 | 87 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
83 | 88 | }; |
... | ... | @@ -86,8 +91,6 @@ |
86 | 91 | |
87 | 92 | /* A lock for concurrent access by app and isr hpet activity. */ |
88 | 93 | static DEFINE_SPINLOCK(hpet_lock); |
89 | -/* A lock for concurrent intermodule access to hpet and isr hpet activity. */ | |
90 | -static DEFINE_SPINLOCK(hpet_task_lock); | |
91 | 94 | |
92 | 95 | #define HPET_DEV_NAME (7) |
93 | 96 | |
... | ... | @@ -99,7 +102,6 @@ |
99 | 102 | unsigned long hd_irqdata; |
100 | 103 | wait_queue_head_t hd_waitqueue; |
101 | 104 | struct fasync_struct *hd_async_queue; |
102 | - struct hpet_task *hd_task; | |
103 | 105 | unsigned int hd_flags; |
104 | 106 | unsigned int hd_irq; |
105 | 107 | unsigned int hd_hdwirq; |
... | ... | @@ -173,11 +175,6 @@ |
173 | 175 | writel(isr, &devp->hd_hpet->hpet_isr); |
174 | 176 | spin_unlock(&hpet_lock); |
175 | 177 | |
176 | - spin_lock(&hpet_task_lock); | |
177 | - if (devp->hd_task) | |
178 | - devp->hd_task->ht_func(devp->hd_task->ht_data); | |
179 | - spin_unlock(&hpet_task_lock); | |
180 | - | |
181 | 178 | wake_up_interruptible(&devp->hd_waitqueue); |
182 | 179 | |
183 | 180 | kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN); |
... | ... | @@ -185,6 +182,67 @@ |
185 | 182 | return IRQ_HANDLED; |
186 | 183 | } |
187 | 184 | |
185 | +static void hpet_timer_set_irq(struct hpet_dev *devp) | |
186 | +{ | |
187 | + unsigned long v; | |
188 | + int irq, gsi; | |
189 | + struct hpet_timer __iomem *timer; | |
190 | + | |
191 | + spin_lock_irq(&hpet_lock); | |
192 | + if (devp->hd_hdwirq) { | |
193 | + spin_unlock_irq(&hpet_lock); | |
194 | + return; | |
195 | + } | |
196 | + | |
197 | + timer = devp->hd_timer; | |
198 | + | |
199 | + /* we prefer level triggered mode */ | |
200 | + v = readl(&timer->hpet_config); | |
201 | + if (!(v & Tn_INT_TYPE_CNF_MASK)) { | |
202 | + v |= Tn_INT_TYPE_CNF_MASK; | |
203 | + writel(v, &timer->hpet_config); | |
204 | + } | |
205 | + spin_unlock_irq(&hpet_lock); | |
206 | + | |
207 | + v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >> | |
208 | + Tn_INT_ROUTE_CAP_SHIFT; | |
209 | + | |
210 | + /* | |
211 | + * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by | |
212 | + * legacy device. In IO APIC mode, we skip all the legacy IRQS. | |
213 | + */ | |
214 | + if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) | |
215 | + v &= ~0xf3df; | |
216 | + else | |
217 | + v &= ~0xffff; | |
218 | + | |
219 | + for (irq = find_first_bit(&v, HPET_MAX_IRQ); irq < HPET_MAX_IRQ; | |
220 | + irq = find_next_bit(&v, HPET_MAX_IRQ, 1 + irq)) { | |
221 | + | |
222 | + if (irq >= NR_IRQS) { | |
223 | + irq = HPET_MAX_IRQ; | |
224 | + break; | |
225 | + } | |
226 | + | |
227 | + gsi = acpi_register_gsi(irq, ACPI_LEVEL_SENSITIVE, | |
228 | + ACPI_ACTIVE_LOW); | |
229 | + if (gsi > 0) | |
230 | + break; | |
231 | + | |
232 | + /* FIXME: Setup interrupt source table */ | |
233 | + } | |
234 | + | |
235 | + if (irq < HPET_MAX_IRQ) { | |
236 | + spin_lock_irq(&hpet_lock); | |
237 | + v = readl(&timer->hpet_config); | |
238 | + v |= irq << Tn_INT_ROUTE_CNF_SHIFT; | |
239 | + writel(v, &timer->hpet_config); | |
240 | + devp->hd_hdwirq = gsi; | |
241 | + spin_unlock_irq(&hpet_lock); | |
242 | + } | |
243 | + return; | |
244 | +} | |
245 | + | |
188 | 246 | static int hpet_open(struct inode *inode, struct file *file) |
189 | 247 | { |
190 | 248 | struct hpet_dev *devp; |
... | ... | @@ -199,8 +257,7 @@ |
199 | 257 | |
200 | 258 | for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next) |
201 | 259 | for (i = 0; i < hpetp->hp_ntimer; i++) |
202 | - if (hpetp->hp_dev[i].hd_flags & HPET_OPEN | |
203 | - || hpetp->hp_dev[i].hd_task) | |
260 | + if (hpetp->hp_dev[i].hd_flags & HPET_OPEN) | |
204 | 261 | continue; |
205 | 262 | else { |
206 | 263 | devp = &hpetp->hp_dev[i]; |
... | ... | @@ -219,6 +276,8 @@ |
219 | 276 | spin_unlock_irq(&hpet_lock); |
220 | 277 | unlock_kernel(); |
221 | 278 | |
279 | + hpet_timer_set_irq(devp); | |
280 | + | |
222 | 281 | return 0; |
223 | 282 | } |
224 | 283 | |
225 | 284 | |
... | ... | @@ -441,8 +500,12 @@ |
441 | 500 | devp->hd_irq = irq; |
442 | 501 | t = devp->hd_ireqfreq; |
443 | 502 | v = readq(&timer->hpet_config); |
444 | - g = v | Tn_INT_ENB_CNF_MASK; | |
445 | 503 | |
504 | + /* 64-bit comparators are not yet supported through the ioctls, | |
505 | + * so force this into 32-bit mode if it supports both modes | |
506 | + */ | |
507 | + g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK; | |
508 | + | |
446 | 509 | if (devp->hd_flags & HPET_PERIODIC) { |
447 | 510 | write_counter(t, &timer->hpet_compare); |
448 | 511 | g |= Tn_TYPE_CNF_MASK; |
... | ... | @@ -451,6 +514,12 @@ |
451 | 514 | v |= Tn_VAL_SET_CNF_MASK; |
452 | 515 | writeq(v, &timer->hpet_config); |
453 | 516 | local_irq_save(flags); |
517 | + | |
518 | + /* NOTE: what we modify here is a hidden accumulator | |
519 | + * register supported by periodic-capable comparators. | |
520 | + * We never want to modify the (single) counter; that | |
521 | + * would affect all the comparators. | |
522 | + */ | |
454 | 523 | m = read_counter(&hpet->hpet_mc); |
455 | 524 | write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); |
456 | 525 | } else { |
... | ... | @@ -604,57 +673,6 @@ |
604 | 673 | return 0; |
605 | 674 | } |
606 | 675 | |
607 | -static inline int hpet_tpcheck(struct hpet_task *tp) | |
608 | -{ | |
609 | - struct hpet_dev *devp; | |
610 | - struct hpets *hpetp; | |
611 | - | |
612 | - devp = tp->ht_opaque; | |
613 | - | |
614 | - if (!devp) | |
615 | - return -ENXIO; | |
616 | - | |
617 | - for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) | |
618 | - if (devp >= hpetp->hp_dev | |
619 | - && devp < (hpetp->hp_dev + hpetp->hp_ntimer) | |
620 | - && devp->hd_hpet == hpetp->hp_hpet) | |
621 | - return 0; | |
622 | - | |
623 | - return -ENXIO; | |
624 | -} | |
625 | - | |
626 | -#if 0 | |
627 | -int hpet_unregister(struct hpet_task *tp) | |
628 | -{ | |
629 | - struct hpet_dev *devp; | |
630 | - struct hpet_timer __iomem *timer; | |
631 | - int err; | |
632 | - | |
633 | - if ((err = hpet_tpcheck(tp))) | |
634 | - return err; | |
635 | - | |
636 | - spin_lock_irq(&hpet_task_lock); | |
637 | - spin_lock(&hpet_lock); | |
638 | - | |
639 | - devp = tp->ht_opaque; | |
640 | - if (devp->hd_task != tp) { | |
641 | - spin_unlock(&hpet_lock); | |
642 | - spin_unlock_irq(&hpet_task_lock); | |
643 | - return -ENXIO; | |
644 | - } | |
645 | - | |
646 | - timer = devp->hd_timer; | |
647 | - writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK), | |
648 | - &timer->hpet_config); | |
649 | - devp->hd_flags &= ~(HPET_IE | HPET_PERIODIC); | |
650 | - devp->hd_task = NULL; | |
651 | - spin_unlock(&hpet_lock); | |
652 | - spin_unlock_irq(&hpet_task_lock); | |
653 | - | |
654 | - return 0; | |
655 | -} | |
656 | -#endif /* 0 */ | |
657 | - | |
658 | 676 | static ctl_table hpet_table[] = { |
659 | 677 | { |
660 | 678 | .ctl_name = CTL_UNNUMBERED, |
... | ... | @@ -746,6 +764,7 @@ |
746 | 764 | static struct hpets *last = NULL; |
747 | 765 | unsigned long period; |
748 | 766 | unsigned long long temp; |
767 | + u32 remainder; | |
749 | 768 | |
750 | 769 | /* |
751 | 770 | * hpet_alloc can be called by platform dependent code. |
... | ... | @@ -809,9 +828,13 @@ |
809 | 828 | printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); |
810 | 829 | printk("\n"); |
811 | 830 | |
812 | - printk(KERN_INFO "hpet%u: %u %d-bit timers, %Lu Hz\n", | |
813 | - hpetp->hp_which, hpetp->hp_ntimer, | |
814 | - cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, hpetp->hp_tick_freq); | |
831 | + temp = hpetp->hp_tick_freq; | |
832 | + remainder = do_div(temp, 1000000); | |
833 | + printk(KERN_INFO | |
834 | + "hpet%u: %u comparators, %d-bit %u.%06u MHz counter\n", | |
835 | + hpetp->hp_which, hpetp->hp_ntimer, | |
836 | + cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, | |
837 | + (unsigned) temp, remainder); | |
815 | 838 | |
816 | 839 | mcfg = readq(&hpet->hpet_config); |
817 | 840 | if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) { |
... | ... | @@ -874,8 +897,6 @@ |
874 | 897 | hdp->hd_address = ioremap(addr.minimum, addr.address_length); |
875 | 898 | |
876 | 899 | if (hpet_is_known(hdp)) { |
877 | - printk(KERN_DEBUG "%s: 0x%lx is busy\n", | |
878 | - __func__, hdp->hd_phys_address); | |
879 | 900 | iounmap(hdp->hd_address); |
880 | 901 | return AE_ALREADY_EXISTS; |
881 | 902 | } |
... | ... | @@ -891,8 +912,6 @@ |
891 | 912 | HPET_RANGE_SIZE); |
892 | 913 | |
893 | 914 | if (hpet_is_known(hdp)) { |
894 | - printk(KERN_DEBUG "%s: 0x%lx is busy\n", | |
895 | - __func__, hdp->hd_phys_address); | |
896 | 915 | iounmap(hdp->hd_address); |
897 | 916 | return AE_ALREADY_EXISTS; |
898 | 917 | } |
drivers/oprofile/buffer_sync.c
... | ... | @@ -5,6 +5,7 @@ |
5 | 5 | * @remark Read the file COPYING |
6 | 6 | * |
7 | 7 | * @author John Levon <levon@movementarian.org> |
8 | + * @author Barry Kasindorf | |
8 | 9 | * |
9 | 10 | * This is the core of the buffer management. Each |
10 | 11 | * CPU buffer is processed and entered into the |
... | ... | @@ -33,7 +34,7 @@ |
33 | 34 | #include "event_buffer.h" |
34 | 35 | #include "cpu_buffer.h" |
35 | 36 | #include "buffer_sync.h" |
36 | - | |
37 | + | |
37 | 38 | static LIST_HEAD(dying_tasks); |
38 | 39 | static LIST_HEAD(dead_tasks); |
39 | 40 | static cpumask_t marked_cpus = CPU_MASK_NONE; |
40 | 41 | |
... | ... | @@ -48,10 +49,11 @@ |
48 | 49 | * Can be invoked from softirq via RCU callback due to |
49 | 50 | * call_rcu() of the task struct, hence the _irqsave. |
50 | 51 | */ |
51 | -static int task_free_notify(struct notifier_block * self, unsigned long val, void * data) | |
52 | +static int | |
53 | +task_free_notify(struct notifier_block *self, unsigned long val, void *data) | |
52 | 54 | { |
53 | 55 | unsigned long flags; |
54 | - struct task_struct * task = data; | |
56 | + struct task_struct *task = data; | |
55 | 57 | spin_lock_irqsave(&task_mortuary, flags); |
56 | 58 | list_add(&task->tasks, &dying_tasks); |
57 | 59 | spin_unlock_irqrestore(&task_mortuary, flags); |
58 | 60 | |
... | ... | @@ -62,13 +64,14 @@ |
62 | 64 | /* The task is on its way out. A sync of the buffer means we can catch |
63 | 65 | * any remaining samples for this task. |
64 | 66 | */ |
65 | -static int task_exit_notify(struct notifier_block * self, unsigned long val, void * data) | |
67 | +static int | |
68 | +task_exit_notify(struct notifier_block *self, unsigned long val, void *data) | |
66 | 69 | { |
67 | 70 | /* To avoid latency problems, we only process the current CPU, |
68 | 71 | * hoping that most samples for the task are on this CPU |
69 | 72 | */ |
70 | 73 | sync_buffer(raw_smp_processor_id()); |
71 | - return 0; | |
74 | + return 0; | |
72 | 75 | } |
73 | 76 | |
74 | 77 | |
75 | 78 | |
... | ... | @@ -77,11 +80,12 @@ |
77 | 80 | * we don't lose any. This does not have to be exact, it's a QoI issue |
78 | 81 | * only. |
79 | 82 | */ |
80 | -static int munmap_notify(struct notifier_block * self, unsigned long val, void * data) | |
83 | +static int | |
84 | +munmap_notify(struct notifier_block *self, unsigned long val, void *data) | |
81 | 85 | { |
82 | 86 | unsigned long addr = (unsigned long)data; |
83 | - struct mm_struct * mm = current->mm; | |
84 | - struct vm_area_struct * mpnt; | |
87 | + struct mm_struct *mm = current->mm; | |
88 | + struct vm_area_struct *mpnt; | |
85 | 89 | |
86 | 90 | down_read(&mm->mmap_sem); |
87 | 91 | |
88 | 92 | |
... | ... | @@ -99,11 +103,12 @@ |
99 | 103 | return 0; |
100 | 104 | } |
101 | 105 | |
102 | - | |
106 | + | |
103 | 107 | /* We need to be told about new modules so we don't attribute to a previously |
104 | 108 | * loaded module, or drop the samples on the floor. |
105 | 109 | */ |
106 | -static int module_load_notify(struct notifier_block * self, unsigned long val, void * data) | |
110 | +static int | |
111 | +module_load_notify(struct notifier_block *self, unsigned long val, void *data) | |
107 | 112 | { |
108 | 113 | #ifdef CONFIG_MODULES |
109 | 114 | if (val != MODULE_STATE_COMING) |
... | ... | @@ -118,7 +123,7 @@ |
118 | 123 | return 0; |
119 | 124 | } |
120 | 125 | |
121 | - | |
126 | + | |
122 | 127 | static struct notifier_block task_free_nb = { |
123 | 128 | .notifier_call = task_free_notify, |
124 | 129 | }; |
... | ... | @@ -135,7 +140,7 @@ |
135 | 140 | .notifier_call = module_load_notify, |
136 | 141 | }; |
137 | 142 | |
138 | - | |
143 | + | |
139 | 144 | static void end_sync(void) |
140 | 145 | { |
141 | 146 | end_cpu_work(); |
142 | 147 | |
143 | 148 | |
... | ... | @@ -208,14 +213,14 @@ |
208 | 213 | * not strictly necessary but allows oprofile to associate |
209 | 214 | * shared-library samples with particular applications |
210 | 215 | */ |
211 | -static unsigned long get_exec_dcookie(struct mm_struct * mm) | |
216 | +static unsigned long get_exec_dcookie(struct mm_struct *mm) | |
212 | 217 | { |
213 | 218 | unsigned long cookie = NO_COOKIE; |
214 | - struct vm_area_struct * vma; | |
215 | - | |
219 | + struct vm_area_struct *vma; | |
220 | + | |
216 | 221 | if (!mm) |
217 | 222 | goto out; |
218 | - | |
223 | + | |
219 | 224 | for (vma = mm->mmap; vma; vma = vma->vm_next) { |
220 | 225 | if (!vma->vm_file) |
221 | 226 | continue; |
222 | 227 | |
223 | 228 | |
... | ... | @@ -235,13 +240,14 @@ |
235 | 240 | * sure to do this lookup before a mm->mmap modification happens so |
236 | 241 | * we don't lose track. |
237 | 242 | */ |
238 | -static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) | |
243 | +static unsigned long | |
244 | +lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) | |
239 | 245 | { |
240 | 246 | unsigned long cookie = NO_COOKIE; |
241 | - struct vm_area_struct * vma; | |
247 | + struct vm_area_struct *vma; | |
242 | 248 | |
243 | 249 | for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { |
244 | - | |
250 | + | |
245 | 251 | if (addr < vma->vm_start || addr >= vma->vm_end) |
246 | 252 | continue; |
247 | 253 | |
248 | 254 | |
249 | 255 | |
... | ... | @@ -263,9 +269,20 @@ |
263 | 269 | return cookie; |
264 | 270 | } |
265 | 271 | |
272 | +static void increment_tail(struct oprofile_cpu_buffer *b) | |
273 | +{ | |
274 | + unsigned long new_tail = b->tail_pos + 1; | |
266 | 275 | |
276 | + rmb(); /* be sure fifo pointers are synchromized */ | |
277 | + | |
278 | + if (new_tail < b->buffer_size) | |
279 | + b->tail_pos = new_tail; | |
280 | + else | |
281 | + b->tail_pos = 0; | |
282 | +} | |
283 | + | |
267 | 284 | static unsigned long last_cookie = INVALID_COOKIE; |
268 | - | |
285 | + | |
269 | 286 | static void add_cpu_switch(int i) |
270 | 287 | { |
271 | 288 | add_event_entry(ESCAPE_CODE); |
272 | 289 | |
273 | 290 | |
274 | 291 | |
275 | 292 | |
... | ... | @@ -278,16 +295,16 @@ |
278 | 295 | { |
279 | 296 | add_event_entry(ESCAPE_CODE); |
280 | 297 | if (in_kernel) |
281 | - add_event_entry(KERNEL_ENTER_SWITCH_CODE); | |
298 | + add_event_entry(KERNEL_ENTER_SWITCH_CODE); | |
282 | 299 | else |
283 | - add_event_entry(KERNEL_EXIT_SWITCH_CODE); | |
300 | + add_event_entry(KERNEL_EXIT_SWITCH_CODE); | |
284 | 301 | } |
285 | - | |
302 | + | |
286 | 303 | static void |
287 | -add_user_ctx_switch(struct task_struct const * task, unsigned long cookie) | |
304 | +add_user_ctx_switch(struct task_struct const *task, unsigned long cookie) | |
288 | 305 | { |
289 | 306 | add_event_entry(ESCAPE_CODE); |
290 | - add_event_entry(CTX_SWITCH_CODE); | |
307 | + add_event_entry(CTX_SWITCH_CODE); | |
291 | 308 | add_event_entry(task->pid); |
292 | 309 | add_event_entry(cookie); |
293 | 310 | /* Another code for daemon back-compat */ |
... | ... | @@ -296,7 +313,7 @@ |
296 | 313 | add_event_entry(task->tgid); |
297 | 314 | } |
298 | 315 | |
299 | - | |
316 | + | |
300 | 317 | static void add_cookie_switch(unsigned long cookie) |
301 | 318 | { |
302 | 319 | add_event_entry(ESCAPE_CODE); |
303 | 320 | |
304 | 321 | |
... | ... | @@ -304,14 +321,79 @@ |
304 | 321 | add_event_entry(cookie); |
305 | 322 | } |
306 | 323 | |
307 | - | |
324 | + | |
308 | 325 | static void add_trace_begin(void) |
309 | 326 | { |
310 | 327 | add_event_entry(ESCAPE_CODE); |
311 | 328 | add_event_entry(TRACE_BEGIN_CODE); |
312 | 329 | } |
313 | 330 | |
331 | +#ifdef CONFIG_OPROFILE_IBS | |
314 | 332 | |
333 | +#define IBS_FETCH_CODE_SIZE 2 | |
334 | +#define IBS_OP_CODE_SIZE 5 | |
335 | +#define IBS_EIP(offset) \ | |
336 | + (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) | |
337 | +#define IBS_EVENT(offset) \ | |
338 | + (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) | |
339 | + | |
340 | +/* | |
341 | + * Add IBS fetch and op entries to event buffer | |
342 | + */ | |
343 | +static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, | |
344 | + int in_kernel, struct mm_struct *mm) | |
345 | +{ | |
346 | + unsigned long rip; | |
347 | + int i, count; | |
348 | + unsigned long ibs_cookie = 0; | |
349 | + off_t offset; | |
350 | + | |
351 | + increment_tail(cpu_buf); /* move to RIP entry */ | |
352 | + | |
353 | + rip = IBS_EIP(cpu_buf->tail_pos); | |
354 | + | |
355 | +#ifdef __LP64__ | |
356 | + rip += IBS_EVENT(cpu_buf->tail_pos) << 32; | |
357 | +#endif | |
358 | + | |
359 | + if (mm) { | |
360 | + ibs_cookie = lookup_dcookie(mm, rip, &offset); | |
361 | + | |
362 | + if (ibs_cookie == NO_COOKIE) | |
363 | + offset = rip; | |
364 | + if (ibs_cookie == INVALID_COOKIE) { | |
365 | + atomic_inc(&oprofile_stats.sample_lost_no_mapping); | |
366 | + offset = rip; | |
367 | + } | |
368 | + if (ibs_cookie != last_cookie) { | |
369 | + add_cookie_switch(ibs_cookie); | |
370 | + last_cookie = ibs_cookie; | |
371 | + } | |
372 | + } else | |
373 | + offset = rip; | |
374 | + | |
375 | + add_event_entry(ESCAPE_CODE); | |
376 | + add_event_entry(code); | |
377 | + add_event_entry(offset); /* Offset from Dcookie */ | |
378 | + | |
379 | + /* we send the Dcookie offset, but send the raw Linear Add also*/ | |
380 | + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); | |
381 | + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); | |
382 | + | |
383 | + if (code == IBS_FETCH_CODE) | |
384 | + count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ | |
385 | + else | |
386 | + count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ | |
387 | + | |
388 | + for (i = 0; i < count; i++) { | |
389 | + increment_tail(cpu_buf); | |
390 | + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); | |
391 | + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); | |
392 | + } | |
393 | +} | |
394 | + | |
395 | +#endif | |
396 | + | |
315 | 397 | static void add_sample_entry(unsigned long offset, unsigned long event) |
316 | 398 | { |
317 | 399 | add_event_entry(offset); |
318 | 400 | |
... | ... | @@ -319,13 +401,13 @@ |
319 | 401 | } |
320 | 402 | |
321 | 403 | |
322 | -static int add_us_sample(struct mm_struct * mm, struct op_sample * s) | |
404 | +static int add_us_sample(struct mm_struct *mm, struct op_sample *s) | |
323 | 405 | { |
324 | 406 | unsigned long cookie; |
325 | 407 | off_t offset; |
326 | - | |
327 | - cookie = lookup_dcookie(mm, s->eip, &offset); | |
328 | - | |
408 | + | |
409 | + cookie = lookup_dcookie(mm, s->eip, &offset); | |
410 | + | |
329 | 411 | if (cookie == INVALID_COOKIE) { |
330 | 412 | atomic_inc(&oprofile_stats.sample_lost_no_mapping); |
331 | 413 | return 0; |
332 | 414 | |
... | ... | @@ -341,13 +423,13 @@ |
341 | 423 | return 1; |
342 | 424 | } |
343 | 425 | |
344 | - | |
426 | + | |
345 | 427 | /* Add a sample to the global event buffer. If possible the |
346 | 428 | * sample is converted into a persistent dentry/offset pair |
347 | 429 | * for later lookup from userspace. |
348 | 430 | */ |
349 | 431 | static int |
350 | -add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) | |
432 | +add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) | |
351 | 433 | { |
352 | 434 | if (in_kernel) { |
353 | 435 | add_sample_entry(s->eip, s->event); |
354 | 436 | |
... | ... | @@ -359,9 +441,9 @@ |
359 | 441 | } |
360 | 442 | return 0; |
361 | 443 | } |
362 | - | |
363 | 444 | |
364 | -static void release_mm(struct mm_struct * mm) | |
445 | + | |
446 | +static void release_mm(struct mm_struct *mm) | |
365 | 447 | { |
366 | 448 | if (!mm) |
367 | 449 | return; |
368 | 450 | |
... | ... | @@ -370,9 +452,9 @@ |
370 | 452 | } |
371 | 453 | |
372 | 454 | |
373 | -static struct mm_struct * take_tasks_mm(struct task_struct * task) | |
455 | +static struct mm_struct *take_tasks_mm(struct task_struct *task) | |
374 | 456 | { |
375 | - struct mm_struct * mm = get_task_mm(task); | |
457 | + struct mm_struct *mm = get_task_mm(task); | |
376 | 458 | if (mm) |
377 | 459 | down_read(&mm->mmap_sem); |
378 | 460 | return mm; |
379 | 461 | |
380 | 462 | |
... | ... | @@ -383,10 +465,10 @@ |
383 | 465 | { |
384 | 466 | return val == ESCAPE_CODE; |
385 | 467 | } |
386 | - | |
387 | 468 | |
469 | + | |
388 | 470 | /* "acquire" as many cpu buffer slots as we can */ |
389 | -static unsigned long get_slots(struct oprofile_cpu_buffer * b) | |
471 | +static unsigned long get_slots(struct oprofile_cpu_buffer *b) | |
390 | 472 | { |
391 | 473 | unsigned long head = b->head_pos; |
392 | 474 | unsigned long tail = b->tail_pos; |
... | ... | @@ -412,19 +494,6 @@ |
412 | 494 | } |
413 | 495 | |
414 | 496 | |
415 | -static void increment_tail(struct oprofile_cpu_buffer * b) | |
416 | -{ | |
417 | - unsigned long new_tail = b->tail_pos + 1; | |
418 | - | |
419 | - rmb(); | |
420 | - | |
421 | - if (new_tail < b->buffer_size) | |
422 | - b->tail_pos = new_tail; | |
423 | - else | |
424 | - b->tail_pos = 0; | |
425 | -} | |
426 | - | |
427 | - | |
428 | 497 | /* Move tasks along towards death. Any tasks on dead_tasks |
429 | 498 | * will definitely have no remaining references in any |
430 | 499 | * CPU buffers at this point, because we use two lists, |
... | ... | @@ -435,8 +504,8 @@ |
435 | 504 | { |
436 | 505 | unsigned long flags; |
437 | 506 | LIST_HEAD(local_dead_tasks); |
438 | - struct task_struct * task; | |
439 | - struct task_struct * ttask; | |
507 | + struct task_struct *task; | |
508 | + struct task_struct *ttask; | |
440 | 509 | |
441 | 510 | spin_lock_irqsave(&task_mortuary, flags); |
442 | 511 | |
... | ... | @@ -493,7 +562,7 @@ |
493 | 562 | { |
494 | 563 | struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); |
495 | 564 | struct mm_struct *mm = NULL; |
496 | - struct task_struct * new; | |
565 | + struct task_struct *new; | |
497 | 566 | unsigned long cookie = 0; |
498 | 567 | int in_kernel = 1; |
499 | 568 | unsigned int i; |
... | ... | @@ -501,7 +570,7 @@ |
501 | 570 | unsigned long available; |
502 | 571 | |
503 | 572 | mutex_lock(&buffer_mutex); |
504 | - | |
573 | + | |
505 | 574 | add_cpu_switch(cpu); |
506 | 575 | |
507 | 576 | /* Remember, only we can modify tail_pos */ |
... | ... | @@ -509,8 +578,8 @@ |
509 | 578 | available = get_slots(cpu_buf); |
510 | 579 | |
511 | 580 | for (i = 0; i < available; ++i) { |
512 | - struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos]; | |
513 | - | |
581 | + struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos]; | |
582 | + | |
514 | 583 | if (is_code(s->eip)) { |
515 | 584 | if (s->event <= CPU_IS_KERNEL) { |
516 | 585 | /* kernel/userspace switch */ |
517 | 586 | |
... | ... | @@ -521,8 +590,18 @@ |
521 | 590 | } else if (s->event == CPU_TRACE_BEGIN) { |
522 | 591 | state = sb_bt_start; |
523 | 592 | add_trace_begin(); |
593 | +#ifdef CONFIG_OPROFILE_IBS | |
594 | + } else if (s->event == IBS_FETCH_BEGIN) { | |
595 | + state = sb_bt_start; | |
596 | + add_ibs_begin(cpu_buf, | |
597 | + IBS_FETCH_CODE, in_kernel, mm); | |
598 | + } else if (s->event == IBS_OP_BEGIN) { | |
599 | + state = sb_bt_start; | |
600 | + add_ibs_begin(cpu_buf, | |
601 | + IBS_OP_CODE, in_kernel, mm); | |
602 | +#endif | |
524 | 603 | } else { |
525 | - struct mm_struct * oldmm = mm; | |
604 | + struct mm_struct *oldmm = mm; | |
526 | 605 | |
527 | 606 | /* userspace context switch */ |
528 | 607 | new = (struct task_struct *)s->event; |
... | ... | @@ -533,13 +612,11 @@ |
533 | 612 | cookie = get_exec_dcookie(mm); |
534 | 613 | add_user_ctx_switch(new, cookie); |
535 | 614 | } |
536 | - } else { | |
537 | - if (state >= sb_bt_start && | |
538 | - !add_sample(mm, s, in_kernel)) { | |
539 | - if (state == sb_bt_start) { | |
540 | - state = sb_bt_ignore; | |
541 | - atomic_inc(&oprofile_stats.bt_lost_no_mapping); | |
542 | - } | |
615 | + } else if (state >= sb_bt_start && | |
616 | + !add_sample(mm, s, in_kernel)) { | |
617 | + if (state == sb_bt_start) { | |
618 | + state = sb_bt_ignore; | |
619 | + atomic_inc(&oprofile_stats.bt_lost_no_mapping); | |
543 | 620 | } |
544 | 621 | } |
545 | 622 |
drivers/oprofile/cpu_buffer.c
... | ... | @@ -5,6 +5,7 @@ |
5 | 5 | * @remark Read the file COPYING |
6 | 6 | * |
7 | 7 | * @author John Levon <levon@movementarian.org> |
8 | + * @author Barry Kasindorf <barry.kasindorf@amd.com> | |
8 | 9 | * |
9 | 10 | * Each CPU has a local buffer that stores PC value/event |
10 | 11 | * pairs. We also log context switches when we notice them. |
... | ... | @@ -209,7 +210,7 @@ |
209 | 210 | return 1; |
210 | 211 | } |
211 | 212 | |
212 | -static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) | |
213 | +static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) | |
213 | 214 | { |
214 | 215 | if (nr_available_slots(cpu_buf) < 4) { |
215 | 216 | cpu_buf->sample_lost_overflow++; |
... | ... | @@ -254,6 +255,75 @@ |
254 | 255 | oprofile_add_ext_sample(pc, regs, event, is_kernel); |
255 | 256 | } |
256 | 257 | |
258 | +#ifdef CONFIG_OPROFILE_IBS | |
259 | + | |
260 | +#define MAX_IBS_SAMPLE_SIZE 14 | |
261 | +static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, | |
262 | + unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) | |
263 | +{ | |
264 | + struct task_struct *task; | |
265 | + | |
266 | + cpu_buf->sample_received++; | |
267 | + | |
268 | + if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) { | |
269 | + cpu_buf->sample_lost_overflow++; | |
270 | + return 0; | |
271 | + } | |
272 | + | |
273 | + is_kernel = !!is_kernel; | |
274 | + | |
275 | + /* notice a switch from user->kernel or vice versa */ | |
276 | + if (cpu_buf->last_is_kernel != is_kernel) { | |
277 | + cpu_buf->last_is_kernel = is_kernel; | |
278 | + add_code(cpu_buf, is_kernel); | |
279 | + } | |
280 | + | |
281 | + /* notice a task switch */ | |
282 | + if (!is_kernel) { | |
283 | + task = current; | |
284 | + | |
285 | + if (cpu_buf->last_task != task) { | |
286 | + cpu_buf->last_task = task; | |
287 | + add_code(cpu_buf, (unsigned long)task); | |
288 | + } | |
289 | + } | |
290 | + | |
291 | + add_code(cpu_buf, ibs_code); | |
292 | + add_sample(cpu_buf, ibs[0], ibs[1]); | |
293 | + add_sample(cpu_buf, ibs[2], ibs[3]); | |
294 | + add_sample(cpu_buf, ibs[4], ibs[5]); | |
295 | + | |
296 | + if (ibs_code == IBS_OP_BEGIN) { | |
297 | + add_sample(cpu_buf, ibs[6], ibs[7]); | |
298 | + add_sample(cpu_buf, ibs[8], ibs[9]); | |
299 | + add_sample(cpu_buf, ibs[10], ibs[11]); | |
300 | + } | |
301 | + | |
302 | + return 1; | |
303 | +} | |
304 | + | |
305 | +void oprofile_add_ibs_sample(struct pt_regs *const regs, | |
306 | + unsigned int * const ibs_sample, u8 code) | |
307 | +{ | |
308 | + int is_kernel = !user_mode(regs); | |
309 | + unsigned long pc = profile_pc(regs); | |
310 | + | |
311 | + struct oprofile_cpu_buffer *cpu_buf = | |
312 | + &per_cpu(cpu_buffer, smp_processor_id()); | |
313 | + | |
314 | + if (!backtrace_depth) { | |
315 | + log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code); | |
316 | + return; | |
317 | + } | |
318 | + | |
319 | + /* if log_sample() fails we can't backtrace since we lost the source | |
320 | + * of this event */ | |
321 | + if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code)) | |
322 | + oprofile_ops.backtrace(regs, backtrace_depth); | |
323 | +} | |
324 | + | |
325 | +#endif | |
326 | + | |
257 | 327 | void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) |
258 | 328 | { |
259 | 329 | struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); |
... | ... | @@ -296,7 +366,7 @@ |
296 | 366 | struct oprofile_cpu_buffer * b = |
297 | 367 | container_of(work, struct oprofile_cpu_buffer, work.work); |
298 | 368 | if (b->cpu != smp_processor_id()) { |
299 | - printk("WQ on CPU%d, prefer CPU%d\n", | |
369 | + printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n", | |
300 | 370 | smp_processor_id(), b->cpu); |
301 | 371 | } |
302 | 372 | sync_buffer(b->cpu); |
drivers/oprofile/cpu_buffer.h
include/linux/hpet.h
... | ... | @@ -37,6 +37,7 @@ |
37 | 37 | #define hpet_compare _u1._hpet_compare |
38 | 38 | |
39 | 39 | #define HPET_MAX_TIMERS (32) |
40 | +#define HPET_MAX_IRQ (32) | |
40 | 41 | |
41 | 42 | /* |
42 | 43 | * HPET general capabilities register |
... | ... | @@ -64,7 +65,7 @@ |
64 | 65 | */ |
65 | 66 | |
66 | 67 | #define Tn_INT_ROUTE_CAP_MASK (0xffffffff00000000ULL) |
67 | -#define Tn_INI_ROUTE_CAP_SHIFT (32UL) | |
68 | +#define Tn_INT_ROUTE_CAP_SHIFT (32UL) | |
68 | 69 | #define Tn_FSB_INT_DELCAP_MASK (0x8000UL) |
69 | 70 | #define Tn_FSB_INT_DELCAP_SHIFT (15) |
70 | 71 | #define Tn_FSB_EN_CNF_MASK (0x4000UL) |
71 | 72 | |
72 | 73 | |
... | ... | @@ -91,23 +92,14 @@ |
91 | 92 | * exported interfaces |
92 | 93 | */ |
93 | 94 | |
94 | -struct hpet_task { | |
95 | - void (*ht_func) (void *); | |
96 | - void *ht_data; | |
97 | - void *ht_opaque; | |
98 | -}; | |
99 | - | |
100 | 95 | struct hpet_data { |
101 | 96 | unsigned long hd_phys_address; |
102 | 97 | void __iomem *hd_address; |
103 | 98 | unsigned short hd_nirqs; |
104 | - unsigned short hd_flags; | |
105 | 99 | unsigned int hd_state; /* timer allocated */ |
106 | 100 | unsigned int hd_irq[HPET_MAX_TIMERS]; |
107 | 101 | }; |
108 | 102 | |
109 | -#define HPET_DATA_PLATFORM 0x0001 /* platform call to hpet_alloc */ | |
110 | - | |
111 | 103 | static inline void hpet_reserve_timer(struct hpet_data *hd, int timer) |
112 | 104 | { |
113 | 105 | hd->hd_state |= (1 << timer); |
... | ... | @@ -125,7 +117,7 @@ |
125 | 117 | unsigned short hi_timer; |
126 | 118 | }; |
127 | 119 | |
128 | -#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */ | |
120 | +#define HPET_INFO_PERIODIC 0x0010 /* periodic-capable comparator */ | |
129 | 121 | |
130 | 122 | #define HPET_IE_ON _IO('h', 0x01) /* interrupt on */ |
131 | 123 | #define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */ |
include/linux/oprofile.h