diff -urN linux-2.4.21-pre5/CREDITS linux-2.4.21-pre5-perfctr/CREDITS --- linux-2.4.21-pre5/CREDITS Sat Mar 1 09:34:28 2003 +++ linux-2.4.21-pre5-perfctr/CREDITS Sun Mar 2 00:14:58 2003 @@ -2377,6 +2377,7 @@ E: mikpe@csd.uu.se W: http://www.csd.uu.se/~mikpe/ D: Miscellaneous fixes +D: Performance-monitoring counters driver N: Reed H. Petty E: rhp@draper.net diff -urN linux-2.4.21-pre5/Documentation/Configure.help linux-2.4.21-pre5-perfctr/Documentation/Configure.help --- linux-2.4.21-pre5/Documentation/Configure.help Sat Mar 1 19:21:55 2003 +++ linux-2.4.21-pre5-perfctr/Documentation/Configure.help Sun Mar 2 00:14:58 2003 @@ -4207,6 +4207,72 @@ If unsure, say N. +Performance-monitoring counters support +CONFIG_PERFCTR + This driver provides access to the performance-monitoring counter + registers available in some (but not all) modern processors. + These special-purpose registers can be programmed to count low-level + performance-related events which occur during program execution, + such as cache misses, pipeline stalls, etc. + + The driver supports most x86-class processors known to have + performance-monitoring counters: Intel Pentium to Pentium 4, + AMD K7, Cyrix 6x86MX/MII/III, VIA C3, and WinChip C6/2/3. + + On processors which have a time-stamp counter but no performance- + monitoring counters, such as the AMD K6 family, the driver supports + plain cycle-count performance measurements only. + + On WinChip C6/2/3 processors the performance-monitoring counters + cannot be used unless the time-stamp counter has been disabled. + Please read for further information. + + You can safely say Y here, even if you intend to run the kernel + on a processor without performance-monitoring counters. + + You can also say M here to compile the driver as a module; the + module will be called `perfctr.o'. + +Additional internal consistency checks +CONFIG_PERFCTR_DEBUG + This option enables additional internal consistency checking in + the perfctr driver. The scope of these checks is unspecified and + may vary between different versions of the driver. + + Enabling this option will reduce performance, so say N unless you + are debugging the driver. + +Init-time hardware tests +CONFIG_PERFCTR_INIT_TESTS + This option makes the driver run additional hardware tests + during initialisation. This is not necessary for normal + operation, but it can be useful during driver development. + + If unsure, say N. + +Virtual performance counters support +CONFIG_PERFCTR_VIRTUAL + The processor's performance-monitoring counters are special-purpose + global registers. This option adds support for virtual per-process + performance-monitoring counters which only run when the process + to which they belong is executing. This improves the accuracy of + performance measurements by reducing "noise" from other processes. + + Say Y. + +Global performance counters support +CONFIG_PERFCTR_GLOBAL + This option adds driver support for global-mode (system-wide) + performance-monitoring counters. In this mode, the driver allows + each performance-monitoring counter on each processor to be + controlled and read. The driver provides a sampling timer to + maintain 64-bit accumulated event counts. + + Global-mode performance counters cannot be used if some process + is currently using virtual-mode performance counters, and vice versa. + + Say Y. + VGA text console CONFIG_VGA_CONSOLE Saying Y here will allow you to use Linux in text mode through a diff -urN linux-2.4.21-pre5/Documentation/ioctl-number.txt linux-2.4.21-pre5-perfctr/Documentation/ioctl-number.txt --- linux-2.4.21-pre5/Documentation/ioctl-number.txt Sat Dec 1 18:27:13 2001 +++ linux-2.4.21-pre5-perfctr/Documentation/ioctl-number.txt Sun Mar 2 00:14:58 2003 @@ -187,5 +187,7 @@ 0xB1 00-1F PPPoX 0xCB 00-1F CBM serial IEC bus in development: +0xD0 all performance counters in development: + 0xFE 00-9F Logical Volume Manager diff -urN linux-2.4.21-pre5/MAINTAINERS linux-2.4.21-pre5-perfctr/MAINTAINERS --- linux-2.4.21-pre5/MAINTAINERS Sat Mar 1 09:34:28 2003 +++ linux-2.4.21-pre5-perfctr/MAINTAINERS Sun Mar 2 00:14:58 2003 @@ -1326,6 +1326,12 @@ W: http://www.realitydiluted.com/projects/nino S: Maintained +PERFORMANCE-MONITORING COUNTERS DRIVER +P: Mikael Pettersson +M: mikpe@csd.uu.se +W: http://www.csd.uu.se/~mikpe/linux/perfctr/ +S: Maintained + PNP SUPPORT P: Tom Lees M: tom@lpsg.demon.co.uk diff -urN linux-2.4.21-pre5/Makefile linux-2.4.21-pre5-perfctr/Makefile --- linux-2.4.21-pre5/Makefile Sat Mar 1 09:34:28 2003 +++ linux-2.4.21-pre5-perfctr/Makefile Sun Mar 2 00:14:58 2003 @@ -190,6 +190,7 @@ DRIVERS-$(CONFIG_BLUEZ) += drivers/bluetooth/bluetooth.o DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o +DRIVERS-$(CONFIG_KPERFCTR) += drivers/perfctr/kperfctr.o DRIVERS := $(DRIVERS-y) diff -urN linux-2.4.21-pre5/arch/i386/config.in linux-2.4.21-pre5-perfctr/arch/i386/config.in --- linux-2.4.21-pre5/arch/i386/config.in Sat Feb 1 19:42:12 2003 +++ linux-2.4.21-pre5-perfctr/arch/i386/config.in Sun Mar 2 00:14:58 2003 @@ -284,6 +284,7 @@ fi source drivers/pci/Config.in +source drivers/perfctr/Config.in bool 'EISA support' CONFIG_EISA diff -urN linux-2.4.21-pre5/arch/i386/kernel/i8259.c linux-2.4.21-pre5-perfctr/arch/i386/kernel/i8259.c --- linux-2.4.21-pre5/arch/i386/kernel/i8259.c Sat Dec 1 18:27:13 2001 +++ linux-2.4.21-pre5-perfctr/arch/i386/kernel/i8259.c Sun Mar 2 00:14:58 2003 @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -487,6 +488,10 @@ /* IPI vectors for APIC spurious and error interrupts */ set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); +#endif + +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_KPERFCTR) + set_intr_gate(LOCAL_PERFCTR_VECTOR, perfctr_interrupt); #endif /* diff -urN linux-2.4.21-pre5/arch/i386/kernel/irq.c linux-2.4.21-pre5-perfctr/arch/i386/kernel/irq.c --- linux-2.4.21-pre5/arch/i386/kernel/irq.c Sun Jan 26 10:12:35 2003 +++ linux-2.4.21-pre5-perfctr/arch/i386/kernel/irq.c Sun Mar 2 00:14:58 2003 @@ -43,6 +43,7 @@ #include #include #include +#include @@ -171,6 +172,13 @@ for (j = 0; j < smp_num_cpus; j++) p += sprintf(p, "%10u ", apic_timer_irqs[cpu_logical_map(j)]); + p += sprintf(p, "\n"); +#endif +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_KPERFCTR) + p += sprintf(p, "PMC: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + apic_lvtpc_irqs[cpu_logical_map(j)]); p += sprintf(p, "\n"); #endif p += sprintf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); diff -urN linux-2.4.21-pre5/arch/i386/kernel/process.c linux-2.4.21-pre5-perfctr/arch/i386/kernel/process.c --- linux-2.4.21-pre5/arch/i386/kernel/process.c Sun Aug 4 12:04:55 2002 +++ linux-2.4.21-pre5-perfctr/arch/i386/kernel/process.c Sun Mar 2 00:14:58 2003 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -516,7 +517,7 @@ */ void exit_thread(void) { - /* nothing to do ... */ + perfctr_exit_thread(¤t->thread); } void flush_thread(void) @@ -600,6 +601,8 @@ unlazy_fpu(current); struct_cpy(&p->thread.i387, ¤t->thread.i387); + perfctr_copy_thread(&p->thread); + return 0; } @@ -682,6 +685,8 @@ *next = &next_p->thread; struct tss_struct *tss = init_tss + smp_processor_id(); + perfctr_suspend_thread(prev); + unlazy_fpu(prev_p); /* @@ -737,6 +742,8 @@ */ tss->bitmap = INVALID_IO_BITMAP_OFFSET; } + + perfctr_resume_thread(next); } asmlinkage int sys_fork(struct pt_regs regs) diff -urN linux-2.4.21-pre5/drivers/Makefile linux-2.4.21-pre5-perfctr/drivers/Makefile --- linux-2.4.21-pre5/drivers/Makefile Sun Jan 26 10:12:39 2003 +++ linux-2.4.21-pre5-perfctr/drivers/Makefile Sun Mar 2 00:14:58 2003 @@ -49,4 +49,7 @@ subdir-$(CONFIG_BLUEZ) += bluetooth +mod-subdirs += perfctr +subdir-$(CONFIG_KPERFCTR) += perfctr + include $(TOPDIR)/Rules.make diff -urN linux-2.4.21-pre5/drivers/perfctr/Config.in linux-2.4.21-pre5-perfctr/drivers/perfctr/Config.in --- linux-2.4.21-pre5/drivers/perfctr/Config.in Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/Config.in Mon Nov 25 14:01:46 2002 @@ -0,0 +1,15 @@ +# $Id: Config.in,v 1.15 2002/11/25 13:01:46 mikpe Exp $ +# Performance-monitoring counters driver configuration +# + +mainmenu_option next_comment +comment 'Performance-monitoring counters support' +tristate 'Performance-monitoring counters support' CONFIG_PERFCTR +if [ "$CONFIG_PERFCTR" != "n" ]; then + define_bool CONFIG_KPERFCTR y + bool ' Additional internal consistency checks' CONFIG_PERFCTR_DEBUG + bool ' Init-time hardware tests' CONFIG_PERFCTR_INIT_TESTS + bool ' Virtual performance counters support' CONFIG_PERFCTR_VIRTUAL $CONFIG_PERFCTR + bool ' Global performance counters support' CONFIG_PERFCTR_GLOBAL $CONFIG_PERFCTR +fi +endmenu diff -urN linux-2.4.21-pre5/drivers/perfctr/Kconfig linux-2.4.21-pre5-perfctr/drivers/perfctr/Kconfig --- linux-2.4.21-pre5/drivers/perfctr/Kconfig Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/Kconfig Mon Nov 25 14:01:46 2002 @@ -0,0 +1,86 @@ +# $Id: Kconfig,v 1.3 2002/11/25 13:01:46 mikpe Exp $ +# Performance-monitoring counters driver configuration +# + +menu "Performance-monitoring counters support" + +config PERFCTR + tristate "Performance monitoring counters support" + help + This driver provides access to the performance-monitoring counter + registers available in some (but not all) modern processors. + These special-purpose registers can be programmed to count low-level + performance-related events which occur during program execution, + such as cache misses, pipeline stalls, etc. + + The driver supports most x86-class processors known to have + performance-monitoring counters: Intel Pentium to Pentium 4, + AMD K7, Cyrix 6x86MX/MII/III, VIA C3, and WinChip C6/2/3. + + On processors which have a time-stamp counter but no performance- + monitoring counters, such as the AMD K6 family, the driver supports + plain cycle-count performance measurements only. + + On WinChip C6/2/3 processors the performance-monitoring counters + cannot be used unless the time-stamp counter has been disabled. + Please read for further information. + + You can safely say Y here, even if you intend to run the kernel + on a processor without performance-monitoring counters. + + You can also say M here to compile the driver as a module; the + module will be called `perfctr.o'. + +config KPERFCTR + bool + depends on PERFCTR + default y + +config PERFCTR_DEBUG + bool "Additional internal consistency checks" + depends on PERFCTR + help + This option enables additional internal consistency checking in + the perfctr driver. The scope of these checks is unspecified and + may vary between different versions of the driver. + + Enabling this option will reduce performance, so say N unless you + are debugging the driver. + +config PERFCTR_INIT_TESTS + bool "Init-time hardware tests" + depends on PERFCTR + help + This option makes the driver run additional hardware tests + during initialisation. This is not necessary for normal + operation, but it can be useful during driver development. + + If unsure, say N. + +config PERFCTR_VIRTUAL + bool "Virtual performance counters support" + depends on PERFCTR + help + The processor's performance-monitoring counters are special-purpose + global registers. This option adds support for virtual per-process + performance-monitoring counters which only run when the process + to which they belong is executing. This improves the accuracy of + performance measurements by reducing "noise" from other processes. + + Say Y. + +config PERFCTR_GLOBAL + bool "Global performance counters support" + depends on PERFCTR + help + This option adds driver support for global-mode (system-wide) + performance-monitoring counters. In this mode, the driver allows + each performance-monitoring counter on each processor to be + controlled and read. The driver provides a sampling timer to + maintain 64-bit accumulated event counts. + + Global-mode performance counters cannot be used if some process + is currently using virtual-mode performance counters, and vice versa. + + Say Y. +endmenu diff -urN linux-2.4.21-pre5/drivers/perfctr/Makefile linux-2.4.21-pre5-perfctr/drivers/perfctr/Makefile --- linux-2.4.21-pre5/drivers/perfctr/Makefile Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/Makefile Thu Dec 5 23:48:43 2002 @@ -0,0 +1,56 @@ +# $Id: Makefile,v 1.13 2002/12/05 22:48:43 mikpe Exp $ +# Makefile for the Performance-monitoring counters driver. + +# construct various object file lists: +# kernel-objs-y kernel objects exporting symbols +# y-objs-y kernel objects not exporting symbols +# m-objs-m perfctr.o if driver is module, empty otherwise +# driver-objs-y objects for perfctr.o module, or empty +driver-objs-y := init.o +driver-objs-$(CONFIG_X86) += x86.o +tests-objs-$(CONFIG_X86) := x86_tests.o +kernel-objs-$(CONFIG_X86) := x86_setup.o +driver-objs-$(CONFIG_PERFCTR_INIT_TESTS) += $(tests-objs-y) +driver-objs-$(CONFIG_PERFCTR_VIRTUAL) += virtual.o +stub-objs-$(CONFIG_PERFCTR)-$(CONFIG_PERFCTR_VIRTUAL) := virtual_stub.o +driver-objs-$(CONFIG_PERFCTR_GLOBAL) += global.o +m-objs-$(CONFIG_PERFCTR) := perfctr.o +y-objs-$(CONFIG_PERFCTR) := $(driver-objs-y) +kernel-objs-y += $(stub-objs-m-y) + +export-objs := $(kernel-objs-y) +perfctr-objs := $(driver-objs-y) +obj-m += $(m-objs-m) + +ifeq ($(VERSION).$(PATCHLEVEL),2.5) +# for 2.5.19 or later +obj-$(CONFIG_KPERFCTR) += kperfctr.o +kperfctr-objs := $(kernel-objs-y) $(y-objs-y) +else +# for 2.4 and 2.2 +O_TARGET := kperfctr.o +obj-y := $(kernel-objs-y) $(y-objs-y) +list-multi := perfctr.o +endif + +ifeq ($(VERSION).$(PATCHLEVEL),2.2) +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) +O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) +endif + +ifneq ($(VERSION).$(PATCHLEVEL),2.5) +include $(TOPDIR)/Rules.make + +perfctr.o: $(perfctr-objs) + $(LD) -r -o $@ $(perfctr-objs) +endif diff -urN linux-2.4.21-pre5/drivers/perfctr/RELEASE-NOTES linux-2.4.21-pre5-perfctr/drivers/perfctr/RELEASE-NOTES --- linux-2.4.21-pre5/drivers/perfctr/RELEASE-NOTES Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/RELEASE-NOTES Sat Mar 1 14:46:22 2003 @@ -0,0 +1,852 @@ +$Id: RELEASE-NOTES,v 1.111.2.1 2003/03/01 13:46:22 mikpe Exp $ + +RELEASE NOTES +============= + +Version 2.4.6, 2003-03-01 +- Kernel 2.4.21-pre5 added set_cpus_allowed(), which required + fixing compat.h and x86_setup.c. +- Fixed init.c for kernel 2.5.63 removing EXPORT_NO_SYMBOLS. + +Version 2.4.5, 2003-02-09 +- Fixed two minor compile warnings in x86_tests.c for 2.5 kernels. + +Version 2.4.4, 2003-01-18 +- Fixed a bug in iresume() where an interrupt-mode counter could + increment unexpectedly, and also miss the overflow interrupt. + The following setup would cause the problem: + P1 has EVNTSELn in non-interrupt mode, counting some high- + frequency event (e.g. INST_RETIRED) in kernel-mode. P2 has + EVNTSELn in interrupt-mode, counting some low-frequency event + (e.g. MMX_ASSIST) in user-mode. P1 suspends. Since EVNTSELn is + in non-interrupt mode, it is not disabled. P2 resumes. First + iresume() finds that the CPU cache ID is not P2's, so it reloads + PERFCTRn with P2's restart value. Then write_control() reloads + EVNTSELn with P2's EVNTSEL. At this point, P2's PERFCTRn has been + counting with P1's EVNTSELn since iresume(), so it will no longer + equal P2's restart value. And if PERFCTRn overflowed, the overflow + will go undetected since P1's EVNTSELn was in non-interrupt mode. + To avoid this problem, iresume() now ensures that a counter's + control register is disabled before reloading the counter. +- Fixed some ugly log messages from the new HT P4 init code: + * forbidden_mask would be printed as "0X" (capital X) + * finalise_backpatching() could trigger a BUG! printk from + p4_write_control() if the CPU the init code runs on was + in the forbidden set. At init-time this is not an error. + Avoided this by temporarily resetting the forbidden_mask. +- Added preliminary support for AMD K8 processors with the + regular 32-bit x86 kernel. The K8 performance counters appear + to be identical or very similar to the K7 performance counters. + +Version 2.4.3, 2002-12-11 +- Added x86.c:perfctr_cpus_forbidden_mask. This bitmask describes + the set of CPUs that must not access the perfctrs. On HT P4 MPs, + only logical CPU #0 in each package is allowed access -- this + avoids the resource conflict that would occur if both logical + processors were to access the perfctrs. In other cases (UP or + non-HT-P4 MPs) the mask is zero. +- vperfctr_control() now calls set_cpus_allowed() to ensure that + the task stays away from CPUs in perfctr_cpus_forbidden_mask. + This is racy with sys_sched_setaffinity(), and possibly some + of the kernel's internal set_cpus_allowed() calls, but the race + is unlikely to occur in current 2.4 kernels. +- Cleaned up the parameter passing protocol between vperfctr_ioctl() + and the individual vperfctr "system call" procedures. +- Added safety check in global.c to disallow global-mode perfctrs + on asymmetric MPs until the API has been fixed. +- Added set_cpus_allowed() implementation for 2.4 kernels, except + those that already have it as indicated by HAVE_SET_CPUS_ALLOWED: + this symbol is added to by the kernel patch. +- 2.2 kernels can't enforce CPU affinity masks, so x86.c warns if + a HT P4 MP runs a 2.2 kernel, and falls back to generic x86 mode. + Added dummy set_cpus_allowed() macro for 2.2 kernels. +- x86_compat.h now implements cpuid_ebx() and cpu_has_ht for old kernels. +- Makefile cleanup: Rules.make is obsolete in 2.5. +- Compile fixes in x86.c and virtual_stub.c: needs to + be included explicitly for the 2.5.50 kernel. + +Version 2.4.2, 2002-11-25 +- Fixed virtual.c:inc_nrctrs() to handle the -EBUSY case correctly. + If the HW was busy (e.g. global running), then the first attempt + to open a vperfctr would fail but further attempts would succeed. + Updated error propagation to distinguish -EBUSY from -ENOMEM. +- Updated global.c for preempt-safety. +- Made the driver safe for preemptible kernels. This required a lot + of analysis, but resulted in relatively few actual code changes. + (Backport from the perfctr-3.1 branch.) +- Ported to 2.5.48: Replaced MOD_INC_USE_COUNT by try_module_get() + and MOD_DEC_USE_COUNT by module_put(). Updated compat.h. +- Ported to 2.5.45: added Kconfig, removed Config.help. + +Version 2.4.1, 2002-10-12 +- RedHat 8.0's 2.4.18-14 kernel does EXPORT_SYMBOL(cpu_khz) while + the vanilla 2.4.18 does not. This clashes with x86_setup.c's + EXPORT_SYMBOL(cpu_khz). I've found no easy way to distinguish + between these kernels at C preprocessing time, so I changed + x86_setup.c to define a trivial perfctr_cpu_khz() function and + EXPORT_SYMBOL that one instead. + +Version 2.4.0, 2002-09-26 +- Config.help updated to state that Pentium 4 is supported. +- 2.5.32 moved ptrace_check_attach() declaration to . +- Removed redundant /proc//perfctr access control check + from vperfctr_stub_open(). Since 2.4.0-pre1 this check didn't + match the real one, which prevented remote opens when the + driver was built as a module. + +Version 2.4.0-pre2, 2002-08-27 +- vperfctr_control() now allows the user to specify that some PMC + sums are not to be cleared when updating the control. + There is a new bitmap field `preserve' in struct vperfctr_control: + if bit i is set then PMC(i)'s sum is not cleared. + `preserve' is a simple `unsigned long' for now, since this type + fits all currently known CPU types. + This change breaks binary compatibility, but user-space code which + clears the entire control record before filling in relevant fields + will continue to work as before after a recompile. + This feature removes a limitation which some people felt was a + problem for some usage scenarios. + +Version 2.4.0-pre1, 2002-08-12 +- Initial implementation of a new remote-control API for virtual + per-process perfctrs. A monitor process may access a target + process' perfctrs via /proc/pid/perfctr and operations on that + file, if the monitor holds the target under ptrace ATTACH control. + Updated virtual.c to allow remote access. + Updated x86.c:perfctr_cpu_ireload() to work also in the remote + control case on SMP machines. + +Version 2.3.12, 2002-08-12 +- Trivial comment fixes in compat.h and x86_compat.h. +- Removed __vperfctr_sample(), vperfctr_stub.sample, and bug_sample() + from UP builds, since they are needed only on SMP. + +Version 2.3.11, 2002-07-21 +- Accumulated sums are now maintained for interrupt-mode perfctrs. + User-space can use the standard syscall-less algorithm for computing + these counters' current sums, should that be needed. + +Version 2.3.10, 2002-07-19 +- Added PERFCTR_X86_INTEL_P4M2 CPU type for Model 2 P4s, since + they have ESCR Event Mask changes in a few events. +- The driver now supports replay tagging events on P4, using the + pebs_enable and pebs_matrix_vert control fields added in 2.3.8. +- Some Pentium MMX and Pentium Pro processors have an erratum + (Pentium erratum #74, Pentium Pro erratum 26) which causes SMM + to shut down if CR4.PCE is set. intel_init() now clears the + RDPMC feature on the affected steppings, to avoid the problem. +- perfctr_cpu_release() now clears the hardware registers and + invalidates the per-cpu cache. This should allow the counter + hardware to power down when not used, especially on P4. +- Callers of update_control() have no active i-mode counters. + Documented this as a precondition, and changed update_control() + to not call isuspend(). update_control() no longer needs hardware + access, which should ease a port to CONFIG_PREEMPT=y. + +Version 2.3.9, 2002-06-27 +- Updated p4_escr_addr() in x86.c to match the latest revision of + Intel's IA32 Volume 3 manual, #245472-007. An error in previous + revisions of this document caused the driver to program the wrong + ESCR in some cases. (CCCRs 12/13/16 with ESCR_SELECT(2) were mapped + to SSU_ESCR0 instead of RAT_ESCR0, affecting the uop_type event.) + +Version 2.3.8, 2002-06-26 +- Added counter overflow interrupt support for Intel P4. +- 2.5.23 dropped smp_num_cpus and cpu_logical_map(). Added + temporary workarounds to x86.c and global.c to allow compilation + and testing under 2.5. May have to change the API (esp. global's) + to be based on the sparse cpu_online_map instead. +- RedHat's 2.4.9-34 defines cpu_relax(). Updated compat.h. +- Added pebs_enable and pebs_matrix_vert fields (currently unused) + to perfctr_cpu_control to support replay tagging events on P4. + Updated the perfctr_cpu_state binary layout magic number. +- Silenced redefinition warnings for MSR_P6_PERFCTR0 and cpu_has_mmx. +- Updated Makefile for the 2.5.19 kernel's Makefile changes. +- Merged the P6 and K7 isuspend/iresume/write_control driver code. +- Added a VC3 specific clear_counters() procedure. +- Removed pointless code from perfctr_cpu_identify_overflow(). +- Removed _vperfctr_get/set_thread() wrappers and thread->perfctr + clobber checks from the DEBUG code. Removed unused "ibuf" and + obsolete si_code fields from vperfctr state and control objects. + Updated the vperfctr state magic number. +- Fixed the CONFIG_PREEMPT anti-dependency check in Config.in. +- vperfctr_control() now preserves the TSC sum on STOP;CONTROL + transitions. The failure to do this caused problems for the + PAPI P4 support being developed. + +Version 2.3.7, 2002-04-14 +- Kernel 2.5.8-pre3 changed the way APIC/SMP interrupt entries + are defined. Defining these with asm() in C is no longer + practical, so the kernel patch for 2.5.8-pre3 now defines + the perfctr interrupt entry in arch/i386/kernel/entry.S. +- Permit use of cascading counters on P4: in the slave counter + one sets the CASCADE flag instead of the ENABLE flag. +- Added P4 hyperthreading bit field definitions. +- Preliminary infrastructure to support a new remote-control + interface via ptrace(). Updates to compat.h, virtual.c, + virtual_stub.c, and x86_setup.c. ptrace_check_attach() + emulation for older kernels is in x86_setup.c since + virtual_stub.c isn't compiled if the driver isn't a module. + +Version 2.3.6, 2002-03-21 +- Rewrote sys_vperfctr_control() to do a proper suspend before + updating the control, and to skip trying to preserve the TSC + start value around the resume. This cleaned up the code and + eliminated the bogus "BUG! resuming non-suspended perfctr" + warnings that control calls to active perfctrs caused. +- Rewrote sys_vperfctr_iresume() to not preserve the TSC start + value around the resume. Since we had just done a suspend(), + this would cause double-accounting of the TSC. + +Version 2.3.5, 2002-03-17 +- Added detection of the VIA C3 Ezra-T processor. +- CPU detection now uses current_cpu_data instead of boot_cpu_data, + to avoid the boot_cpu_data.x86_vendor bug which is present is + all current 2.2/2.4/2.5 kernels. The bug caused the x86_vendor + field to be cleared on SMP machines, which in turn tricked the + driver to identify MP AMD K7 machines as MP Intel P6, with + disastrous results when the wrong MSRs were programmed. +- Updated compat.h for /proc// inode change in 2.5.4. +- Added a check to prevent building on preemptible 2.4/2.5 kernels, + since the driver isn't yet safe for those. +- Put perfctr's configuration help text in Config.help in this + directory: kernel 2.5.3-pre5 changed from a having a common + Configure.help file to having local Config.help files. + +Version 2.3.4, 2002-01-23 +- Updated virtual.c for remap_page_range() change in 2.5.3-pre1. + Added emulation for older kernels to compat.h. +- Permit use of tagging on P4 for at-retirement counting. This may + not yet work as expected, since up-stream (tag producing) counters + aren't disabled at context switches: a process may therefore see + more tagged uops than expected. +- Fixed uses of __FUNCTION__ to comply with changes in GCC 3.0.3. + +Version 2.3.3, 2001-12-31 +- Minor x86.c cleanup: reordered function definitions so that + write_control comes after isuspend/iresume: this makes it easier + to follow the runtime control flow. +- Fixed isuspend()/iresume()'s broken cache checking protocol. The + old protocol didn't handle process migration across CPUs in SMP + machines correctly, as illustrated by the following scenario: + P1 runs on CPU1 and suspends. P1 and CPU1 now have the same + cache id (->k1.id). P1 is resumed and suspended on CPU2: the state + in CPU1 is now stale. Then P1 is resumed on CPU1, and no other + process has been using CPU1's performance counters since P1's last + suspend on CPU1. The old protocol would see matching cache ids and + that P1's i-mode EVNTSELs are stopped, so it would accept the cache + and resume P1 with CPU1's stale PERFCTRS values. + In the new protocol isuspend() records the active CPU in the + state object, and iresume() checks if both the CPU and the control + id match. The new protocol is also simpler since iresume() no longer + checks if the i-mode EVNTSELs are cleared or not. +- P6 nasty i-mode to a-mode context switch bug fixed: p6_isuspend() + used to simply clear EVNTSEL0's Enable flag in order to stop all + i-mode counters. Unfortunately, that was insufficient as shown by + the following case (which actually happened). + P1 has EVNTSEL0 in a-mode and EVNTSEL1 in i-mode. P1 suspends: + PERFCTR1 is stopped but EVNTSEL1 is still in i-mode. P2 has EVNTSEL0 + in a-mode and no EVNTSEL1. P2 resumes and updates EVNTSEL0. This + activates not only P2's PERFCTR0 but also the dormant PERFCTR1. If + PERFCTR1 overflows, then P2 will receive an unexpected interrupt. If + PERFCTR1 doesn't overflow, but P2 suspends and P1 resumes, then P1 + will find that PERFCTR1 has a larger than expected value. + p6_isuspend() and p6_iresume() were changed to ignore the global + Enable flag and to disable/enable each i-mode EVNTSEL individually, + just like how it's done on the K7. +- x86.c cleanups: P5MMX, MII, C6, VC3, P6, K7, and P4 now all + use the same rdpmc_read_counters() method. VIA C3 now uses + p6_write_control() instead of its own method. +- Removed "pmc_map[] must be identity" restriction from P6 and K7. + The API uses the virtual counter index to distinguish a-mode + and i-mode counters, but P6 events aren't entirely symmetric: + this lead to some strange cases with the old pmc_map[] rule. + P6 and K7 isuspend() now need access to the control, so + update_control() and its callers had to be changed to allow it + to isuspend() _before_ the new control is installed. +- P4 write_control fixes: changed the ESCR cache to be indexed by + MSR offset from 0x3A0, and changed P4 write_control to index the + CCCR/ESCR cache with physical instead of virtual indices. Added + call to debug_evntsel_cache(), after updating it for pmc_map[]. +- Added P4 and Generic support to x86_tests.c, and some cleanups. + +Version 2.3.2, 2001-11-19 +- P4 fix: the mapping from CCCR 17 to its associated ESCRs was + wrong due to an off-by-one error in x86.c:p4_escr_addr(). +- P4 fix: also clear the PEBS MSRs when initialising the driver. +- Minor cleanup in x86.c: replaced the "clear MSRs" loops with + calls to a helper procedure. + +Version 2.3.1, 2001-11-06 +- Microscopic P4 cleanups. Testing on my new P4 box has confirmed + that the PMAVAIL flag in MSR_IA32_MISC_ENABLE is read-only. + +Version 2.3, 2001-10-24 +- Added support for multiple interrupt-mode virtual perfctrs + with automatic restart. Added an identify_overflow() method + to x86.c to identify and reset the overflowed counters. + Added checks to ensure that the user-specified restart values + for interrupt-mode counters are negative. + Updated virtual.c's signal delivery interface to pass a + bitmask describing which counters overflowed; the siginfo + si_code is now fixed as SI_PMC_OVF (fault-class). +- Fixed some typos in x86.c. Added a note about the C3 Ezra. +- Added EXPORT_NO_SYMBOLS to init.c, for compatibility with + announced changes in modutils 2.5. + +Version 2.2, 2001-10-09 +- Added preliminary support for the Pentium 4. Only basic stuff + for now: no cascading counters, overflow interrupts, tagged + micro-ops, or use of DS/PEBS. The code compiles but hasn't been + tested on an actual Pentium 4. + +Version 2.1.4, 2001-09-30 +- No driver-level changes. + +Version 2.1.3, 2001-09-13 +- Fixed a compilation problem where virtual_stub couldn't be compiled + in modular kernels older than 2.2.20pre10 if KMOD was disabled, due + to an incompatible stub definition of request_module(). +- Replaced most occurrences of "VIA Cyrix III / C3" with "VIA C3". + +Version 2.1.2, 2001-09-05 +- Added MODULE_LICENSE() tag, for compatibility with the tainted/ + non-tainted kernel stuff being put into 2.4.9-ac and modutils. +- VIA C3 support is not "preliminary" any more. Testing has revealed + that the reserved bits in the C3's EVNTSEL1 have no function and + need not be preserved. The driver now fills these bits with zeroes. + (Thanks to Dave Jones @ SuSE for running these tests.) +- Minor bug fix in the perfctr interrupt assembly code. + (Inherited from the 2.4 kernel. Fixed in 2.4.9-ac4.) + +Version 2.1.1, 2001-08-28 +- Preliminary recognition of Pentium 4 processors, including + checking the IA32_MISC_ENABLE MSR. +- Moved %cr4 access functions from to + x86_compat.h, to work around changes in 2.4.9-ac3. +- More %cr4 cleanups possible since the removal of dodgy_tsc() + in Version 2.1: moved {set,clear}_in_cr4_local() into x86.c, + and eliminated the set_in_cr4() compat macro. +- Fixed a bug in x86.c:finalise_backpatching(): the fake cstatus + mustn't include i-mode counters unless we have PCINT support. + Failure to check this cased fatal init-time oopses in some + configs (CONFIG_X86_UP_APIC set but no local APIC in the CPU). +- Minor comment updates in x86.c due to AMD #22007 Revision J. +- Removed '%' before 'cr4' in printouts from x86_tests.c, to + avoid the '%' being mutated by log-reading user-space code. + +Version 2.1, 2001-08-19 +- Fixed a call backpatching bug, caused by an incompatibility + between the 2.4 and 2.2 kernels' xchg() macros. The 2.2 version + lacks a "volatile" causing gcc to remove the entire statement + if xchg() is used for side-effect only. Reverted to a plain + assignment, which is safe since the 2.0.1 backpatching changes. +- Fixed a bug where an attempt to use /proc//perfctr on an + unsupported processor would cause a (well-behaved) kernel oops, + due to calling a NULL function pointer in x86.c, vperfctr_open() + now returns -ENODEV if virtual.c hasn't been initialised. +- Removed the WinChip configuration option, the dodgy_tsc() callback, + and the clr_cap_tsc() x86_compat macro. WinChip users should configure + for generic 586 or less and use the kernel's "notsc" boot parameter. + This cleans up the driver and the 2.4 kernel patches, at the expense + of more code in the 2.2 kernel patches to implement "notsc" support. +- Minor cleanup: moved version number definition from init.c to + a separate file, version.h. + +Version 2.0.1, 2001-08-14 +- The unsynchronised backpatching in x86.c didn't work on SMP, + due to Pentium III erratum E49, and similar errata for other + P6 processors. (The change in 2.0-pre6 was insufficient.) + x86.c now finalises the backpatching at driver init time, + by "priming" the relevant code paths. To make this feasible, + the isuspend() and iresume() methods are now merged into + the other high-level methods; virtual.c became a bit cleaner. +- Removed obsolete "WinChip pmc_map[] must be identity" check. + +Version 2.0, 2001-08-08 +- Resurrected partial support for interrupt-mode virtual perfctrs. + virtual.c permits a single i-mode perfctr, in addition to TSC + and a number of a-mode perfctrs. BUG: The i-mode PMC must be last, + which constrains CPUs like the P6 where we currently restrict + the pmc_map[] to be the identity mapping. (Not a problem for + K7 since it is symmetric, or P4 since it is expected to use a + non-identity pmc_map[].) + New perfctr_cpu_ireload() procedure to force reload of i-mode + PMCs from their start values before resuming. Currently, this + just invalidates the CPU cache, which forces the following + iresume() and resume() to do the right thing. + perfctr_cpu_update_control() now calls setup_imode_start_values() + to "prime" i-mode PMCs from the control.ireset[] array. +- Bug fix in perfctr_cpu_update_control(): start by clearing cstatus. + Prevents a failed attempt to update the control from leaving the + object in a state with old cstatus != 0 but new control. + +Version 2.0-pre7, 2001-08-07 +- Cleaned up the driver's debugging code (virtual, x86). +- Internal driver rearrangements. The low-level driver (x86) now handles + sampling/suspending/resuming counters. Merged counter state (sums and + start values) and CPU control data to a single "CPU state" object. + This simplifies the high-level drivers, and permits some optimisations + in the low-level driver by avoiding the need to buffer tsc/pmc samples + in memory before updating the accumulated sums (not yet implemented). +- Removed the read_counters, write_control, disable_rdpmc, and enable_rdpmc + methods from , since they have been obsoleted by the + new suspend/resume/sample methods. +- Rearranged the 'cstatus' encoding slightly by putting 'nractrs' in + the low 7 bits; this was done because 'nractrs' is retrieved more + often than 'nrctrs'. +- Removed the obsolete 'status' field from vperfctr_state. Exported + 'cstatus' and its access methods to user-space. (Remove the + control.tsc_on/nractrs/nrictrs fields entirely?) +- Removed WinChip "fake TSC" support. The user-space library can now + sample with slightly less overhead on sane processors. +- WinChip and VIA C3 now use p5mmx_read_counters() instead of their + own versions. + +Version 2.0-pre6, 2001-07-27 +- New patches for kernels 2.4.6, 2.4.7, and 2.4.7-ac1. +- Sampling bug fix for SMP. Normally processes are suspended and + resumed many times per second, but on SMP machines it is possible + for a process to run for a long time without being suspended. + Since sampling is performed at the suspend and resume actions, + a performance counter may wrap around more than once between + sampling points. When this occurs, the accumulated counts will + be highly variable and much lower than expected. + A software timer is now used to ensure that sampling deadlines + aren't missed on SMP machines. (The timer is run by the same code + which runs the ITIMER_VIRTUAL interval timer.) +- Bug fix in the x86 "redirect call" backpatching routine. To be + SMP safe, a bus-locked write to the code must be used. +- Bug fix in the internal debugging code (CONFIG_PERFCTR_DEBUG). + The "shadow" data structure used to detect if a process' perfctr + pointer has been clobbered could cause lockups with SMP kernels. + Rewrote the code to be simpler and more robust. +- Minor performance tweak for the P5/P5MMX read counters procedures, + to work around the P5's cache which doesn't allocate a cache line + on a write miss. +- To avoid undetected data layout mismatches, the user-space library + now checks the data layout version field in a virtual perfctr when + it is being mmap:ed into the user's address space. +- A few minor cleanups. + +Version 2.0-pre5, 2001-06-11 +- Internally use a single 'cstatus' field instead of the three + tsc_on/nractrs/nrictrs fields. Should reduce overhead slightly. +- Reorder the fields in cpu_control so that 'cstatus' and other + frequently used fields get small offsets -- avoids some disp32 + addressing modes in timing-critical code. +- Fixed a bug in p6_iresume where it forgot to invalidate the + EVNTSEL cache, causing p6_write_control to fail to reload the + MSRs. (K7 had a similar bug.) Since i-mode support is disabled + at the moment, no-one was actually bitten by this. +- Fixed another iresume/write_control cache invalidation bug where a + switch to an "uninitialised" CPU would fail to initialise the MSRs. +- Added a CONFIG_PERFCTR_DEBUG option to enable internal consistency + checks. Currently, this checks that a task's vperfctr pointer + isn't clobbered behind our backs, that resume and suspend for + a vperfctr are performed on the same CPU, and that the EVNTSEL + cache is semi-consistent when reloading is optimised away. + ("semi" because it only checks that the cache agrees with the + user's control data, and not that the cache agrees with the MSRs.) +- Minor cleanups. + +Version 2.0-pre4, 2001-04-30 +- Cleanups in x86.c. #defines introduced for magic constants. + More sharing of procedures between different CPU drivers. + Fixed a bug where k7_iresume() could cause k7_write_control() + to fail to reload the correct EVNTSELs. + The WinChip C6/2/3 driver now "fakes" an incrementing TSC. +- General cleanups: s/__inline__/inline/ following Linux kernel + coding standards, and renamed the low-level control objects to + cpu_control to distinguish them from {v,g}perfctr_control objects. +- O_CREAT is now interpreted when /proc/self/perfctr is opened: + if the vperfctr does not exist, then it is created; if the + vperfctr does exist, then EEXIST is returned (unfortunately + O_EXCL doesn't work, since it's intercepted by the VFS layer). + "perfex -i" uses this to avoid having to create a vperfctr when + only an INFO command is to be issued. + libperfctr.c:vperfctr_open() uses this to decide whether to + UNLINK the newly opened vperfctr in case of errors or not. +- Cleaned up virtual.c's 2.4/2.2 VFS interface code a little, + and eliminated the OWNER_THIS_MODULE compat macro. +- Added MOD_{INC,DEC}_USE_COUNTs to virtual.c's file_operations + open and release procedures for 2.2 kernels. This should + simulate 2.4's fops_get/put at >open() and >release(). + +Version 2.0-pre3, 2001-04-17 +- Interrupt-mode virtual perfctrs are temporarily disabled since + x86.c doesn't yet detect which PMC overflowed. The old API + could be made to work, but it was broken anyway. +- Integrated the new P4-ready data structures and APIs. + The driver compiles but the user-space stuff hasn't been + updated yet, so there may be some remaining bugs. + + I have not yet committed to all details of this API. Some + things, like accumulating counters in virtual.c and global.c, + are uglier now, and going from a single "status == nrctrs" + field to three separate fields (tsc_on, nrctrs, nrictrs) + cannot be good for performance. + + In the new API the control information is split in separate + arrays depending on their use, i.e. a struct-of-arrays layout + instead of an array-of-struct layout. The advantage of the + struct-of-arrays layout is that it should cause fewer cache + lines to be touched at the performance-critical operations. + The disadvantage is that the layout changes whenever the + number of array elements has to be increased -- as is the + case for the future Pentium 4 support (18 counters). + +Version 2.0-pre2, 2001-04-07 +- Removed automatic inheritance of per-process virtual perfctrs + across fork(). Unless wait4() is modified, it's difficult to + communicate the final values back to the parent: the now + abandoned code did this in a way which made it impossible + to distinguish one child's final counts from another's. + Inheritance can be implemented in user-space anyway, so the + loss is not great. The interface between the driver and the rest + of the kernel is now smaller and simpler than before. +- Simulating cpu_khz by a macro in very old kernels broke since + there's also a struct field with that name :-( Instead of + putting the ugly workaround back in, I decided to drop support + for kernels older than 2.2.16. +- Preliminary support for the VIA C3 processor -- the C3 is + apparently a faster version of the VIA Cyrix III. +- Added rdtsc cost deduction to the init tests code, and changed + it to output per-instruction costs as well. +- More cleanups, making 2.2 compatibility crud less visible. + +Version 2.0-pre1, 2001-03-25 +- First round of API and coding changes/cleanups for version 2.0: + made perfctr_info.version a string, moved some perfctr_info inits + to x86.c and eliminated some redundant variables, removed dead VFS + code from virtual.c, removed obsolete K7 tests from x86_tests.c, + removed mmu_cr4_features wrappers from x86_compat.h, minor cleanup + in virtual_stub.c. +- Fixed an include file problem which made some C compilers (not gcc) + fail when compiling user-space applications using the driver. +- Added missing EXPORT_SYMBOL declarations needed by the UP-APIC PM + code when the driver is built as a module. +- Preliminary changes in x86.c to deal with UP-APIC power management + issues in 2.4-ac kernels. The PM callback is only a stub for now. + +Version 1.9, 2001-02-13 +- Fixed compilation problems for 2.2 and SMP kernels. +- Found updated documentation on "VIA Cyrix III". Apparently, there + are two distinct chips: the older Joshua (a Cyrix design) and the + newer Samuel (a Centaur design). Our current code supported Joshua, + but mistook Samuel for Joshua. Corrected the identification of Samuel + and added explicit support for it. Samuel's EVNTSEL1 is not well- + documented, so there are some new Samuel-specific tests in x86_tests.c. +- Added preliminary interrupt-mode support for AMD K7. +- Small tweaks to virtual.c's interrupt handling. + +Version 1.8, 2001-01-23 +- Added preliminary interrupt-mode support to virtual perfctrs. + Currently for P6 only, and the local APIC must have been enabled. + Tested on 2.4.0-ac10 with CONFIG_X86_UP_APIC=y. + When an i-mode vperfctr interrupts on overflow, the counters are + suspended and a user-specified signal is sent to the process. The + user's signal handler can read the trap pc from the mmap:ed vperfctr, + and should then issue an IRESUME ioctl to restart the counters. + The next version will support buffering and automatic restart. +- Some cleanups in the x86.c init and exit code. Removed the implicit + smp_call_function() calls from x86_compat.h. + +Version 1.7, 2001-01-01 +- Updated Makefile for 2.4.0-test13-pre3 Rules.make changes. +- Removed PERFCTR_ATTACH ioctl from /dev/perfctr, making the + vperfctrs only accessible via /proc/self/perfctr. Removed + the "attach" code from virtual.c, and temporarily commented + out the "vperfctr fs" code. Moved /dev/perfctr initialisation + and implementation from init.c to global.c. +- Eliminated CONFIG_VPERFCTR_PROC, making /proc/pid/perfctr + mandatory if CONFIG_PERFCTR_VIRTUAL is set. +- Some 2.2/2.4 compatibility cleanups. +- VIA Cyrix III detection bug fix. Contrary to VIA's documentation, + the Cyrix III vendor field is Centaur, not Cyrix. + +Version 1.6, 2000-11-21 +- Preliminary implementation of /proc/pid/perfctr. Seems to work, + but virtual.c and virtual_stub.c is again filled with + #if LINUX_VERSION_CODE crap which will need to be cleaned up. + The INFO ioctl is now implemented by vperfctrs too, to avoid the + need for opening /dev/perfctr. +- virtual.c now puts the perfctr pointer in filp->private_data + instead of inode->u.generic_ip. The main reason for this change + is that proc-fs places a dentry pointer in inode->u.generic_ip. +- sys_vperfctr_control() no longer resets the virtual TSC + if it already is active. The virtual TSC therefore runs + continuously from its first activation until the process + stops or unlinks its vperfctrs. +- Updates for 2.4.0-test11pre6. Use 2.4-style cpu_has_XXX + feature testing macros. Updated x86_compat.h to implement + missing cpu_has_mmx and cpu_has_msr, and compatibility + macros for 2.2. Changed vperfctr_fs_read_super() to use + new_inode(sb) instead of get_empty_inode() + some init code. +- Updates for 2.4.0-test9. Fixed x86_compat.h for cpu_khz change. + Since drivers/Makefile was converted to the new list style, + it became more difficult to handle CONFIG_PERFCTR=m. Changed + Config.in to set CONFIG_KPERFCTR=y when CONFIG_PERFCTR != n, + resulting in a much cleaner kernel patch for 2.4.0-test9. +- Removed d_alloc_root wrapper since 2.2 doesn't need it any more. +- When building for 2.2.18pre, use some of its 2.4 compatibility + features (module_init, module_exit and DECLARE_MUTEX). +- Updates for 2.4.0-test8: repaired kernel patch for new parameter + in do_fork, and fixed CLONE_PERFCTR conflict with CLONE_THREAD. + +Version 1.5, 2000-09-03 +- Dropped support for intermediate 2.3 and early 2.4.0-test kernels. + The code now supports kernels 2.2.xx and 2.4.0-test7 or later only. + Cleanups in compat.h and virtual.c. +- Rewrote the Makefile to use object file lists instead of conditionals. + This gets slightly hairy since kernel extensions are needed even + when the driver proper is built as a module. +- Removed the definition of CONFIG_PERFCTR_X86 from Config.in. + Use the 2.4 standard CONFIG_X86 instead. The 2.2.xx kernel + patches now define CONFIG_X86 in arch/i386/config.in. +- Cleaned up the vperfctr inheritance filter. Instead of setting + a disable flag (CLONE_KTHREAD) when kernel-internal threads are + created, I now set CLONE_PERFCTR in sys_fork and sys_vfork. +- /dev/perfctr no longer accepts the SAMPLE and UNLINK ioctls. + All operations pertaining to a process' virtual perfctrs must + be applied to the fd returned from the ATTACH ioctl. +- Removed the remote-control features from the virtual perfctrs. + Significant simplifications in virtual.c. Removed some now + unused stuff from compat.h and virtual_stub.c. + +Version 1.4, 2000-08-11 +- Fixed a memory leak bug in virtual.c. An extraneous dget() in + get_vperfctr_filp() prevented reclaiming the dentry and inode + allocated for a vperfctr file. +- Major changes to the VFS interface in virtual.c. Starting with + 2.4.0-test6, inode->i_sb == NULL no longer works. Added code to + register a "vperfctr" fs and define a superblock and a mount point. + Completely rewrote the dentry init code. Most of the new code is + adapted from fs/pipe.c, with simplifications and macros to continue + supporting 2.2.x kernels. `ls -l /proc/*/fd/' now prints recognizable + names for vperfctr files. +- Cleaned up virtual.c slightly. Removed "#if 1" tests around the + vperfctr inheritance code. Rewrote vperfctr_alloc and vperfctr_free + to use the virt_to_page and {Set,Clear}PageReserved macros; + also updated compat.h to provide these for older kernels. +- Updated for 2.4.0-test3: a dummy `open' file operation is no longer + required by drivers/char/misc.c. +- Updated for `owner' field in file_operations added in 2.4.0-test2. + Removed MOD_{INC,DEC}_USE_COUNT from init.c (except when compiling + for 2.2.x) and virtual.c. Added MOD_{INC,DEC}_USE_COUNT to the + reserve/release functions in x86.c -- needed because the driver + may be active even if no open file refers to it. Using can_unload + in the module struct instead is possible but not as tidy. + +Version 1.3, 2000-06-29 +- Implemented inheritance for virtual perfctrs: fork() copies the + evntsel data to the child, exit() stops the child's counters but + does not detach the vperfctr object, and wait() adds the child's + counters to the parent's `children' counters. + Added a CLONE_KTHREAD flag to prevent inheritance to threads + created implicitly by request_module() and kernel_thread(). +- Fixed a half-broken printk() in x86_tests.c. +- Added checks to virtual.c to prevent the remote-control interface + from trying to activate dead vperfctrs. +- Updated vperfctr_attach() for changes in 2.3.99-pre7 and 2.4.0-test2. +- Fixed a problem introduced in 1.2 which caused linker errors if + CONFIG_PERFCTR=m and CONFIG_PERFCTR_INIT_TESTS=y. +- Export CPU kHz via a new field in PERFCTR_INFO ioctl, to enable + user-space to map accumulated TSC counts to actual time. + +Version 1.2, 2000-05-24 +- Added support for generic x86 processors with a time-stamp counter + but no performance-monitoring counters. By using the driver to + virtualise the TSC, accurate cycle-count measurements are now + possible on PMC-less processors like the AMD K6. +- Removed some of the special-casing of the x86 time-stamp counter. + It's now "just another counter", except that no evntsel is + needed to enable it. +- WinChip bug fix: the "fake TSC" code would increment an + uninitialised counter. +- Reorganised the x86 driver. Moved the optional init-time testing + code to a separate source file. +- Miscellaneous code cleanups and naming convention changes. + +Version 1.1, 2000-05-13 +- vperfctr_attach() now accepts pid 0 as an alias for the current + process. This reduces the number of getpid() calls needed in + the user-space library. (Suggested by Ulrich Drepper.) +- Added support for the VIA Cyrix III processor. +- Tuned the x86 driver interface. Replaced function pointers + with stubs which rewrite callers to invoke the correct callees. +- Added ARRAY_SIZE definition to compat.h for 2.2.x builds. +- Updated for 2.3.48 inode changes. +- Moved code closer to 2.3.x coding standards. Removed init_module + and cleanup_module, added __exit, module_init, and module_exit, + and extended "compat.h" accordingly. Cleaned up + and a little. + +Version 1.0, 2000-01-31 +- Prepared the driver to cope with non-x86 architectures: + - Moved generic parts of to . + - Merged driver's private "x86.h" into . + - Config.in now defines CONFIG_PERFCTR_${ARCH}, and Makefile uses + it to select appropriate arch-dependent object files +- The driver now reads the low 32 bits of the counters, + instead of 40 or 48 bits zero-extended to 64 bits. + Sums are still 64 bits. This was done to reduce the number + of cache lines needed for certain data structures, to + simplify and improve the performance of the sampling + procedures, and to change 64+(64-64) arithmetic to 64+(32-32) + for the benefit of gcc on x86. This change doesn't reduce + precision, as long as no event occurs more than 2^32 times + between two sampling points. +- PERFCTR_GLOBAL_READ now forces all CPUs to be sampled, if the + sampling timer isn't running. + +Version 0.11, 2000-01-30 +- Added a missing EXPORT_SYMBOL which prevented the driver + from being built as a module in SMP kernels. +- Support for the CPU sampling instructions (i.e. RDPMC and + RDTSC on x86) is now announced explicitly by PERFCTR_INFO. +- The x86 hardware driver now keeps CR4.PCE globally enabled. + There are two reasons for this. First, the cost of toggling + this flag at process suspend/resume is high. Second, changes + in kernel 2.3.40 imply that any processor's %cr4 may be updated + asynchronously from the global variable mmu_cr4_features. + +Version 0.10, 2000-01-23 +- Added support for global-mode perfctrs (global.c). +- There is now a config option controlling whether to + perform init-time hardware tests or not. +- Added a hardware reserve/release mechanism so that multiple + high-level services don't simultaneously use the hardware. +- The driver is now officially device . +- Tuned the 64-bit tsc/msr/pmc read operations in x86.c. +- Support for virtual perfctrs can now be enabled or disabled + via CONFIG_PERFCTR_VIRTUAL. +- Added support for the WinChip 3 processor. +- Split the code into several files: x86.c (x86 drivers), + virtual.c (virtualised perfctrs), setup.c (boot-time actions), + init.c (driver top-level and init code). + +Version 0.9, 2000-01-02 +- The driver can now be built as a module. +- Dropped sys_perfctr() system call and went back to using a + /dev/perfctr character device. Generic operations are now + ioctl commands on /dev/perfctr, and control operations on + virtual perfctrs are ioctl commands on their file descriptors. + Initially this change was done because new system calls in 2.3.x + made maintenance and binary compatibility with 2.2.x hard, but + the new API is actually cleaner than the previous system call. +- Moved this code from arch/i386/kernel/ to drivers/perfctr/. + +Version 0.8, 1999-11-14 +- Made the process management callback functions inline to + reduce scheduling overhead for processes not using perfctrs. +- Changed the 'status' field to contain the number of active + counters. Changed read_counters, write_control, and accumulate + to use this information to avoid unnecessary work. +- Fixed a bug in k7_check_control() which caused it to + require all four counters to be enabled. +- Fixed sys_perfctr() to return -ENODEV instead of -ENOSYS + if the processor doesn't support perfctrs. +- Some code cleanups. +- Evntsel MSRs are updated lazily, and counters are not written to. + + The following table lists the costs (in cycles) of various + instructions which access the counter or evntsel registers. + The table was derived from data collected by init-time tests + run by previous versions of this driver. + + Processor P5 P5MMX PII PIII K7 + Clock freq. (MHz) 133 233 266 450 500 + + RDPMC n/a 14 31 36 13 + RDMSR (counter) 29 28 81 80 52 + WRMSR (counter) 35 37 97 115 80 + WRMSR (evntsel) 33 37 88 105 232 + + Several things are apparent from this table: + + 1. It's much cheaper to use RDPMC than RDMSR to read the counters. + 2. It's much more expensive to reset a counter than to read it. + 3. It's expensive to write to an evntsel register. + + As of version 0.8, this driver uses the following strategies: + * The evntsel registers are updated lazily. A per_cpu_control[] + array caches the contents of each CPU's evntsel registers, + and only when a process requires a different setup are the + evntsel registers written to. In most cases, this eliminates the + need to reprogram the evntsel registers when switching processes. + The older drivers would write to the evntsel registers both at + process suspend and resume. + * The counter registers are read both at process resume and suspend, + and the difference is added to the process' accumulated counters. + The older drivers would reset the counters at resume, read them + at suspend, and add the values read to the accumulated counters. + * Only those registers enabled by the user's control information + are manipulated, instead of blindly manipulating all of them. + +Version 0.7 1999-10-25 +- The init-time checks in version 0.6 of this driver showed that + RDMSR is a lot slower than RDPMC for reading the PMCs. The driver + now uses RDPMC instead of RDMSR whenever possible. +- Added an mmap() operation to perfctr files. This allows any client + to read the accumulated counter state without making a system call. + The old "sync to user-provided buffer" method has been removed, + as it entailed additional copy operations and only worked for the + "active" process. The PERFCTR_READ operation has been replaced + by a simpler PERFCTR_SAMPLE operation, for the benefit of pre-MMX + Intel P5 processors which cannot sample counters in user-mode. + This rewrite actually simplified the code. +- The AMD K7 should now be supported correctly. The init-time checks + in version 0.6 of this driver revealed that each K7 counter has + its own ENable bit. (Thanks to Nathan Slingerland for running the + test and reporting the results to me.) +- Plugged a potential memory leak in perfctr_attach_task(). +- No longer piggyback on prctl(); sys_perfctr() is a real system call. +- Some code cleanups. + +Version 0.6 1999-09-08 +- Temporarily added some init-time code that checks the + costs of RDPMC/RDMSR/WRMSR operations applied to perfctr MSRs, + the semantics of the ENable bit on the Athlon, and gets + the boot-time value of the WinChip CESR register. + This code can be turned off by #defining INIT_DEBUG to 0. +- Preliminary support for the AMD K7 Athlon processor. +- The code will now build in both 2.3.x and 2.2.x kernels. + +Version 0.5 1999-08-29 +- The user-space buffer is updated whenever state.status changes, + even when a remote command triggers the change. +- Reworked and simplified the high-level code. All accesses + now require an attached file in order to implement proper + accounting and syncronisation. The only exception is UNLINK: + a process may always UNLINK its own PMCs. +- Fixed counting bug in sys_perfctr_read(). +- Improved support for the Intel Pentium III. +- Another WinChip fix: fake TSC update at process resume. +- The code should now be safe for 'gcc -fstrict-aliasing'. + +Version 0.4 1999-07-31 +- Implemented PERFCTR_ATTACH and PERFCTR_{READ,CONTROL,STOP,UNLINK} + on attached perfctrs. An attached perfctr is represented as a file. +- Fixed an error in the WinChip-specific code. +- Perfctrs now survive exec(). + +Version 0.3 1999-07-22 +- Interface now via sys_prctl() instead of /dev/perfctr. +- Added NYI stubs for accessing other processes' perfctrs. +- Moved to dynamic allocation of a task's perfctr state. +- Minor code cleanups. + +Version 0.2 1999-06-07 +- Added support for WinChip CPUs. +- Restart counters from zero, not their previous values. This + corrected a problem for Intel P6 (WRMSR writes 32 bits to a PERFCTR + MSR and then sign-extends to 40 bits), and also simplified the code. +- Added support for syncing the kernel's counter values to a user- + provided buffer each time a process is resumed. This feature, and + the fact that the driver enables RDPMC in processes using PMCs, + allows user-level computation of a process' accumulated counter + values without incurring the overhead of making a system call. + +Version 0.1 1999-05-30 +- First public release. diff -urN linux-2.4.21-pre5/drivers/perfctr/compat.h linux-2.4.21-pre5-perfctr/drivers/perfctr/compat.h --- linux-2.4.21-pre5/drivers/perfctr/compat.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/compat.h Sat Mar 1 14:46:23 2003 @@ -0,0 +1,161 @@ +/* $Id: compat.h,v 1.33.2.1 2003/03/01 13:46:23 mikpe Exp $ + * Performance-monitoring counters driver. + * Compatibility definitions for 2.2/2.4/2.5 kernels. + * + * Copyright (C) 1999-2003 Mikael Pettersson + */ +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,17) +#define EXPORT_SYMBOL_pidhash EXPORT_SYMBOL(pidhash) +#else +#define EXPORT_SYMBOL_pidhash /*empty*/ +#endif + +#if defined(CONFIG_X86) && LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +static inline void rep_nop(void) +{ + __asm__ __volatile__("rep;nop"); +} +#endif + +#if defined(CONFIG_X86) && LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) && !defined(cpu_relax) +#define cpu_relax() rep_nop() +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,15) +#define task_has_cpu(tsk) ((tsk)->has_cpu) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,15) +extern int ptrace_check_attach(struct task_struct *child, int kill); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,20) +#define TASK_IS_PTRACED(tsk) ((tsk)->flags & PF_PTRACED) +#else +#define TASK_IS_PTRACED(tsk) ((tsk)->ptrace & PT_PTRACED) +#endif + +/* /proc/pid/ inodes changes */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,4) +#include +#define proc_pid_inode_denotes_task(inode,tsk) \ + ((tsk) == PROC_I((inode))->task) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +#define proc_pid_inode_denotes_task(inode,tsk) \ + ((tsk) == (inode)->u.proc_i.task) +#else +#define proc_pid_inode_denotes_task(inode,tsk) \ + ((tsk)->pid == ((inode)->i_ino >> 16)) +#endif + +/* remap_page_range() changed in 2.5.3-pre1 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3) +#include +static inline int perfctr_remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_t prot) +{ + return remap_page_range(from, to, size, prot); +} +#undef remap_page_range +#define remap_page_range(vma,from,to,size,prot) perfctr_remap_page_range((vma),(from),(to),(size),(prot)) +#endif + +/* module changes in 2.5.48 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) +static inline void try_module_get(void *m) { MOD_INC_USE_COUNT; } +#define module_put(MOD) MOD_DEC_USE_COUNT +#endif + +/* added in 2.4.9-ac */ +#if !defined(MODULE_LICENSE) +#define MODULE_LICENSE(license) /*empty*/ +#endif + +/* preemption wrappers for old kernels */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#ifdef CONFIG_PREEMPT +#error "not yet ported to 2.4+PREEMPT" +#endif +#define preempt_disable() do{}while(0) +#define preempt_enable() do{}while(0) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + +#define EXPORT_SYMBOL_tasklist_lock /*empty*/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,4) +#define EXPORT_SYMBOL___put_task_struct EXPORT_SYMBOL(__put_task_struct) +#else +#define put_task_struct(tsk) free_task_struct((tsk)) +#define EXPORT_SYMBOL___put_task_struct /* empty */ +#endif + +#define vma_pgoff(vma) ((vma)->vm_pgoff) +#define task_thread(tsk) (&(tsk)->thread) + +#define get_cpus_allowed(tsk) ((tsk)->cpus_allowed) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && !defined(HAVE_SET_CPUS_ALLOWED) +#ifdef CONFIG_SMP +extern void set_cpus_allowed(struct task_struct*, unsigned long); +#else +#define set_cpus_allowed(tsk, mask) do{}while(0) +#endif +#endif + +#else /* 2.4 simulation for 2.2 */ + +#define EXPORT_SYMBOL_tasklist_lock EXPORT_SYMBOL(tasklist_lock) + +#if !defined(CONFIG_KMOD) && defined(request_module) /* < 2.2.20pre10 */ +#undef request_module +static inline int request_module(const char *name) { return -ENOSYS; } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +#ifdef MODULE +#define module_init(x) int init_module(void) { return x(); } +#define module_exit(x) void cleanup_module(void) { x(); } +#else +#define module_init(x) /* explicit call is needed */ +#define module_exit(x) /* empty */ +#endif /* MODULE */ +#define DECLARE_MUTEX(name) struct semaphore name = MUTEX +#endif /* < 2.2.18 */ + +/* {get,put}_task_struct() are unsafe no-ops */ +#define get_task_struct(tsk) do{}while(0) +#define put_task_struct(tsk) do{}while(0) +#define EXPORT_SYMBOL___put_task_struct /* empty */ + +/* XXX: is this the correct 2.2 replacement for task_{,un}lock() ??? */ +#define task_lock(tsk) spin_lock_irq(&runqueue_lock) +#define task_unlock(tsk) spin_unlock_irq(&runqueue_lock) + +#define virt_to_page(kaddr) (mem_map + MAP_NR(kaddr)) + +#define fops_get(fops) (fops) + +#define vma_pgoff(vma) ((vma)->vm_offset) /* NOT, but suffices for != 0 */ + +#define get_zeroed_page(mask) get_free_page((mask)) +#define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags) +#define ClearPageReserved(page) clear_bit(PG_reserved, &(page)->flags) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#ifdef MODULE +#define __cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES))) +#define __exit /* empty */ +#else +#define __exit __attribute__((unused, __section__("text.init"))) +#endif + +#define task_thread(tsk) (&(tsk)->tss) + +#define get_cpus_allowed(tsk) (-1UL) +#define set_cpus_allowed(tsk, mask) do{}while(0) + +#endif /* 2.4 simulation for 2.2 */ diff -urN linux-2.4.21-pre5/drivers/perfctr/global.c linux-2.4.21-pre5-perfctr/drivers/perfctr/global.c --- linux-2.4.21-pre5/drivers/perfctr/global.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/global.c Thu Dec 5 23:51:51 2002 @@ -0,0 +1,294 @@ +/* $Id: global.c,v 1.22 2002/12/05 22:51:51 mikpe Exp $ + * Global-mode performance-monitoring counters via /dev/perfctr. + * + * Copyright (C) 2000-2002 Mikael Pettersson + * + * XXX: Doesn't do any authentication yet. Should we limit control + * to root, or base it on having write access to /dev/perfctr? + */ +#include +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#include + +#include "compat.h" +#include "global.h" + +/* XXX: broken in 2.5.23+, solution TDB */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,23) +#define smp_num_cpus 1 +#define cpu_logical_map(i) (i) +#endif + +static const char this_service[] = __FILE__; +static int hardware_is_ours = 0; +static struct timer_list sampling_timer; + +static unsigned int nr_active_cpus = 0; + +struct gperfctr { + struct perfctr_cpu_state cpu_state; + spinlock_t lock; +} __attribute__((__aligned__(SMP_CACHE_BYTES))); + +static struct gperfctr per_cpu_gperfctr[NR_CPUS] __cacheline_aligned; + +static int reserve_hardware(void) +{ + const char *other; + + if( hardware_is_ours ) + return 0; + other = perfctr_cpu_reserve(this_service); + if( other ) { + printk(KERN_ERR __FILE__ ":%s: failed because hardware is taken by '%s'\n", + __FUNCTION__, other); + return -EBUSY; + } + hardware_is_ours = 1; + (void)try_module_get(THIS_MODULE); + return 0; +} + +static void release_hardware(void) +{ + nr_active_cpus = 0; + if( hardware_is_ours ) { + hardware_is_ours = 0; + del_timer(&sampling_timer); + sampling_timer.data = 0; + perfctr_cpu_release(this_service); + module_put(THIS_MODULE); + } +} + +static void sample_this_cpu(void *unused) +{ + /* PREEMPT note: when called via smp_call_function(), + this is in IRQ context with preemption disabled. */ + struct gperfctr *perfctr; + + perfctr = &per_cpu_gperfctr[smp_processor_id()]; + if( !perfctr_cstatus_enabled(perfctr->cpu_state.cstatus) ) + return; + spin_lock(&perfctr->lock); + perfctr_cpu_sample(&perfctr->cpu_state); + spin_unlock(&perfctr->lock); +} + +static void sample_all_cpus(void) +{ + preempt_disable(); + smp_call_function(sample_this_cpu, NULL, 1, 1); + sample_this_cpu(NULL); + preempt_enable(); +} + +static void sampling_timer_function(unsigned long interval) +{ + sample_all_cpus(); + sampling_timer.expires = jiffies + interval; + add_timer(&sampling_timer); +} + +static unsigned long usectojiffies(unsigned long usec) +{ + usec += 1000000 / HZ - 1; + usec /= 1000000 / HZ; + return usec; +} + +static void start_sampling_timer(unsigned long interval_usec) +{ + if( interval_usec > 0 ) { + unsigned long interval = usectojiffies(interval_usec); + init_timer(&sampling_timer); + sampling_timer.function = sampling_timer_function; + sampling_timer.data = interval; + sampling_timer.expires = jiffies + interval; + add_timer(&sampling_timer); + } +} + +static void start_this_cpu(void *unused) +{ + /* PREEMPT note: when called via smp_call_function(), + this is in IRQ context with preemption disabled. */ + struct gperfctr *perfctr; + + perfctr = &per_cpu_gperfctr[smp_processor_id()]; + if( perfctr_cstatus_enabled(perfctr->cpu_state.cstatus) ) + perfctr_cpu_resume(&perfctr->cpu_state); +} + +static void start_all_cpus(void) +{ + preempt_disable(); + smp_call_function(start_this_cpu, NULL, 1, 1); + start_this_cpu(NULL); + preempt_enable(); +} + +static int gperfctr_control(struct gperfctr_control *argp) +{ + unsigned long interval_usec; + unsigned int nrcpus, i; + int last_active, ret; + struct gperfctr *perfctr; + struct perfctr_cpu_control cpu_control; + static DECLARE_MUTEX(control_mutex); + + if( nr_active_cpus > 0 ) + return -EBUSY; /* you have to stop them first */ + if( get_user(interval_usec, &argp->interval_usec) ) + return -EFAULT; + if( get_user(nrcpus, &argp->nrcpus) ) + return -EFAULT; + if( nrcpus > smp_num_cpus ) + return -EINVAL; + down(&control_mutex); + last_active = -1; + for(i = 0; i < nrcpus; ++i) { + ret = -EFAULT; + if( copy_from_user(&cpu_control, + &argp->cpu_control[i], + sizeof cpu_control) ) + goto out_up; + /* we don't permit i-mode counters */ + ret = -EPERM; + if( cpu_control.nrictrs != 0 ) + goto out_up; + perfctr = &per_cpu_gperfctr[cpu_logical_map(i)]; + spin_lock(&perfctr->lock); + memset(&perfctr->cpu_state.sum, 0, sizeof perfctr->cpu_state.sum); + perfctr->cpu_state.control = cpu_control; + ret = perfctr_cpu_update_control(&perfctr->cpu_state); + spin_unlock(&perfctr->lock); + if( ret < 0 ) + goto out_up; + if( perfctr_cstatus_enabled(perfctr->cpu_state.cstatus) ) + last_active = i; + } + for(; i < smp_num_cpus; ++i) { + perfctr = &per_cpu_gperfctr[cpu_logical_map(i)]; + memset(&perfctr->cpu_state, 0, sizeof perfctr->cpu_state); + } + nr_active_cpus = ret = last_active + 1; + if( ret > 0 ) { + if( reserve_hardware() < 0 ) { + nr_active_cpus = 0; + ret = -EBUSY; + } else { + start_all_cpus(); + start_sampling_timer(interval_usec); + } + } + out_up: + up(&control_mutex); + return ret; +} + +static int gperfctr_read(struct gperfctr_state *arg) +{ + unsigned nrcpus, i; + struct gperfctr *perfctr; + struct gperfctr_cpu_state state; + + if( get_user(nrcpus, &arg->nrcpus) ) + return -EFAULT; + if( nrcpus > smp_num_cpus ) + nrcpus = smp_num_cpus; + if( sampling_timer.data == 0 ) /* no timer; sample now */ + sample_all_cpus(); + for(i = 0; i < nrcpus; ++i) { + perfctr = &per_cpu_gperfctr[cpu_logical_map(i)]; + spin_lock(&perfctr->lock); + state.cpu_control = perfctr->cpu_state.control; + state.sum = perfctr->cpu_state.sum; + spin_unlock(&perfctr->lock); + if( copy_to_user(&arg->cpu_state[i], &state, sizeof state) ) + return -EFAULT; + } + return nr_active_cpus; +} + +static int gperfctr_stop(void) +{ + release_hardware(); + return 0; +} + +static int dev_perfctr_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + switch( cmd ) { + case PERFCTR_INFO: + return sys_perfctr_info((struct perfctr_info*)arg); + case GPERFCTR_CONTROL: + return gperfctr_control((struct gperfctr_control*)arg); + case GPERFCTR_READ: + return gperfctr_read((struct gperfctr_state*)arg); + case GPERFCTR_STOP: + return gperfctr_stop(); + } + return -EINVAL; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +static struct file_operations dev_perfctr_file_ops = { + .owner = THIS_MODULE, + .ioctl = dev_perfctr_ioctl, +}; +#else +static int dev_perfctr_open(struct inode *inode, struct file *filp) +{ + MOD_INC_USE_COUNT; + return 0; +} +static int dev_perfctr_release(struct inode *inode, struct file *filp) +{ + MOD_DEC_USE_COUNT; + return 0; +} +static struct file_operations dev_perfctr_file_ops = { + .open = dev_perfctr_open, + .release = dev_perfctr_release, + .ioctl = dev_perfctr_ioctl, +}; +#endif + +static struct miscdevice dev_perfctr = { + .minor = 182, + .name = "perfctr", + .fops = &dev_perfctr_file_ops, +}; + +int __init gperfctr_init(void) +{ + int i, err; + + /* XXX: current API cannot handle CPU affinity restrictions */ + if( perfctr_cpus_forbidden_mask ) { + printk(KERN_ERR "gperfctr: asymmetric MP not yet supported\n"); + return 0; /* XXX: init.c treats error as fatal :-( */ + } + if( (err = misc_register(&dev_perfctr)) != 0 ) { + printk(KERN_ERR "/dev/perfctr: failed to register, errno %d\n", + -err); + return err; + } + for(i = 0; i < NR_CPUS; ++i) + per_cpu_gperfctr[i].lock = SPIN_LOCK_UNLOCKED; + return 0; +} + +void gperfctr_exit(void) +{ + misc_deregister(&dev_perfctr); +} diff -urN linux-2.4.21-pre5/drivers/perfctr/global.h linux-2.4.21-pre5-perfctr/drivers/perfctr/global.h --- linux-2.4.21-pre5/drivers/perfctr/global.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/global.h Mon Nov 18 02:42:38 2002 @@ -0,0 +1,13 @@ +/* $Id: global.h,v 1.6 2002/11/18 01:42:38 mikpe Exp $ + * Global-mode performance-monitoring counters. + * + * Copyright (C) 2000-2002 Mikael Pettersson + */ + +#ifdef CONFIG_PERFCTR_GLOBAL +extern int gperfctr_init(void); +extern void gperfctr_exit(void); +#else +static inline int gperfctr_init(void) { return 0; } +static inline void gperfctr_exit(void) { } +#endif diff -urN linux-2.4.21-pre5/drivers/perfctr/init.c linux-2.4.21-pre5-perfctr/drivers/perfctr/init.c --- linux-2.4.21-pre5/drivers/perfctr/init.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/init.c Sat Mar 1 14:46:23 2003 @@ -0,0 +1,68 @@ +/* $Id: init.c,v 1.49.2.1 2003/03/01 13:46:23 mikpe Exp $ + * Performance-monitoring counters driver. + * Top-level initialisation code. + * + * Copyright (C) 1999-2003 Mikael Pettersson + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "compat.h" +#include "virtual.h" +#include "global.h" +#include "version.h" + +MODULE_AUTHOR("Mikael Pettersson "); +MODULE_DESCRIPTION("Performance-monitoring counters driver"); +MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,63) +EXPORT_NO_SYMBOLS; +#endif + +struct perfctr_info perfctr_info = { + .version = VERSION +#ifdef CONFIG_PERFCTR_DEBUG + " DEBUG" +#endif +}; + +int sys_perfctr_info(struct perfctr_info *argp) +{ + if( copy_to_user(argp, &perfctr_info, sizeof perfctr_info) ) + return -EFAULT; + return 0; +} + +int __init perfctr_init(void) +{ + int err; + if( (err = perfctr_cpu_init()) != 0 ) { + printk(KERN_INFO "perfctr: not supported by this processor\n"); + return err; + } + if( (err = vperfctr_init()) != 0 ) + return err; + if( (err = gperfctr_init()) != 0 ) + return err; + printk(KERN_INFO "perfctr: driver %s, cpu type %s at %lu kHz\n", + perfctr_info.version, + perfctr_cpu_name[perfctr_info.cpu_type], + perfctr_info.cpu_khz); + return 0; +} + +void __exit perfctr_exit(void) +{ + gperfctr_exit(); + vperfctr_exit(); + perfctr_cpu_exit(); +} + +module_init(perfctr_init) +module_exit(perfctr_exit) diff -urN linux-2.4.21-pre5/drivers/perfctr/version.h linux-2.4.21-pre5-perfctr/drivers/perfctr/version.h --- linux-2.4.21-pre5/drivers/perfctr/version.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/version.h Sat Mar 1 14:46:23 2003 @@ -0,0 +1 @@ +#define VERSION "2.4.6" diff -urN linux-2.4.21-pre5/drivers/perfctr/virtual.c linux-2.4.21-pre5-perfctr/drivers/perfctr/virtual.c --- linux-2.4.21-pre5/drivers/perfctr/virtual.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/virtual.c Tue Dec 10 02:56:50 2002 @@ -0,0 +1,755 @@ +/* $Id: virtual.c,v 1.62 2002/12/10 01:56:50 mikpe Exp $ + * Virtual per-process performance counters. + * + * Copyright (C) 1999-2002 Mikael Pettersson + */ +#include +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "compat.h" +#include "virtual.h" + +/**************************************************************** + * * + * Data types and macros. * + * * + ****************************************************************/ + +struct vperfctr { +/* User-visible fields: (must be first for mmap()) */ + struct vperfctr_state state; +/* Kernel-private fields: */ + atomic_t count; + spinlock_t owner_lock; + struct task_struct *owner; +#ifdef CONFIG_SMP + unsigned int sampling_timer; +#endif +#ifdef CONFIG_PERFCTR_DEBUG + unsigned start_smp_id; + unsigned suspended; +#endif +#if PERFCTR_INTERRUPT_SUPPORT + unsigned int iresume_cstatus; +#endif +}; +#define IS_RUNNING(perfctr) perfctr_cstatus_enabled((perfctr)->state.cpu_state.cstatus) +#define IS_IMODE(perfctr) perfctr_cstatus_has_ictrs((perfctr)->state.cpu_state.cstatus) + +#ifdef CONFIG_PERFCTR_DEBUG +#define debug_free(perfctr) \ +do { \ + int i; \ + for(i = 0; i < PAGE_SIZE/sizeof(int); ++i) \ + ((int*)(perfctr))[i] = 0xfedac0ed; \ +} while( 0 ) +#define debug_init(perfctr) do { (perfctr)->suspended = 1; } while( 0 ) +#define debug_suspend(perfctr) \ +do { \ + if( (perfctr)->suspended ) \ + printk(KERN_ERR "%s: BUG! suspending non-running perfctr (pid %d, comm %s)\n", \ + __FUNCTION__, current->pid, current->comm); \ + (perfctr)->suspended = 1; \ +} while( 0 ) +#define debug_resume(perfctr) \ +do { \ + if( !(perfctr)->suspended ) \ + printk(KERN_ERR "%s: BUG! resuming non-suspended perfctr (pid %d, comm %s)\n", \ + __FUNCTION__, current->pid, current->comm); \ + (perfctr)->suspended = 0; \ +} while( 0 ) +#define debug_check_smp_id(perfctr) \ +do { \ + if( (perfctr)->start_smp_id != smp_processor_id() ) { \ + printk(KERN_ERR "%s: BUG! current cpu %u differs from start cpu %u (pid %d, comm %s)\n", \ + __FUNCTION__, smp_processor_id(), (perfctr)->start_smp_id, \ + current->pid, current->comm); \ + return; \ + } \ +} while( 0 ) +#define debug_set_smp_id(perfctr) \ + do { (perfctr)->start_smp_id = smp_processor_id(); } while( 0 ) +#else /* CONFIG_PERFCTR_DEBUG */ +#define debug_free(perfctr) do{}while(0) +#define debug_init(perfctr) do{}while(0) +#define debug_suspend(perfctr) do{}while(0) +#define debug_resume(perfctr) do{}while(0) +#define debug_check_smp_id(perfctr) do{}while(0) +#define debug_set_smp_id(perfctr) do{}while(0) +#endif /* CONFIG_PERFCTR_DEBUG */ + +/**************************************************************** + * * + * Resource management. * + * * + ****************************************************************/ + +/* XXX: perhaps relax this to number of _live_ perfctrs */ +static spinlock_t nrctrs_lock = SPIN_LOCK_UNLOCKED; +static int nrctrs; +static const char this_service[] = __FILE__; +#if PERFCTR_INTERRUPT_SUPPORT +static void vperfctr_ihandler(unsigned long pc); +#endif + +static int inc_nrctrs(void) +{ + const char *other; + + other = NULL; + spin_lock(&nrctrs_lock); + if( ++nrctrs == 1 ) { + other = perfctr_cpu_reserve(this_service); + if( other ) + nrctrs = 0; + } + spin_unlock(&nrctrs_lock); + if( other ) { + printk(KERN_ERR __FILE__ + ": cannot operate, perfctr hardware taken by '%s'\n", + other); + return -EBUSY; + } +#if PERFCTR_INTERRUPT_SUPPORT + perfctr_cpu_set_ihandler(vperfctr_ihandler); +#endif + return 0; +} + +static void dec_nrctrs(void) +{ + spin_lock(&nrctrs_lock); + if( --nrctrs == 0 ) + perfctr_cpu_release(this_service); + spin_unlock(&nrctrs_lock); +} + +static struct vperfctr *vperfctr_alloc(void) +{ + unsigned long page; + + if( inc_nrctrs() != 0 ) + return ERR_PTR(-EBUSY); + page = get_zeroed_page(GFP_KERNEL); + if( !page ) { + dec_nrctrs(); + return ERR_PTR(-ENOMEM); + } + SetPageReserved(virt_to_page(page)); + return (struct vperfctr*) page; +} + +static void vperfctr_free(struct vperfctr *perfctr) +{ + debug_free(perfctr); + ClearPageReserved(virt_to_page(perfctr)); + free_page((unsigned long)perfctr); + dec_nrctrs(); +} + +static struct vperfctr *get_empty_vperfctr(void) +{ + struct vperfctr *perfctr = vperfctr_alloc(); + if( !IS_ERR(perfctr) ) { + perfctr->state.magic = VPERFCTR_MAGIC; + atomic_set(&perfctr->count, 1); + spin_lock_init(&perfctr->owner_lock); + debug_init(perfctr); + } + return perfctr; +} + +static void put_vperfctr(struct vperfctr *perfctr) +{ + if( atomic_dec_and_test(&perfctr->count) ) + vperfctr_free(perfctr); +} + +/**************************************************************** + * * + * Basic counter operations. * + * These must all be called by the owner process only. * + * These must all be called with preemption disabled. * + * * + ****************************************************************/ + +/* PRE: perfctr == TASK_VPERFCTR(current) && IS_RUNNING(perfctr) + * Suspend the counters. + */ +static inline void vperfctr_suspend(struct vperfctr *perfctr) +{ + debug_suspend(perfctr); + debug_check_smp_id(perfctr); + perfctr_cpu_suspend(&perfctr->state.cpu_state); +} + +static inline void vperfctr_reset_sampling_timer(struct vperfctr *perfctr) +{ +#ifdef CONFIG_SMP + /* XXX: base the value on perfctr_info.cpu_khz instead! */ + perfctr->sampling_timer = HZ/2; +#endif +} + +/* PRE: perfctr == TASK_VPERFCTR(current) && IS_RUNNING(perfctr) + * Restart the counters. + */ +static inline void vperfctr_resume(struct vperfctr *perfctr) +{ + debug_resume(perfctr); + perfctr_cpu_resume(&perfctr->state.cpu_state); + vperfctr_reset_sampling_timer(perfctr); + debug_set_smp_id(perfctr); +} + +/* Sample the counters but do not suspend them. */ +static void vperfctr_sample(struct vperfctr *perfctr) +{ + if( IS_RUNNING(perfctr) ) { + debug_check_smp_id(perfctr); + perfctr_cpu_sample(&perfctr->state.cpu_state); + vperfctr_reset_sampling_timer(perfctr); + } +} + +#if PERFCTR_INTERRUPT_SUPPORT +/* vperfctr interrupt handler (XXX: add buffering support) */ +/* PREEMPT note: called in IRQ context with preemption disabled. */ +static void vperfctr_ihandler(unsigned long pc) +{ + struct task_struct *tsk = current; + struct vperfctr *perfctr; + unsigned int pmc_mask; + siginfo_t si; + + perfctr = task_thread(tsk)->perfctr; + if( !perfctr ) { + printk(KERN_ERR "%s: BUG! pid %d has no vperfctr\n", + __FUNCTION__, tsk->pid); + return; + } + if( !IS_IMODE(perfctr) ) { + printk(KERN_ERR "%s: BUG! vperfctr has cstatus %#x (pid %d, comm %s)\n", + __FUNCTION__, perfctr->state.cpu_state.cstatus, tsk->pid, tsk->comm); + return; + } + vperfctr_suspend(perfctr); + pmc_mask = perfctr_cpu_identify_overflow(&perfctr->state.cpu_state); + if( !pmc_mask ) { + printk(KERN_ERR "%s: BUG! pid %d has unidentifiable overflow source\n", + __FUNCTION__, tsk->pid); + return; + } + /* suspend a-mode and i-mode PMCs, leaving only TSC on */ + /* XXX: some people also want to suspend the TSC */ + perfctr->iresume_cstatus = perfctr->state.cpu_state.cstatus; + if( perfctr_cstatus_has_tsc(perfctr->iresume_cstatus) ) { + perfctr->state.cpu_state.cstatus = perfctr_mk_cstatus(1, 0, 0); + vperfctr_resume(perfctr); + } else + perfctr->state.cpu_state.cstatus = 0; + si.si_signo = perfctr->state.si_signo; + si.si_errno = 0; + si.si_code = SI_PMC_OVF; + si.si_pmc_ovf_mask = pmc_mask; + if( !send_sig_info(si.si_signo, &si, tsk) ) + send_sig(si.si_signo, tsk, 1); +} +#endif + +/**************************************************************** + * * + * Process management operations. * + * These must all, with the exception of __vperfctr_exit(), * + * be called by the owner process only. * + * * + ****************************************************************/ + +/* Called from exit_thread() or sys_vperfctr_unlink(). + * The vperfctr has just been detached from its owner. + * If the counters are running, stop them and sample their final values. + * Mark this perfctr as dead and decrement its use count. + * PREEMPT note: exit_thread() does not run with preemption disabled. + */ +void __vperfctr_exit(struct vperfctr *perfctr) +{ + struct task_struct *owner; + + spin_lock(&perfctr->owner_lock); + owner = perfctr->owner; + /* task_thread(owner)->perfctr = NULL was done by the caller */ + perfctr->owner = NULL; + spin_unlock(&perfctr->owner_lock); + + if( IS_RUNNING(perfctr) && owner == current ) { + preempt_disable(); + vperfctr_suspend(perfctr); + preempt_enable(); + } + perfctr->state.cpu_state.cstatus = 0; +#if PERFCTR_INTERRUPT_SUPPORT + perfctr->iresume_cstatus = 0; +#endif + put_vperfctr(perfctr); +} + +/* schedule() --> switch_to() --> .. --> __vperfctr_suspend(). + * If the counters are running, suspend them. + * PREEMPT note: switch_to() runs with preemption disabled. + */ +void __vperfctr_suspend(struct vperfctr *perfctr) +{ + if( IS_RUNNING(perfctr) ) + vperfctr_suspend(perfctr); +} + +/* schedule() --> switch_to() --> .. --> __vperfctr_resume(). + * PRE: perfctr == TASK_VPERFCTR(current) + * If the counters are runnable, resume them. + * PREEMPT note: switch_to() runs with preemption disabled. + */ +void __vperfctr_resume(struct vperfctr *perfctr) +{ + if( IS_RUNNING(perfctr) ) + vperfctr_resume(perfctr); +} + +#ifdef CONFIG_SMP +/* Called from update_one_process() [triggered by timer interrupt]. + * PRE: perfctr == TASK_VPERFCTR(current). + * Sample the counters but do not suspend them. + * Needed on SMP to avoid precision loss due to multiple counter + * wraparounds between resume/suspend for CPU-bound processes. + * PREEMPT note: called in IRQ context with preemption disabled. + */ +void __vperfctr_sample(struct vperfctr *perfctr) +{ + if( --perfctr->sampling_timer == 0 ) + vperfctr_sample(perfctr); +} +#endif + +/**************************************************************** + * * + * Virtual perfctr "system calls". * + * These can be called by the owner process, or by a monitor * + * process which has the owner under ptrace ATTACH control. * + * * + ****************************************************************/ + +/* obsolete. subsumed by control(). must be called with preemption disabled */ +static int sys_vperfctr_stop(struct vperfctr *perfctr, struct task_struct *tsk) +{ + if( IS_RUNNING(perfctr) ) { + if( tsk == current ) + vperfctr_suspend(perfctr); + perfctr->state.cpu_state.cstatus = 0; +#if PERFCTR_INTERRUPT_SUPPORT + perfctr->iresume_cstatus = 0; +#endif + } + return 0; +} + +static int sys_vperfctr_control(struct vperfctr *perfctr, + struct vperfctr_control *argp, + struct task_struct *tsk) +{ + struct vperfctr_control control; + int err; + unsigned int next_cstatus; + unsigned int nrctrs, i; + + if( copy_from_user(&control, argp, sizeof control) ) + return -EFAULT; + + if( control.cpu_control.nractrs || control.cpu_control.nrictrs ) { + unsigned long old_mask = get_cpus_allowed(tsk); + unsigned long new_mask = old_mask & ~perfctr_cpus_forbidden_mask; + if( !new_mask ) + return -EINVAL; + if( new_mask != old_mask ) + set_cpus_allowed(tsk, new_mask); + } + + /* PREEMPT note: preemption is disabled over the entire + region since we're updating an active perfctr. */ + preempt_disable(); + sys_vperfctr_stop(perfctr, tsk); + perfctr->state.cpu_state.control = control.cpu_control; + /* remote access note: perfctr_cpu_update_control() is ok */ + err = perfctr_cpu_update_control(&perfctr->state.cpu_state); + if( err < 0 ) + goto out; + next_cstatus = perfctr->state.cpu_state.cstatus; + if( !perfctr_cstatus_enabled(next_cstatus) ) + goto out; + + /* XXX: validate si_signo? */ + perfctr->state.si_signo = control.si_signo; + + if( !perfctr_cstatus_has_tsc(next_cstatus) ) + perfctr->state.cpu_state.sum.tsc = 0; + + nrctrs = perfctr_cstatus_nrctrs(next_cstatus); + for(i = 0; i < nrctrs; ++i) + if( !(control.preserve & (1<state.cpu_state.sum.pmc[i] = 0; + + if( tsk == current ) + vperfctr_resume(perfctr); + + out: + preempt_enable(); + return err; +} + +static int sys_vperfctr_iresume(struct vperfctr *perfctr, struct task_struct *tsk) +{ +#if PERFCTR_INTERRUPT_SUPPORT + unsigned int iresume_cstatus; + + iresume_cstatus = perfctr->iresume_cstatus; + if( !perfctr_cstatus_has_ictrs(iresume_cstatus) ) + return -EPERM; + + /* PREEMPT note: preemption is disabled over the entire + region because we're updating an active perfctr. */ + preempt_disable(); + + if( IS_RUNNING(perfctr) && tsk == current ) + vperfctr_suspend(perfctr); + + perfctr->state.cpu_state.cstatus = iresume_cstatus; + perfctr->iresume_cstatus = 0; + + /* remote access note: perfctr_cpu_ireload() is ok */ + perfctr_cpu_ireload(&perfctr->state.cpu_state); + + if( tsk == current ) + vperfctr_resume(perfctr); + + preempt_enable(); + + return 0; +#else + return -ENOSYS; +#endif +} + +static int sys_vperfctr_unlink(struct vperfctr *perfctr, struct task_struct *tsk) +{ + task_thread(tsk)->perfctr = NULL; + __vperfctr_exit(perfctr); + return 0; +} + +/* + * Sample the counters and update state. + * This operation is used on processors like the pre-MMX Intel P5, + * which cannot sample the counter registers in user-mode. + */ +static int sys_vperfctr_sample(struct vperfctr *perfctr, struct task_struct *tsk) +{ + if( tsk == current ) { + preempt_disable(); + vperfctr_sample(perfctr); + preempt_enable(); + } + return 0; +} + +/**************************************************************** + * * + * Virtual perfctr file operations. * + * * + ****************************************************************/ + +static int vperfctr_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct vperfctr *perfctr; + + /* Only allow read-only mapping of first page. */ + if( (vma->vm_end - vma->vm_start) != PAGE_SIZE || + vma_pgoff(vma) != 0 || + (pgprot_val(vma->vm_page_prot) & _PAGE_RW) || + (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) ) + return -EPERM; + perfctr = filp->private_data; + if( !perfctr ) + return -EPERM; + return remap_page_range(vma, vma->vm_start, virt_to_phys(perfctr), + PAGE_SIZE, vma->vm_page_prot); +} + +static int vperfctr_release(struct inode *inode, struct file *filp) +{ + struct vperfctr *perfctr = filp->private_data; + filp->private_data = NULL; + if( perfctr ) + put_vperfctr(perfctr); + return 0; +} + +static int vperfctr_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct vperfctr *perfctr; + struct task_struct *tsk; + int ret; + + switch( cmd ) { + case PERFCTR_INFO: + return sys_perfctr_info((struct perfctr_info*)arg); + } + perfctr = filp->private_data; + if( !perfctr ) + return -EINVAL; + tsk = current; + if( perfctr != task_thread(current)->perfctr ) { + spin_lock(&perfctr->owner_lock); + tsk = perfctr->owner; + if( tsk ) + get_task_struct(tsk); + spin_unlock(&perfctr->owner_lock); + if( !tsk ) + return -ESRCH; + ret = ptrace_check_attach(tsk, 0); + if( ret < 0 ) + goto out; + } + switch( cmd ) { + case VPERFCTR_CONTROL: + ret = sys_vperfctr_control(perfctr, (struct vperfctr_control*)arg, tsk); + break; + case VPERFCTR_STOP: + preempt_disable(); + ret = sys_vperfctr_stop(perfctr, tsk); + preempt_enable(); + break; + case VPERFCTR_UNLINK: + ret = sys_vperfctr_unlink(perfctr, tsk); + break; + case VPERFCTR_SAMPLE: + ret = sys_vperfctr_sample(perfctr, tsk); + break; + case VPERFCTR_IRESUME: + ret = sys_vperfctr_iresume(perfctr, tsk); + break; + default: + ret = -EINVAL; + } + out: + if( tsk != current ) + put_task_struct(tsk); + return ret; +} + +/* Map a /proc/$pid/$file inode to its task struct. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +static struct task_struct *get_task_by_proc_pid_inode(struct inode *inode) +{ + int pid = inode->i_ino >> 16; + struct task_struct *tsk; + + read_lock(&tasklist_lock); + tsk = find_task_by_pid(pid); + if( tsk ) + get_task_struct(tsk); /* dummy in 2.2 */ + read_unlock(&tasklist_lock); + return tsk; +} +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */ +static struct task_struct *get_task_by_proc_pid_inode(struct inode *inode) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,4) + struct task_struct *tsk = PROC_I(inode)->task; +#else + struct task_struct *tsk = inode->u.proc_i.task; +#endif + get_task_struct(tsk); + return tsk; +} +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */ + +static int vperfctr_init_done; + +static int vperfctr_open(struct inode *inode, struct file *filp) +{ + struct task_struct *tsk; + struct vperfctr *perfctr; + int err; + + /* The link from /proc//perfctr exists even if the + hardware detection failed. Disallow open in this case. */ + if( !vperfctr_init_done ) + return -ENODEV; + + /* + * Allocating a new vperfctr object for the O_CREAT case is + * done before the self-or-remote-control check. + * This is because get_empty_vperfctr() may sleep, and in the + * remote control case, the child may have been killed while we + * slept. Instead of dealing with the ugly revalidation issues, + * we allocate ahead of time, and remember to deallocate in case + * of errors. + * If we only supported 2.4+ kernels, this would be much less of + * an issue, since the task pointer itself remains valid across + * a sleep thanks to get_task_struct(). + */ + perfctr = NULL; + if( filp->f_flags & O_CREAT ) { + perfctr = get_empty_vperfctr(); /* may sleep */ + if( IS_ERR(perfctr) ) + return PTR_ERR(perfctr); + } + tsk = current; + if( !proc_pid_inode_denotes_task(inode, tsk) ) { /* remote? */ + tsk = get_task_by_proc_pid_inode(inode); + err = -ESRCH; + if( !tsk ) + goto err_perfctr; + err = ptrace_check_attach(tsk, 0); + if( err < 0 ) + goto err_tsk; + } + if( filp->f_flags & O_CREAT ) { + err = -EEXIST; + if( task_thread(tsk)->perfctr ) + goto err_tsk; + perfctr->owner = tsk; + task_thread(tsk)->perfctr = perfctr; + } else { + perfctr = task_thread(tsk)->perfctr; + /* In the /proc/pid/perfctr API, there is one user, viz. + ioctl PERFCTR_INFO, for which it's ok for perfctr to + be NULL. Hence no non-NULL check here. */ + } + filp->private_data = perfctr; + if( perfctr ) + atomic_inc(&perfctr->count); + if( tsk != current ) + put_task_struct(tsk); + return 0; + err_tsk: + if( tsk != current ) + put_task_struct(tsk); + err_perfctr: + if( perfctr ) /* can only occur if filp->f_flags & O_CREAT */ + put_vperfctr(perfctr); + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + +static struct file_operations vperfctr_file_ops = { + .owner = THIS_MODULE, + .mmap = vperfctr_mmap, + .release = vperfctr_release, + .ioctl = vperfctr_ioctl, + .open = vperfctr_open, +}; + +#if !defined(MODULE) +void perfctr_set_proc_pid_ops(struct inode *inode) +{ + inode->i_fop = &vperfctr_file_ops; +} +#endif + +#else /* 2.2 :-( */ + +#include + +#if defined(MODULE) +static int vperfctr_release_22(struct inode *inode, struct file *filp) +{ + vperfctr_release(inode, filp); + MOD_DEC_USE_COUNT; /* 2.4 kernel does this for us */ + return 0; +} +static int vperfctr_open_22(struct inode *inode, struct file *filp) +{ + int ret; + MOD_INC_USE_COUNT; /* 2.4 kernel does this for us */ + ret = vperfctr_open(inode, filp); + if( ret < 0 ) + MOD_DEC_USE_COUNT; + return ret; +} +#else /* !MODULE */ +#define vperfctr_release_22 vperfctr_release +#define vperfctr_open_22 vperfctr_open +#endif /* MODULE */ + +static struct file_operations vperfctr_file_ops = { + .mmap = vperfctr_mmap, + .release = vperfctr_release_22, + .ioctl = vperfctr_ioctl, + .open = vperfctr_open_22, +}; + +#if !defined(MODULE) +struct inode_operations perfctr_proc_pid_inode_operations = { + .default_file_ops = &vperfctr_file_ops, + .permission = proc_permission, +}; +#endif + +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) */ + +/**************************************************************** + * * + * module_init/exit * + * * + ****************************************************************/ + +#ifdef MODULE +static struct vperfctr_stub off; + +static void vperfctr_stub_init(void) +{ + write_lock(&vperfctr_stub_lock); + off = vperfctr_stub; + vperfctr_stub.exit = __vperfctr_exit; + vperfctr_stub.suspend = __vperfctr_suspend; + vperfctr_stub.resume = __vperfctr_resume; +#ifdef CONFIG_SMP + vperfctr_stub.sample = __vperfctr_sample; +#endif + vperfctr_stub.file_ops = &vperfctr_file_ops; + write_unlock(&vperfctr_stub_lock); +} + +static void vperfctr_stub_exit(void) +{ + write_lock(&vperfctr_stub_lock); + vperfctr_stub = off; + write_unlock(&vperfctr_stub_lock); +} +#else +static inline void vperfctr_stub_init(void) { } +static inline void vperfctr_stub_exit(void) { } +#endif /* MODULE */ + +int __init vperfctr_init(void) +{ + vperfctr_stub_init(); + vperfctr_init_done = 1; + return 0; +} + +void __exit vperfctr_exit(void) +{ + vperfctr_stub_exit(); +} diff -urN linux-2.4.21-pre5/drivers/perfctr/virtual.h linux-2.4.21-pre5-perfctr/drivers/perfctr/virtual.h --- linux-2.4.21-pre5/drivers/perfctr/virtual.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/virtual.h Mon Nov 18 02:42:38 2002 @@ -0,0 +1,13 @@ +/* $Id: virtual.h,v 1.10 2002/11/18 01:42:38 mikpe Exp $ + * Virtual per-process performance counters. + * + * Copyright (C) 1999-2002 Mikael Pettersson + */ + +#ifdef CONFIG_PERFCTR_VIRTUAL +extern int vperfctr_init(void); +extern void vperfctr_exit(void); +#else +static inline int vperfctr_init(void) { return 0; } +static inline void vperfctr_exit(void) { } +#endif diff -urN linux-2.4.21-pre5/drivers/perfctr/virtual_stub.c linux-2.4.21-pre5-perfctr/drivers/perfctr/virtual_stub.c --- linux-2.4.21-pre5/drivers/perfctr/virtual_stub.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/virtual_stub.c Sat Nov 30 22:11:21 2002 @@ -0,0 +1,101 @@ +/* $Id: virtual_stub.c,v 1.18 2002/11/30 21:11:21 mikpe Exp $ + * Kernel stub used to support virtual perfctrs when the + * perfctr driver is built as a module. + * + * Copyright (C) 2000-2002 Mikael Pettersson + */ +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" + +static void bug(const char *func, void *callee) +{ + printk(KERN_ERR __FILE__ ": BUG! call to __vperfctr_%s " + "from %p, pid %u, '%s' when perfctr module is not loaded\n", + func, callee, current->pid, current->comm); + task_thread(current)->perfctr = NULL; +} + +static void bug_exit(struct vperfctr *perfctr) +{ + bug("exit", __builtin_return_address(0)); +} + +static void bug_suspend(struct vperfctr *perfctr) +{ + bug("suspend", __builtin_return_address(0)); +} + +static void bug_resume(struct vperfctr *perfctr) +{ + bug("resume", __builtin_return_address(0)); +} + +#ifdef CONFIG_SMP +static void bug_sample(struct vperfctr *perfctr) +{ + bug("sample", __builtin_return_address(0)); +} +#endif + +static int vperfctr_stub_open(struct inode *inode, struct file *filp) +{ + struct file_operations *fops; + + read_lock(&vperfctr_stub_lock); + fops = fops_get(vperfctr_stub.file_ops); + read_unlock(&vperfctr_stub_lock); + if( !fops && request_module("perfctr") == 0 ) { + read_lock(&vperfctr_stub_lock); + fops = fops_get(vperfctr_stub.file_ops); + read_unlock(&vperfctr_stub_lock); + } + if( !fops ) + return -ENOSYS; + filp->f_op = fops; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + inode->i_fop = fops; /* no fops_get since only filp->f_op counts */ +#endif + return fops->open(inode, filp); +} + +static struct file_operations vperfctr_stub_file_ops = { + .open = vperfctr_stub_open, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +void perfctr_set_proc_pid_ops(struct inode *inode) +{ + inode->i_fop = &vperfctr_stub_file_ops; +} +#else +#include +struct inode_operations perfctr_proc_pid_inode_operations = { + .default_file_ops = &vperfctr_stub_file_ops, + .permission = proc_permission, +}; +#endif + +struct vperfctr_stub vperfctr_stub = { + .exit = bug_exit, + .suspend = bug_suspend, + .resume = bug_resume, +#ifdef CONFIG_SMP + .sample = bug_sample, +#endif + .file_ops = NULL, +}; +rwlock_t vperfctr_stub_lock = RW_LOCK_UNLOCKED; + +EXPORT_SYMBOL(vperfctr_stub); +EXPORT_SYMBOL(vperfctr_stub_lock); +EXPORT_SYMBOL_pidhash; +EXPORT_SYMBOL___put_task_struct; +#ifdef CONFIG_SMP +EXPORT_SYMBOL_tasklist_lock; +#endif diff -urN linux-2.4.21-pre5/drivers/perfctr/x86.c linux-2.4.21-pre5-perfctr/drivers/perfctr/x86.c --- linux-2.4.21-pre5/drivers/perfctr/x86.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/x86.c Mon Jan 20 20:17:05 2003 @@ -0,0 +1,1647 @@ +/* $Id: x86.c,v 1.87 2003/01/20 19:17:05 mikpe Exp $ + * x86 performance-monitoring counters driver. + * + * Copyright (C) 1999-2003 Mikael Pettersson + */ +#include +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#include +#undef MSR_P6_PERFCTR0 + +#include "compat.h" +#include "x86_compat.h" +#include "x86_tests.h" + +/* Support for lazy evntsel and perfctr MSR updates. */ +struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */ + union { + unsigned int p5_cesr; + unsigned int id; /* cache owner id */ + } k1; + struct { + /* NOTE: these caches have physical indices, not virtual */ + unsigned int evntsel[18]; + unsigned int escr[0x3E2-0x3A0]; + unsigned int pebs_enable; + unsigned int pebs_matrix_vert; + } control; +} __attribute__((__aligned__(SMP_CACHE_BYTES))); +static struct per_cpu_cache per_cpu_cache[NR_CPUS] __cacheline_aligned; + +/* Intel P5, Cyrix 6x86MX/MII/III, Centaur WinChip C6/2/3 */ +#define MSR_P5_CESR 0x11 +#define MSR_P5_CTR0 0x12 /* .. 0x13 */ +#define P5_CESR_CPL 0x00C0 +#define P5_CESR_RESERVED (~0x01FF) +#define MII_CESR_RESERVED (~0x05FF) +#define C6_CESR_RESERVED (~0x00FF) + +/* Intel P6, VIA C3 */ +#define MSR_P6_PERFCTR0 0xC1 /* .. 0xC2 */ +#define MSR_P6_EVNTSEL0 0x186 /* .. 0x187 */ +#define P6_EVNTSEL_ENABLE 0x00400000 +#define P6_EVNTSEL_INT 0x00100000 +#define P6_EVNTSEL_CPL 0x00030000 +#define P6_EVNTSEL_RESERVED 0x00280000 +#define VC3_EVNTSEL1_RESERVED (~0x1FF) + +/* AMD K7 */ +#define MSR_K7_EVNTSEL0 0xC0010000 /* .. 0xC0010003 */ +#define MSR_K7_PERFCTR0 0xC0010004 /* .. 0xC0010007 */ + +/* Intel P4 */ +#define MSR_P4_MISC_ENABLE 0x1A0 +#define MSR_P4_MISC_ENABLE_PERF_AVAIL (1<<7) /* read-only status bit */ +#define MSR_P4_MISC_ENABLE_PEBS_UNAVAIL (1<<12) /* read-only status bit */ +#define MSR_P4_PERFCTR0 0x300 /* .. 0x311 */ +#define MSR_P4_CCCR0 0x360 /* .. 0x371 */ +#define MSR_P4_ESCR0 0x3A0 /* .. 0x3E1, with some gaps */ + +#define MSR_P4_PEBS_ENABLE 0x3F1 +#define P4_PE_REPLAY_TAG_BITS 0x00000607 +#define P4_PE_UOP_TAG 0x01000000 +#define P4_PE_RESERVED 0xFEFFF9F8 /* only allow ReplayTagging */ + +#define MSR_P4_PEBS_MATRIX_VERT 0x3F2 +#define P4_PMV_REPLAY_TAG_BITS 0x00000003 +#define P4_PMV_RESERVED 0xFFFFFFFC + +#define P4_CCCR_OVF 0x80000000 +#define P4_CCCR_CASCADE 0x40000000 +#define P4_CCCR_OVF_PMI_T1 0x08000000 +#define P4_CCCR_OVF_PMI_T0 0x04000000 +#define P4_CCCR_ACTIVE_THREAD 0x00030000 +#define P4_CCCR_ENABLE 0x00001000 +#define P4_CCCR_ESCR_SELECT(X) (((X) >> 13) & 0x7) +#define P4_CCCR_RESERVED (0x30000FFF|P4_CCCR_OVF|P4_CCCR_OVF_PMI_T1) +#define P4_CCCR_REQUIRED P4_CCCR_ACTIVE_THREAD + +#define P4_ESCR_CPL_T1 0x00000003 +#define P4_ESCR_CPL_T0 0x0000000C /* must be non-zero */ +#define P4_ESCR_TAG_ENABLE 0x00000010 +#define P4_ESCR_RESERVED (0x80000000|P4_ESCR_CPL_T1) + +#define P4_FAST_RDPMC 0x80000000 +#define P4_MASK_FAST_RDPMC 0x0000001F /* we only need low 5 bits */ + +#define rdmsrl(msr,low) \ + __asm__ __volatile__("rdmsr" : "=a"(low) : "c"(msr) : "edx") +#define rdpmcl(ctr,low) \ + __asm__ __volatile__("rdpmc" : "=a"(low) : "c"(ctr) : "edx") + +static void clear_msr_range(unsigned int base, unsigned int n) +{ + unsigned int i; + + for(i = 0; i < n; ++i) + wrmsr(base+i, 0, 0); +} + +static inline void set_in_cr4_local(unsigned int mask) +{ + write_cr4(read_cr4() | mask); +} + +static inline void clear_in_cr4_local(unsigned int mask) +{ + write_cr4(read_cr4() & ~mask); +} + +static unsigned int new_id(void) +{ + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + static unsigned int counter; + int id; + + spin_lock(&lock); + id = ++counter; + spin_unlock(&lock); + return id; +} + +#if defined(CONFIG_SMP) && PERFCTR_INTERRUPT_SUPPORT + +static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, + const struct per_cpu_cache *cpu) +{ + state->k2.isuspend_cpu = cpu; +} + +static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, + const struct per_cpu_cache *cpu) +{ + return state->k2.isuspend_cpu == cpu; +} + +static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) +{ + state->k2.isuspend_cpu = NULL; +} + +#else +static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, + const struct per_cpu_cache *cpu) { } +static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, + const struct per_cpu_cache *cpu) { return 1; } +static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { } +#endif + +/**************************************************************** + * * + * Driver procedures. * + * * + ****************************************************************/ + +/* + * Intel P5 family (Pentium, family code 5). + * - One TSC and two 40-bit PMCs. + * - A single 32-bit CESR (MSR 0x11) controls both PMCs. + * CESR has two halves, each controlling one PMC. + * To keep the API reasonably clean, the user puts 16 bits of + * control data in each counter's evntsel; the driver combines + * these to a single 32-bit CESR value. + * - Overflow interrupts are not available. + * - Pentium MMX added the RDPMC instruction. RDPMC has lower + * overhead than RDMSR and it can be used in user-mode code. + * - The MMX events are not symmetric: some events are only available + * for some PMC, and some event codes denote different events + * depending on which PMCs they control. + */ + +/* shared with MII and C6 */ +static int p5_like_check_control(struct perfctr_cpu_state *state, + unsigned int reserved_bits, int is_c6) +{ + unsigned short cesr_half[2]; + unsigned int pmc, evntsel, i; + + if( state->control.nrictrs != 0 || state->control.nractrs > 2 ) + return -EINVAL; + cesr_half[0] = 0; + cesr_half[1] = 0; + for(i = 0; i < state->control.nractrs; ++i) { + pmc = state->control.pmc_map[i]; + if( pmc > 1 || cesr_half[pmc] != 0 ) + return -EINVAL; + evntsel = state->control.evntsel[i]; + /* protect reserved bits */ + if( (evntsel & reserved_bits) != 0 ) + return -EPERM; + /* the CPL field (if defined) must be non-zero */ + if( !is_c6 && !(evntsel & P5_CESR_CPL) ) + return -EINVAL; + cesr_half[pmc] = evntsel; + } + state->k1.p5_cesr = (cesr_half[1] << 16) | cesr_half[0]; + return 0; +} + +static int p5_check_control(struct perfctr_cpu_state *state) +{ + return p5_like_check_control(state, P5_CESR_RESERVED, 0); +} + +/* shared with MII but not C6 */ +static void p5_write_control(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cpu; + unsigned int cesr; + + cesr = state->k1.p5_cesr; + if( !cesr ) /* no PMC is on (this test doesn't work on C6) */ + return; + cpu = &per_cpu_cache[smp_processor_id()]; + if( cpu->k1.p5_cesr != cesr ) { + cpu->k1.p5_cesr = cesr; + wrmsr(MSR_P5_CESR, cesr, 0); + } +} + +static void p5_read_counters(const struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + unsigned int cstatus, nrctrs, i; + + /* The P5 doesn't allocate a cache line on a write miss, so do + a dummy read to avoid a write miss here _and_ a read miss + later in our caller. */ + asm("" : : "r"(ctrs->tsc)); + + cstatus = state->cstatus; + if( perfctr_cstatus_has_tsc(cstatus) ) + rdtscl(ctrs->tsc); + nrctrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int pmc = state->control.pmc_map[i]; + rdmsrl(MSR_P5_CTR0+pmc, ctrs->pmc[i]); + } +} + +/* used by P5MMX, MII, C6, VC3, P6, K7, P4 */ +static void rdpmc_read_counters(const struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + unsigned int cstatus, nrctrs, i; + + cstatus = state->cstatus; + if( perfctr_cstatus_has_tsc(cstatus) ) + rdtscl(ctrs->tsc); + nrctrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int pmc = state->control.pmc_map[i]; + rdpmcl(pmc, ctrs->pmc[i]); + } +} + +/* shared with MII and C6 */ +static void p5_clear_counters(void) +{ + clear_msr_range(MSR_P5_CESR, 1+2); +} + +/* + * Cyrix 6x86/MII/III. + * - Same MSR assignments as P5 MMX. Has RDPMC and two 48-bit PMCs. + * - Event codes and CESR formatting as in the plain P5 subset. + * - Many but not all P5 MMX event codes are implemented. + * - Cyrix adds a few more event codes. The event code is widened + * to 7 bits, and Cyrix puts the high bit in CESR bit 10 + * (and CESR bit 26 for PMC1). + */ + +static int mii_check_control(struct perfctr_cpu_state *state) +{ + return p5_like_check_control(state, MII_CESR_RESERVED, 0); +} + +/* + * Centaur WinChip C6/2/3. + * - Same MSR assignments as P5 MMX. Has RDPMC and two 40-bit PMCs. + * - CESR is formatted with two halves, like P5. However, there + * are no defined control fields for e.g. CPL selection, and + * there is no defined method for stopping the counters. + * - Only a few event codes are defined. + * - The 64-bit TSC is synthesised from the low 32 bits of the + * two PMCs, and CESR has to be set up appropriately. + * Reprogramming CESR causes RDTSC to yield invalid results. + * (The C6 may also hang in this case, due to C6 erratum I-13.) + * Therefore, using the PMCs on any of these processors requires + * that the TSC is not accessed at all: + * 1. The kernel must be configured or a TSC-less processor, i.e. + * generic 586 or less. + * 2. The "notsc" boot parameter must be passed to the kernel. + * 3. User-space libraries and code must also be configured and + * compiled for a generic 586 or less. + */ + +#if !defined(CONFIG_X86_TSC) +static int c6_check_control(struct perfctr_cpu_state *state) +{ + if( state->control.tsc_on ) + return -EINVAL; + return p5_like_check_control(state, C6_CESR_RESERVED, 1); +} + +static void c6_write_control(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cpu; + unsigned int cesr; + + if( perfctr_cstatus_nractrs(state->cstatus) == 0 ) /* no PMC is on */ + return; + cpu = &per_cpu_cache[smp_processor_id()]; + cesr = state->k1.p5_cesr; + if( cpu->k1.p5_cesr != cesr ) { + cpu->k1.p5_cesr = cesr; + wrmsr(MSR_P5_CESR, cesr, 0); + } +} +#endif + +/* + * Intel P6 family (Pentium Pro, Pentium II, and Pentium III cores, + * and Xeon and Celeron versions of Pentium II and III cores). + * - One TSC and two 40-bit PMCs. + * - One 32-bit EVNTSEL MSR for each PMC. + * - EVNTSEL0 contains a global enable/disable bit. + * That bit is reserved in EVNTSEL1. + * - Each EVNTSEL contains a CPL field. + * - Overflow interrupts are possible, but requires that the + * local APIC is available. Some Mobile P6s have no local APIC. + * - The PMCs cannot be initialised with arbitrary values, since + * wrmsr fills the high bits by sign-extending from bit 31. + * - Most events are symmetric, but a few are not. + */ + +/* shared with K7 */ +static int p6_like_check_control(struct perfctr_cpu_state *state, int is_k7) +{ + unsigned int evntsel, i, nractrs, nrctrs, pmc_mask, pmc; + + nractrs = state->control.nractrs; + nrctrs = nractrs + state->control.nrictrs; + if( nrctrs < nractrs || nrctrs > (is_k7 ? 4 : 2) ) + return -EINVAL; + + pmc_mask = 0; + for(i = 0; i < nrctrs; ++i) { + pmc = state->control.pmc_map[i]; + if( pmc >= (is_k7 ? 4 : 2) || (pmc_mask & (1<control.evntsel[i]; + /* protect reserved bits */ + if( evntsel & P6_EVNTSEL_RESERVED ) + return -EPERM; + /* check ENable bit */ + if( is_k7 ) { + /* ENable bit must be set in each evntsel */ + if( !(evntsel & P6_EVNTSEL_ENABLE) ) + return -EINVAL; + } else { + /* only evntsel[0] has the ENable bit */ + if( evntsel & P6_EVNTSEL_ENABLE ) { + if( pmc > 0 ) + return -EPERM; + } else { + if( pmc == 0 ) + return -EINVAL; + } + } + /* the CPL field must be non-zero */ + if( !(evntsel & P6_EVNTSEL_CPL) ) + return -EINVAL; + /* INT bit must be off for a-mode and on for i-mode counters */ + if( evntsel & P6_EVNTSEL_INT ) { + if( i < nractrs ) + return -EINVAL; + } else { + if( i >= nractrs ) + return -EINVAL; + } + } + state->k1.id = new_id(); + return 0; +} + +static int p6_check_control(struct perfctr_cpu_state *state) +{ + return p6_like_check_control(state, 0); +} + +#ifdef CONFIG_PERFCTR_DEBUG +static void debug_evntsel_cache(const struct perfctr_cpu_state *state, + const struct per_cpu_cache *cpu) +{ + unsigned int nrctrs, i; + + nrctrs = perfctr_cstatus_nrctrs(state->cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int evntsel = state->control.evntsel[i]; + unsigned int pmc = state->control.pmc_map[i]; + if( evntsel != cpu->control.evntsel[pmc] ) { + printk(KERN_ERR "perfctr/x86.c: (pid %d, comm %s) " + "evntsel[%u] is %#x, should be %#x\n", + current->pid, current->comm, + i, cpu->control.evntsel[pmc], evntsel); + return; + } + } +} +#else +static inline void debug_evntsel_cache(const struct perfctr_cpu_state *s, + const struct per_cpu_cache *c) +{ } +#endif + +#if PERFCTR_INTERRUPT_SUPPORT +/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */ +/* shared with K7 */ +static void p6_like_isuspend(struct perfctr_cpu_state *state, + unsigned int msr_evntsel0) +{ + struct per_cpu_cache *cpu; + unsigned int cstatus, nrctrs, i; + + cpu = &per_cpu_cache[smp_processor_id()]; + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { + unsigned int pmc, now; + pmc = state->control.pmc_map[i]; + cpu->control.evntsel[pmc] = 0; + wrmsr(msr_evntsel0+pmc, 0, 0); + rdpmcl(pmc, now); + state->sum.pmc[i] += now - state->start.pmc[i]; + state->start.pmc[i] = now; + } + /* cpu->k1.id is still == state->k1.id */ + set_isuspend_cpu(state, cpu); +} + +/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */ +/* shared with K7 */ +static void p6_like_iresume(const struct perfctr_cpu_state *state, + unsigned int msr_evntsel0, + unsigned int msr_perfctr0) +{ + struct per_cpu_cache *cpu; + unsigned int cstatus, nrctrs, i; + + cpu = &per_cpu_cache[smp_processor_id()]; + if( cpu->k1.id == state->k1.id ) { + cpu->k1.id = 0; /* force reload of cleared EVNTSELs */ + if( is_isuspend_cpu(state, cpu) ) + return; /* skip reload of PERFCTRs */ + } + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { + unsigned int pmc = state->control.pmc_map[i]; + /* If the control wasn't ours we must disable the evntsels + before reinitialising the counters, to prevent unexpected + counter increments and missed overflow interrupts. */ + if( cpu->control.evntsel[pmc] ) { + cpu->control.evntsel[pmc] = 0; + wrmsr(msr_evntsel0+pmc, 0, 0); + } + /* the -1 is correct for K7 but ignored by P6 */ + wrmsr(msr_perfctr0+pmc, state->start.pmc[i], -1); + } + /* cpu->k1.id remains != state->k1.id */ +} + +static void p6_isuspend(struct perfctr_cpu_state *state) +{ + p6_like_isuspend(state, MSR_P6_EVNTSEL0); +} + +static void p6_iresume(const struct perfctr_cpu_state *state) +{ + p6_like_iresume(state, MSR_P6_EVNTSEL0, MSR_P6_PERFCTR0); +} +#endif /* PERFCTR_INTERRUPT_SUPPORT */ + +/* shared with K7 and VC3 */ +static void p6_like_write_control(const struct perfctr_cpu_state *state, + unsigned int msr_evntsel0) +{ + struct per_cpu_cache *cpu; + unsigned int nrctrs, i; + + cpu = &per_cpu_cache[smp_processor_id()]; + if( cpu->k1.id == state->k1.id ) { + debug_evntsel_cache(state, cpu); + return; + } + nrctrs = perfctr_cstatus_nrctrs(state->cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int evntsel = state->control.evntsel[i]; + unsigned int pmc = state->control.pmc_map[i]; + if( evntsel != cpu->control.evntsel[pmc] ) { + cpu->control.evntsel[pmc] = evntsel; + wrmsr(msr_evntsel0+pmc, evntsel, 0); + } + } + cpu->k1.id = state->k1.id; +} + +/* shared with VC3 */ +static void p6_write_control(const struct perfctr_cpu_state *state) +{ + p6_like_write_control(state, MSR_P6_EVNTSEL0); +} + +static void p6_clear_counters(void) +{ + clear_msr_range(MSR_P6_EVNTSEL0, 2); + clear_msr_range(MSR_P6_PERFCTR0, 2); +} + +/* + * AMD K7 family (Athlon, Duron). + * - Somewhat similar to the Intel P6 family. + * - Four 48-bit PMCs. + * - Four 32-bit EVNTSEL MSRs with similar layout as in P6. + * - Completely different MSR assignments :-( + * - Fewer countable events defined :-( + * - The events appear to be completely symmetric. + * - The EVNTSEL MSRs are symmetric since each has its own enable bit. + * - Publicly available documentation is incomplete. + */ + +static int k7_check_control(struct perfctr_cpu_state *state) +{ + return p6_like_check_control(state, 1); +} + +#if PERFCTR_INTERRUPT_SUPPORT +static void k7_isuspend(struct perfctr_cpu_state *state) +{ + p6_like_isuspend(state, MSR_K7_EVNTSEL0); +} + +static void k7_iresume(const struct perfctr_cpu_state *state) +{ + p6_like_iresume(state, MSR_K7_EVNTSEL0, MSR_K7_PERFCTR0); +} +#endif /* PERFCTR_INTERRUPT_SUPPORT */ + +static void k7_write_control(const struct perfctr_cpu_state *state) +{ + p6_like_write_control(state, MSR_K7_EVNTSEL0); +} + +static void k7_clear_counters(void) +{ + clear_msr_range(MSR_K7_EVNTSEL0, 4+4); +} + +/* + * VIA C3 family. + * - A Centaur design somewhat similar to the P6/Celeron. + * - PERFCTR0 is an alias for the TSC, and EVNTSEL0 is read-only. + * - PERFCTR1 is 32 bits wide. + * - EVNTSEL1 has no defined control fields, and there is no + * defined method for stopping the counter. + * - According to testing, the reserved fields in EVNTSEL1 have + * no function. We always fill them with zeroes. + * - Only a few event codes are defined. + * - No local APIC or interrupt-mode support. + * - pmc_map[0] must be 1, if nractrs == 1. + */ +static int vc3_check_control(struct perfctr_cpu_state *state) +{ + if( state->control.nrictrs || state->control.nractrs > 1 ) + return -EINVAL; + if( state->control.nractrs == 1 ) { + if( state->control.pmc_map[0] != 1 ) + return -EINVAL; + if( state->control.evntsel[0] & VC3_EVNTSEL1_RESERVED ) + return -EPERM; + state->k1.id = state->control.evntsel[0]; + } else + state->k1.id = 0; + return 0; +} + +static void vc3_clear_counters(void) +{ + /* Not documented, but seems to be default after boot. */ + wrmsr(MSR_P6_EVNTSEL0+1, 0x00070079, 0); +} + +/* + * Intel Pentium 4. + * Current implementation restrictions: + * - No DS/PEBS support. + */ + +/* + * Table 15-4 in the IA32 Volume 3 manual contains a 18x8 entry mapping + * from counter/CCCR number (0-17) and ESCR SELECT value (0-7) to the + * actual ESCR MSR number. This mapping contains some repeated patterns, + * so we can compact it to a 4x8 table of MSR offsets: + * + * 1. CCCRs 16 and 17 are mapped just like CCCRs 13 and 14, respectively. + * Thus, we only consider the 16 CCCRs 0-15. + * 2. The CCCRs are organised in pairs, and both CCCRs in a pair use the + * same mapping. Thus, we only consider the 8 pairs 0-7. + * 3. In each pair of pairs, the second odd-numbered pair has the same domain + * as the first even-numbered pair, and the range is 1+ the range of the + * the first even-numbered pair. For example, CCCR(0) and (1) map ESCR + * SELECT(7) to 0x3A0, and CCCR(2) and (3) map it to 0x3A1. + * The only exception is that pair (7) [CCCRs 14 and 15] does not have + * ESCR SELECT(3) in its domain, like pair (6) [CCCRs 12 and 13] has. + * NOTE: Revisions of IA32 Volume 3 older than #245472-007 had an error + * in this table: CCCRs 12, 13, and 16 had their mappings for ESCR SELECT + * values 2 and 3 swapped. + * 4. All MSR numbers are on the form 0x3??. Instead of storing these as + * 16-bit numbers, the table only stores the 8-bit offsets from 0x300. + */ + +static const unsigned char p4_cccr_escr_map[4][8] = { + /* 0x00 and 0x01 as is, 0x02 and 0x03 are +1 */ + [0x00/4] { [7] 0xA0, + [6] 0xA2, + [2] 0xAA, + [4] 0xAC, + [0] 0xB2, + [1] 0xB4, + [3] 0xB6, + [5] 0xC8, }, + /* 0x04 and 0x05 as is, 0x06 and 0x07 are +1 */ + [0x04/4] { [0] 0xC0, + [2] 0xC2, + [1] 0xC4, }, + /* 0x08 and 0x09 as is, 0x0A and 0x0B are +1 */ + [0x08/4] { [1] 0xA4, + [0] 0xA6, + [5] 0xA8, + [2] 0xAE, + [3] 0xB0, }, + /* 0x0C, 0x0D, and 0x10 as is, + 0x0E, 0x0F, and 0x11 are +1 except [3] is not in the domain */ + [0x0C/4] { [4] 0xB8, + [5] 0xCC, + [6] 0xE0, + [0] 0xBA, + [2] 0xBC, + [3] 0xBE, + [1] 0xCA, }, +}; + +static unsigned int p4_escr_addr(unsigned int pmc, unsigned int cccr_val) +{ + unsigned int escr_select, pair, escr_offset; + + escr_select = P4_CCCR_ESCR_SELECT(cccr_val); + if( pmc > 0x11 ) + return 0; /* pmc range error */ + if( pmc > 0x0F ) + pmc -= 3; /* 0 <= pmc <= 0x0F */ + pair = pmc / 2; /* 0 <= pair <= 7 */ + escr_offset = p4_cccr_escr_map[pair / 2][escr_select]; + if( !escr_offset || (pair == 7 && escr_select == 3) ) + return 0; /* ESCR SELECT range error */ + return escr_offset + (pair & 1) + 0x300; +}; + +static int p4_check_control(struct perfctr_cpu_state *state) +{ + unsigned int i, nractrs, nrctrs, pmc_mask; + + nractrs = state->control.nractrs; + nrctrs = nractrs + state->control.nrictrs; + if( nrctrs < nractrs || nrctrs > 18 ) + return -EINVAL; + + pmc_mask = 0; + for(i = 0; i < nrctrs; ++i) { + unsigned int pmc, cccr_val, escr_val, escr_addr; + /* check that pmc_map[] is well-defined; + pmc_map[i] is what we pass to RDPMC, the PMC itself + is extracted by masking off the FAST_RDPMC flag */ + pmc = state->control.pmc_map[i] & ~P4_FAST_RDPMC; + if( pmc >= 18 || (pmc_mask & (1<control.evntsel[i]; + if( cccr_val & P4_CCCR_RESERVED ) + return -EPERM; + if( (cccr_val & P4_CCCR_REQUIRED) != P4_CCCR_REQUIRED ) + return -EINVAL; + if( !(cccr_val & (P4_CCCR_ENABLE | P4_CCCR_CASCADE)) ) + return -EINVAL; + if( cccr_val & P4_CCCR_OVF_PMI_T0 ) { + if( i < nractrs ) + return -EINVAL; + } else { + if( i >= nractrs ) + return -EINVAL; + } + /* check ESCR contents */ + escr_val = state->control.evntsel_aux[i]; + if( escr_val & P4_ESCR_RESERVED ) + return -EPERM; + if( !(escr_val & P4_ESCR_CPL_T0) ) + return -EINVAL; + /* compute and cache ESCR address */ + escr_addr = p4_escr_addr(pmc, cccr_val); + if( !escr_addr ) + return -EINVAL; /* ESCR SELECT range error */ + /* XXX: Two counters could map to the same ESCR. Should we + check that they use the same ESCR value? */ + state->k2.p4_escr_map[i] = escr_addr - MSR_P4_ESCR0; + } + /* check ReplayTagging control (PEBS_ENABLE and PEBS_MATRIX_VERT) */ + if( state->control.p4.pebs_enable ) { + if( !nrctrs ) + return -EPERM; + if( state->control.p4.pebs_enable & P4_PE_RESERVED ) + return -EPERM; + if( !(state->control.p4.pebs_enable & P4_PE_UOP_TAG ) ) + return -EINVAL; + if( !(state->control.p4.pebs_enable & P4_PE_REPLAY_TAG_BITS) ) + return -EINVAL; + if( state->control.p4.pebs_matrix_vert & P4_PMV_RESERVED ) + return -EPERM; + if( !(state->control.p4.pebs_matrix_vert & P4_PMV_REPLAY_TAG_BITS) ) + return -EINVAL; + } else if( state->control.p4.pebs_matrix_vert ) + return -EPERM; + state->k1.id = new_id(); + return 0; +} + +#if PERFCTR_INTERRUPT_SUPPORT +/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */ +/* XXX: merge with p6_like_isuspend() later */ +static void p4_isuspend(struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cpu; + unsigned int cstatus, nrctrs, i; + + cpu = &per_cpu_cache[smp_processor_id()]; + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { + unsigned int pmc_raw, pmc_idx, now; + pmc_raw = state->control.pmc_map[i]; + pmc_idx = pmc_raw & P4_MASK_FAST_RDPMC; + cpu->control.evntsel[pmc_idx] = 0; + wrmsr(MSR_P4_CCCR0+pmc_idx, 0, 0); /* P4 quirk: also clear OVF */ + /* Erratum N17 does not apply since we read only low 32 bits. */ + rdpmcl(pmc_raw, now); + state->sum.pmc[i] += now - state->start.pmc[i]; + state->start.pmc[i] = now; + } + /* cpu->k1.id is still == state->k1.id */ + set_isuspend_cpu(state, cpu); + /* another P4 quirk: must unmask LVTPC */ + apic_write(APIC_LVTPC, LOCAL_PERFCTR_VECTOR); +} + +/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */ +/* XXX: merge with p6_like_iresume() later */ +static void p4_iresume(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cpu; + unsigned int cstatus, nrctrs, i; + + cpu = &per_cpu_cache[smp_processor_id()]; + if( cpu->k1.id == state->k1.id ) { + cpu->k1.id = 0; /* force reload of cleared EVNTSELs */ + if( is_isuspend_cpu(state, cpu) ) + return; /* skip reload of PERFCTRs */ + } + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { + unsigned int pmc = state->control.pmc_map[i] & P4_MASK_FAST_RDPMC; + /* If the control wasn't ours we must disable the evntsels + before reinitialising the counters, to prevent unexpected + counter increments and missed overflow interrupts. */ + if( cpu->control.evntsel[pmc] ) { + cpu->control.evntsel[pmc] = 0; + wrmsr(MSR_P4_CCCR0+pmc, 0, 0); + } + /* Erratum N15 does not apply since the CCCR is disabled. */ + wrmsr(MSR_P4_PERFCTR0+pmc, state->start.pmc[i], -1); + } + /* cpu->k1.id remains != state->k1.id */ +} +#endif /* PERFCTR_INTERRUPT_SUPPORT */ + +static void p4_write_control(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cpu; + unsigned int nrctrs, i; + + /* XXX: temporary debug check */ + if( (perfctr_cpus_forbidden_mask & (1 << smp_processor_id())) && + perfctr_cstatus_nrctrs(state->cstatus) ) + printk(KERN_ERR "%s: BUG! CPU %u is in the forbidden set\n", + __FUNCTION__, smp_processor_id()); + cpu = &per_cpu_cache[smp_processor_id()]; + if( cpu->k1.id == state->k1.id ) { + debug_evntsel_cache(state, cpu); + return; + } + nrctrs = perfctr_cstatus_nrctrs(state->cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int escr_val, escr_off, cccr_val, pmc; + escr_val = state->control.evntsel_aux[i]; + escr_off = state->k2.p4_escr_map[i]; + if( escr_val != cpu->control.escr[escr_off] ) { + cpu->control.escr[escr_off] = escr_val; + wrmsr(MSR_P4_ESCR0+escr_off, escr_val, 0); + } + cccr_val = state->control.evntsel[i]; + pmc = state->control.pmc_map[i] & P4_MASK_FAST_RDPMC; + if( cccr_val != cpu->control.evntsel[pmc] ) { + cpu->control.evntsel[pmc] = cccr_val; + wrmsr(MSR_P4_CCCR0+pmc, cccr_val, 0); + } + } + if( state->control.p4.pebs_enable != cpu->control.pebs_enable ) { + cpu->control.pebs_enable = state->control.p4.pebs_enable; + wrmsr(MSR_P4_PEBS_ENABLE, cpu->control.pebs_enable, 0); + } + if( state->control.p4.pebs_matrix_vert != cpu->control.pebs_matrix_vert ) { + cpu->control.pebs_matrix_vert = state->control.p4.pebs_matrix_vert; + wrmsr(MSR_P4_PEBS_MATRIX_VERT, cpu->control.pebs_matrix_vert, 0); + } + cpu->k1.id = state->k1.id; +} + +static void p4_clear_counters(void) +{ + /* MSR 0x3F0 seems to have a default value of 0xFC00, but current + docs doesn't fully define it, so leave it alone for now. */ + /* clear PEBS_ENABLE and PEBS_MATRIX_VERT; they handle both PEBS + and ReplayTagging, and should exist even if PEBS is disabled */ + clear_msr_range(0x3F1, 2); + clear_msr_range(0x3A0, 31); + clear_msr_range(0x3C0, 6); + clear_msr_range(0x3C8, 6); + clear_msr_range(0x3E0, 2); + clear_msr_range(MSR_P4_CCCR0, 18); + clear_msr_range(MSR_P4_PERFCTR0, 18); +} + +/* + * Generic driver for any x86 with a working TSC. + */ + +static int generic_check_control(struct perfctr_cpu_state *state) +{ + if( state->control.nractrs || state->control.nrictrs ) + return -EINVAL; + return 0; +} + +static void generic_write_control(const struct perfctr_cpu_state *state) +{ +} + +static void generic_read_counters(const struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + rdtscl(ctrs->tsc); +} + +static void generic_clear_counters(void) +{ +} + +/* + * Driver methods, internal and exported. + * + * Frequently called functions (write_control, read_counters, + * isuspend and iresume) are back-patched to invoke the correct + * processor-specific methods directly, thereby saving the + * overheads of indirect function calls. + * + * Backpatchable call sites must have been "finalised" after + * initialisation. The reason for this is that unsynchronised code + * modification doesn't work in multiprocessor systems, due to + * Intel P6 errata. Consequently, all backpatchable call sites + * must be known and local to this file. + */ + +static int redirect_call_disable; + +static void redirect_call(void *ra, void *to) +{ + /* XXX: make this function __init later */ + if( redirect_call_disable ) + printk(KERN_ERR __FILE__ ":%s: unresolved call to %p at %p\n", + __FUNCTION__, to, ra); + /* we can only redirect `call near relative' instructions */ + if( *((unsigned char*)ra - 5) != 0xE8 ) { + printk(KERN_WARNING __FILE__ ":%s: unable to redirect caller %p to %p\n", + __FUNCTION__, ra, to); + return; + } + *(int*)((char*)ra - 4) = (char*)to - (char*)ra; +} + +static void (*write_control)(const struct perfctr_cpu_state*); +static void perfctr_cpu_write_control(const struct perfctr_cpu_state *state) +{ + redirect_call(__builtin_return_address(0), write_control); + return write_control(state); +} + +static void (*read_counters)(const struct perfctr_cpu_state*, + struct perfctr_low_ctrs*); +static void perfctr_cpu_read_counters(const struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + redirect_call(__builtin_return_address(0), read_counters); + return read_counters(state, ctrs); +} + +#if PERFCTR_INTERRUPT_SUPPORT +static void (*cpu_isuspend)(struct perfctr_cpu_state*); +static void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) +{ + redirect_call(__builtin_return_address(0), cpu_isuspend); + return cpu_isuspend(state); +} + +static void (*cpu_iresume)(const struct perfctr_cpu_state*); +static void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) +{ + redirect_call(__builtin_return_address(0), cpu_iresume); + return cpu_iresume(state); +} + +/* Call perfctr_cpu_ireload() just before perfctr_cpu_resume() to + bypass internal caching and force a reload if the I-mode PMCs. */ +void perfctr_cpu_ireload(struct perfctr_cpu_state *state) +{ +#if !defined(CONFIG_SMP) + /* This works because we know the process owning 'state' + * will be resumed on the current CPU. + */ + struct per_cpu_cache *cpu; + cpu = &per_cpu_cache[smp_processor_id()]; + cpu->k1.id = 0; +#else + /* This works because of the additional is_isuspend_cpu() check + * that iresume() performs on SMP. The UP version above fails + * when iresume() is issued via remote control on SMP, since we + * don't know on which CPU the process owning 'state' will be + * resumed. Inferior alternatives are: + * 1. Allocate a new control id to 'state', but that is ugly. + * 2. Invalidate cpu->k1.id on every CPU, but this would penalise + * other processes, require a locking protocol for cpu->k1.id + * accesses, or require an expensive smp_call_function() call + * to perform the update atomically on all CPUs. + */ + clear_isuspend_cpu(state); +#endif +} + +/* PRE: the counters have been suspended and sampled by perfctr_cpu_suspend() */ +unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state *state) +{ + unsigned int cstatus, nrctrs, pmc, pmc_mask; + + cstatus = state->cstatus; + pmc = perfctr_cstatus_nractrs(cstatus); + nrctrs = perfctr_cstatus_nrctrs(cstatus); + + for(pmc_mask = 0; pmc < nrctrs; ++pmc) { + if( (int)state->start.pmc[pmc] >= 0 ) { /* XXX: ">" ? */ + /* XXX: "+=" to correct for overshots */ + state->start.pmc[pmc] = state->control.ireset[pmc]; + pmc_mask |= (1 << pmc); + /* On a P4 we should now clear the OVF flag in the + counter's CCCR. However, p4_isuspend() already + did that as a side-effect of clearing the CCCR + in order to stop the i-mode counters. */ + } + } + return pmc_mask; +} +#endif /* PERFCTR_INTERRUPT_SUPPORT */ + +static inline int check_ireset(const struct perfctr_cpu_state *state) +{ +#if PERFCTR_INTERRUPT_SUPPORT + unsigned int nrctrs, i; + + i = state->control.nractrs; + nrctrs = i + state->control.nrictrs; + for(; i < nrctrs; ++i) + if( state->control.ireset[i] >= 0 ) + return -EINVAL; +#endif + return 0; +} + +static inline void setup_imode_start_values(struct perfctr_cpu_state *state) +{ +#if PERFCTR_INTERRUPT_SUPPORT + unsigned int cstatus, nrctrs, i; + + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) + state->start.pmc[i] = state->control.ireset[i]; +#endif +} + +static inline void debug_no_imode(const struct perfctr_cpu_state *state) +{ +#ifdef CONFIG_PERFCTR_DEBUG + if( perfctr_cstatus_has_ictrs(state->cstatus) ) + printk(KERN_ERR "perfctr/x86.c: BUG! updating control in" + " perfctr %p on cpu %u while it has cstatus %x" + " (pid %d, comm %s)\n", + state, smp_processor_id(), state->cstatus, + current->pid, current->comm); +#endif +} + +static int (*check_control)(struct perfctr_cpu_state*); +int perfctr_cpu_update_control(struct perfctr_cpu_state *state) +{ + int err; + + debug_no_imode(state); + clear_isuspend_cpu(state); + state->cstatus = 0; + + /* disallow i-mode counters if we cannot catch the interrupts */ + if( !(perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT) + && state->control.nrictrs ) + return -EPERM; + + err = check_control(state); + if( err < 0 ) + return err; + err = check_ireset(state); + if( err < 0 ) + return err; + state->cstatus = perfctr_mk_cstatus(state->control.tsc_on, + state->control.nractrs, + state->control.nrictrs); + setup_imode_start_values(state); + return 0; +} + +void perfctr_cpu_suspend(struct perfctr_cpu_state *state) +{ + unsigned int i, cstatus, nractrs; + struct perfctr_low_ctrs now; + +#if PERFCTR_INTERRUPT_SUPPORT + if( perfctr_cstatus_has_ictrs(state->cstatus) ) + perfctr_cpu_isuspend(state); +#endif + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if( perfctr_cstatus_has_tsc(cstatus) ) + state->sum.tsc += now.tsc - state->start.tsc; + nractrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nractrs; ++i) + state->sum.pmc[i] += now.pmc[i] - state->start.pmc[i]; + /* perfctr_cpu_disable_rdpmc(); */ /* not for x86 */ +} + +void perfctr_cpu_resume(struct perfctr_cpu_state *state) +{ +#if PERFCTR_INTERRUPT_SUPPORT + if( perfctr_cstatus_has_ictrs(state->cstatus) ) + perfctr_cpu_iresume(state); +#endif + /* perfctr_cpu_enable_rdpmc(); */ /* not for x86 or global-mode */ + perfctr_cpu_write_control(state); + perfctr_cpu_read_counters(state, &state->start); + /* XXX: if (SMP && start.tsc == now.tsc) ++now.tsc; */ +} + +void perfctr_cpu_sample(struct perfctr_cpu_state *state) +{ + unsigned int i, cstatus, nractrs; + struct perfctr_low_ctrs now; + + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if( perfctr_cstatus_has_tsc(cstatus) ) { + state->sum.tsc += now.tsc - state->start.tsc; + state->start.tsc = now.tsc; + } + nractrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nractrs; ++i) { + state->sum.pmc[i] += now.pmc[i] - state->start.pmc[i]; + state->start.pmc[i] = now.pmc[i]; + } +} + +static void (*clear_counters)(void); +static void perfctr_cpu_clear_counters(void) +{ + return clear_counters(); +} + +/**************************************************************** + * * + * Processor detection and initialisation procedures. * + * * + ****************************************************************/ + +static inline void set_perfctr_cpus_forbidden_mask(unsigned long mask) +{ +#if !defined(perfctr_cpus_forbidden_mask) + perfctr_cpus_forbidden_mask = mask; +#endif +} + +/* see comment above at redirect_call() */ +static void __init finalise_backpatching(void) +{ + struct per_cpu_cache *cpu; + struct perfctr_cpu_state state; + unsigned long old_mask; + + old_mask = perfctr_cpus_forbidden_mask; + set_perfctr_cpus_forbidden_mask(0); + + cpu = &per_cpu_cache[smp_processor_id()]; + memset(cpu, 0, sizeof *cpu); + memset(&state, 0, sizeof state); + state.cstatus = + (perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT) + ? perfctr_mk_cstatus(0, 0, 1) + : 0; + perfctr_cpu_sample(&state); + perfctr_cpu_resume(&state); + perfctr_cpu_suspend(&state); + + set_perfctr_cpus_forbidden_mask(old_mask); + + redirect_call_disable = 1; +} + +#ifdef CONFIG_SMP + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +static inline int __init p4_ht_mask_setup(void) +{ + printk(KERN_WARNING "perfctr/x86.c: hyper-threaded P4s detected:" + " kernel too old! -- bailing out\n"); + return -ENODEV; +} +#else + +unsigned long perfctr_cpus_forbidden_mask; + +static void __init p4_ht_mask_setup_cpu(void *ignore) +{ + unsigned int local_apic_physical_id = cpuid_ebx(1) >> 24; + unsigned int logical_processor_id = local_apic_physical_id & 1; + if( logical_processor_id != 0 ) { + unsigned long bit = 1 << smp_processor_id(); + atomic_set_mask(bit, &perfctr_cpus_forbidden_mask); + } +} + +static inline int __init p4_ht_mask_setup(void) +{ + smp_call_function(p4_ht_mask_setup_cpu, NULL, 1, 1); + p4_ht_mask_setup_cpu(NULL); + printk(KERN_INFO "perfctr/x86.c: hyper-threaded P4s detected: restricting" + " access for CPUs 0x%lX\n", perfctr_cpus_forbidden_mask); + return 0; +} +#endif + +static int __init p4_ht_init(void) +{ + unsigned int nr_siblings; + + if( !cpu_has_ht ) + return 0; + nr_siblings = (cpuid_ebx(1) >> 16) & 0xFF; + if( nr_siblings > 2 ) { + printk(KERN_WARNING "perfctr/x86.c: hyper-threaded P4s detected:" + " unsupported number of siblings: %u -- bailing out\n", + nr_siblings); + return -ENODEV; + } + return (nr_siblings < 2) ? 0 : p4_ht_mask_setup(); +} +#else /* SMP */ +#define p4_ht_init() (0) +#endif /* SMP */ + +static int __init intel_init(void) +{ + unsigned int misc_enable; + + if( !cpu_has_tsc ) + return -ENODEV; + switch( current_cpu_data.x86 ) { + case 5: + if( cpu_has_mmx ) { + perfctr_info.cpu_type = PERFCTR_X86_INTEL_P5MMX; + read_counters = rdpmc_read_counters; + + /* Avoid Pentium Erratum 74. */ + if( current_cpu_data.x86_model == 4 && + (current_cpu_data.x86_mask == 4 || + (current_cpu_data.x86_mask == 3 && + ((cpuid_eax(1) >> 12) & 0x3) == 1)) ) + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + } else { + perfctr_info.cpu_type = PERFCTR_X86_INTEL_P5; + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + read_counters = p5_read_counters; + } + write_control = p5_write_control; + check_control = p5_check_control; + clear_counters = p5_clear_counters; + perfctr_p5_init_tests(); + return 0; + case 6: + if( current_cpu_data.x86_model >= 7 ) /* PIII */ + perfctr_info.cpu_type = PERFCTR_X86_INTEL_PIII; + else if( current_cpu_data.x86_model >= 3 ) /* PII or Celeron */ + perfctr_info.cpu_type = PERFCTR_X86_INTEL_PII; + else { + perfctr_info.cpu_type = PERFCTR_X86_INTEL_P6; + + /* Avoid Pentium Pro Erratum 26. */ + if( current_cpu_data.x86_mask < 9 ) + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + } + read_counters = rdpmc_read_counters; + write_control = p6_write_control; + check_control = p6_check_control; + clear_counters = p6_clear_counters; +#if PERFCTR_INTERRUPT_SUPPORT + if( cpu_has_apic ) { + perfctr_info.cpu_features |= PERFCTR_FEATURE_PCINT; + cpu_isuspend = p6_isuspend; + cpu_iresume = p6_iresume; + } +#endif + perfctr_p6_init_tests(); + return 0; + case 15: /* Pentium 4 */ + rdmsrl(MSR_P4_MISC_ENABLE, misc_enable); + if( !(misc_enable & MSR_P4_MISC_ENABLE_PERF_AVAIL) ) + break; + if( p4_ht_init() != 0 ) + break; + if( current_cpu_data.x86_model >= 2 ) + /* Model 2 changed the ESCR Event Mask programming + details for several events. */ + perfctr_info.cpu_type = PERFCTR_X86_INTEL_P4M2; + else + perfctr_info.cpu_type = PERFCTR_X86_INTEL_P4; + read_counters = rdpmc_read_counters; + write_control = p4_write_control; + check_control = p4_check_control; + clear_counters = p4_clear_counters; +#if PERFCTR_INTERRUPT_SUPPORT + if( cpu_has_apic ) { + perfctr_info.cpu_features |= PERFCTR_FEATURE_PCINT; + cpu_isuspend = p4_isuspend; + cpu_iresume = p4_iresume; + } +#endif + perfctr_p4_init_tests(); + return 0; + } + return -ENODEV; +} + +static int __init amd_init(void) +{ + if( !cpu_has_tsc ) + return -ENODEV; + switch( current_cpu_data.x86 ) { + case 15: /* K8. Looks like a K7. */ + case 6: /* K7. Model 1 does not have a local APIC. + AMD Document #22007 Revision J hints that APIC-less + K7s signal overflows as debug interrupts. */ + perfctr_info.cpu_type = PERFCTR_X86_AMD_K7; + read_counters = rdpmc_read_counters; + write_control = k7_write_control; + check_control = k7_check_control; + clear_counters = k7_clear_counters; +#if PERFCTR_INTERRUPT_SUPPORT + if( cpu_has_apic ) { + perfctr_info.cpu_features |= PERFCTR_FEATURE_PCINT; + cpu_isuspend = k7_isuspend; + cpu_iresume = k7_iresume; + } +#endif + perfctr_k7_init_tests(); + return 0; + } + return -ENODEV; +} + +static int __init cyrix_init(void) +{ + if( !cpu_has_tsc ) + return -ENODEV; + switch( current_cpu_data.x86 ) { + case 6: /* 6x86MX, MII, or III */ + perfctr_info.cpu_type = PERFCTR_X86_CYRIX_MII; + read_counters = rdpmc_read_counters; + write_control = p5_write_control; + check_control = mii_check_control; + clear_counters = p5_clear_counters; + perfctr_mii_init_tests(); + return 0; + } + return -ENODEV; +} + +static int __init centaur_init(void) +{ + switch( current_cpu_data.x86 ) { +#if !defined(CONFIG_X86_TSC) + case 5: + switch( current_cpu_data.x86_model ) { + case 4: /* WinChip C6 */ + perfctr_info.cpu_type = PERFCTR_X86_WINCHIP_C6; + break; + case 8: /* WinChip 2, 2A, or 2B */ + case 9: /* WinChip 3, a 2A with larger cache and lower voltage */ + perfctr_info.cpu_type = PERFCTR_X86_WINCHIP_2; + break; + default: + return -ENODEV; + } + /* + * TSC must be inaccessible for perfctrs to work. + */ + if( !(read_cr4() & X86_CR4_TSD) || cpu_has_tsc ) + return -ENODEV; + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDTSC; + read_counters = rdpmc_read_counters; + write_control = c6_write_control; + check_control = c6_check_control; + clear_counters = p5_clear_counters; + perfctr_c6_init_tests(); + return 0; +#endif + case 6: /* VIA C3 */ + if( !cpu_has_tsc ) + return -ENODEV; + switch( current_cpu_data.x86_model ) { + case 6: /* VIA C3 (Cyrix III) */ + case 7: /* VIA C3 Samuel 2 or Ezra */ + case 8: /* VIA C3 Ezra-T */ + break; + default: + return -ENODEV; + } + perfctr_info.cpu_type = PERFCTR_X86_VIA_C3; + read_counters = rdpmc_read_counters; + write_control = p6_write_control; + check_control = vc3_check_control; + clear_counters = vc3_clear_counters; + perfctr_vc3_init_tests(); + return 0; + } + return -ENODEV; +} + +static int __init generic_init(void) +{ + if( !cpu_has_tsc ) + return -ENODEV; + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + perfctr_info.cpu_type = PERFCTR_X86_GENERIC; + check_control = generic_check_control; + write_control = generic_write_control; + read_counters = generic_read_counters; + clear_counters = generic_clear_counters; + perfctr_generic_init_tests(); + return 0; +} + +static char generic_name[] __initdata = "Generic x86 with TSC"; +static char p5_name[] __initdata = "Intel Pentium"; +static char p5mmx_name[] __initdata = "Intel Pentium MMX"; +static char p6_name[] __initdata = "Intel Pentium Pro"; +static char pii_name[] __initdata = "Intel Pentium II"; +static char piii_name[] __initdata = "Intel Pentium III"; +static char mii_name[] __initdata = "Cyrix 6x86MX/MII/III"; +static char wcc6_name[] __initdata = "WinChip C6"; +static char wc2_name[] __initdata = "WinChip 2/3"; +static char k7_name[] __initdata = "AMD K7"; +static char vc3_name[] __initdata = "VIA C3"; +static char p4_name[] __initdata = "Intel Pentium 4"; +static char p4m2_name[] __initdata = "Intel Pentium 4 Model 2"; + +char *perfctr_cpu_name[] __initdata = { + [PERFCTR_X86_GENERIC] generic_name, + [PERFCTR_X86_INTEL_P5] p5_name, + [PERFCTR_X86_INTEL_P5MMX] p5mmx_name, + [PERFCTR_X86_INTEL_P6] p6_name, + [PERFCTR_X86_INTEL_PII] pii_name, + [PERFCTR_X86_INTEL_PIII] piii_name, + [PERFCTR_X86_CYRIX_MII] mii_name, + [PERFCTR_X86_WINCHIP_C6] wcc6_name, + [PERFCTR_X86_WINCHIP_2] wc2_name, + [PERFCTR_X86_AMD_K7] k7_name, + [PERFCTR_X86_VIA_C3] vc3_name, + [PERFCTR_X86_INTEL_P4] p4_name, + [PERFCTR_X86_INTEL_P4M2] p4m2_name, +}; + +static void __init perfctr_cpu_init_one(void *ignore) +{ + /* PREEMPT note: when called via smp_call_function(), + this is in IRQ context with preemption disabled. */ + perfctr_cpu_clear_counters(); +#if PERFCTR_INTERRUPT_SUPPORT + if( cpu_has_apic ) + apic_write(APIC_LVTPC, LOCAL_PERFCTR_VECTOR); +#endif + if( perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC ) + set_in_cr4_local(X86_CR4_PCE); +} + +static void __exit perfctr_cpu_exit_one(void *ignore) +{ + /* PREEMPT note: when called via smp_call_function(), + this is in IRQ context with preemption disabled. */ + perfctr_cpu_clear_counters(); +#if PERFCTR_INTERRUPT_SUPPORT + if( cpu_has_apic ) + apic_write(APIC_LVTPC, APIC_DM_NMI | APIC_LVT_MASKED); +#endif + if( perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC ) + clear_in_cr4_local(X86_CR4_PCE); +} + +#if defined(NMI_LOCAL_APIC) && defined(CONFIG_PM) + +static void __init unregister_nmi_pmdev(void) +{ + if( nmi_pmdev ) { + apic_pm_unregister(nmi_pmdev); + nmi_pmdev = 0; + } +} + +static int x86_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + /* XXX: incomplete */ + return 0; +} + +static struct pm_dev *x86_pmdev; + +static void __init x86_pm_init(void) +{ + x86_pmdev = apic_pm_register(PM_SYS_DEV, 0, x86_pm_callback); +} + +static void __exit x86_pm_exit(void) +{ + if( x86_pmdev ) { + apic_pm_unregister(x86_pmdev); + x86_pmdev = NULL; + } +} + +#else + +static inline void unregister_nmi_pmdev(void) { } +static inline void x86_pm_init(void) { } +static inline void x86_pm_exit(void) { } + +#endif /* NMI_LOCAL_APIC && CONFIG_PM */ + +#if defined(NMI_LOCAL_APIC) + +static void __init disable_nmi_watchdog(void) +{ + if( nmi_perfctr_msr ) { + nmi_perfctr_msr = 0; + printk(KERN_NOTICE "perfctr: disabled nmi_watchdog\n"); + unregister_nmi_pmdev(); + } +} + +#else + +static inline void disable_nmi_watchdog(void) { } + +#endif + +static void invalidate_per_cpu_cache(void) +{ + /* + * per_cpu_cache[] is initialised to contain "impossible" + * evntsel values guaranteed to differ from anything accepted + * by perfctr_cpu_update_control(). This way, initialisation of + * a CPU's evntsel MSRs will happen automatically the first time + * perfctr_cpu_write_control() executes on it. + * All-bits-one works for all currently supported processors. + * The memset also sets the ids to -1, which is intentional. + */ + memset(per_cpu_cache, ~0, sizeof per_cpu_cache); +} + +int __init perfctr_cpu_init(void) +{ + int err = -ENODEV; + + preempt_disable(); + + /* RDPMC and RDTSC are on by default. They will be disabled + by the init procedures if necessary. */ + perfctr_info.cpu_features = PERFCTR_FEATURE_RDPMC | PERFCTR_FEATURE_RDTSC; + + if( cpu_has_msr ) { + switch( current_cpu_data.x86_vendor ) { + case X86_VENDOR_INTEL: + err = intel_init(); + break; + case X86_VENDOR_AMD: + err = amd_init(); + break; + case X86_VENDOR_CYRIX: + err = cyrix_init(); + break; + case X86_VENDOR_CENTAUR: + err = centaur_init(); + } + } + if( err ) { + err = generic_init(); /* last resort */ + if( err ) + goto out; + } + /* + * Put the hardware in a sane state: + * - finalise resolution of backpatchable call sites + * - clear perfctr MSRs + * - set up APIC_LVTPC + * - set CR4.PCE [on permanently due to __flush_tlb_global()] + * - install our default interrupt handler + */ + if( perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC ) + mmu_cr4_features |= X86_CR4_PCE; + finalise_backpatching(); + perfctr_cpu_init_one(NULL); + smp_call_function(perfctr_cpu_init_one, NULL, 1, 1); + perfctr_cpu_set_ihandler(NULL); + /* + * Fix up the connection to the local APIC: + * - disable and disconnect the NMI watchdog + * - register our PM callback + */ + disable_nmi_watchdog(); + x86_pm_init(); + + invalidate_per_cpu_cache(); + + perfctr_info.cpu_khz = perfctr_cpu_khz(); + /* XXX: TDB */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,23) +/* export the cpu_online_map instead? */ +#define smp_num_cpus num_online_cpus() +#endif + perfctr_info.nrcpus = smp_num_cpus; + + out: + preempt_enable(); + return err; +} + +void __exit perfctr_cpu_exit(void) +{ + preempt_disable(); + if( perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC ) + mmu_cr4_features &= ~X86_CR4_PCE; + perfctr_cpu_exit_one(NULL); + smp_call_function(perfctr_cpu_exit_one, NULL, 1, 1); + perfctr_cpu_set_ihandler(NULL); + x86_pm_exit(); + /* XXX: restart nmi watchdog? */ + preempt_enable(); +} + +/**************************************************************** + * * + * Hardware reservation. * + * * + ****************************************************************/ + +static const char *current_service = 0; + +const char *perfctr_cpu_reserve(const char *service) +{ + if( current_service ) + return current_service; + current_service = service; + (void)try_module_get(THIS_MODULE); + return 0; +} + +static void perfctr_cpu_clear_one(void *ignore) +{ + /* PREEMPT note: when called via smp_call_function(), + this is in IRQ context with preemption disabled. */ + perfctr_cpu_clear_counters(); +} + +void perfctr_cpu_release(const char *service) +{ + if( service != current_service ) { + printk(KERN_ERR "%s: attempt by %s to release while reserved by %s\n", + __FUNCTION__, service, current_service); + } else { + /* power down the counters */ + preempt_disable(); + invalidate_per_cpu_cache(); + perfctr_cpu_clear_one(NULL); + smp_call_function(perfctr_cpu_clear_one, NULL, 1, 1); + preempt_enable(); + perfctr_cpu_set_ihandler(NULL); + current_service = 0; + module_put(THIS_MODULE); + } +} diff -urN linux-2.4.21-pre5/drivers/perfctr/x86_compat.h linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_compat.h --- linux-2.4.21-pre5/drivers/perfctr/x86_compat.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_compat.h Tue Dec 10 02:53:23 2002 @@ -0,0 +1,88 @@ +/* $Id: x86_compat.h,v 1.28 2002/12/10 01:53:23 mikpe Exp $ + * Performance-monitoring counters driver. + * x86-specific compatibility definitions for 2.2/2.4/2.5 kernels. + * + * Copyright (C) 2000-2002 Mikael Pettersson + */ +#include +#include + +/* 2.4.9-ac3 added {read,write}_cr4() macros in */ +#if !defined(write_cr4) +static inline void write_cr4(unsigned int x) +{ + __asm__ __volatile__("movl %0,%%cr4" : : "r"(x)); +} + +static inline unsigned int read_cr4(void) +{ + unsigned int x; + __asm__ __volatile__("movl %%cr4,%0" : "=r"(x)); + return x; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,18) + +/* missing from */ +#define cpu_has_msr boot_cpu_has(X86_FEATURE_MSR) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + +/* missing from */ +#define cpu_has_mmx (test_bit(X86_FEATURE_MMX, boot_cpu_data.x86_capability)) +#define cpu_has_msr (test_bit(X86_FEATURE_MSR, boot_cpu_data.x86_capability)) +#define cpu_has_ht (test_bit(28, boot_cpu_data.x86_capability)) + +#else /* 2.4 simulation for 2.2 */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,21) +static inline unsigned int cpuid_eax(unsigned int op) +{ + unsigned int eax; + + __asm__("cpuid" + : "=a" (eax) + : "0" (op) + : "bx", "cx", "dx"); + return eax; +} + +static inline unsigned int cpuid_ebx(unsigned int op) +{ + unsigned int eax, ebx; + + __asm__("cpuid" + : "=a" (eax), "=b" (ebx) + : "0" (op) + : "cx", "dx"); + return ebx; +} +#endif + +#define cpu_has_mmx (boot_cpu_data.x86_capability & X86_FEATURE_MMX) +#define cpu_has_msr (boot_cpu_data.x86_capability & X86_FEATURE_MSR) +#define cpu_has_tsc (boot_cpu_data.x86_capability & X86_FEATURE_TSC) +#define cpu_has_ht (boot_cpu_data.x86_capability & (1 << 28)) + +#define X86_CR4_TSD 0x0004 +#define X86_CR4_PCE 0x0100 + +unsigned long mmu_cr4_features; /*fake*/ + +#endif /* 2.4 simulation for 2.2 */ + +/* irq_enter() and irq_exit() take two parameters in 2.4. However, + we only use them to disable preemption in the interrupt handler, + which isn't needed in non-preemptive 2.4 kernels. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#ifdef CONFIG_PREEMPT +#error "not yet ported to 2.4+PREEMPT" +#endif +#undef irq_enter +#undef irq_exit +#define irq_enter() do{}while(0) +#define irq_exit() do{}while(0) +#endif + +extern unsigned long perfctr_cpu_khz(void); diff -urN linux-2.4.21-pre5/drivers/perfctr/x86_setup.c linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_setup.c --- linux-2.4.21-pre5/drivers/perfctr/x86_setup.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_setup.c Sat Mar 1 14:46:23 2003 @@ -0,0 +1,178 @@ +/* $Id: x86_setup.c,v 1.33.2.1 2003/03/01 13:46:23 mikpe Exp $ + * Performance-monitoring counters driver. + * x86-specific kernel-resident code. + * + * Copyright (C) 1999-2003 Mikael Pettersson + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "x86_compat.h" +#include "compat.h" + +/* XXX: belongs to a virtual_compat.c file */ +#if defined(CONFIG_SMP) && LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && !defined(HAVE_SET_CPUS_ALLOWED) +/** + * set_cpus_allowed() - change a given task's processor affinity + * @p: task to bind + * @new_mask: bitmask of allowed processors + * + * Upon return, the task is running on a legal processor. Note the caller + * must have a valid reference to the task: it must not exit() prematurely. + * This call can sleep; do not hold locks on call. + */ +void set_cpus_allowed(struct task_struct *p, unsigned long new_mask) +{ + new_mask &= cpu_online_map; + BUG_ON(!new_mask); + + p->cpus_allowed = new_mask; + + /* + * If the task is on a no-longer-allowed processor, we need to move + * it. If the task is not current, then set need_resched and send + * its processor an IPI to reschedule. + */ + if (!(p->cpus_runnable & p->cpus_allowed)) { + if (p != current) { + p->need_resched = 1; + smp_send_reschedule(p->processor); + } + /* + * Wait until we are on a legal processor. If the task is + * current, then we should be on a legal processor the next + * time we reschedule. Otherwise, we need to wait for the IPI. + */ + while (!(p->cpus_runnable & p->cpus_allowed)) + schedule(); + } +} +EXPORT_SYMBOL(set_cpus_allowed); +#endif + +#ifdef CONFIG_PERFCTR_VIRTUAL /* XXX: actually generic, not x86-specific */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,32) +#include +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,15) +#include +#else /* < 2.4.15 */ +int ptrace_check_attach(struct task_struct *child, int kill) +{ + if (!TASK_IS_PTRACED(child)) + return -ESRCH; + + if (child->p_pptr != current) + return -ESRCH; + + if (!kill) { + if (child->state != TASK_STOPPED) + return -ESRCH; +#ifdef CONFIG_SMP + /* Make sure the child gets off its CPU.. */ + for (;;) { + task_lock(child); + if (!task_has_cpu(child)) + break; + task_unlock(child); + do { + if (child->state != TASK_STOPPED) + return -ESRCH; + barrier(); + cpu_relax(); + } while (task_has_cpu(child)); + } + task_unlock(child); +#endif + } + + /* All systems go.. */ + return 0; +} +#endif /* < 2.4.15 */ +EXPORT_SYMBOL(ptrace_check_attach); +#endif + +#if PERFCTR_INTERRUPT_SUPPORT +unsigned int apic_lvtpc_irqs[NR_CPUS]; + +static void perfctr_default_ihandler(unsigned long pc) +{ + ++apic_lvtpc_irqs[smp_processor_id()]; +} + +static perfctr_ihandler_t perfctr_ihandler = perfctr_default_ihandler; + +void do_perfctr_interrupt(struct pt_regs *regs) +{ + /* PREEMPT note: invoked via an interrupt gate, which + masks interrupts. We're still on the originating CPU. */ + /* XXX: should be rewritten in assembly and inlined below */ + /* XXX: recursive interrupts? delay the ACK, mask LVTPC, or queue? */ + ack_APIC_irq(); + irq_enter(); + (*perfctr_ihandler)(regs->eip); + irq_exit(); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,8) +extern asmlinkage void perfctr_interrupt(void); +#else /* < 2.5.8 */ +#define BUILD_PERFCTR_INTERRUPT(x,v) XBUILD_PERFCTR_INTERRUPT(x,v) +#define XBUILD_PERFCTR_INTERRUPT(x,v) \ +asmlinkage void x(void); \ +__asm__( \ + "\n.text\n\t" \ + __ALIGN_STR "\n\t" \ + ".type " SYMBOL_NAME_STR(x) ",@function\n" \ + ".globl " SYMBOL_NAME_STR(x) "\n" \ +SYMBOL_NAME_STR(x) ":\n\t" \ + "pushl $" #v "-256\n\t" \ + SAVE_ALL \ + "pushl %esp\n\t" \ + "call " SYMBOL_NAME_STR(do_ ## x) "\n\t" \ + "addl $4,%esp\n\t" \ + "jmp ret_from_intr\n\t" \ + ".size " SYMBOL_NAME_STR(x) ",.-" SYMBOL_NAME_STR(x) "\n" \ + ".previous\n"); +BUILD_PERFCTR_INTERRUPT(perfctr_interrupt,LOCAL_PERFCTR_VECTOR) +#endif /* < 2.5.8 */ + +void perfctr_cpu_set_ihandler(perfctr_ihandler_t ihandler) +{ + perfctr_ihandler = ihandler ? ihandler : perfctr_default_ihandler; +} +#endif + +extern unsigned long cpu_khz; + +/* Wrapper to avoid namespace clash in RedHat 8.0's 2.4.18-14 kernel. */ +unsigned long perfctr_cpu_khz(void) +{ + return cpu_khz; +} + +#ifdef CONFIG_PERFCTR_MODULE +EXPORT_SYMBOL(mmu_cr4_features); +EXPORT_SYMBOL(perfctr_cpu_khz); + +#ifdef NMI_LOCAL_APIC +EXPORT_SYMBOL(nmi_perfctr_msr); + +#ifdef CONFIG_PM +EXPORT_SYMBOL(apic_pm_register); +EXPORT_SYMBOL(apic_pm_unregister); +EXPORT_SYMBOL(nmi_pmdev); +#endif /* CONFIG_PM */ + +#endif /* NMI_LOCAL_APIC */ + +#if PERFCTR_INTERRUPT_SUPPORT +EXPORT_SYMBOL(perfctr_cpu_set_ihandler); +#endif /* PERFCTR_INTERRUPT_SUPPORT */ + +#endif /* MODULE */ diff -urN linux-2.4.21-pre5/drivers/perfctr/x86_tests.c linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_tests.c --- linux-2.4.21-pre5/drivers/perfctr/x86_tests.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_tests.c Sun Feb 9 12:54:32 2003 @@ -0,0 +1,213 @@ +/* $Id: x86_tests.c,v 1.19 2003/02/09 11:54:32 mikpe Exp $ + * Performance-monitoring counters driver. + * Optional x86-specific init-time tests. + * + * Copyright (C) 1999-2003 Mikael Pettersson + */ +#include +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#undef MSR_P6_PERFCTR0 +#undef MSR_P4_IQ_CCCR0 +#undef MSR_P4_CRU_ESCR0 +#include "compat.h" +#include "x86_compat.h" +#include "x86_tests.h" + +#define MSR_P5_CESR 0x11 +#define MSR_P5_CTR0 0x12 +#define P5_CESR_VAL (0x16 | (3<<6)) +#define MSR_P6_PERFCTR0 0xC1 +#define MSR_P6_EVNTSEL0 0x186 +#define P6_EVNTSEL0_VAL (0xC0 | (3<<16) | (1<<22)) +#define MSR_K7_EVNTSEL0 0xC0010000 +#define MSR_K7_PERFCTR0 0xC0010004 +#define K7_EVNTSEL0_VAL (0xC0 | (3<<16) | (1<<22)) +#define VC3_EVNTSEL1_VAL 0xC0 +#define MSR_P4_IQ_COUNTER0 0x30C +#define MSR_P4_IQ_CCCR0 0x36C +#define MSR_P4_CRU_ESCR0 0x3B8 +#define P4_CRU_ESCR0_VAL ((2<<25) | (1<<9) | (0x3<<2)) +#define P4_IQ_CCCR0_VAL ((0x3<<16) | (4<<13) | (1<<12)) + +#define NITER 64 +#define X2(S) S";"S +#define X8(S) X2(X2(X2(S))) + +static void __init do_rdpmc(unsigned pmc, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("rdpmc") : : "c"(pmc) : "eax", "edx"); +} + +static void __init do_rdmsr(unsigned msr, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("rdmsr") : : "c"(msr) : "eax", "edx"); +} + +static void __init do_wrmsr(unsigned msr, unsigned data) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("wrmsr") : : "c"(msr), "a"(data), "d"(0)); +} + +static void __init do_rdcr4(unsigned unused1, unsigned unused2) +{ + unsigned i; + unsigned dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("movl %%cr4,%0") : "=r"(dummy)); +} + +static void __init do_wrcr4(unsigned cr4, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("movl %0,%%cr4") : : "r"(cr4)); +} + +static void __init do_rdtsc(unsigned unused1, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("rdtsc") : : : "eax", "edx"); +} + +static void __init do_empty_loop(unsigned unused1, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__("" : : "c"(0)); +} + +static unsigned __init run(void (*doit)(unsigned, unsigned), + unsigned arg1, unsigned arg2) +{ + unsigned start, dummy, stop; + rdtsc(start, dummy); + (*doit)(arg1, arg2); /* should take < 2^32 cycles to complete */ + rdtsc(stop, dummy); + return stop - start; +} + +static void __init init_tests_message(void) +{ + printk(KERN_INFO "Please email the following PERFCTR INIT lines " + "to mikpe@csd.uu.se\n" + KERN_INFO "To remove this message, rebuild the driver " + "with CONFIG_PERFCTR_INIT_TESTS=n\n"); + printk(KERN_INFO "PERFCTR INIT: vendor %u, family %u, model %u, stepping %u, clock %lu kHz\n", + current_cpu_data.x86_vendor, + current_cpu_data.x86, + current_cpu_data.x86_model, + current_cpu_data.x86_mask, + perfctr_cpu_khz()); +} + +static void __init +measure_overheads(unsigned msr_evntsel0, unsigned evntsel0, unsigned msr_perfctr0, + unsigned msr_cccr, unsigned cccr_val) +{ + int i; + unsigned int loop, ticks[11]; + const char *name[11]; + + if( msr_evntsel0 ) + wrmsr(msr_evntsel0, 0, 0); + if( msr_cccr ) + wrmsr(msr_cccr, 0, 0); + + name[0] = "rdtsc"; + ticks[0] = run(do_rdtsc, 0, 0); + name[1] = "rdpmc"; + ticks[1] = (perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC) + ? run(do_rdpmc,1,0) : 0; + name[2] = "rdmsr (counter)"; + ticks[2] = msr_perfctr0 ? run(do_rdmsr, msr_perfctr0, 0) : 0; + name[3] = msr_cccr ? "rdmsr (escr)" : "rdmsr (evntsel)"; + ticks[3] = msr_evntsel0 ? run(do_rdmsr, msr_evntsel0, 0) : 0; + name[4] = "wrmsr (counter)"; + ticks[4] = msr_perfctr0 ? run(do_wrmsr, msr_perfctr0, 0) : 0; + name[5] = msr_cccr ? "wrmsr (escr)" : "wrmsr (evntsel)"; + ticks[5] = msr_evntsel0 ? run(do_wrmsr, msr_evntsel0, evntsel0) : 0; + name[6] = "read cr4"; + ticks[6] = run(do_rdcr4, 0, 0); + name[7] = "write cr4"; + ticks[7] = run(do_wrcr4, read_cr4(), 0); + name[8] = "rdpmc (fast)"; + ticks[8] = msr_cccr ? run(do_rdpmc, 0x80000001, 0) : 0; + name[9] = "rdmsr (cccr)"; + ticks[9] = msr_cccr ? run(do_rdmsr, msr_cccr, 0) : 0; + name[10] = "wrmsr (cccr)"; + ticks[10] = msr_cccr ? run(do_wrmsr, msr_cccr, cccr_val) : 0; + + loop = run(do_empty_loop, 0, 0); + + if( msr_evntsel0 ) + wrmsr(msr_evntsel0, 0, 0); + if( msr_cccr ) + wrmsr(msr_cccr, 0, 0); + + init_tests_message(); + printk(KERN_INFO "PERFCTR INIT: NITER == %u\n", NITER); + printk(KERN_INFO "PERFCTR INIT: loop overhead is %u cycles\n", loop); + for(i = 0; i < ARRAY_SIZE(ticks); ++i) { + unsigned int x; + if( !ticks[i] ) + continue; + x = ((ticks[i] - loop) * 10) / NITER; + printk(KERN_INFO "PERFCTR INIT: %s cost is %u.%u cycles (%u total)\n", + name[i], x/10, x%10, ticks[i]); + } +} + +void __init perfctr_p5_init_tests(void) +{ + measure_overheads(MSR_P5_CESR, P5_CESR_VAL, MSR_P5_CTR0, 0, 0); +} + +void __init perfctr_p6_init_tests(void) +{ + measure_overheads(MSR_P6_EVNTSEL0, P6_EVNTSEL0_VAL, MSR_P6_PERFCTR0, 0, 0); +} + +void __init perfctr_k7_init_tests(void) +{ + measure_overheads(MSR_K7_EVNTSEL0, K7_EVNTSEL0_VAL, MSR_K7_PERFCTR0, 0, 0); +} + +#if !defined(CONFIG_X86_TSC) +void __init perfctr_c6_init_tests(void) +{ + unsigned int cesr, dummy; + + rdmsr(MSR_P5_CESR, cesr, dummy); + init_tests_message(); + printk(KERN_INFO "PERFCTR INIT: boot CESR == %#08x\n", cesr); +} +#endif + +void __init perfctr_vc3_init_tests(void) +{ + measure_overheads(MSR_P6_EVNTSEL0+1, VC3_EVNTSEL1_VAL, MSR_P6_PERFCTR0+1, 0, 0); +} + +void __init perfctr_p4_init_tests(void) +{ + measure_overheads(MSR_P4_CRU_ESCR0, P4_CRU_ESCR0_VAL, MSR_P4_IQ_COUNTER0, + MSR_P4_IQ_CCCR0, P4_IQ_CCCR0_VAL); +} + +void __init perfctr_generic_init_tests(void) +{ + measure_overheads(0, 0, 0, 0, 0); +} diff -urN linux-2.4.21-pre5/drivers/perfctr/x86_tests.h linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_tests.h --- linux-2.4.21-pre5/drivers/perfctr/x86_tests.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/drivers/perfctr/x86_tests.h Mon Nov 18 02:42:38 2002 @@ -0,0 +1,25 @@ +/* $Id: x86_tests.h,v 1.7 2002/11/18 01:42:38 mikpe Exp $ + * Performance-monitoring counters driver. + * Optional x86-specific init-time tests. + * + * Copyright (C) 1999-2002 Mikael Pettersson + */ + +#ifdef CONFIG_PERFCTR_INIT_TESTS +extern void perfctr_p5_init_tests(void); +extern void perfctr_p6_init_tests(void); +extern void perfctr_k7_init_tests(void); +extern void perfctr_c6_init_tests(void); +extern void perfctr_vc3_init_tests(void); +extern void perfctr_p4_init_tests(void); +extern void perfctr_generic_init_tests(void); +#else +#define perfctr_p5_init_tests() +#define perfctr_p6_init_tests() +#define perfctr_k7_init_tests() +#define perfctr_c6_init_tests() +#define perfctr_vc3_init_tests() +#define perfctr_p4_init_tests() +#define perfctr_generic_init_tests() +#endif +#define perfctr_mii_init_tests() perfctr_p5_init_tests() diff -urN linux-2.4.21-pre5/fs/proc/base.c linux-2.4.21-pre5-perfctr/fs/proc/base.c --- linux-2.4.21-pre5/fs/proc/base.c Sun Aug 4 12:05:09 2002 +++ linux-2.4.21-pre5-perfctr/fs/proc/base.c Sun Mar 2 00:14:58 2003 @@ -25,6 +25,7 @@ #include #include #include +#include /* * For hysterical raisins we keep the same inumbers as in the old procfs. @@ -540,6 +541,7 @@ PROC_PID_MAPS, PROC_PID_CPU, PROC_PID_MOUNTS, + PROC_PID_PERFCTR, PROC_PID_FD_DIR = 0x8000, /* 0x8000-0xffff */ }; @@ -560,6 +562,9 @@ E(PROC_PID_ROOT, "root", S_IFLNK|S_IRWXUGO), E(PROC_PID_EXE, "exe", S_IFLNK|S_IRWXUGO), E(PROC_PID_MOUNTS, "mounts", S_IFREG|S_IRUGO), +#ifdef CONFIG_PERFCTR_VIRTUAL + E(PROC_PID_PERFCTR, "perfctr", PERFCTR_PROC_PID_MODE), +#endif {0,0,NULL,0} }; #undef E @@ -928,6 +933,11 @@ case PROC_PID_MOUNTS: inode->i_fop = &proc_mounts_operations; break; +#ifdef CONFIG_PERFCTR_VIRTUAL + case PROC_PID_PERFCTR: + perfctr_set_proc_pid_ops(inode); + break; +#endif default: printk("procfs: impossible type (%d)",p->type); iput(inode); diff -urN linux-2.4.21-pre5/include/asm-i386/apic.h linux-2.4.21-pre5-perfctr/include/asm-i386/apic.h --- linux-2.4.21-pre5/include/asm-i386/apic.h Sat Mar 1 09:44:07 2003 +++ linux-2.4.21-pre5-perfctr/include/asm-i386/apic.h Sun Mar 2 00:14:58 2003 @@ -94,6 +94,9 @@ #define NMI_LOCAL_APIC 2 #define NMI_INVALID 3 +extern struct pm_dev *nmi_pmdev; +extern unsigned int nmi_perfctr_msr; + #endif /* CONFIG_X86_LOCAL_APIC */ #endif /* __ASM_APIC_H */ diff -urN linux-2.4.21-pre5/include/asm-i386/hw_irq.h linux-2.4.21-pre5-perfctr/include/asm-i386/hw_irq.h --- linux-2.4.21-pre5/include/asm-i386/hw_irq.h Sat Mar 1 09:44:07 2003 +++ linux-2.4.21-pre5-perfctr/include/asm-i386/hw_irq.h Sun Mar 2 00:14:58 2003 @@ -49,14 +49,15 @@ * sources per level' errata. */ #define LOCAL_TIMER_VECTOR 0xef +#define LOCAL_PERFCTR_VECTOR 0xee /* - * First APIC vector available to drivers: (vectors 0x30-0xee) + * First APIC vector available to drivers: (vectors 0x30-0xed) * we start at 0x31 to spread out vectors evenly between priority * levels. (0x80 is the syscall vector) */ #define FIRST_DEVICE_VECTOR 0x31 -#define FIRST_SYSTEM_VECTOR 0xef +#define FIRST_SYSTEM_VECTOR 0xee extern int irq_vector[NR_IRQS]; #define IO_APIC_VECTOR(irq) irq_vector[irq] diff -urN linux-2.4.21-pre5/include/asm-i386/perfctr.h linux-2.4.21-pre5-perfctr/include/asm-i386/perfctr.h --- linux-2.4.21-pre5/include/asm-i386/perfctr.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/include/asm-i386/perfctr.h Tue Dec 10 02:52:19 2002 @@ -0,0 +1,175 @@ +/* $Id: perfctr.h,v 1.34 2002/12/10 01:52:19 mikpe Exp $ + * x86 Performance-Monitoring Counters driver + * + * Copyright (C) 1999-2002 Mikael Pettersson + */ +#ifndef _ASM_I386_PERFCTR_H +#define _ASM_I386_PERFCTR_H + +struct perfctr_sum_ctrs { + unsigned long long tsc; + unsigned long long pmc[18]; +}; + +struct perfctr_low_ctrs { + unsigned int tsc; + unsigned int pmc[18]; +}; + +struct perfctr_cpu_control { + unsigned int tsc_on; + unsigned int nractrs; /* # of a-mode counters */ + unsigned int nrictrs; /* # of i-mode counters */ + unsigned int pmc_map[18]; + unsigned int evntsel[18]; /* one per counter, even on P5 */ + unsigned int evntsel_aux[18]; /* e.g. P4 ESCR contents */ + struct { + unsigned int pebs_enable; /* for replay tagging */ + unsigned int pebs_matrix_vert; /* for replay tagging */ + } p4; + int ireset[18]; /* <= 0, for i-mode counters */ +}; + +struct perfctr_cpu_state { + unsigned int cstatus; + union { + unsigned int p5_cesr; + unsigned int id; /* cache owner id */ + } k1; + struct perfctr_sum_ctrs sum; + struct perfctr_low_ctrs start; + struct perfctr_cpu_control control; + struct { + unsigned int p4_escr_map[18]; + const void *isuspend_cpu; + } k2; +}; + +/* `struct perfctr_cpu_state' binary layout version number */ +#define PERFCTR_CPU_STATE_MAGIC 0x0201 /* 2.1 */ + +/* cstatus is a re-encoding of control.tsc_on/nractrs/nrictrs + which should have less overhead in most cases */ + +static inline +unsigned int perfctr_mk_cstatus(unsigned int tsc_on, unsigned int nractrs, + unsigned int nrictrs) +{ + return (tsc_on<<31) | (nrictrs<<16) | ((nractrs+nrictrs)<<8) | nractrs; +} + +static inline unsigned int perfctr_cstatus_enabled(unsigned int cstatus) +{ + return cstatus; +} + +static inline int perfctr_cstatus_has_tsc(unsigned int cstatus) +{ + return (int)cstatus < 0; /* test and jump on sign */ +} + +static inline unsigned int perfctr_cstatus_nractrs(unsigned int cstatus) +{ + return cstatus & 0x7F; /* and with imm8 */ +} + +static inline unsigned int perfctr_cstatus_nrctrs(unsigned int cstatus) +{ + return (cstatus >> 8) & 0x7F; +} + +static inline unsigned int perfctr_cstatus_has_ictrs(unsigned int cstatus) +{ + return cstatus & (0x7F << 16); +} + +/* + * 'struct siginfo' support for perfctr overflow signals. + * In unbuffered mode, si_code is set to SI_PMC_OVF and a bitmask + * describing which perfctrs overflowed is put in si_pmc_ovf_mask. + * A bitmask is used since more than one perfctr can have overflowed + * by the time the interrupt handler runs. + * + * glibc's doesn't seem to define __SI_FAULT or __SI_CODE(), + * and including as well may cause redefinition errors, + * so the user and kernel values are different #defines here. + */ +#ifdef __KERNEL__ +#define SI_PMC_OVF (__SI_FAULT|'P') +#else +#define SI_PMC_OVF ('P') +#endif +#define si_pmc_ovf_mask _sifields._pad[0] /* XXX: use an unsigned field later */ + +#ifdef __KERNEL__ + +#if defined(CONFIG_PERFCTR) || defined(CONFIG_PERFCTR_MODULE) + +/* Driver init/exit. */ +extern int perfctr_cpu_init(void); +extern void perfctr_cpu_exit(void); + +/* CPU type name. */ +extern char *perfctr_cpu_name[]; + +/* Hardware reservation. */ +extern const char *perfctr_cpu_reserve(const char *service); +extern void perfctr_cpu_release(const char *service); + +/* PRE: state has no running interrupt-mode counters. + Check that the new control data is valid. + Update the driver's private control data. + Returns a negative error code if the control data is invalid. */ +extern int perfctr_cpu_update_control(struct perfctr_cpu_state *state); + +/* Read a-mode counters. Subtract from start and accumulate into sums. + Must be called with preemption disabled. */ +extern void perfctr_cpu_suspend(struct perfctr_cpu_state *state); + +/* Write control registers. Read a-mode counters into start. + Must be called with preemption disabled. */ +extern void perfctr_cpu_resume(struct perfctr_cpu_state *state); + +/* Perform an efficient combined suspend/resume operation. + Must be called with preemption disabled. */ +extern void perfctr_cpu_sample(struct perfctr_cpu_state *state); + +/* The type of a perfctr overflow interrupt handler. + It will be called in IRQ context, with preemption disabled. */ +typedef void (*perfctr_ihandler_t)(unsigned long pc); + +#include + +#if defined(CONFIG_X86_LOCAL_APIC) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +#include +#include +struct hw_interrupt_type; +#include +#ifdef LOCAL_PERFCTR_VECTOR +#define PERFCTR_INTERRUPT_SUPPORT 1 +#endif +#endif + +#if PERFCTR_INTERRUPT_SUPPORT +extern unsigned int apic_lvtpc_irqs[]; +extern void perfctr_interrupt(void); +extern void perfctr_cpu_set_ihandler(perfctr_ihandler_t); +extern void perfctr_cpu_ireload(struct perfctr_cpu_state*); +extern unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state*); +#else +static inline void perfctr_cpu_set_ihandler(perfctr_ihandler_t x) { } +#endif + +#if defined(CONFIG_SMP) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +/* CPUs in `perfctr_cpus_forbidden_mask' must not use the + performance-monitoring counters. TSC use is unrestricted. */ +extern unsigned long perfctr_cpus_forbidden_mask; +#else +#define perfctr_cpus_forbidden_mask (0UL) +#endif + +#endif /* CONFIG_PERFCTR */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_I386_PERFCTR_H */ diff -urN linux-2.4.21-pre5/include/asm-i386/processor.h linux-2.4.21-pre5-perfctr/include/asm-i386/processor.h --- linux-2.4.21-pre5/include/asm-i386/processor.h Sat Mar 1 09:44:07 2003 +++ linux-2.4.21-pre5-perfctr/include/asm-i386/processor.h Sun Mar 2 00:14:58 2003 @@ -366,6 +366,11 @@ unsigned long __cacheline_filler[5]; }; +/* + * Virtual per-process performance-monitoring counters. + */ +struct vperfctr; /* opaque; no need to depend on */ + struct thread_struct { unsigned long esp0; unsigned long eip; @@ -385,6 +390,8 @@ /* IO permissions */ int ioperm; unsigned long io_bitmap[IO_BITMAP_SIZE+1]; +/* performance counters */ + struct vperfctr *perfctr; }; #define INIT_THREAD { \ diff -urN linux-2.4.21-pre5/include/linux/perfctr.h linux-2.4.21-pre5-perfctr/include/linux/perfctr.h --- linux-2.4.21-pre5/include/linux/perfctr.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.21-pre5-perfctr/include/linux/perfctr.h Mon Nov 18 02:42:38 2002 @@ -0,0 +1,192 @@ +/* $Id: perfctr.h,v 1.39 2002/11/18 01:42:38 mikpe Exp $ + * Performance-Monitoring Counters driver + * + * Copyright (C) 1999-2002 Mikael Pettersson + */ +#ifndef _LINUX_PERFCTR_H +#define _LINUX_PERFCTR_H + +#include + +struct perfctr_info { + char version[32]; + unsigned char nrcpus; + unsigned char cpu_type; + unsigned char cpu_features; + unsigned long cpu_khz; +}; + +/* cpu_type values */ +#define PERFCTR_X86_GENERIC 0 /* any x86 with rdtsc */ +#define PERFCTR_X86_INTEL_P5 1 /* no rdpmc */ +#define PERFCTR_X86_INTEL_P5MMX 2 +#define PERFCTR_X86_INTEL_P6 3 +#define PERFCTR_X86_INTEL_PII 4 +#define PERFCTR_X86_INTEL_PIII 5 +#define PERFCTR_X86_CYRIX_MII 6 +#define PERFCTR_X86_WINCHIP_C6 7 /* no rdtsc */ +#define PERFCTR_X86_WINCHIP_2 8 /* no rdtsc */ +#define PERFCTR_X86_AMD_K7 9 +#define PERFCTR_X86_VIA_C3 10 /* no pmc0 */ +#define PERFCTR_X86_INTEL_P4 11 /* model 0 and 1 */ +#define PERFCTR_X86_INTEL_P4M2 12 /* model 2 and above */ + +/* cpu_features flag bits */ +#define PERFCTR_FEATURE_RDPMC 0x01 +#define PERFCTR_FEATURE_RDTSC 0x02 +#define PERFCTR_FEATURE_PCINT 0x04 + +/* user's view of mmap:ed virtual perfctr */ +struct vperfctr_state { + unsigned int magic; + int si_signo; + struct perfctr_cpu_state cpu_state; +}; + +/* `struct vperfctr_state' binary layout version number */ +#define VPERFCTR_STATE_MAGIC 0x0201 /* 2.1 */ +#define VPERFCTR_MAGIC ((VPERFCTR_STATE_MAGIC<<16)|PERFCTR_CPU_STATE_MAGIC) + +/* parameter in VPERFCTR_CONTROL command */ +struct vperfctr_control { + int si_signo; + struct perfctr_cpu_control cpu_control; + unsigned long preserve; +}; + +/* parameter in GPERFCTR_CONTROL command */ +struct gperfctr_control { + unsigned long interval_usec; + unsigned int nrcpus; + struct perfctr_cpu_control cpu_control[1]; /* actually 'nrcpus' */ +}; + +/* returned by GPERFCTR_READ command */ +struct gperfctr_cpu_state { + struct perfctr_cpu_control cpu_control; + struct perfctr_sum_ctrs sum; +}; +struct gperfctr_state { + unsigned nrcpus; + struct gperfctr_cpu_state cpu_state[1]; /* actually 'nrcpus' */ +}; + +#include +#define _PERFCTR_IOCTL 0xD0 /* 'P'+128, currently unassigned */ + +#define PERFCTR_INFO _IOR(_PERFCTR_IOCTL,0,struct perfctr_info) + +#define VPERFCTR_SAMPLE _IO(_PERFCTR_IOCTL,1) +#define VPERFCTR_UNLINK _IO(_PERFCTR_IOCTL,2) +#define VPERFCTR_CONTROL _IOW(_PERFCTR_IOCTL,3,struct vperfctr_control) +#define VPERFCTR_STOP _IO(_PERFCTR_IOCTL,4) +#define VPERFCTR_IRESUME _IO(_PERFCTR_IOCTL,5) + +#define GPERFCTR_CONTROL _IOW(_PERFCTR_IOCTL,16,struct gperfctr_control) +#define GPERFCTR_READ _IOR(_PERFCTR_IOCTL,17,struct gperfctr_state) +#define GPERFCTR_STOP _IO(_PERFCTR_IOCTL,18) + +#ifdef __KERNEL__ + +extern struct perfctr_info perfctr_info; +extern int sys_perfctr_info(struct perfctr_info*); + +/* + * Virtual per-process performance-monitoring counters. + */ +struct vperfctr; /* opaque */ + +#ifdef CONFIG_PERFCTR_VIRTUAL + +/* process management operations */ +extern struct vperfctr *__vperfctr_copy(struct vperfctr*); +extern void __vperfctr_exit(struct vperfctr*); +extern void __vperfctr_suspend(struct vperfctr*); +extern void __vperfctr_resume(struct vperfctr*); +extern void __vperfctr_sample(struct vperfctr*); + +#ifdef CONFIG_PERFCTR_MODULE +extern struct vperfctr_stub { + void (*exit)(struct vperfctr*); + void (*suspend)(struct vperfctr*); + void (*resume)(struct vperfctr*); +#ifdef CONFIG_SMP + void (*sample)(struct vperfctr*); +#endif + struct file_operations *file_ops; +} vperfctr_stub; +/* lock taken on module load/unload and ->file_ops access; + the process management operations don't take the lock + because the module is known to be loaded and in use */ +extern rwlock_t vperfctr_stub_lock; +#define _vperfctr_exit(x) vperfctr_stub.exit((x)) +#define _vperfctr_suspend(x) vperfctr_stub.suspend((x)) +#define _vperfctr_resume(x) vperfctr_stub.resume((x)) +#define _vperfctr_sample(x) vperfctr_stub.sample((x)) +#else /* !CONFIG_PERFCTR_MODULE */ +#define _vperfctr_exit(x) __vperfctr_exit((x)) +#define _vperfctr_suspend(x) __vperfctr_suspend((x)) +#define _vperfctr_resume(x) __vperfctr_resume((x)) +#define _vperfctr_sample(x) __vperfctr_sample((x)) +#endif /* CONFIG_PERFCTR_MODULE */ + +static inline void perfctr_copy_thread(struct thread_struct *thread) +{ + thread->perfctr = NULL; +} + +static inline void perfctr_exit_thread(struct thread_struct *thread) +{ + struct vperfctr *perfctr; + perfctr = thread->perfctr; + if( perfctr ) { + thread->perfctr = NULL; + _vperfctr_exit(perfctr); + } +} + +static inline void perfctr_suspend_thread(struct thread_struct *prev) +{ + struct vperfctr *perfctr; + perfctr = prev->perfctr; + if( perfctr ) + _vperfctr_suspend(perfctr); +} + +/* PRE: next is current */ +static inline void perfctr_resume_thread(struct thread_struct *next) +{ + struct vperfctr *perfctr; + perfctr = next->perfctr; + if( perfctr ) + _vperfctr_resume(perfctr); +} + +static inline void perfctr_sample_thread(struct thread_struct *thread) +{ +#ifdef CONFIG_SMP + struct vperfctr *perfctr; + perfctr = thread->perfctr; + if( perfctr ) + _vperfctr_sample(perfctr); +#endif +} + +#define PERFCTR_PROC_PID_MODE (0 | S_IRUSR) +extern void perfctr_set_proc_pid_ops(struct inode *inode); +/* for 2.2: */ +extern struct inode_operations perfctr_proc_pid_inode_operations; + +#else /* !CONFIG_PERFCTR_VIRTUAL */ + +static inline void perfctr_copy_thread(struct thread_struct *t) { } +static inline void perfctr_exit_thread(struct thread_struct *t) { } +static inline void perfctr_suspend_thread(struct thread_struct *t) { } +static inline void perfctr_resume_thread(struct thread_struct *t) { } +static inline void perfctr_sample_thread(struct thread_struct *t) { } + +#endif /* CONFIG_PERFCTR_VIRTUAL */ + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_PERFCTR_H */ diff -urN linux-2.4.21-pre5/kernel/timer.c linux-2.4.21-pre5-perfctr/kernel/timer.c --- linux-2.4.21-pre5/kernel/timer.c Sun Jan 26 10:12:50 2003 +++ linux-2.4.21-pre5-perfctr/kernel/timer.c Sun Mar 2 00:14:58 2003 @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -586,6 +587,7 @@ do_process_times(p, user, system); do_it_virt(p, user); do_it_prof(p); + perfctr_sample_thread(&p->thread); } /*