diff --git a/[refs] b/[refs] index 2e75e49e77c5..e80ff4af7144 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: d9f599e1e6d019968b35d2dc63074b9e8964fa69 +refs/heads/master: 4fd38e4595e2f6c9d27732c042a0e16b2753049c diff --git a/trunk/Documentation/DocBook/libata.tmpl b/trunk/Documentation/DocBook/libata.tmpl index ba9975771503..ff3e5bec1c24 100644 --- a/trunk/Documentation/DocBook/libata.tmpl +++ b/trunk/Documentation/DocBook/libata.tmpl @@ -107,10 +107,6 @@ void (*dev_config) (struct ata_port *, struct ata_device *); issue of SET FEATURES - XFER MODE, and prior to operation. - Called by ata_device_add() after ata_dev_identify() determines - a device is present. - - This entry may be specified as NULL in ata_port_operations. @@ -154,8 +150,8 @@ unsigned int (*mode_filter) (struct ata_port *, struct ata_device *, unsigned in Taskfile read/write -void (*tf_load) (struct ata_port *ap, struct ata_taskfile *tf); -void (*tf_read) (struct ata_port *ap, struct ata_taskfile *tf); +void (*sff_tf_load) (struct ata_port *ap, struct ata_taskfile *tf); +void (*sff_tf_read) (struct ata_port *ap, struct ata_taskfile *tf); @@ -164,36 +160,35 @@ void (*tf_read) (struct ata_port *ap, struct ata_taskfile *tf); hardware registers / DMA buffers, to obtain the current set of taskfile register values. Most drivers for taskfile-based hardware (PIO or MMIO) use - ata_tf_load() and ata_tf_read() for these hooks. + ata_sff_tf_load() and ata_sff_tf_read() for these hooks. PIO data read/write -void (*data_xfer) (struct ata_device *, unsigned char *, unsigned int, int); +void (*sff_data_xfer) (struct ata_device *, unsigned char *, unsigned int, int); All bmdma-style drivers must implement this hook. This is the low-level operation that actually copies the data bytes during a PIO data transfer. -Typically the driver -will choose one of ata_pio_data_xfer_noirq(), ata_pio_data_xfer(), or -ata_mmio_data_xfer(). +Typically the driver will choose one of ata_sff_data_xfer_noirq(), +ata_sff_data_xfer(), or ata_sff_data_xfer32(). ATA command execute -void (*exec_command)(struct ata_port *ap, struct ata_taskfile *tf); +void (*sff_exec_command)(struct ata_port *ap, struct ata_taskfile *tf); causes an ATA command, previously loaded with ->tf_load(), to be initiated in hardware. - Most drivers for taskfile-based hardware use ata_exec_command() + Most drivers for taskfile-based hardware use ata_sff_exec_command() for this hook. @@ -218,8 +213,8 @@ command. Read specific ATA shadow registers -u8 (*check_status)(struct ata_port *ap); -u8 (*check_altstatus)(struct ata_port *ap); +u8 (*sff_check_status)(struct ata_port *ap); +u8 (*sff_check_altstatus)(struct ata_port *ap); @@ -227,20 +222,14 @@ u8 (*check_altstatus)(struct ata_port *ap); hardware. On some hardware, reading the Status register has the side effect of clearing the interrupt condition. Most drivers for taskfile-based hardware use - ata_check_status() for this hook. - - - Note that because this is called from ata_device_add(), at - least a dummy function that clears device interrupts must be - provided for all drivers, even if the controller doesn't - actually have a taskfile status register. + ata_sff_check_status() for this hook. Select ATA device on bus -void (*dev_select)(struct ata_port *ap, unsigned int device); +void (*sff_dev_select)(struct ata_port *ap, unsigned int device); @@ -251,9 +240,7 @@ void (*dev_select)(struct ata_port *ap, unsigned int device); Most drivers for taskfile-based hardware use - ata_std_dev_select() for this hook. Controllers which do not - support second drives on a port (such as SATA contollers) will - use ata_noop_dev_select(). + ata_sff_dev_select() for this hook. @@ -441,13 +428,13 @@ void (*irq_clear) (struct ata_port *); to struct ata_host_set. - Most legacy IDE drivers use ata_interrupt() for the + Most legacy IDE drivers use ata_sff_interrupt() for the irq_handler hook, which scans all ports in the host_set, determines which queued command was active (if any), and calls - ata_host_intr(ap,qc). + ata_sff_host_intr(ap,qc). - Most legacy IDE drivers use ata_bmdma_irq_clear() for the + Most legacy IDE drivers use ata_sff_irq_clear() for the irq_clear() hook, which simply clears the interrupt and error flags in the DMA status register. @@ -496,10 +483,6 @@ void (*host_stop) (struct ata_host_set *host_set); data from port at this time. - Many drivers use ata_port_stop() as this hook, which frees the - PRD table. - - ->host_stop() is called after all ->port_stop() calls have completed. The hook must finalize hardware shutdown, release DMA and other resources, etc. diff --git a/trunk/Documentation/i2c/writing-clients b/trunk/Documentation/i2c/writing-clients index 3219ee0dbfef..5ebf5af1d716 100644 --- a/trunk/Documentation/i2c/writing-clients +++ b/trunk/Documentation/i2c/writing-clients @@ -74,6 +74,11 @@ structure at all. You should use this to keep device-specific data. /* retrieve the value */ void *i2c_get_clientdata(const struct i2c_client *client); +Note that starting with kernel 2.6.34, you don't have to set the `data' field +to NULL in remove() or if probe() failed anymore. The i2c-core does this +automatically on these occasions. Those are also the only times the core will +touch this field. + Accessing the client ==================== diff --git a/trunk/Documentation/input/elantech.txt b/trunk/Documentation/input/elantech.txt index a10c3b6ba7c4..56941ae1f5db 100644 --- a/trunk/Documentation/input/elantech.txt +++ b/trunk/Documentation/input/elantech.txt @@ -333,14 +333,14 @@ byte 0: byte 1: bit 7 6 5 4 3 2 1 0 - x15 x14 x13 x12 x11 x10 x9 x8 + . . . . . x10 x9 x8 byte 2: bit 7 6 5 4 3 2 1 0 x7 x6 x5 x4 x4 x2 x1 x0 - x15..x0 = absolute x value (horizontal) + x10..x0 = absolute x value (horizontal) byte 3: @@ -350,14 +350,14 @@ byte 3: byte 4: bit 7 6 5 4 3 2 1 0 - y15 y14 y13 y12 y11 y10 y8 y8 + . . . . . . y9 y8 byte 5: bit 7 6 5 4 3 2 1 0 y7 y6 y5 y4 y3 y2 y1 y0 - y15..y0 = absolute y value (vertical) + y9..y0 = absolute y value (vertical) 4.2.2 Two finger touch diff --git a/trunk/Documentation/kprobes.txt b/trunk/Documentation/kprobes.txt index 61c291cddf18..2f9115c0ae62 100644 --- a/trunk/Documentation/kprobes.txt +++ b/trunk/Documentation/kprobes.txt @@ -165,8 +165,8 @@ the user entry_handler invocation is also skipped. 1.4 How Does Jump Optimization Work? -If your kernel is built with CONFIG_OPTPROBES=y (currently this flag -is automatically set 'y' on x86/x86-64, non-preemptive kernel) and +If you configured your kernel with CONFIG_OPTPROBES=y (currently +this option is supported on x86/x86-64, non-preemptive kernel) and the "debug.kprobes_optimization" kernel parameter is set to 1 (see sysctl(8)), Kprobes tries to reduce probe-hit overhead by using a jump instruction instead of a breakpoint instruction at each probepoint. @@ -271,6 +271,8 @@ tweak the kernel's execution path, you need to suppress optimization, using one of the following techniques: - Specify an empty function for the kprobe's post_handler or break_handler. or +- Config CONFIG_OPTPROBES=n. + or - Execute 'sysctl -w debug.kprobes_optimization=n' 2. Architectures Supported @@ -305,6 +307,10 @@ it useful to "Compile the kernel with debug info" (CONFIG_DEBUG_INFO), so you can use "objdump -d -l vmlinux" to see the source-to-object code mapping. +If you want to reduce probing overhead, set "Kprobes jump optimization +support" (CONFIG_OPTPROBES) to "y". You can find this option under the +"Kprobes" line. + 4. API Reference The Kprobes API includes a "register" function and an "unregister" diff --git a/trunk/Documentation/spi/spidev_test.c b/trunk/Documentation/spi/spidev_test.c index 10abd3773e49..16feda901469 100644 --- a/trunk/Documentation/spi/spidev_test.c +++ b/trunk/Documentation/spi/spidev_test.c @@ -58,7 +58,7 @@ static void transfer(int fd) }; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret == 1) + if (ret < 1) pabort("can't send spi message"); for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { diff --git a/trunk/Documentation/trace/kprobetrace.txt b/trunk/Documentation/trace/kprobetrace.txt index ec94748ae65b..a9100b28eb84 100644 --- a/trunk/Documentation/trace/kprobetrace.txt +++ b/trunk/Documentation/trace/kprobetrace.txt @@ -40,9 +40,7 @@ Synopsis of kprobe_events $stack : Fetch stack address. $retval : Fetch return value.(*) +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**) - NAME=FETCHARG : Set NAME as the argument name of FETCHARG. - FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types - (u8/u16/u32/u64/s8/s16/s32/s64) are supported. + NAME=FETCHARG: Set NAME as the argument name of FETCHARG. (*) only for return probe. (**) this is useful for fetching a field of data structures. diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 5085c90a6ec8..d5b0b1b6dc52 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -4353,13 +4353,13 @@ M: Paul Mackerras M: Ingo Molnar M: Arnaldo Carvalho de Melo S: Supported -F: kernel/perf_event*.c +F: kernel/perf_event.c F: include/linux/perf_event.h -F: arch/*/kernel/perf_event*.c -F: arch/*/kernel/*/perf_event*.c -F: arch/*/kernel/*/*/perf_event*.c +F: arch/*/kernel/perf_event.c +F: arch/*/kernel/*/perf_event.c +F: arch/*/kernel/*/*/perf_event.c F: arch/*/include/asm/perf_event.h -F: arch/*/lib/perf_event*.c +F: arch/*/lib/perf_event.c F: arch/*/kernel/perf_callchain.c F: tools/perf/ diff --git a/trunk/arch/Kconfig b/trunk/arch/Kconfig index acda512da2e2..e5eb1337a537 100644 --- a/trunk/arch/Kconfig +++ b/trunk/arch/Kconfig @@ -42,10 +42,15 @@ config KPROBES If in doubt, say "N". config OPTPROBES - def_bool y - depends on KPROBES && HAVE_OPTPROBES + bool "Kprobes jump optimization support (EXPERIMENTAL)" + default y + depends on KPROBES depends on !PREEMPT + depends on HAVE_OPTPROBES select KALLSYMS_ALL + help + This option will allow kprobes to optimize breakpoint to + a jump for reducing its overhead. config HAVE_EFFICIENT_UNALIGNED_ACCESS bool @@ -137,17 +142,6 @@ config HAVE_HW_BREAKPOINT bool depends on PERF_EVENTS -config HAVE_MIXED_BREAKPOINTS_REGS - bool - depends on HAVE_HW_BREAKPOINT - help - Depending on the arch implementation of hardware breakpoints, - some of them have separate registers for data and instruction - breakpoints addresses, others have mixed registers to store - them but define the access type in a control register. - Select this option if your arch implements breakpoints under the - latter fashion. - config HAVE_USER_RETURN_NOTIFIER bool diff --git a/trunk/arch/arm/plat-omap/include/plat/usb.h b/trunk/arch/arm/plat-omap/include/plat/usb.h index 568578db93b6..876ca8d5e927 100644 --- a/trunk/arch/arm/plat-omap/include/plat/usb.h +++ b/trunk/arch/arm/plat-omap/include/plat/usb.h @@ -46,7 +46,7 @@ struct ehci_hcd_omap_platform_data { struct omap_musb_board_data { u8 interface_type; u8 mode; - u8 power; + u16 power; }; enum musb_interface {MUSB_INTERFACE_ULPI, MUSB_INTERFACE_UTMI}; diff --git a/trunk/arch/mips/Kconfig b/trunk/arch/mips/Kconfig index 29e86923d1bf..7e6fd1cbd3f8 100644 --- a/trunk/arch/mips/Kconfig +++ b/trunk/arch/mips/Kconfig @@ -49,7 +49,7 @@ config AR7 family: TNETD7100, 7200 and 7300. config BCM47XX - bool "BCM47XX based boards" + bool "Broadcom BCM47XX based boards" select CEVT_R4K select CSRC_R4K select DMA_NONCOHERENT @@ -509,6 +509,7 @@ config SIBYTE_SWARM bool "Sibyte BCM91250A-SWARM" select BOOT_ELF32 select DMA_COHERENT + select HAVE_PATA_PLATFORM select NR_CPUS_DEFAULT_2 select SIBYTE_SB1250 select SWAP_IO_SPACE @@ -523,6 +524,7 @@ config SIBYTE_LITTLESUR depends on EXPERIMENTAL select BOOT_ELF32 select DMA_COHERENT + select HAVE_PATA_PLATFORM select NR_CPUS_DEFAULT_2 select SIBYTE_SB1250 select SWAP_IO_SPACE @@ -1305,6 +1307,33 @@ config CPU_CAVIUM_OCTEON endchoice +if CPU_LOONGSON2F +config CPU_NOP_WORKAROUNDS + bool + +config CPU_JUMP_WORKAROUNDS + bool + +config CPU_LOONGSON2F_WORKAROUNDS + bool "Loongson 2F Workarounds" + default y + select CPU_NOP_WORKAROUNDS + select CPU_JUMP_WORKAROUNDS + help + Loongson 2F01 / 2F02 processors have the NOP & JUMP issues which + require workarounds. Without workarounds the system may hang + unexpectedly. For more information please refer to the gas + -mfix-loongson2f-nop and -mfix-loongson2f-jump options. + + Loongson 2F03 and later have fixed these issues and no workarounds + are needed. The workarounds have no significant side effect on them + but may decrease the performance of the system so this option should + be disabled unless the kernel is intended to be run on 2F01 or 2F02 + systems. + + If unsure, please say Y. +endif # CPU_LOONGSON2F + config SYS_SUPPORTS_ZBOOT bool select HAVE_KERNEL_GZIP diff --git a/trunk/arch/mips/Makefile b/trunk/arch/mips/Makefile index 2f2eac233322..0b9c01add0a0 100644 --- a/trunk/arch/mips/Makefile +++ b/trunk/arch/mips/Makefile @@ -136,6 +136,19 @@ cflags-$(CONFIG_CPU_LOONGSON2E) += \ $(call cc-option,-march=loongson2e,-march=r4600) cflags-$(CONFIG_CPU_LOONGSON2F) += \ $(call cc-option,-march=loongson2f,-march=r4600) +# enable the workarounds for loongson2f +ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS + ifeq ($(call as-option,-Wa$(comma)-mfix-loongson2f-nop,),) + $(error only binutils >= 2.20.2 have needed option -mfix-loongson2f-nop) + else + cflags-$(CONFIG_CPU_NOP_WORKAROUNDS) += -Wa$(comma)-mfix-loongson2f-nop + endif + ifeq ($(call as-option,-Wa$(comma)-mfix-loongson2f-jump,),) + $(error only binutils >= 2.20.2 have needed option -mfix-loongson2f-jump) + else + cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-loongson2f-jump + endif +endif cflags-$(CONFIG_CPU_MIPS32_R1) += $(call cc-option,-march=mips32,-mips32 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS32) \ -Wa,-mips32 -Wa,--trap diff --git a/trunk/arch/mips/alchemy/devboards/db1200/setup.c b/trunk/arch/mips/alchemy/devboards/db1200/setup.c index be7e92ea01f3..887619547553 100644 --- a/trunk/arch/mips/alchemy/devboards/db1200/setup.c +++ b/trunk/arch/mips/alchemy/devboards/db1200/setup.c @@ -66,12 +66,16 @@ static int __init db1200_arch_init(void) set_irq_type(AU1200_GPIO7_INT, IRQF_TRIGGER_LOW); bcsr_init_irq(DB1200_INT_BEGIN, DB1200_INT_END, AU1200_GPIO7_INT); - /* do not autoenable these: CPLD has broken edge int handling, - * and the CD handler setup requires manual enabling to work - * around that. + /* insert/eject pairs: one of both is always screaming. To avoid + * issues they must not be automatically enabled when initially + * requested. */ irq_to_desc(DB1200_SD0_INSERT_INT)->status |= IRQ_NOAUTOEN; irq_to_desc(DB1200_SD0_EJECT_INT)->status |= IRQ_NOAUTOEN; + irq_to_desc(DB1200_PC0_INSERT_INT)->status |= IRQ_NOAUTOEN; + irq_to_desc(DB1200_PC0_EJECT_INT)->status |= IRQ_NOAUTOEN; + irq_to_desc(DB1200_PC1_INSERT_INT)->status |= IRQ_NOAUTOEN; + irq_to_desc(DB1200_PC1_EJECT_INT)->status |= IRQ_NOAUTOEN; return 0; } diff --git a/trunk/arch/mips/configs/bcm63xx_defconfig b/trunk/arch/mips/configs/bcm63xx_defconfig index 7fee0273c829..6389ca0fdc6c 100644 --- a/trunk/arch/mips/configs/bcm63xx_defconfig +++ b/trunk/arch/mips/configs/bcm63xx_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.30-rc6 -# Sun May 31 20:17:18 2009 +# Linux kernel version: 2.6.34-rc2 +# Tue Mar 23 10:36:32 2010 # CONFIG_MIPS=y @@ -9,13 +9,14 @@ CONFIG_MIPS=y # Machine selection # # CONFIG_MACH_ALCHEMY is not set +# CONFIG_AR7 is not set # CONFIG_BCM47XX is not set CONFIG_BCM63XX=y # CONFIG_MIPS_COBALT is not set # CONFIG_MACH_DECSTATION is not set # CONFIG_MACH_JAZZ is not set # CONFIG_LASAT is not set -# CONFIG_LEMOTE_FULONG is not set +# CONFIG_MACH_LOONGSON is not set # CONFIG_MIPS_MALTA is not set # CONFIG_MIPS_SIM is not set # CONFIG_NEC_MARKEINS is not set @@ -26,6 +27,7 @@ CONFIG_BCM63XX=y # CONFIG_PNX8550_STB810 is not set # CONFIG_PMC_MSP is not set # CONFIG_PMC_YOSEMITE is not set +# CONFIG_POWERTV is not set # CONFIG_SGI_IP22 is not set # CONFIG_SGI_IP27 is not set # CONFIG_SGI_IP28 is not set @@ -45,13 +47,17 @@ CONFIG_BCM63XX=y # CONFIG_WR_PPMC is not set # CONFIG_CAVIUM_OCTEON_SIMULATOR is not set # CONFIG_CAVIUM_OCTEON_REFERENCE_BOARD is not set +# CONFIG_ALCHEMY_GPIO_INDIRECT is not set # # CPU support # +CONFIG_BCM63XX_CPU_6338=y +CONFIG_BCM63XX_CPU_6345=y CONFIG_BCM63XX_CPU_6348=y CONFIG_BCM63XX_CPU_6358=y CONFIG_BOARD_BCM963XX=y +CONFIG_LOONGSON_UART_BASE=y CONFIG_RWSEM_GENERIC_SPINLOCK=y # CONFIG_ARCH_HAS_ILOG2_U32 is not set # CONFIG_ARCH_HAS_ILOG2_U64 is not set @@ -69,10 +75,8 @@ CONFIG_CEVT_R4K=y CONFIG_CSRC_R4K_LIB=y CONFIG_CSRC_R4K=y CONFIG_DMA_NONCOHERENT=y -CONFIG_DMA_NEED_PCI_MAP_STATE=y -CONFIG_EARLY_PRINTK=y +CONFIG_NEED_DMA_MAP_STATE=y CONFIG_SYS_HAS_EARLY_PRINTK=y -# CONFIG_HOTPLUG_CPU is not set # CONFIG_NO_IOPORT is not set CONFIG_GENERIC_GPIO=y CONFIG_CPU_BIG_ENDIAN=y @@ -85,7 +89,8 @@ CONFIG_MIPS_L1_CACHE_SHIFT=5 # # CPU selection # -# CONFIG_CPU_LOONGSON2 is not set +# CONFIG_CPU_LOONGSON2E is not set +# CONFIG_CPU_LOONGSON2F is not set CONFIG_CPU_MIPS32_R1=y # CONFIG_CPU_MIPS32_R2 is not set # CONFIG_CPU_MIPS64_R1 is not set @@ -128,7 +133,7 @@ CONFIG_CPU_HAS_PREFETCH=y CONFIG_MIPS_MT_DISABLED=y # CONFIG_MIPS_MT_SMP is not set # CONFIG_MIPS_MT_SMTC is not set -CONFIG_CPU_HAS_LLSC=y +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set CONFIG_CPU_HAS_SYNC=y CONFIG_GENERIC_HARDIRQS=y CONFIG_GENERIC_IRQ_PROBE=y @@ -146,9 +151,8 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -CONFIG_UNEVICTABLE_LRU=y -CONFIG_HAVE_MLOCK=y -CONFIG_HAVE_MLOCKED_PAGE_BIT=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 CONFIG_TICK_ONESHOT=y CONFIG_NO_HZ=y # CONFIG_HIGH_RES_TIMERS is not set @@ -170,6 +174,7 @@ CONFIG_PREEMPT_NONE=y CONFIG_LOCKDEP_SUPPORT=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y # # General setup @@ -189,15 +194,12 @@ CONFIG_LOCALVERSION="" # # RCU Subsystem # -CONFIG_CLASSIC_RCU=y # CONFIG_TREE_RCU is not set -# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_PREEMPT_RCU is not set +CONFIG_TINY_RCU=y # CONFIG_TREE_RCU_TRACE is not set -# CONFIG_PREEMPT_RCU_TRACE is not set # CONFIG_IKCONFIG is not set CONFIG_LOG_BUF_SHIFT=17 -# CONFIG_GROUP_SCHED is not set -# CONFIG_CGROUPS is not set CONFIG_SYSFS_DEPRECATED=y CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_RELAY is not set @@ -205,11 +207,11 @@ CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_BLK_DEV_INITRD is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y CONFIG_EMBEDDED=y CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_EXTRA_PASS is not set -# CONFIG_STRIP_ASM_SYMS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y @@ -223,6 +225,10 @@ CONFIG_BASE_FULL=y # CONFIG_EVENTFD is not set # CONFIG_SHMEM is not set # CONFIG_AIO is not set + +# +# Kernel Performance Events And Counters +# # CONFIG_VM_EVENT_COUNTERS is not set CONFIG_PCI_QUIRKS=y # CONFIG_SLUB_DEBUG is not set @@ -231,14 +237,17 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set -# CONFIG_MARKERS is not set CONFIG_HAVE_OPROFILE=y + +# +# GCOV-based kernel profiling +# # CONFIG_SLOW_WORK is not set -# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y CONFIG_BASE_SMALL=0 # CONFIG_MODULES is not set CONFIG_BLOCK=y -# CONFIG_LBD is not set +CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set @@ -246,14 +255,41 @@ CONFIG_BLOCK=y # IO Schedulers # CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set # CONFIG_IOSCHED_DEADLINE is not set # CONFIG_IOSCHED_CFQ is not set -# CONFIG_DEFAULT_AS is not set # CONFIG_DEFAULT_DEADLINE is not set # CONFIG_DEFAULT_CFQ is not set CONFIG_DEFAULT_NOOP=y CONFIG_DEFAULT_IOSCHED="noop" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set # CONFIG_FREEZER is not set # @@ -263,15 +299,12 @@ CONFIG_HW_HAS_PCI=y CONFIG_PCI=y CONFIG_PCI_DOMAINS=y # CONFIG_ARCH_SUPPORTS_MSI is not set -# CONFIG_PCI_LEGACY is not set # CONFIG_PCI_STUB is not set # CONFIG_PCI_IOV is not set CONFIG_MMU=y CONFIG_PCCARD=y -# CONFIG_PCMCIA_DEBUG is not set CONFIG_PCMCIA=y CONFIG_PCMCIA_LOAD_CIS=y -CONFIG_PCMCIA_IOCTL=y CONFIG_CARDBUS=y # @@ -295,6 +328,7 @@ CONFIG_TRAD_SIGNALS=y # # Power management options # +CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_SUSPEND_POSSIBLE=y # CONFIG_PM is not set CONFIG_NET=y @@ -333,6 +367,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_NETFILTER is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set # CONFIG_TIPC is not set # CONFIG_ATM is not set # CONFIG_BRIDGE is not set @@ -347,6 +382,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set # CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set # CONFIG_NET_SCHED is not set # CONFIG_DCB is not set @@ -359,7 +395,27 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_IRDA is not set # CONFIG_BT is not set # CONFIG_AF_RXRPC is not set -# CONFIG_WIRELESS is not set +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set +CONFIG_MAC80211=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +# CONFIG_MAC80211_RC_DEFAULT_PID is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUG_MENU is not set # CONFIG_WIMAX is not set # CONFIG_RFKILL is not set # CONFIG_NET_9P is not set @@ -471,6 +527,7 @@ CONFIG_HAVE_IDE=y # # SCSI device support # +CONFIG_SCSI_MOD=y # CONFIG_RAID_ATTRS is not set # CONFIG_SCSI is not set # CONFIG_SCSI_DMA is not set @@ -484,13 +541,16 @@ CONFIG_HAVE_IDE=y # # -# Enable only one of the two stacks, unless you know what you are doing +# You can enable one or both FireWire driver stacks. +# + +# +# The newer stack is recommended. # # CONFIG_FIREWIRE is not set # CONFIG_IEEE1394 is not set # CONFIG_I2O is not set CONFIG_NETDEVICES=y -CONFIG_COMPAT_NET_DEV_OPS=y # CONFIG_DUMMY is not set # CONFIG_BONDING is not set # CONFIG_MACVLAN is not set @@ -529,6 +589,7 @@ CONFIG_MII=y # CONFIG_SMC91X is not set # CONFIG_DM9000 is not set # CONFIG_ETHOC is not set +# CONFIG_SMSC911X is not set # CONFIG_DNET is not set # CONFIG_NET_TULIP is not set # CONFIG_HP100 is not set @@ -541,17 +602,48 @@ CONFIG_MII=y # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set # CONFIG_NET_PCI is not set # CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851_MLL is not set # CONFIG_ATL2 is not set CONFIG_BCM63XX_ENET=y # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set # CONFIG_TR is not set - -# -# Wireless LAN -# -# CONFIG_WLAN_PRE80211 is not set -# CONFIG_WLAN_80211 is not set +CONFIG_WLAN=y +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AIRO_CS is not set +# CONFIG_PCMCIA_WL3501 is not set +# CONFIG_PRISM54 is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_ADM8211 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_MWL8K is not set +# CONFIG_ATH_COMMON is not set +CONFIG_B43=y +CONFIG_B43_PCI_AUTOSELECT=y +CONFIG_B43_PCICORE_AUTOSELECT=y +# CONFIG_B43_PCMCIA is not set +CONFIG_B43_PIO=y +# CONFIG_B43_PHY_LP is not set +CONFIG_B43_LEDS=y +# CONFIG_B43_DEBUG is not set +# CONFIG_B43LEGACY is not set +# CONFIG_HOSTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2200 is not set +# CONFIG_IWLWIFI is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_P54_COMMON is not set +# CONFIG_RT2X00 is not set +# CONFIG_WL12XX is not set +# CONFIG_ZD1211RW is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -574,6 +666,7 @@ CONFIG_BCM63XX_ENET=y # CONFIG_NETCONSOLE is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_VMXNET3 is not set # CONFIG_ISDN is not set # CONFIG_PHONE is not set @@ -607,6 +700,7 @@ CONFIG_BCM63XX_ENET=y CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_TIMBERDALE is not set CONFIG_SERIAL_BCM63XX=y CONFIG_SERIAL_BCM63XX_CONSOLE=y # CONFIG_UNIX98_PTYS is not set @@ -629,6 +723,11 @@ CONFIG_LEGACY_PTY_COUNT=256 CONFIG_DEVPORT=y # CONFIG_I2C is not set # CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y # CONFIG_GPIO_SYSFS is not set @@ -636,6 +735,8 @@ CONFIG_GPIOLIB=y # # Memory mapped GPIO expanders: # +# CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_SCH is not set # # I2C GPIO expanders: @@ -644,16 +745,21 @@ CONFIG_GPIOLIB=y # # PCI GPIO expanders: # +# CONFIG_GPIO_CS5535 is not set # CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_LANGWELL is not set # # SPI GPIO expanders: # + +# +# AC97 GPIO expanders: +# # CONFIG_W1 is not set # CONFIG_POWER_SUPPLY is not set # CONFIG_HWMON is not set # CONFIG_THERMAL is not set -# CONFIG_THERMAL_HWMON is not set # CONFIG_WATCHDOG is not set CONFIG_SSB_POSSIBLE=y @@ -662,15 +768,16 @@ CONFIG_SSB_POSSIBLE=y # CONFIG_SSB=y CONFIG_SSB_SPROM=y +CONFIG_SSB_BLOCKIO=y CONFIG_SSB_PCIHOST_POSSIBLE=y CONFIG_SSB_PCIHOST=y -# CONFIG_SSB_B43_PCI_BRIDGE is not set +CONFIG_SSB_B43_PCI_BRIDGE=y CONFIG_SSB_PCMCIAHOST_POSSIBLE=y # CONFIG_SSB_PCMCIAHOST is not set # CONFIG_SSB_SILENT is not set # CONFIG_SSB_DEBUG is not set CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y -# CONFIG_SSB_DRIVER_PCICORE is not set +CONFIG_SSB_DRIVER_PCICORE=y # CONFIG_SSB_DRIVER_MIPS is not set # @@ -680,27 +787,15 @@ CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y # CONFIG_MFD_SM501 is not set # CONFIG_HTC_PASIC3 is not set # CONFIG_MFD_TMIO is not set +# CONFIG_MFD_TIMBERDALE is not set +# CONFIG_LPC_SCH is not set # CONFIG_REGULATOR is not set - -# -# Multimedia devices -# - -# -# Multimedia core support -# -# CONFIG_VIDEO_DEV is not set -# CONFIG_DVB_CORE is not set -# CONFIG_VIDEO_MEDIA is not set - -# -# Multimedia drivers -# -# CONFIG_DAB is not set +# CONFIG_MEDIA_SUPPORT is not set # # Graphics support # +# CONFIG_VGA_ARB is not set # CONFIG_DRM is not set # CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set @@ -710,11 +805,7 @@ CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y # # Display device support # -CONFIG_DISPLAY_SUPPORT=y - -# -# Display hardware drivers -# +# CONFIG_DISPLAY_SUPPORT is not set # CONFIG_SOUND is not set CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y @@ -741,13 +832,14 @@ CONFIG_USB=y # USB Host Controller Drivers # # CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_XHCI_HCD is not set CONFIG_USB_EHCI_HCD=y # CONFIG_USB_EHCI_ROOT_HUB_TT is not set # CONFIG_USB_EHCI_TT_NEWSCHED is not set -CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y # CONFIG_USB_OXU210HP_HCD is not set # CONFIG_USB_ISP116X_HCD is not set # CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set CONFIG_USB_OHCI_HCD=y # CONFIG_USB_OHCI_HCD_SSB is not set CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y @@ -796,7 +888,6 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # CONFIG_USB_RIO500 is not set # CONFIG_USB_LEGOTOWER is not set # CONFIG_USB_LCD is not set -# CONFIG_USB_BERRY_CHARGE is not set # CONFIG_USB_LED is not set # CONFIG_USB_CYPRESS_CY7C63 is not set # CONFIG_USB_CYTHERM is not set @@ -807,8 +898,8 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # CONFIG_USB_LD is not set # CONFIG_USB_TRANCEVIBRATOR is not set # CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set # CONFIG_USB_ISIGHTFW is not set -# CONFIG_USB_VST is not set # CONFIG_USB_GADGET is not set # @@ -819,7 +910,29 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # CONFIG_UWB is not set # CONFIG_MMC is not set # CONFIG_MEMSTICK is not set -# CONFIG_NEW_LEDS is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y + +# +# iptables trigger is under Netfilter config (LED target) +# # CONFIG_ACCESSIBILITY is not set # CONFIG_INFINIBAND is not set CONFIG_RTC_LIB=y @@ -827,6 +940,10 @@ CONFIG_RTC_LIB=y # CONFIG_DMADEVICES is not set # CONFIG_AUXDISPLAY is not set # CONFIG_UIO is not set + +# +# TI VLYNQ +# # CONFIG_STAGING is not set # @@ -838,12 +955,16 @@ CONFIG_RTC_LIB=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_FS_POSIX_ACL is not set -# CONFIG_FILE_LOCKING is not set # CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set # CONFIG_OCFS2_FS is not set # CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FILE_LOCKING is not set +CONFIG_FSNOTIFY=y # CONFIG_DNOTIFY is not set # CONFIG_INOTIFY is not set +CONFIG_INOTIFY_USER=y # CONFIG_QUOTA is not set # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set @@ -875,8 +996,6 @@ CONFIG_PROC_KCORE=y CONFIG_PROC_SYSCTL=y CONFIG_PROC_PAGE_MONITOR=y CONFIG_SYSFS=y -CONFIG_TMPFS=y -# CONFIG_TMPFS_POSIX_ACL is not set # CONFIG_HUGETLB_PAGE is not set # CONFIG_CONFIGFS_FS is not set CONFIG_MISC_FILESYSTEMS=y @@ -888,6 +1007,7 @@ CONFIG_MISC_FILESYSTEMS=y # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set # CONFIG_SQUASHFS is not set # CONFIG_VXFS_FS is not set @@ -898,7 +1018,6 @@ CONFIG_MISC_FILESYSTEMS=y # CONFIG_ROMFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set -# CONFIG_NILFS2_FS is not set # CONFIG_NETWORK_FILESYSTEMS is not set # @@ -906,7 +1025,46 @@ CONFIG_MISC_FILESYSTEMS=y # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y -# CONFIG_NLS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set # CONFIG_DLM is not set # @@ -918,29 +1076,23 @@ CONFIG_ENABLE_WARN_DEPRECATED=y CONFIG_ENABLE_MUST_CHECK=y CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set # CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set # CONFIG_DEBUG_KERNEL is not set # CONFIG_DEBUG_MEMORY_INIT is not set -# CONFIG_RCU_CPU_STALL_DETECTOR is not set CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_TRACING_SUPPORT=y - -# -# Tracers -# -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_SCHED_TRACER is not set -# CONFIG_CONTEXT_SWITCH_TRACER is not set -# CONFIG_EVENT_TRACER is not set -# CONFIG_BOOT_TRACER is not set -# CONFIG_TRACE_BRANCH_PROFILING is not set -# CONFIG_KMEMTRACE is not set -# CONFIG_WORKQUEUE_TRACER is not set -# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y +CONFIG_EARLY_PRINTK=y CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="console=ttyS0,115200" # CONFIG_CMDLINE_OVERRIDE is not set @@ -951,8 +1103,108 @@ CONFIG_CMDLINE="console=ttyS0,115200" # CONFIG_KEYS is not set # CONFIG_SECURITY is not set # CONFIG_SECURITYFS is not set -# CONFIG_SECURITY_FILE_CAPABILITIES is not set -# CONFIG_CRYPTO is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_HW is not set # CONFIG_BINARY_PRINTF is not set # diff --git a/trunk/arch/mips/include/asm/cmpxchg.h b/trunk/arch/mips/include/asm/cmpxchg.h index ed9aaaaf0749..2d28017e95d0 100644 --- a/trunk/arch/mips/include/asm/cmpxchg.h +++ b/trunk/arch/mips/include/asm/cmpxchg.h @@ -16,7 +16,7 @@ ({ \ __typeof(*(m)) __ret; \ \ - if (kernel_uses_llsc && R10000_LLSC_WAR) { \ + if (kernel_uses_llsc && R10000_LLSC_WAR) { \ __asm__ __volatile__( \ " .set push \n" \ " .set noat \n" \ diff --git a/trunk/arch/mips/include/asm/mach-loongson/loongson.h b/trunk/arch/mips/include/asm/mach-loongson/loongson.h index 1cf7b1401ee4..fcdbe3a4ce1f 100644 --- a/trunk/arch/mips/include/asm/mach-loongson/loongson.h +++ b/trunk/arch/mips/include/asm/mach-loongson/loongson.h @@ -307,7 +307,7 @@ extern unsigned long _loongson_addrwincfg_base; */ #define LOONGSON_ADDRWIN_CFG(s, d, w, src, dst, size) do {\ s##_WIN##w##_BASE = (src); \ - s##_WIN##w##_MMAP = (src) | ADDRWIN_MAP_DST_##d; \ + s##_WIN##w##_MMAP = (dst) | ADDRWIN_MAP_DST_##d; \ s##_WIN##w##_MASK = ~(size-1); \ } while (0) diff --git a/trunk/arch/mips/include/asm/pgtable-64.h b/trunk/arch/mips/include/asm/pgtable-64.h index 26dc69d792a6..1be4b0fa30da 100644 --- a/trunk/arch/mips/include/asm/pgtable-64.h +++ b/trunk/arch/mips/include/asm/pgtable-64.h @@ -120,9 +120,14 @@ #endif #define FIRST_USER_ADDRESS 0UL -#define VMALLOC_START MAP_BASE +/* + * TLB refill handlers also map the vmalloc area into xuseg. Avoid + * the first couple of pages so NULL pointer dereferences will still + * reliably trap. + */ +#define VMALLOC_START (MAP_BASE + (2 * PAGE_SIZE)) #define VMALLOC_END \ - (VMALLOC_START + \ + (MAP_BASE + \ min(PTRS_PER_PGD * PTRS_PER_PMD * PTRS_PER_PTE * PAGE_SIZE, \ (1UL << cpu_vmbits)) - (1UL << 32)) diff --git a/trunk/arch/mips/include/asm/ptrace.h b/trunk/arch/mips/include/asm/ptrace.h index ce47118e52b7..cdc6a46efd98 100644 --- a/trunk/arch/mips/include/asm/ptrace.h +++ b/trunk/arch/mips/include/asm/ptrace.h @@ -142,9 +142,9 @@ extern int ptrace_set_watch_regs(struct task_struct *child, extern asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit); -extern NORET_TYPE void die(const char *, const struct pt_regs *) ATTRIB_NORET; +extern NORET_TYPE void die(const char *, struct pt_regs *) ATTRIB_NORET; -static inline void die_if_kernel(const char *str, const struct pt_regs *regs) +static inline void die_if_kernel(const char *str, struct pt_regs *regs) { if (unlikely(!user_mode(regs))) die(str, regs); diff --git a/trunk/arch/mips/include/asm/stackframe.h b/trunk/arch/mips/include/asm/stackframe.h index c8419129e770..58730c5ce4bf 100644 --- a/trunk/arch/mips/include/asm/stackframe.h +++ b/trunk/arch/mips/include/asm/stackframe.h @@ -121,7 +121,7 @@ .endm #else .macro get_saved_sp /* Uniprocessor variation */ -#ifdef CONFIG_CPU_LOONGSON2F +#ifdef CONFIG_CPU_JUMP_WORKAROUNDS /* * Clear BTB (branch target buffer), forbid RAS (return address * stack) to workaround the Out-of-order Issue in Loongson2F diff --git a/trunk/arch/mips/include/asm/uasm.h b/trunk/arch/mips/include/asm/uasm.h index 11a8b5252549..697e40c06497 100644 --- a/trunk/arch/mips/include/asm/uasm.h +++ b/trunk/arch/mips/include/asm/uasm.h @@ -167,6 +167,24 @@ static inline void __cpuinit uasm_l##lb(struct uasm_label **lab, u32 *addr) \ #define uasm_i_ssnop(buf) uasm_i_sll(buf, 0, 0, 1) #define uasm_i_ehb(buf) uasm_i_sll(buf, 0, 0, 3) +static inline void uasm_i_dsrl_safe(u32 **p, unsigned int a1, + unsigned int a2, unsigned int a3) +{ + if (a3 < 32) + uasm_i_dsrl(p, a1, a2, a3); + else + uasm_i_dsrl32(p, a1, a2, a3 - 32); +} + +static inline void uasm_i_dsll_safe(u32 **p, unsigned int a1, + unsigned int a2, unsigned int a3) +{ + if (a3 < 32) + uasm_i_dsll(p, a1, a2, a3); + else + uasm_i_dsll32(p, a1, a2, a3 - 32); +} + /* Handle relocations. */ struct uasm_reloc { u32 *addr; diff --git a/trunk/arch/mips/jazz/setup.c b/trunk/arch/mips/jazz/setup.c index 7043f6b9ff3c..0d0f054a02f4 100644 --- a/trunk/arch/mips/jazz/setup.c +++ b/trunk/arch/mips/jazz/setup.c @@ -76,15 +76,9 @@ void __init plat_mem_setup(void) #ifdef CONFIG_VT screen_info = (struct screen_info) { - 0, 0, /* orig-x, orig-y */ - 0, /* unused */ - 0, /* orig_video_page */ - 0, /* orig_video_mode */ - 160, /* orig_video_cols */ - 0, 0, 0, /* unused, ega_bx, unused */ - 64, /* orig_video_lines */ - 0, /* orig_video_isVGA */ - 16 /* orig_video_points */ + .orig_video_cols = 160, + .orig_video_lines = 64, + .orig_video_points = 16, }; #endif diff --git a/trunk/arch/mips/kernel/traps.c b/trunk/arch/mips/kernel/traps.c index 1a4dd657ccb9..d612c6dcb746 100644 --- a/trunk/arch/mips/kernel/traps.c +++ b/trunk/arch/mips/kernel/traps.c @@ -352,9 +352,10 @@ void show_registers(const struct pt_regs *regs) static DEFINE_SPINLOCK(die_lock); -void __noreturn die(const char * str, const struct pt_regs * regs) +void __noreturn die(const char * str, struct pt_regs * regs) { static int die_counter; + int sig = SIGSEGV; #ifdef CONFIG_MIPS_MT_SMTC unsigned long dvpret = dvpe(); #endif /* CONFIG_MIPS_MT_SMTC */ @@ -365,6 +366,10 @@ void __noreturn die(const char * str, const struct pt_regs * regs) #ifdef CONFIG_MIPS_MT_SMTC mips_mt_regdump(dvpret); #endif /* CONFIG_MIPS_MT_SMTC */ + + if (notify_die(DIE_OOPS, str, regs, 0, current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) + sig = 0; + printk("%s[#%d]:\n", str, ++die_counter); show_registers(regs); add_taint(TAINT_DIE); @@ -379,7 +384,7 @@ void __noreturn die(const char * str, const struct pt_regs * regs) panic("Fatal exception"); } - do_exit(SIGSEGV); + do_exit(sig); } extern struct exception_table_entry __start___dbe_table[]; @@ -1557,12 +1562,7 @@ static char panic_null_cerr[] __cpuinitdata = void __cpuinit set_uncached_handler(unsigned long offset, void *addr, unsigned long size) { -#ifdef CONFIG_32BIT - unsigned long uncached_ebase = KSEG1ADDR(ebase); -#endif -#ifdef CONFIG_64BIT - unsigned long uncached_ebase = TO_UNCAC(ebase); -#endif + unsigned long uncached_ebase = CKSEG1ADDR(ebase); if (!addr) panic(panic_null_cerr); diff --git a/trunk/arch/mips/loongson/common/machtype.c b/trunk/arch/mips/loongson/common/machtype.c index 853f184b793e..81fbe6b73f91 100644 --- a/trunk/arch/mips/loongson/common/machtype.c +++ b/trunk/arch/mips/loongson/common/machtype.c @@ -24,7 +24,7 @@ static const char *system_types[] = { [MACH_LEMOTE_FL2F] "lemote-fuloong-2f-box", [MACH_LEMOTE_ML2F7] "lemote-mengloong-2f-7inches", [MACH_LEMOTE_YL2F89] "lemote-yeeloong-2f-8.9inches", - [MACH_DEXXON_GDIUM2F10] "dexxon-gidum-2f-10inches", + [MACH_DEXXON_GDIUM2F10] "dexxon-gdium-2f", [MACH_LEMOTE_NAS] "lemote-nas-2f", [MACH_LEMOTE_LL2F] "lemote-lynloong-2f", [MACH_LOONGSON_END] NULL, diff --git a/trunk/arch/mips/loongson/common/mem.c b/trunk/arch/mips/loongson/common/mem.c index ec2f7964a0b0..30eba6001205 100644 --- a/trunk/arch/mips/loongson/common/mem.c +++ b/trunk/arch/mips/loongson/common/mem.c @@ -75,7 +75,7 @@ pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long end = offset + size; if (__uncached_access(file, offset)) { - if (((uca_start && offset) >= uca_start) && + if (uca_start && (offset >= uca_start) && (end <= uca_end)) return __pgprot((pgprot_val(vma_prot) & ~_CACHE_MASK) | @@ -96,7 +96,7 @@ static int __init find_vga_mem_init(void) return 0; for_each_pci_dev(dev) { - if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { + if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { for (idx = 0; idx < PCI_NUM_RESOURCES; idx++) { r = &dev->resource[idx]; if (!r->start && r->end) diff --git a/trunk/arch/mips/loongson/common/reset.c b/trunk/arch/mips/loongson/common/reset.c index 4bd9c18b07a5..9e10d6225d9b 100644 --- a/trunk/arch/mips/loongson/common/reset.c +++ b/trunk/arch/mips/loongson/common/reset.c @@ -16,13 +16,31 @@ #include +static inline void loongson_reboot(void) +{ +#ifndef CONFIG_CPU_JUMP_WORKAROUNDS + ((void (*)(void))ioremap_nocache(LOONGSON_BOOT_BASE, 4)) (); +#else + void (*func)(void); + + func = (void *)ioremap_nocache(LOONGSON_BOOT_BASE, 4); + + __asm__ __volatile__( + " .set noat \n" + " jr %[func] \n" + " .set at \n" + : /* No outputs */ + : [func] "r" (func)); +#endif +} + static void loongson_restart(char *command) { /* do preparation for reboot */ mach_prepare_reboot(); /* reboot via jumping to boot base address */ - ((void (*)(void))ioremap_nocache(LOONGSON_BOOT_BASE, 4)) (); + loongson_reboot(); } static void loongson_poweroff(void) diff --git a/trunk/arch/mips/loongson/common/setup.c b/trunk/arch/mips/loongson/common/setup.c index 4cd2aa9a342c..27d826bc7103 100644 --- a/trunk/arch/mips/loongson/common/setup.c +++ b/trunk/arch/mips/loongson/common/setup.c @@ -41,15 +41,12 @@ void __init plat_mem_setup(void) conswitchp = &vga_con; screen_info = (struct screen_info) { - 0, 25, /* orig-x, orig-y */ - 0, /* unused */ - 0, /* orig-video-page */ - 0, /* orig-video-mode */ - 80, /* orig-video-cols */ - 0, 0, 0, /* ega_ax, ega_bx, ega_cx */ - 25, /* orig-video-lines */ - VIDEO_TYPE_VGAC, /* orig-video-isVGA */ - 16 /* orig-video-points */ + .orig_x = 0, + .orig_y = 25, + .orig_video_cols = 80, + .orig_video_lines = 25, + .orig_video_isVGA = VIDEO_TYPE_VGAC, + .orig_video_points = 16, }; #elif defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; diff --git a/trunk/arch/mips/loongson/lemote-2f/irq.c b/trunk/arch/mips/loongson/lemote-2f/irq.c index 882dfcd42c00..1d8b4d28a058 100644 --- a/trunk/arch/mips/loongson/lemote-2f/irq.c +++ b/trunk/arch/mips/loongson/lemote-2f/irq.c @@ -79,7 +79,7 @@ void mach_irq_dispatch(unsigned int pending) if (pending & CAUSEF_IP7) do_IRQ(LOONGSON_TIMER_IRQ); else if (pending & CAUSEF_IP6) { /* North Bridge, Perf counter */ -#ifdef CONFIG_OPROFILE +#if defined(CONFIG_OPROFILE) || defined(CONFIG_OPROFILE_MODULE) do_IRQ(LOONGSON2_PERFCNT_IRQ); #endif bonito_irqdispatch(); diff --git a/trunk/arch/mips/mm/tlbex.c b/trunk/arch/mips/mm/tlbex.c index d1f68aadbc4c..86f004dc8355 100644 --- a/trunk/arch/mips/mm/tlbex.c +++ b/trunk/arch/mips/mm/tlbex.c @@ -31,6 +31,16 @@ #include #include +/* + * TLB load/store/modify handlers. + * + * Only the fastpath gets synthesized at runtime, the slowpath for + * do_page_fault remains normal asm. + */ +extern void tlb_do_page_fault_0(void); +extern void tlb_do_page_fault_1(void); + + static inline int r45k_bvahwbug(void) { /* XXX: We should probe for the presence of this bug, but we don't. */ @@ -83,6 +93,7 @@ enum label_id { label_nopage_tlbm, label_smp_pgtable_change, label_r3000_write_probe_fail, + label_large_segbits_fault, #ifdef CONFIG_HUGETLB_PAGE label_tlb_huge_update, #endif @@ -101,6 +112,7 @@ UASM_L_LA(_nopage_tlbs) UASM_L_LA(_nopage_tlbm) UASM_L_LA(_smp_pgtable_change) UASM_L_LA(_r3000_write_probe_fail) +UASM_L_LA(_large_segbits_fault) #ifdef CONFIG_HUGETLB_PAGE UASM_L_LA(_tlb_huge_update) #endif @@ -157,6 +169,10 @@ static u32 tlb_handler[128] __cpuinitdata; static struct uasm_label labels[128] __cpuinitdata; static struct uasm_reloc relocs[128] __cpuinitdata; +#ifdef CONFIG_64BIT +static int check_for_high_segbits __cpuinitdata; +#endif + #ifndef CONFIG_MIPS_PGD_C0_CONTEXT /* * CONFIG_MIPS_PGD_C0_CONTEXT implies 64 bit and lack of pgd_current, @@ -408,7 +424,7 @@ static __cpuinit __maybe_unused void build_convert_pte_to_entrylo(u32 **p, UASM_i_ROTR(p, reg, reg, ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC)); } else { #ifdef CONFIG_64BIT_PHYS_ADDR - uasm_i_dsrl(p, reg, reg, ilog2(_PAGE_GLOBAL)); + uasm_i_dsrl_safe(p, reg, reg, ilog2(_PAGE_GLOBAL)); #else UASM_i_SRL(p, reg, reg, ilog2(_PAGE_GLOBAL)); #endif @@ -532,7 +548,24 @@ build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r, * The vmalloc handling is not in the hotpath. */ uasm_i_dmfc0(p, tmp, C0_BADVADDR); - uasm_il_bltz(p, r, tmp, label_vmalloc); + + if (check_for_high_segbits) { + /* + * The kernel currently implicitely assumes that the + * MIPS SEGBITS parameter for the processor is + * (PGDIR_SHIFT+PGDIR_BITS) or less, and will never + * allocate virtual addresses outside the maximum + * range for SEGBITS = (PGDIR_SHIFT+PGDIR_BITS). But + * that doesn't prevent user code from accessing the + * higher xuseg addresses. Here, we make sure that + * everything but the lower xuseg addresses goes down + * the module_alloc/vmalloc path. + */ + uasm_i_dsrl_safe(p, ptr, tmp, PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3); + uasm_il_bnez(p, r, ptr, label_vmalloc); + } else { + uasm_il_bltz(p, r, tmp, label_vmalloc); + } /* No uasm_i_nop needed here, since the next insn doesn't touch TMP. */ #ifdef CONFIG_MIPS_PGD_C0_CONTEXT @@ -549,14 +582,14 @@ build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r, * SMTC uses TCBind value as "CPU" index */ uasm_i_mfc0(p, ptr, C0_TCBIND); - uasm_i_dsrl(p, ptr, ptr, 19); + uasm_i_dsrl_safe(p, ptr, ptr, 19); # else /* * 64 bit SMP running in XKPHYS has smp_processor_id() << 3 * stored in CONTEXT. */ uasm_i_dmfc0(p, ptr, C0_CONTEXT); - uasm_i_dsrl(p, ptr, ptr, 23); + uasm_i_dsrl_safe(p, ptr, ptr, 23); # endif UASM_i_LA_mostly(p, tmp, pgdc); uasm_i_daddu(p, ptr, ptr, tmp); @@ -569,44 +602,78 @@ build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r, uasm_l_vmalloc_done(l, *p); - if (PGDIR_SHIFT - 3 < 32) /* get pgd offset in bytes */ - uasm_i_dsrl(p, tmp, tmp, PGDIR_SHIFT-3); - else - uasm_i_dsrl32(p, tmp, tmp, PGDIR_SHIFT - 3 - 32); + /* get pgd offset in bytes */ + uasm_i_dsrl_safe(p, tmp, tmp, PGDIR_SHIFT - 3); uasm_i_andi(p, tmp, tmp, (PTRS_PER_PGD - 1)<<3); uasm_i_daddu(p, ptr, ptr, tmp); /* add in pgd offset */ #ifndef __PAGETABLE_PMD_FOLDED uasm_i_dmfc0(p, tmp, C0_BADVADDR); /* get faulting address */ uasm_i_ld(p, ptr, 0, ptr); /* get pmd pointer */ - uasm_i_dsrl(p, tmp, tmp, PMD_SHIFT-3); /* get pmd offset in bytes */ + uasm_i_dsrl_safe(p, tmp, tmp, PMD_SHIFT-3); /* get pmd offset in bytes */ uasm_i_andi(p, tmp, tmp, (PTRS_PER_PMD - 1)<<3); uasm_i_daddu(p, ptr, ptr, tmp); /* add in pmd offset */ #endif } +enum vmalloc64_mode {not_refill, refill}; /* * BVADDR is the faulting address, PTR is scratch. * PTR will hold the pgd for vmalloc. */ static void __cpuinit build_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r, - unsigned int bvaddr, unsigned int ptr) + unsigned int bvaddr, unsigned int ptr, + enum vmalloc64_mode mode) { long swpd = (long)swapper_pg_dir; + int single_insn_swpd; + int did_vmalloc_branch = 0; + + single_insn_swpd = uasm_in_compat_space_p(swpd) && !uasm_rel_lo(swpd); uasm_l_vmalloc(l, *p); - if (uasm_in_compat_space_p(swpd) && !uasm_rel_lo(swpd)) { - uasm_il_b(p, r, label_vmalloc_done); - uasm_i_lui(p, ptr, uasm_rel_hi(swpd)); - } else { - UASM_i_LA_mostly(p, ptr, swpd); - uasm_il_b(p, r, label_vmalloc_done); - if (uasm_in_compat_space_p(swpd)) - uasm_i_addiu(p, ptr, ptr, uasm_rel_lo(swpd)); - else - uasm_i_daddiu(p, ptr, ptr, uasm_rel_lo(swpd)); + if (mode == refill && check_for_high_segbits) { + if (single_insn_swpd) { + uasm_il_bltz(p, r, bvaddr, label_vmalloc_done); + uasm_i_lui(p, ptr, uasm_rel_hi(swpd)); + did_vmalloc_branch = 1; + /* fall through */ + } else { + uasm_il_bgez(p, r, bvaddr, label_large_segbits_fault); + } + } + if (!did_vmalloc_branch) { + if (uasm_in_compat_space_p(swpd) && !uasm_rel_lo(swpd)) { + uasm_il_b(p, r, label_vmalloc_done); + uasm_i_lui(p, ptr, uasm_rel_hi(swpd)); + } else { + UASM_i_LA_mostly(p, ptr, swpd); + uasm_il_b(p, r, label_vmalloc_done); + if (uasm_in_compat_space_p(swpd)) + uasm_i_addiu(p, ptr, ptr, uasm_rel_lo(swpd)); + else + uasm_i_daddiu(p, ptr, ptr, uasm_rel_lo(swpd)); + } + } + if (mode == refill && check_for_high_segbits) { + uasm_l_large_segbits_fault(l, *p); + /* + * We get here if we are an xsseg address, or if we are + * an xuseg address above (PGDIR_SHIFT+PGDIR_BITS) boundary. + * + * Ignoring xsseg (assume disabled so would generate + * (address errors?), the only remaining possibility + * is the upper xuseg addresses. On processors with + * TLB_SEGBITS <= PGDIR_SHIFT+PGDIR_BITS, these + * addresses would have taken an address error. We try + * to mimic that here by taking a load/istream page + * fault. + */ + UASM_i_LA(p, ptr, (unsigned long)tlb_do_page_fault_0); + uasm_i_jr(p, ptr); + uasm_i_nop(p); } } @@ -720,9 +787,9 @@ static void __cpuinit build_update_entries(u32 **p, unsigned int tmp, UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */ UASM_i_ROTR(p, ptep, ptep, ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC)); } else { - uasm_i_dsrl(p, tmp, tmp, ilog2(_PAGE_GLOBAL)); /* convert to entrylo0 */ + uasm_i_dsrl_safe(p, tmp, tmp, ilog2(_PAGE_GLOBAL)); /* convert to entrylo0 */ UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */ - uasm_i_dsrl(p, ptep, ptep, ilog2(_PAGE_GLOBAL)); /* convert to entrylo1 */ + uasm_i_dsrl_safe(p, ptep, ptep, ilog2(_PAGE_GLOBAL)); /* convert to entrylo1 */ } UASM_i_MTC0(p, ptep, C0_ENTRYLO1); /* load it */ } else { @@ -793,9 +860,9 @@ static void __cpuinit build_r4000_tlb_refill_handler(void) uasm_i_dmfc0(&p, K0, C0_BADVADDR); uasm_i_dmfc0(&p, K1, C0_ENTRYHI); uasm_i_xor(&p, K0, K0, K1); - uasm_i_dsrl32(&p, K1, K0, 62 - 32); - uasm_i_dsrl(&p, K0, K0, 12 + 1); - uasm_i_dsll32(&p, K0, K0, 64 + 12 + 1 - segbits - 32); + uasm_i_dsrl_safe(&p, K1, K0, 62); + uasm_i_dsrl_safe(&p, K0, K0, 12 + 1); + uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits); uasm_i_or(&p, K0, K0, K1); uasm_il_bnez(&p, &r, K0, label_leave); /* No need for uasm_i_nop */ @@ -825,7 +892,7 @@ static void __cpuinit build_r4000_tlb_refill_handler(void) #endif #ifdef CONFIG_64BIT - build_get_pgd_vmalloc64(&p, &l, &r, K0, K1); + build_get_pgd_vmalloc64(&p, &l, &r, K0, K1, refill); #endif /* @@ -934,15 +1001,6 @@ static void __cpuinit build_r4000_tlb_refill_handler(void) dump_handler((u32 *)ebase, 64); } -/* - * TLB load/store/modify handlers. - * - * Only the fastpath gets synthesized at runtime, the slowpath for - * do_page_fault remains normal asm. - */ -extern void tlb_do_page_fault_0(void); -extern void tlb_do_page_fault_1(void); - /* * 128 instructions for the fastpath handler is generous and should * never be exceeded. @@ -1302,7 +1360,7 @@ build_r4000_tlbchange_handler_tail(u32 **p, struct uasm_label **l, uasm_i_eret(p); /* return from trap */ #ifdef CONFIG_64BIT - build_get_pgd_vmalloc64(p, l, r, tmp, ptr); + build_get_pgd_vmalloc64(p, l, r, tmp, ptr, not_refill); #endif } @@ -1322,9 +1380,9 @@ static void __cpuinit build_r4000_tlb_load_handler(void) uasm_i_dmfc0(&p, K0, C0_BADVADDR); uasm_i_dmfc0(&p, K1, C0_ENTRYHI); uasm_i_xor(&p, K0, K0, K1); - uasm_i_dsrl32(&p, K1, K0, 62 - 32); - uasm_i_dsrl(&p, K0, K0, 12 + 1); - uasm_i_dsll32(&p, K0, K0, 64 + 12 + 1 - segbits - 32); + uasm_i_dsrl_safe(&p, K1, K0, 62); + uasm_i_dsrl_safe(&p, K0, K0, 12 + 1); + uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits); uasm_i_or(&p, K0, K0, K1); uasm_il_bnez(&p, &r, K0, label_leave); /* No need for uasm_i_nop */ @@ -1526,6 +1584,10 @@ void __cpuinit build_tlb_refill_handler(void) */ static int run_once = 0; +#ifdef CONFIG_64BIT + check_for_high_segbits = current_cpu_data.vmbits > (PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3); +#endif + switch (current_cpu_type()) { case CPU_R2000: case CPU_R3000: diff --git a/trunk/arch/mips/nxp/pnx8550/common/reset.c b/trunk/arch/mips/nxp/pnx8550/common/reset.c index 76bc3ec634ee..fadd8744a6bc 100644 --- a/trunk/arch/mips/nxp/pnx8550/common/reset.c +++ b/trunk/arch/mips/nxp/pnx8550/common/reset.c @@ -20,6 +20,8 @@ * Reset the PNX8550 board. * */ +#include + #include #include diff --git a/trunk/arch/mips/pci/pci-sb1250.c b/trunk/arch/mips/pci/pci-sb1250.c index ada24e6f951f..1711e8e101bc 100644 --- a/trunk/arch/mips/pci/pci-sb1250.c +++ b/trunk/arch/mips/pci/pci-sb1250.c @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -254,7 +255,7 @@ static int __init sb1250_pcibios_init(void) * XXX ehs: Should this happen in PCI Device mode? */ io_map_base = ioremap(A_PHYS_LDTPCI_IO_MATCH_BYTES, 1024 * 1024); - sb1250_controller.io_map_base = io_map_base; + sb1250_controller.io_map_base = (unsigned long)io_map_base; set_io_port_base((unsigned long)io_map_base); #ifdef CONFIG_SIBYTE_HAS_LDT diff --git a/trunk/arch/mips/sgi-ip22/ip22-berr.c b/trunk/arch/mips/sgi-ip22/ip22-berr.c index de6a0cc32fea..911d3999c0c7 100644 --- a/trunk/arch/mips/sgi-ip22/ip22-berr.c +++ b/trunk/arch/mips/sgi-ip22/ip22-berr.c @@ -89,7 +89,7 @@ static void print_buserr(void) void ip22_be_interrupt(int irq) { const int field = 2 * sizeof(unsigned long); - const struct pt_regs *regs = get_irq_regs(); + struct pt_regs *regs = get_irq_regs(); save_and_clear_buserr(); print_buserr(); diff --git a/trunk/arch/mips/sgi-ip22/ip28-berr.c b/trunk/arch/mips/sgi-ip22/ip28-berr.c index 30e12e2ec4b5..88c684e05a3d 100644 --- a/trunk/arch/mips/sgi-ip22/ip28-berr.c +++ b/trunk/arch/mips/sgi-ip22/ip28-berr.c @@ -453,7 +453,7 @@ static int ip28_be_interrupt(const struct pt_regs *regs) void ip22_be_interrupt(int irq) { - const struct pt_regs *regs = get_irq_regs(); + struct pt_regs *regs = get_irq_regs(); count_be_interrupt++; diff --git a/trunk/arch/mips/sibyte/swarm/setup.c b/trunk/arch/mips/sibyte/swarm/setup.c index 5277aac96b0f..c308989fc464 100644 --- a/trunk/arch/mips/sibyte/swarm/setup.c +++ b/trunk/arch/mips/sibyte/swarm/setup.c @@ -145,15 +145,14 @@ void __init plat_mem_setup(void) #ifdef CONFIG_VT screen_info = (struct screen_info) { - 0, 0, /* orig-x, orig-y */ - 0, /* unused */ - 52, /* orig_video_page */ - 3, /* orig_video_mode */ - 80, /* orig_video_cols */ - 4626, 3, 9, /* unused, ega_bx, unused */ - 25, /* orig_video_lines */ - 0x22, /* orig_video_isVGA */ - 16 /* orig_video_points */ + .orig_video_page = 52, + .orig_video_mode = 3, + .orig_video_cols = 80, + .flags = 12, + .orig_video_ega_bx = 3, + .orig_video_lines = 25, + .orig_video_isVGA = 0x22, + .orig_video_points = 16, }; /* XXXKW for CFE, get lines/cols from environment */ #endif diff --git a/trunk/arch/sh/Kconfig b/trunk/arch/sh/Kconfig index e6d8ab5cfa9d..8d90564c2bcf 100644 --- a/trunk/arch/sh/Kconfig +++ b/trunk/arch/sh/Kconfig @@ -44,7 +44,6 @@ config SUPERH32 select HAVE_FUNCTION_GRAPH_TRACER select HAVE_ARCH_KGDB select HAVE_HW_BREAKPOINT - select HAVE_MIXED_BREAKPOINTS_REGS select PERF_EVENTS if HAVE_HW_BREAKPOINT select ARCH_HIBERNATION_POSSIBLE if MMU diff --git a/trunk/arch/sh/include/asm/hw_breakpoint.h b/trunk/arch/sh/include/asm/hw_breakpoint.h index e14cad96798f..965dd780d51b 100644 --- a/trunk/arch/sh/include/asm/hw_breakpoint.h +++ b/trunk/arch/sh/include/asm/hw_breakpoint.h @@ -46,14 +46,10 @@ struct pmu; /* Maximum number of UBC channels */ #define HBP_NUM 2 -static inline int hw_breakpoint_slots(int type) -{ - return HBP_NUM; -} - /* arch/sh/kernel/hw_breakpoint.c */ -extern int arch_check_bp_in_kernelspace(struct perf_event *bp); -extern int arch_validate_hwbkpt_settings(struct perf_event *bp); +extern int arch_check_va_in_userspace(unsigned long va, u16 hbp_len); +extern int arch_validate_hwbkpt_settings(struct perf_event *bp, + struct task_struct *tsk); extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, unsigned long val, void *data); diff --git a/trunk/arch/sh/kernel/hw_breakpoint.c b/trunk/arch/sh/kernel/hw_breakpoint.c index 1f2cf6229862..675eea7785d9 100644 --- a/trunk/arch/sh/kernel/hw_breakpoint.c +++ b/trunk/arch/sh/kernel/hw_breakpoint.c @@ -119,17 +119,26 @@ static int get_hbp_len(u16 hbp_len) return len_in_bytes; } +/* + * Check for virtual address in user space. + */ +int arch_check_va_in_userspace(unsigned long va, u16 hbp_len) +{ + unsigned int len; + + len = get_hbp_len(hbp_len); + + return (va <= TASK_SIZE - len); +} + /* * Check for virtual address in kernel space. */ -int arch_check_bp_in_kernelspace(struct perf_event *bp) +static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) { unsigned int len; - unsigned long va; - struct arch_hw_breakpoint *info = counter_arch_bp(bp); - va = info->address; - len = get_hbp_len(info->len); + len = get_hbp_len(hbp_len); return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); } @@ -217,7 +226,8 @@ static int arch_build_bp_info(struct perf_event *bp) /* * Validate the arch-specific HW Breakpoint register settings */ -int arch_validate_hwbkpt_settings(struct perf_event *bp) +int arch_validate_hwbkpt_settings(struct perf_event *bp, + struct task_struct *tsk) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); unsigned int align; @@ -260,6 +270,15 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) if (info->address & align) return -EINVAL; + /* Check that the virtual address is in the proper range */ + if (tsk) { + if (!arch_check_va_in_userspace(info->address, info->len)) + return -EFAULT; + } else { + if (!arch_check_va_in_kernelspace(info->address, info->len)) + return -EFAULT; + } + return 0; } @@ -344,7 +363,8 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) perf_bp_event(bp, args->regs); /* Deliver the signal to userspace */ - if (!arch_check_bp_in_kernelspace(bp)) { + if (arch_check_va_in_userspace(bp->attr.bp_addr, + bp->attr.bp_len)) { siginfo_t info; info.si_signo = args->signr; diff --git a/trunk/arch/sh/kernel/ptrace_32.c b/trunk/arch/sh/kernel/ptrace_32.c index d4104ce9fe53..7759a9a93211 100644 --- a/trunk/arch/sh/kernel/ptrace_32.c +++ b/trunk/arch/sh/kernel/ptrace_32.c @@ -85,7 +85,7 @@ static int set_single_step(struct task_struct *tsk, unsigned long addr) bp = thread->ptrace_bps[0]; if (!bp) { - ptrace_breakpoint_init(&attr); + hw_breakpoint_init(&attr); attr.bp_addr = addr; attr.bp_len = HW_BREAKPOINT_LEN_2; diff --git a/trunk/arch/x86/Kconfig b/trunk/arch/x86/Kconfig index 01177dcbe261..9458685902bd 100644 --- a/trunk/arch/x86/Kconfig +++ b/trunk/arch/x86/Kconfig @@ -53,15 +53,11 @@ config X86 select HAVE_KERNEL_LZMA select HAVE_KERNEL_LZO select HAVE_HW_BREAKPOINT - select HAVE_MIXED_BREAKPOINTS_REGS select PERF_EVENTS select ANON_INODES select HAVE_ARCH_KMEMCHECK select HAVE_USER_RETURN_NOTIFIER -config INSTRUCTION_DECODER - def_bool (KPROBES || PERF_EVENTS) - config OUTPUT_FORMAT string default "elf32-i386" if X86_32 diff --git a/trunk/arch/x86/Kconfig.cpu b/trunk/arch/x86/Kconfig.cpu index 918fbb1855cc..a19829374e6a 100644 --- a/trunk/arch/x86/Kconfig.cpu +++ b/trunk/arch/x86/Kconfig.cpu @@ -502,3 +502,23 @@ config CPU_SUP_UMC_32 CPU might render the kernel unbootable. If unsure, say N. + +config X86_DS + def_bool X86_PTRACE_BTS + depends on X86_DEBUGCTLMSR + select HAVE_HW_BRANCH_TRACER + +config X86_PTRACE_BTS + bool "Branch Trace Store" + default y + depends on X86_DEBUGCTLMSR + depends on BROKEN + ---help--- + This adds a ptrace interface to the hardware's branch trace store. + + Debuggers may use it to collect an execution trace of the debugged + application in order to answer the question 'how did I get here?'. + Debuggers may trace user mode as well as kernel mode. + + Say Y unless there is no application development on this machine + and you want to save a small amount of code size. diff --git a/trunk/arch/x86/Kconfig.debug b/trunk/arch/x86/Kconfig.debug index bd58c8abbfbd..bc01e3ebfeb2 100644 --- a/trunk/arch/x86/Kconfig.debug +++ b/trunk/arch/x86/Kconfig.debug @@ -174,6 +174,15 @@ config IOMMU_LEAK Add a simple leak tracer to the IOMMU code. This is useful when you are debugging a buggy device driver that leaks IOMMU mappings. +config X86_DS_SELFTEST + bool "DS selftest" + default y + depends on DEBUG_KERNEL + depends on X86_DS + ---help--- + Perform Debug Store selftests at boot time. + If in doubt, say "N". + config HAVE_MMIOTRACE_SUPPORT def_bool y diff --git a/trunk/arch/x86/include/asm/apic.h b/trunk/arch/x86/include/asm/apic.h index 1fa03e04ae44..b4ac2cdcb64f 100644 --- a/trunk/arch/x86/include/asm/apic.h +++ b/trunk/arch/x86/include/asm/apic.h @@ -373,7 +373,6 @@ extern atomic_t init_deasserted; extern int wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip); #endif -#ifdef CONFIG_X86_LOCAL_APIC static inline u32 apic_read(u32 reg) { return apic->read(reg); @@ -404,19 +403,10 @@ static inline u32 safe_apic_wait_icr_idle(void) return apic->safe_wait_icr_idle(); } -#else /* CONFIG_X86_LOCAL_APIC */ - -static inline u32 apic_read(u32 reg) { return 0; } -static inline void apic_write(u32 reg, u32 val) { } -static inline u64 apic_icr_read(void) { return 0; } -static inline void apic_icr_write(u32 low, u32 high) { } -static inline void apic_wait_icr_idle(void) { } -static inline u32 safe_apic_wait_icr_idle(void) { return 0; } - -#endif /* CONFIG_X86_LOCAL_APIC */ static inline void ack_APIC_irq(void) { +#ifdef CONFIG_X86_LOCAL_APIC /* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie. @@ -424,6 +414,7 @@ static inline void ack_APIC_irq(void) /* Docs say use 0 for future compatibility */ apic_write(APIC_EOI, 0); +#endif } static inline unsigned default_get_apic_id(unsigned long x) diff --git a/trunk/arch/x86/include/asm/ds.h b/trunk/arch/x86/include/asm/ds.h new file mode 100644 index 000000000000..70dac199b093 --- /dev/null +++ b/trunk/arch/x86/include/asm/ds.h @@ -0,0 +1,302 @@ +/* + * Debug Store (DS) support + * + * This provides a low-level interface to the hardware's Debug Store + * feature that is used for branch trace store (BTS) and + * precise-event based sampling (PEBS). + * + * It manages: + * - DS and BTS hardware configuration + * - buffer overflow handling (to be done) + * - buffer access + * + * It does not do: + * - security checking (is the caller allowed to trace the task) + * - buffer allocation (memory accounting) + * + * + * Copyright (C) 2007-2009 Intel Corporation. + * Markus Metzger , 2007-2009 + */ + +#ifndef _ASM_X86_DS_H +#define _ASM_X86_DS_H + + +#include +#include +#include + + +#ifdef CONFIG_X86_DS + +struct task_struct; +struct ds_context; +struct ds_tracer; +struct bts_tracer; +struct pebs_tracer; + +typedef void (*bts_ovfl_callback_t)(struct bts_tracer *); +typedef void (*pebs_ovfl_callback_t)(struct pebs_tracer *); + + +/* + * A list of features plus corresponding macros to talk about them in + * the ds_request function's flags parameter. + * + * We use the enum to index an array of corresponding control bits; + * we use the macro to index a flags bit-vector. + */ +enum ds_feature { + dsf_bts = 0, + dsf_bts_kernel, +#define BTS_KERNEL (1 << dsf_bts_kernel) + /* trace kernel-mode branches */ + + dsf_bts_user, +#define BTS_USER (1 << dsf_bts_user) + /* trace user-mode branches */ + + dsf_bts_overflow, + dsf_bts_max, + dsf_pebs = dsf_bts_max, + + dsf_pebs_max, + dsf_ctl_max = dsf_pebs_max, + dsf_bts_timestamps = dsf_ctl_max, +#define BTS_TIMESTAMPS (1 << dsf_bts_timestamps) + /* add timestamps into BTS trace */ + +#define BTS_USER_FLAGS (BTS_KERNEL | BTS_USER | BTS_TIMESTAMPS) +}; + + +/* + * Request BTS or PEBS + * + * Due to alignement constraints, the actual buffer may be slightly + * smaller than the requested or provided buffer. + * + * Returns a pointer to a tracer structure on success, or + * ERR_PTR(errcode) on failure. + * + * The interrupt threshold is independent from the overflow callback + * to allow users to use their own overflow interrupt handling mechanism. + * + * The function might sleep. + * + * task: the task to request recording for + * cpu: the cpu to request recording for + * base: the base pointer for the (non-pageable) buffer; + * size: the size of the provided buffer in bytes + * ovfl: pointer to a function to be called on buffer overflow; + * NULL if cyclic buffer requested + * th: the interrupt threshold in records from the end of the buffer; + * -1 if no interrupt threshold is requested. + * flags: a bit-mask of the above flags + */ +extern struct bts_tracer *ds_request_bts_task(struct task_struct *task, + void *base, size_t size, + bts_ovfl_callback_t ovfl, + size_t th, unsigned int flags); +extern struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size, + bts_ovfl_callback_t ovfl, + size_t th, unsigned int flags); +extern struct pebs_tracer *ds_request_pebs_task(struct task_struct *task, + void *base, size_t size, + pebs_ovfl_callback_t ovfl, + size_t th, unsigned int flags); +extern struct pebs_tracer *ds_request_pebs_cpu(int cpu, + void *base, size_t size, + pebs_ovfl_callback_t ovfl, + size_t th, unsigned int flags); + +/* + * Release BTS or PEBS resources + * Suspend and resume BTS or PEBS tracing + * + * Must be called with irq's enabled. + * + * tracer: the tracer handle returned from ds_request_~() + */ +extern void ds_release_bts(struct bts_tracer *tracer); +extern void ds_suspend_bts(struct bts_tracer *tracer); +extern void ds_resume_bts(struct bts_tracer *tracer); +extern void ds_release_pebs(struct pebs_tracer *tracer); +extern void ds_suspend_pebs(struct pebs_tracer *tracer); +extern void ds_resume_pebs(struct pebs_tracer *tracer); + +/* + * Release BTS or PEBS resources + * Suspend and resume BTS or PEBS tracing + * + * Cpu tracers must call this on the traced cpu. + * Task tracers must call ds_release_~_noirq() for themselves. + * + * May be called with irq's disabled. + * + * Returns 0 if successful; + * -EPERM if the cpu tracer does not trace the current cpu. + * -EPERM if the task tracer does not trace itself. + * + * tracer: the tracer handle returned from ds_request_~() + */ +extern int ds_release_bts_noirq(struct bts_tracer *tracer); +extern int ds_suspend_bts_noirq(struct bts_tracer *tracer); +extern int ds_resume_bts_noirq(struct bts_tracer *tracer); +extern int ds_release_pebs_noirq(struct pebs_tracer *tracer); +extern int ds_suspend_pebs_noirq(struct pebs_tracer *tracer); +extern int ds_resume_pebs_noirq(struct pebs_tracer *tracer); + + +/* + * The raw DS buffer state as it is used for BTS and PEBS recording. + * + * This is the low-level, arch-dependent interface for working + * directly on the raw trace data. + */ +struct ds_trace { + /* the number of bts/pebs records */ + size_t n; + /* the size of a bts/pebs record in bytes */ + size_t size; + /* pointers into the raw buffer: + - to the first entry */ + void *begin; + /* - one beyond the last entry */ + void *end; + /* - one beyond the newest entry */ + void *top; + /* - the interrupt threshold */ + void *ith; + /* flags given on ds_request() */ + unsigned int flags; +}; + +/* + * An arch-independent view on branch trace data. + */ +enum bts_qualifier { + bts_invalid, +#define BTS_INVALID bts_invalid + + bts_branch, +#define BTS_BRANCH bts_branch + + bts_task_arrives, +#define BTS_TASK_ARRIVES bts_task_arrives + + bts_task_departs, +#define BTS_TASK_DEPARTS bts_task_departs + + bts_qual_bit_size = 4, + bts_qual_max = (1 << bts_qual_bit_size), +}; + +struct bts_struct { + __u64 qualifier; + union { + /* BTS_BRANCH */ + struct { + __u64 from; + __u64 to; + } lbr; + /* BTS_TASK_ARRIVES or BTS_TASK_DEPARTS */ + struct { + __u64 clock; + pid_t pid; + } event; + } variant; +}; + + +/* + * The BTS state. + * + * This gives access to the raw DS state and adds functions to provide + * an arch-independent view of the BTS data. + */ +struct bts_trace { + struct ds_trace ds; + + int (*read)(struct bts_tracer *tracer, const void *at, + struct bts_struct *out); + int (*write)(struct bts_tracer *tracer, const struct bts_struct *in); +}; + + +/* + * The PEBS state. + * + * This gives access to the raw DS state and the PEBS-specific counter + * reset value. + */ +struct pebs_trace { + struct ds_trace ds; + + /* the number of valid counters in the below array */ + unsigned int counters; + +#define MAX_PEBS_COUNTERS 4 + /* the counter reset value */ + unsigned long long counter_reset[MAX_PEBS_COUNTERS]; +}; + + +/* + * Read the BTS or PEBS trace. + * + * Returns a view on the trace collected for the parameter tracer. + * + * The view remains valid as long as the traced task is not running or + * the tracer is suspended. + * Writes into the trace buffer are not reflected. + * + * tracer: the tracer handle returned from ds_request_~() + */ +extern const struct bts_trace *ds_read_bts(struct bts_tracer *tracer); +extern const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer); + + +/* + * Reset the write pointer of the BTS/PEBS buffer. + * + * Returns 0 on success; -Eerrno on error + * + * tracer: the tracer handle returned from ds_request_~() + */ +extern int ds_reset_bts(struct bts_tracer *tracer); +extern int ds_reset_pebs(struct pebs_tracer *tracer); + +/* + * Set the PEBS counter reset value. + * + * Returns 0 on success; -Eerrno on error + * + * tracer: the tracer handle returned from ds_request_pebs() + * counter: the index of the counter + * value: the new counter reset value + */ +extern int ds_set_pebs_reset(struct pebs_tracer *tracer, + unsigned int counter, u64 value); + +/* + * Initialization + */ +struct cpuinfo_x86; +extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *); + +/* + * Context switch work + */ +extern void ds_switch_to(struct task_struct *prev, struct task_struct *next); + +#else /* CONFIG_X86_DS */ + +struct cpuinfo_x86; +static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {} +static inline void ds_switch_to(struct task_struct *prev, + struct task_struct *next) {} + +#endif /* CONFIG_X86_DS */ +#endif /* _ASM_X86_DS_H */ diff --git a/trunk/arch/x86/include/asm/hw_breakpoint.h b/trunk/arch/x86/include/asm/hw_breakpoint.h index 942255310e6a..2a1bd8f4f23a 100644 --- a/trunk/arch/x86/include/asm/hw_breakpoint.h +++ b/trunk/arch/x86/include/asm/hw_breakpoint.h @@ -41,16 +41,12 @@ struct arch_hw_breakpoint { /* Total number of available HW breakpoint registers */ #define HBP_NUM 4 -static inline int hw_breakpoint_slots(int type) -{ - return HBP_NUM; -} - struct perf_event; struct pmu; -extern int arch_check_bp_in_kernelspace(struct perf_event *bp); -extern int arch_validate_hwbkpt_settings(struct perf_event *bp); +extern int arch_check_va_in_userspace(unsigned long va, u8 hbp_len); +extern int arch_validate_hwbkpt_settings(struct perf_event *bp, + struct task_struct *tsk); extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, unsigned long val, void *data); diff --git a/trunk/arch/x86/include/asm/insn.h b/trunk/arch/x86/include/asm/insn.h index 88c765e16410..96c2e0ad04ca 100644 --- a/trunk/arch/x86/include/asm/insn.h +++ b/trunk/arch/x86/include/asm/insn.h @@ -68,8 +68,6 @@ struct insn { const insn_byte_t *next_byte; }; -#define MAX_INSN_SIZE 16 - #define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6) #define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3) #define X86_MODRM_RM(modrm) ((modrm) & 0x07) diff --git a/trunk/arch/x86/include/asm/io.h b/trunk/arch/x86/include/asm/io.h index a1dcfa3ab17d..30a3e9776123 100644 --- a/trunk/arch/x86/include/asm/io.h +++ b/trunk/arch/x86/include/asm/io.h @@ -347,6 +347,7 @@ extern void __iomem *early_ioremap(resource_size_t phys_addr, extern void __iomem *early_memremap(resource_size_t phys_addr, unsigned long size); extern void early_iounmap(void __iomem *addr, unsigned long size); +extern void fixup_early_ioremap(void); #define IO_SPACE_LIMIT 0xffff diff --git a/trunk/arch/x86/include/asm/kprobes.h b/trunk/arch/x86/include/asm/kprobes.h index 547882539157..4ffa345a8ccb 100644 --- a/trunk/arch/x86/include/asm/kprobes.h +++ b/trunk/arch/x86/include/asm/kprobes.h @@ -24,7 +24,6 @@ #include #include #include -#include #define __ARCH_WANT_KPROBES_INSN_SLOT @@ -37,6 +36,7 @@ typedef u8 kprobe_opcode_t; #define RELATIVEJUMP_SIZE 5 #define RELATIVECALL_OPCODE 0xe8 #define RELATIVE_ADDR_SIZE 4 +#define MAX_INSN_SIZE 16 #define MAX_STACK_SIZE 64 #define MIN_STACK_SIZE(ADDR) \ (((MAX_STACK_SIZE) < (((unsigned long)current_thread_info()) + \ diff --git a/trunk/arch/x86/include/asm/msr-index.h b/trunk/arch/x86/include/asm/msr-index.h index bc473acfa7f9..4604e6a54d36 100644 --- a/trunk/arch/x86/include/asm/msr-index.h +++ b/trunk/arch/x86/include/asm/msr-index.h @@ -71,14 +71,11 @@ #define MSR_IA32_LASTINTTOIP 0x000001de /* DEBUGCTLMSR bits (others vary by model): */ -#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */ -#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */ -#define DEBUGCTLMSR_TR (1UL << 6) -#define DEBUGCTLMSR_BTS (1UL << 7) -#define DEBUGCTLMSR_BTINT (1UL << 8) -#define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9) -#define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10) -#define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11) +#define _DEBUGCTLMSR_LBR 0 /* last branch recording */ +#define _DEBUGCTLMSR_BTF 1 /* single-step on branches */ + +#define DEBUGCTLMSR_LBR (1UL << _DEBUGCTLMSR_LBR) +#define DEBUGCTLMSR_BTF (1UL << _DEBUGCTLMSR_BTF) #define MSR_IA32_MC0_CTL 0x00000400 #define MSR_IA32_MC0_STATUS 0x00000401 @@ -362,8 +359,6 @@ #define MSR_P4_U2L_ESCR0 0x000003b0 #define MSR_P4_U2L_ESCR1 0x000003b1 -#define MSR_P4_PEBS_MATRIX_VERT 0x000003f2 - /* Intel Core-based CPU performance counters */ #define MSR_CORE_PERF_FIXED_CTR0 0x00000309 #define MSR_CORE_PERF_FIXED_CTR1 0x0000030a diff --git a/trunk/arch/x86/include/asm/perf_event.h b/trunk/arch/x86/include/asm/perf_event.h index 254883d0c7e0..db6109a885a7 100644 --- a/trunk/arch/x86/include/asm/perf_event.h +++ b/trunk/arch/x86/include/asm/perf_event.h @@ -5,7 +5,7 @@ * Performance event hw details: */ -#define X86_PMC_MAX_GENERIC 32 +#define X86_PMC_MAX_GENERIC 8 #define X86_PMC_MAX_FIXED 3 #define X86_PMC_IDX_GENERIC 0 @@ -18,31 +18,39 @@ #define MSR_ARCH_PERFMON_EVENTSEL0 0x186 #define MSR_ARCH_PERFMON_EVENTSEL1 0x187 -#define ARCH_PERFMON_EVENTSEL_EVENT 0x000000FFULL -#define ARCH_PERFMON_EVENTSEL_UMASK 0x0000FF00ULL -#define ARCH_PERFMON_EVENTSEL_USR (1ULL << 16) -#define ARCH_PERFMON_EVENTSEL_OS (1ULL << 17) -#define ARCH_PERFMON_EVENTSEL_EDGE (1ULL << 18) -#define ARCH_PERFMON_EVENTSEL_INT (1ULL << 20) -#define ARCH_PERFMON_EVENTSEL_ANY (1ULL << 21) -#define ARCH_PERFMON_EVENTSEL_ENABLE (1ULL << 22) -#define ARCH_PERFMON_EVENTSEL_INV (1ULL << 23) -#define ARCH_PERFMON_EVENTSEL_CMASK 0xFF000000ULL - -#define AMD64_EVENTSEL_EVENT \ - (ARCH_PERFMON_EVENTSEL_EVENT | (0x0FULL << 32)) -#define INTEL_ARCH_EVENT_MASK \ - (ARCH_PERFMON_EVENTSEL_UMASK | ARCH_PERFMON_EVENTSEL_EVENT) - -#define X86_RAW_EVENT_MASK \ - (ARCH_PERFMON_EVENTSEL_EVENT | \ - ARCH_PERFMON_EVENTSEL_UMASK | \ - ARCH_PERFMON_EVENTSEL_EDGE | \ - ARCH_PERFMON_EVENTSEL_INV | \ - ARCH_PERFMON_EVENTSEL_CMASK) -#define AMD64_RAW_EVENT_MASK \ - (X86_RAW_EVENT_MASK | \ - AMD64_EVENTSEL_EVENT) +#define ARCH_PERFMON_EVENTSEL_ENABLE (1 << 22) +#define ARCH_PERFMON_EVENTSEL_ANY (1 << 21) +#define ARCH_PERFMON_EVENTSEL_INT (1 << 20) +#define ARCH_PERFMON_EVENTSEL_OS (1 << 17) +#define ARCH_PERFMON_EVENTSEL_USR (1 << 16) + +/* + * Includes eventsel and unit mask as well: + */ + + +#define INTEL_ARCH_EVTSEL_MASK 0x000000FFULL +#define INTEL_ARCH_UNIT_MASK 0x0000FF00ULL +#define INTEL_ARCH_EDGE_MASK 0x00040000ULL +#define INTEL_ARCH_INV_MASK 0x00800000ULL +#define INTEL_ARCH_CNT_MASK 0xFF000000ULL +#define INTEL_ARCH_EVENT_MASK (INTEL_ARCH_UNIT_MASK|INTEL_ARCH_EVTSEL_MASK) + +/* + * filter mask to validate fixed counter events. + * the following filters disqualify for fixed counters: + * - inv + * - edge + * - cnt-mask + * The other filters are supported by fixed counters. + * The any-thread option is supported starting with v3. + */ +#define INTEL_ARCH_FIXED_MASK \ + (INTEL_ARCH_CNT_MASK| \ + INTEL_ARCH_INV_MASK| \ + INTEL_ARCH_EDGE_MASK|\ + INTEL_ARCH_UNIT_MASK|\ + INTEL_ARCH_EVENT_MASK) #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) @@ -59,7 +67,7 @@ union cpuid10_eax { struct { unsigned int version_id:8; - unsigned int num_counters:8; + unsigned int num_events:8; unsigned int bit_width:8; unsigned int mask_length:8; } split; @@ -68,7 +76,7 @@ union cpuid10_eax { union cpuid10_edx { struct { - unsigned int num_counters_fixed:4; + unsigned int num_events_fixed:4; unsigned int reserved:28; } split; unsigned int full; @@ -128,18 +136,6 @@ extern void perf_events_lapic_init(void); #define PERF_EVENT_INDEX_OFFSET 0 -/* - * Abuse bit 3 of the cpu eflags register to indicate proper PEBS IP fixups. - * This flag is otherwise unused and ABI specified to be 0, so nobody should - * care what we do with it. - */ -#define PERF_EFLAGS_EXACT (1UL << 3) - -struct pt_regs; -extern unsigned long perf_instruction_pointer(struct pt_regs *regs); -extern unsigned long perf_misc_flags(struct pt_regs *regs); -#define perf_misc_flags(regs) perf_misc_flags(regs) - #else static inline void init_hw_perf_events(void) { } static inline void perf_events_lapic_init(void) { } diff --git a/trunk/arch/x86/include/asm/perf_event_p4.h b/trunk/arch/x86/include/asm/perf_event_p4.h deleted file mode 100644 index b05400a542ff..000000000000 --- a/trunk/arch/x86/include/asm/perf_event_p4.h +++ /dev/null @@ -1,794 +0,0 @@ -/* - * Netburst Perfomance Events (P4, old Xeon) - */ - -#ifndef PERF_EVENT_P4_H -#define PERF_EVENT_P4_H - -#include -#include - -/* - * NetBurst has perfomance MSRs shared between - * threads if HT is turned on, ie for both logical - * processors (mem: in turn in Atom with HT support - * perf-MSRs are not shared and every thread has its - * own perf-MSRs set) - */ -#define ARCH_P4_TOTAL_ESCR (46) -#define ARCH_P4_RESERVED_ESCR (2) /* IQ_ESCR(0,1) not always present */ -#define ARCH_P4_MAX_ESCR (ARCH_P4_TOTAL_ESCR - ARCH_P4_RESERVED_ESCR) -#define ARCH_P4_MAX_CCCR (18) -#define ARCH_P4_MAX_COUNTER (ARCH_P4_MAX_CCCR / 2) - -#define P4_ESCR_EVENT_MASK 0x7e000000U -#define P4_ESCR_EVENT_SHIFT 25 -#define P4_ESCR_EVENTMASK_MASK 0x01fffe00U -#define P4_ESCR_EVENTMASK_SHIFT 9 -#define P4_ESCR_TAG_MASK 0x000001e0U -#define P4_ESCR_TAG_SHIFT 5 -#define P4_ESCR_TAG_ENABLE 0x00000010U -#define P4_ESCR_T0_OS 0x00000008U -#define P4_ESCR_T0_USR 0x00000004U -#define P4_ESCR_T1_OS 0x00000002U -#define P4_ESCR_T1_USR 0x00000001U - -#define P4_ESCR_EVENT(v) ((v) << P4_ESCR_EVENT_SHIFT) -#define P4_ESCR_EMASK(v) ((v) << P4_ESCR_EVENTMASK_SHIFT) -#define P4_ESCR_TAG(v) ((v) << P4_ESCR_TAG_SHIFT) - -/* Non HT mask */ -#define P4_ESCR_MASK \ - (P4_ESCR_EVENT_MASK | \ - P4_ESCR_EVENTMASK_MASK | \ - P4_ESCR_TAG_MASK | \ - P4_ESCR_TAG_ENABLE | \ - P4_ESCR_T0_OS | \ - P4_ESCR_T0_USR) - -/* HT mask */ -#define P4_ESCR_MASK_HT \ - (P4_ESCR_MASK | P4_ESCR_T1_OS | P4_ESCR_T1_USR) - -#define P4_CCCR_OVF 0x80000000U -#define P4_CCCR_CASCADE 0x40000000U -#define P4_CCCR_OVF_PMI_T0 0x04000000U -#define P4_CCCR_OVF_PMI_T1 0x08000000U -#define P4_CCCR_FORCE_OVF 0x02000000U -#define P4_CCCR_EDGE 0x01000000U -#define P4_CCCR_THRESHOLD_MASK 0x00f00000U -#define P4_CCCR_THRESHOLD_SHIFT 20 -#define P4_CCCR_COMPLEMENT 0x00080000U -#define P4_CCCR_COMPARE 0x00040000U -#define P4_CCCR_ESCR_SELECT_MASK 0x0000e000U -#define P4_CCCR_ESCR_SELECT_SHIFT 13 -#define P4_CCCR_ENABLE 0x00001000U -#define P4_CCCR_THREAD_SINGLE 0x00010000U -#define P4_CCCR_THREAD_BOTH 0x00020000U -#define P4_CCCR_THREAD_ANY 0x00030000U -#define P4_CCCR_RESERVED 0x00000fffU - -#define P4_CCCR_THRESHOLD(v) ((v) << P4_CCCR_THRESHOLD_SHIFT) -#define P4_CCCR_ESEL(v) ((v) << P4_CCCR_ESCR_SELECT_SHIFT) - -/* Custom bits in reerved CCCR area */ -#define P4_CCCR_CACHE_OPS_MASK 0x0000003fU - - -/* Non HT mask */ -#define P4_CCCR_MASK \ - (P4_CCCR_OVF | \ - P4_CCCR_CASCADE | \ - P4_CCCR_OVF_PMI_T0 | \ - P4_CCCR_FORCE_OVF | \ - P4_CCCR_EDGE | \ - P4_CCCR_THRESHOLD_MASK | \ - P4_CCCR_COMPLEMENT | \ - P4_CCCR_COMPARE | \ - P4_CCCR_ESCR_SELECT_MASK | \ - P4_CCCR_ENABLE) - -/* HT mask */ -#define P4_CCCR_MASK_HT (P4_CCCR_MASK | P4_CCCR_THREAD_ANY) - -#define P4_GEN_ESCR_EMASK(class, name, bit) \ - class##__##name = ((1 << bit) << P4_ESCR_EVENTMASK_SHIFT) -#define P4_ESCR_EMASK_BIT(class, name) class##__##name - -/* - * config field is 64bit width and consists of - * HT << 63 | ESCR << 32 | CCCR - * where HT is HyperThreading bit (since ESCR - * has it reserved we may use it for own purpose) - * - * note that this is NOT the addresses of respective - * ESCR and CCCR but rather an only packed value should - * be unpacked and written to a proper addresses - * - * the base idea is to pack as much info as - * possible - */ -#define p4_config_pack_escr(v) (((u64)(v)) << 32) -#define p4_config_pack_cccr(v) (((u64)(v)) & 0xffffffffULL) -#define p4_config_unpack_escr(v) (((u64)(v)) >> 32) -#define p4_config_unpack_cccr(v) (((u64)(v)) & 0xffffffffULL) - -#define p4_config_unpack_emask(v) \ - ({ \ - u32 t = p4_config_unpack_escr((v)); \ - t = t & P4_ESCR_EVENTMASK_MASK; \ - t = t >> P4_ESCR_EVENTMASK_SHIFT; \ - t; \ - }) - -#define p4_config_unpack_event(v) \ - ({ \ - u32 t = p4_config_unpack_escr((v)); \ - t = t & P4_ESCR_EVENT_MASK; \ - t = t >> P4_ESCR_EVENT_SHIFT; \ - t; \ - }) - -#define p4_config_unpack_cache_event(v) (((u64)(v)) & P4_CCCR_CACHE_OPS_MASK) - -#define P4_CONFIG_HT_SHIFT 63 -#define P4_CONFIG_HT (1ULL << P4_CONFIG_HT_SHIFT) - -static inline bool p4_is_event_cascaded(u64 config) -{ - u32 cccr = p4_config_unpack_cccr(config); - return !!(cccr & P4_CCCR_CASCADE); -} - -static inline int p4_ht_config_thread(u64 config) -{ - return !!(config & P4_CONFIG_HT); -} - -static inline u64 p4_set_ht_bit(u64 config) -{ - return config | P4_CONFIG_HT; -} - -static inline u64 p4_clear_ht_bit(u64 config) -{ - return config & ~P4_CONFIG_HT; -} - -static inline int p4_ht_active(void) -{ -#ifdef CONFIG_SMP - return smp_num_siblings > 1; -#endif - return 0; -} - -static inline int p4_ht_thread(int cpu) -{ -#ifdef CONFIG_SMP - if (smp_num_siblings == 2) - return cpu != cpumask_first(__get_cpu_var(cpu_sibling_map)); -#endif - return 0; -} - -static inline int p4_should_swap_ts(u64 config, int cpu) -{ - return p4_ht_config_thread(config) ^ p4_ht_thread(cpu); -} - -static inline u32 p4_default_cccr_conf(int cpu) -{ - /* - * Note that P4_CCCR_THREAD_ANY is "required" on - * non-HT machines (on HT machines we count TS events - * regardless the state of second logical processor - */ - u32 cccr = P4_CCCR_THREAD_ANY; - - if (!p4_ht_thread(cpu)) - cccr |= P4_CCCR_OVF_PMI_T0; - else - cccr |= P4_CCCR_OVF_PMI_T1; - - return cccr; -} - -static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) -{ - u32 escr = 0; - - if (!p4_ht_thread(cpu)) { - if (!exclude_os) - escr |= P4_ESCR_T0_OS; - if (!exclude_usr) - escr |= P4_ESCR_T0_USR; - } else { - if (!exclude_os) - escr |= P4_ESCR_T1_OS; - if (!exclude_usr) - escr |= P4_ESCR_T1_USR; - } - - return escr; -} - -enum P4_EVENTS { - P4_EVENT_TC_DELIVER_MODE, - P4_EVENT_BPU_FETCH_REQUEST, - P4_EVENT_ITLB_REFERENCE, - P4_EVENT_MEMORY_CANCEL, - P4_EVENT_MEMORY_COMPLETE, - P4_EVENT_LOAD_PORT_REPLAY, - P4_EVENT_STORE_PORT_REPLAY, - P4_EVENT_MOB_LOAD_REPLAY, - P4_EVENT_PAGE_WALK_TYPE, - P4_EVENT_BSQ_CACHE_REFERENCE, - P4_EVENT_IOQ_ALLOCATION, - P4_EVENT_IOQ_ACTIVE_ENTRIES, - P4_EVENT_FSB_DATA_ACTIVITY, - P4_EVENT_BSQ_ALLOCATION, - P4_EVENT_BSQ_ACTIVE_ENTRIES, - P4_EVENT_SSE_INPUT_ASSIST, - P4_EVENT_PACKED_SP_UOP, - P4_EVENT_PACKED_DP_UOP, - P4_EVENT_SCALAR_SP_UOP, - P4_EVENT_SCALAR_DP_UOP, - P4_EVENT_64BIT_MMX_UOP, - P4_EVENT_128BIT_MMX_UOP, - P4_EVENT_X87_FP_UOP, - P4_EVENT_TC_MISC, - P4_EVENT_GLOBAL_POWER_EVENTS, - P4_EVENT_TC_MS_XFER, - P4_EVENT_UOP_QUEUE_WRITES, - P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, - P4_EVENT_RETIRED_BRANCH_TYPE, - P4_EVENT_RESOURCE_STALL, - P4_EVENT_WC_BUFFER, - P4_EVENT_B2B_CYCLES, - P4_EVENT_BNR, - P4_EVENT_SNOOP, - P4_EVENT_RESPONSE, - P4_EVENT_FRONT_END_EVENT, - P4_EVENT_EXECUTION_EVENT, - P4_EVENT_REPLAY_EVENT, - P4_EVENT_INSTR_RETIRED, - P4_EVENT_UOPS_RETIRED, - P4_EVENT_UOP_TYPE, - P4_EVENT_BRANCH_RETIRED, - P4_EVENT_MISPRED_BRANCH_RETIRED, - P4_EVENT_X87_ASSIST, - P4_EVENT_MACHINE_CLEAR, - P4_EVENT_INSTR_COMPLETED, -}; - -#define P4_OPCODE(event) event##_OPCODE -#define P4_OPCODE_ESEL(opcode) ((opcode & 0x00ff) >> 0) -#define P4_OPCODE_EVNT(opcode) ((opcode & 0xff00) >> 8) -#define P4_OPCODE_PACK(event, sel) (((event) << 8) | sel) - -/* - * Comments below the event represent ESCR restriction - * for this event and counter index per ESCR - * - * MSR_P4_IQ_ESCR0 and MSR_P4_IQ_ESCR1 are available only on early - * processor builds (family 0FH, models 01H-02H). These MSRs - * are not available on later versions, so that we don't use - * them completely - * - * Also note that CCCR1 do not have P4_CCCR_ENABLE bit properly - * working so that we should not use this CCCR and respective - * counter as result - */ -enum P4_EVENT_OPCODES { - P4_OPCODE(P4_EVENT_TC_DELIVER_MODE) = P4_OPCODE_PACK(0x01, 0x01), - /* - * MSR_P4_TC_ESCR0: 4, 5 - * MSR_P4_TC_ESCR1: 6, 7 - */ - - P4_OPCODE(P4_EVENT_BPU_FETCH_REQUEST) = P4_OPCODE_PACK(0x03, 0x00), - /* - * MSR_P4_BPU_ESCR0: 0, 1 - * MSR_P4_BPU_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_ITLB_REFERENCE) = P4_OPCODE_PACK(0x18, 0x03), - /* - * MSR_P4_ITLB_ESCR0: 0, 1 - * MSR_P4_ITLB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_MEMORY_CANCEL) = P4_OPCODE_PACK(0x02, 0x05), - /* - * MSR_P4_DAC_ESCR0: 8, 9 - * MSR_P4_DAC_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_MEMORY_COMPLETE) = P4_OPCODE_PACK(0x08, 0x02), - /* - * MSR_P4_SAAT_ESCR0: 8, 9 - * MSR_P4_SAAT_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_LOAD_PORT_REPLAY) = P4_OPCODE_PACK(0x04, 0x02), - /* - * MSR_P4_SAAT_ESCR0: 8, 9 - * MSR_P4_SAAT_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_STORE_PORT_REPLAY) = P4_OPCODE_PACK(0x05, 0x02), - /* - * MSR_P4_SAAT_ESCR0: 8, 9 - * MSR_P4_SAAT_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_MOB_LOAD_REPLAY) = P4_OPCODE_PACK(0x03, 0x02), - /* - * MSR_P4_MOB_ESCR0: 0, 1 - * MSR_P4_MOB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_PAGE_WALK_TYPE) = P4_OPCODE_PACK(0x01, 0x04), - /* - * MSR_P4_PMH_ESCR0: 0, 1 - * MSR_P4_PMH_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_BSQ_CACHE_REFERENCE) = P4_OPCODE_PACK(0x0c, 0x07), - /* - * MSR_P4_BSU_ESCR0: 0, 1 - * MSR_P4_BSU_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_IOQ_ALLOCATION) = P4_OPCODE_PACK(0x03, 0x06), - /* - * MSR_P4_FSB_ESCR0: 0, 1 - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_IOQ_ACTIVE_ENTRIES) = P4_OPCODE_PACK(0x1a, 0x06), - /* - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_FSB_DATA_ACTIVITY) = P4_OPCODE_PACK(0x17, 0x06), - /* - * MSR_P4_FSB_ESCR0: 0, 1 - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_BSQ_ALLOCATION) = P4_OPCODE_PACK(0x05, 0x07), - /* - * MSR_P4_BSU_ESCR0: 0, 1 - */ - - P4_OPCODE(P4_EVENT_BSQ_ACTIVE_ENTRIES) = P4_OPCODE_PACK(0x06, 0x07), - /* - * NOTE: no ESCR name in docs, it's guessed - * MSR_P4_BSU_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_SSE_INPUT_ASSIST) = P4_OPCODE_PACK(0x34, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_PACKED_SP_UOP) = P4_OPCODE_PACK(0x08, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_PACKED_DP_UOP) = P4_OPCODE_PACK(0x0c, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_SCALAR_SP_UOP) = P4_OPCODE_PACK(0x0a, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_SCALAR_DP_UOP) = P4_OPCODE_PACK(0x0e, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_64BIT_MMX_UOP) = P4_OPCODE_PACK(0x02, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_128BIT_MMX_UOP) = P4_OPCODE_PACK(0x1a, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_X87_FP_UOP) = P4_OPCODE_PACK(0x04, 0x01), - /* - * MSR_P4_FIRM_ESCR0: 8, 9 - * MSR_P4_FIRM_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_TC_MISC) = P4_OPCODE_PACK(0x06, 0x01), - /* - * MSR_P4_TC_ESCR0: 4, 5 - * MSR_P4_TC_ESCR1: 6, 7 - */ - - P4_OPCODE(P4_EVENT_GLOBAL_POWER_EVENTS) = P4_OPCODE_PACK(0x13, 0x06), - /* - * MSR_P4_FSB_ESCR0: 0, 1 - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_TC_MS_XFER) = P4_OPCODE_PACK(0x05, 0x00), - /* - * MSR_P4_MS_ESCR0: 4, 5 - * MSR_P4_MS_ESCR1: 6, 7 - */ - - P4_OPCODE(P4_EVENT_UOP_QUEUE_WRITES) = P4_OPCODE_PACK(0x09, 0x00), - /* - * MSR_P4_MS_ESCR0: 4, 5 - * MSR_P4_MS_ESCR1: 6, 7 - */ - - P4_OPCODE(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE) = P4_OPCODE_PACK(0x05, 0x02), - /* - * MSR_P4_TBPU_ESCR0: 4, 5 - * MSR_P4_TBPU_ESCR1: 6, 7 - */ - - P4_OPCODE(P4_EVENT_RETIRED_BRANCH_TYPE) = P4_OPCODE_PACK(0x04, 0x02), - /* - * MSR_P4_TBPU_ESCR0: 4, 5 - * MSR_P4_TBPU_ESCR1: 6, 7 - */ - - P4_OPCODE(P4_EVENT_RESOURCE_STALL) = P4_OPCODE_PACK(0x01, 0x01), - /* - * MSR_P4_ALF_ESCR0: 12, 13, 16 - * MSR_P4_ALF_ESCR1: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_WC_BUFFER) = P4_OPCODE_PACK(0x05, 0x05), - /* - * MSR_P4_DAC_ESCR0: 8, 9 - * MSR_P4_DAC_ESCR1: 10, 11 - */ - - P4_OPCODE(P4_EVENT_B2B_CYCLES) = P4_OPCODE_PACK(0x16, 0x03), - /* - * MSR_P4_FSB_ESCR0: 0, 1 - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_BNR) = P4_OPCODE_PACK(0x08, 0x03), - /* - * MSR_P4_FSB_ESCR0: 0, 1 - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_SNOOP) = P4_OPCODE_PACK(0x06, 0x03), - /* - * MSR_P4_FSB_ESCR0: 0, 1 - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_RESPONSE) = P4_OPCODE_PACK(0x04, 0x03), - /* - * MSR_P4_FSB_ESCR0: 0, 1 - * MSR_P4_FSB_ESCR1: 2, 3 - */ - - P4_OPCODE(P4_EVENT_FRONT_END_EVENT) = P4_OPCODE_PACK(0x08, 0x05), - /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_EXECUTION_EVENT) = P4_OPCODE_PACK(0x0c, 0x05), - /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_REPLAY_EVENT) = P4_OPCODE_PACK(0x09, 0x05), - /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_INSTR_RETIRED) = P4_OPCODE_PACK(0x02, 0x04), - /* - * MSR_P4_CRU_ESCR0: 12, 13, 16 - * MSR_P4_CRU_ESCR1: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_UOPS_RETIRED) = P4_OPCODE_PACK(0x01, 0x04), - /* - * MSR_P4_CRU_ESCR0: 12, 13, 16 - * MSR_P4_CRU_ESCR1: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_UOP_TYPE) = P4_OPCODE_PACK(0x02, 0x02), - /* - * MSR_P4_RAT_ESCR0: 12, 13, 16 - * MSR_P4_RAT_ESCR1: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_BRANCH_RETIRED) = P4_OPCODE_PACK(0x06, 0x05), - /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_MISPRED_BRANCH_RETIRED) = P4_OPCODE_PACK(0x03, 0x04), - /* - * MSR_P4_CRU_ESCR0: 12, 13, 16 - * MSR_P4_CRU_ESCR1: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_X87_ASSIST) = P4_OPCODE_PACK(0x03, 0x05), - /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_MACHINE_CLEAR) = P4_OPCODE_PACK(0x02, 0x05), - /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 - */ - - P4_OPCODE(P4_EVENT_INSTR_COMPLETED) = P4_OPCODE_PACK(0x07, 0x04), - /* - * MSR_P4_CRU_ESCR0: 12, 13, 16 - * MSR_P4_CRU_ESCR1: 14, 15, 17 - */ -}; - -/* - * a caller should use P4_ESCR_EMASK_NAME helper to - * pick the EventMask needed, for example - * - * P4_ESCR_EMASK_NAME(P4_EVENT_TC_DELIVER_MODE, DD) - */ -enum P4_ESCR_EMASKS { - P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DD, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DB, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DI, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BD, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BB, 4), - P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BI, 5), - P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, ID, 6), - - P4_GEN_ESCR_EMASK(P4_EVENT_BPU_FETCH_REQUEST, TCMISS, 0), - - P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, HIT, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, MISS, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, HIT_UK, 2), - - P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_CANCEL, ST_RB_FULL, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_CANCEL, 64K_CONF, 3), - - P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_COMPLETE, LSC, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_COMPLETE, SSC, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_LOAD_PORT_REPLAY, SPLIT_LD, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_STORE_PORT_REPLAY, SPLIT_ST, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, NO_STA, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, NO_STD, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, PARTIAL_DATA, 4), - P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, UNALGN_ADDR, 5), - - P4_GEN_ESCR_EMASK(P4_EVENT_PAGE_WALK_TYPE, DTMISS, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_PAGE_WALK_TYPE, ITMISS, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITS, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITE, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITM, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITS, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITE, 4), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITM, 5), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_MISS, 8), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_MISS, 9), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, WR_2ndL_MISS, 10), - - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, DEFAULT, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, ALL_READ, 5), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, ALL_WRITE, 6), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_UC, 7), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WC, 8), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WT, 9), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WP, 10), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WB, 11), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, OWN, 13), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, OTHER, 14), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, PREFETCH, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, DEFAULT, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, ALL_READ, 5), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, ALL_WRITE, 6), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_UC, 7), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WC, 8), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WT, 9), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WP, 10), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WB, 11), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, OWN, 13), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, OTHER, 14), - P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, PREFETCH, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_DRV, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OWN, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OTHER, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_DRV, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_OWN, 4), - P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_OTHER, 5), - - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_TYPE0, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_TYPE1, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LEN0, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LEN1, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_IO_TYPE, 5), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LOCK_TYPE, 6), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_CACHE_TYPE, 7), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_SPLIT_TYPE, 8), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_DEM_TYPE, 9), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_ORD_TYPE, 10), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE0, 11), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE1, 12), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE2, 13), - - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_TYPE0, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_TYPE1, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LEN0, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LEN1, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_IO_TYPE, 5), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LOCK_TYPE, 6), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_CACHE_TYPE, 7), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_SPLIT_TYPE, 8), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_DEM_TYPE, 9), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_ORD_TYPE, 10), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE0, 11), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE1, 12), - P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE2, 13), - - P4_GEN_ESCR_EMASK(P4_EVENT_SSE_INPUT_ASSIST, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_PACKED_SP_UOP, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_PACKED_DP_UOP, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_SCALAR_SP_UOP, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_SCALAR_DP_UOP, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_64BIT_MMX_UOP, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_128BIT_MMX_UOP, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_X87_FP_UOP, ALL, 15), - - P4_GEN_ESCR_EMASK(P4_EVENT_TC_MISC, FLUSH, 4), - - P4_GEN_ESCR_EMASK(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING, 0), - - P4_GEN_ESCR_EMASK(P4_EVENT_TC_MS_XFER, CISC, 0), - - P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_TC_BUILD, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_TC_DELIVER, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_ROM, 2), - - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, CONDITIONAL, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, CALL, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, RETURN, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, INDIRECT, 4), - - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, CONDITIONAL, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, CALL, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, RETURN, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, INDIRECT, 4), - - P4_GEN_ESCR_EMASK(P4_EVENT_RESOURCE_STALL, SBFULL, 5), - - P4_GEN_ESCR_EMASK(P4_EVENT_WC_BUFFER, WCB_EVICTS, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_WC_BUFFER, WCB_FULL_EVICTS, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_FRONT_END_EVENT, NBOGUS, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_FRONT_END_EVENT, BOGUS, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS0, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS1, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS2, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS3, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS0, 4), - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS1, 5), - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS2, 6), - P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS3, 7), - - P4_GEN_ESCR_EMASK(P4_EVENT_REPLAY_EVENT, NBOGUS, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_REPLAY_EVENT, BOGUS, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, NBOGUSNTAG, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, NBOGUSTAG, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, BOGUSNTAG, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, BOGUSTAG, 3), - - P4_GEN_ESCR_EMASK(P4_EVENT_UOPS_RETIRED, NBOGUS, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_UOPS_RETIRED, BOGUS, 1), - - P4_GEN_ESCR_EMASK(P4_EVENT_UOP_TYPE, TAGLOADS, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_UOP_TYPE, TAGSTORES, 2), - - P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMNP, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMNM, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMTP, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMTM, 3), - - P4_GEN_ESCR_EMASK(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS, 0), - - P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, FPSU, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, FPSO, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, POAO, 2), - P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, POAU, 3), - P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, PREA, 4), - - P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, CLEAR, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, MOCLEAR, 1), - P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, SMCLEAR, 2), - - P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_COMPLETED, NBOGUS, 0), - P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_COMPLETED, BOGUS, 1), -}; - -/* P4 PEBS: stale for a while */ -#define P4_PEBS_METRIC_MASK 0x00001fffU -#define P4_PEBS_UOB_TAG 0x01000000U -#define P4_PEBS_ENABLE 0x02000000U - -/* Replay metrics for MSR_IA32_PEBS_ENABLE and MSR_P4_PEBS_MATRIX_VERT */ -#define P4_PEBS__1stl_cache_load_miss_retired 0x3000001 -#define P4_PEBS__2ndl_cache_load_miss_retired 0x3000002 -#define P4_PEBS__dtlb_load_miss_retired 0x3000004 -#define P4_PEBS__dtlb_store_miss_retired 0x3000004 -#define P4_PEBS__dtlb_all_miss_retired 0x3000004 -#define P4_PEBS__tagged_mispred_branch 0x3018000 -#define P4_PEBS__mob_load_replay_retired 0x3000200 -#define P4_PEBS__split_load_retired 0x3000400 -#define P4_PEBS__split_store_retired 0x3000400 - -#define P4_VERT__1stl_cache_load_miss_retired 0x0000001 -#define P4_VERT__2ndl_cache_load_miss_retired 0x0000001 -#define P4_VERT__dtlb_load_miss_retired 0x0000001 -#define P4_VERT__dtlb_store_miss_retired 0x0000002 -#define P4_VERT__dtlb_all_miss_retired 0x0000003 -#define P4_VERT__tagged_mispred_branch 0x0000010 -#define P4_VERT__mob_load_replay_retired 0x0000001 -#define P4_VERT__split_load_retired 0x0000001 -#define P4_VERT__split_store_retired 0x0000002 - -enum P4_CACHE_EVENTS { - P4_CACHE__NONE, - - P4_CACHE__1stl_cache_load_miss_retired, - P4_CACHE__2ndl_cache_load_miss_retired, - P4_CACHE__dtlb_load_miss_retired, - P4_CACHE__dtlb_store_miss_retired, - P4_CACHE__itlb_reference_hit, - P4_CACHE__itlb_reference_miss, - - P4_CACHE__MAX -}; - -#endif /* PERF_EVENT_P4_H */ diff --git a/trunk/arch/x86/include/asm/processor.h b/trunk/arch/x86/include/asm/processor.h index 32428b410b55..b753ea59703a 100644 --- a/trunk/arch/x86/include/asm/processor.h +++ b/trunk/arch/x86/include/asm/processor.h @@ -21,6 +21,7 @@ struct mm_struct; #include #include #include +#include #include #include @@ -28,7 +29,6 @@ struct mm_struct; #include #include #include -#include #define HBP_NUM 4 /* @@ -473,6 +473,10 @@ struct thread_struct { unsigned long iopl; /* Max allowed port in the bitmap, in bytes: */ unsigned io_bitmap_max; +/* MSR_IA32_DEBUGCTLMSR value to switch in if TIF_DEBUGCTLMSR is set. */ + unsigned long debugctlmsr; + /* Debug Store context; see asm/ds.h */ + struct ds_context *ds_ctx; }; static inline unsigned long native_get_debugreg(int regno) @@ -799,7 +803,7 @@ extern void cpu_init(void); static inline unsigned long get_debugctlmsr(void) { - unsigned long debugctlmsr = 0; + unsigned long debugctlmsr = 0; #ifndef CONFIG_X86_DEBUGCTLMSR if (boot_cpu_data.x86 < 6) @@ -807,6 +811,21 @@ static inline unsigned long get_debugctlmsr(void) #endif rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr); + return debugctlmsr; +} + +static inline unsigned long get_debugctlmsr_on_cpu(int cpu) +{ + u64 debugctlmsr = 0; + u32 val1, val2; + +#ifndef CONFIG_X86_DEBUGCTLMSR + if (boot_cpu_data.x86 < 6) + return 0; +#endif + rdmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR, &val1, &val2); + debugctlmsr = val1 | ((u64)val2 << 32); + return debugctlmsr; } @@ -819,6 +838,18 @@ static inline void update_debugctlmsr(unsigned long debugctlmsr) wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr); } +static inline void update_debugctlmsr_on_cpu(int cpu, + unsigned long debugctlmsr) +{ +#ifndef CONFIG_X86_DEBUGCTLMSR + if (boot_cpu_data.x86 < 6) + return; +#endif + wrmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR, + (u32)((u64)debugctlmsr), + (u32)((u64)debugctlmsr >> 32)); +} + /* * from system description table in BIOS. Mostly for MCA use, but * others may find it useful: diff --git a/trunk/arch/x86/include/asm/ptrace-abi.h b/trunk/arch/x86/include/asm/ptrace-abi.h index 52b098a6eebb..86723035a515 100644 --- a/trunk/arch/x86/include/asm/ptrace-abi.h +++ b/trunk/arch/x86/include/asm/ptrace-abi.h @@ -82,6 +82,61 @@ #ifndef __ASSEMBLY__ #include -#endif + +/* configuration/status structure used in PTRACE_BTS_CONFIG and + PTRACE_BTS_STATUS commands. +*/ +struct ptrace_bts_config { + /* requested or actual size of BTS buffer in bytes */ + __u32 size; + /* bitmask of below flags */ + __u32 flags; + /* buffer overflow signal */ + __u32 signal; + /* actual size of bts_struct in bytes */ + __u32 bts_size; +}; +#endif /* __ASSEMBLY__ */ + +#define PTRACE_BTS_O_TRACE 0x1 /* branch trace */ +#define PTRACE_BTS_O_SCHED 0x2 /* scheduling events w/ jiffies */ +#define PTRACE_BTS_O_SIGNAL 0x4 /* send SIG on buffer overflow + instead of wrapping around */ +#define PTRACE_BTS_O_ALLOC 0x8 /* (re)allocate buffer */ + +#define PTRACE_BTS_CONFIG 40 +/* Configure branch trace recording. + ADDR points to a struct ptrace_bts_config. + DATA gives the size of that buffer. + A new buffer is allocated, if requested in the flags. + An overflow signal may only be requested for new buffers. + Returns the number of bytes read. +*/ +#define PTRACE_BTS_STATUS 41 +/* Return the current configuration in a struct ptrace_bts_config + pointed to by ADDR; DATA gives the size of that buffer. + Returns the number of bytes written. +*/ +#define PTRACE_BTS_SIZE 42 +/* Return the number of available BTS records for draining. + DATA and ADDR are ignored. +*/ +#define PTRACE_BTS_GET 43 +/* Get a single BTS record. + DATA defines the index into the BTS array, where 0 is the newest + entry, and higher indices refer to older entries. + ADDR is pointing to struct bts_struct (see asm/ds.h). +*/ +#define PTRACE_BTS_CLEAR 44 +/* Clear the BTS buffer. + DATA and ADDR are ignored. +*/ +#define PTRACE_BTS_DRAIN 45 +/* Read all available BTS records and clear the buffer. + ADDR points to an array of struct bts_struct. + DATA gives the size of that buffer. + BTS records are read from oldest to newest. + Returns number of BTS records drained. +*/ #endif /* _ASM_X86_PTRACE_ABI_H */ diff --git a/trunk/arch/x86/include/asm/ptrace.h b/trunk/arch/x86/include/asm/ptrace.h index 78cd1ea94500..69a686a7dff0 100644 --- a/trunk/arch/x86/include/asm/ptrace.h +++ b/trunk/arch/x86/include/asm/ptrace.h @@ -289,6 +289,12 @@ extern int do_get_thread_area(struct task_struct *p, int idx, extern int do_set_thread_area(struct task_struct *p, int idx, struct user_desc __user *info, int can_allocate); +#ifdef CONFIG_X86_PTRACE_BTS +extern void ptrace_bts_untrace(struct task_struct *tsk); + +#define arch_ptrace_untrace(tsk) ptrace_bts_untrace(tsk) +#endif /* CONFIG_X86_PTRACE_BTS */ + #endif /* __KERNEL__ */ #endif /* !__ASSEMBLY__ */ diff --git a/trunk/arch/x86/include/asm/thread_info.h b/trunk/arch/x86/include/asm/thread_info.h index d017ed5502e2..e0d28901e969 100644 --- a/trunk/arch/x86/include/asm/thread_info.h +++ b/trunk/arch/x86/include/asm/thread_info.h @@ -92,7 +92,8 @@ struct thread_info { #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ #define TIF_FREEZE 23 /* is freezing for suspend */ #define TIF_FORCED_TF 24 /* true if TF in eflags artificially */ -#define TIF_BLOCKSTEP 25 /* set when we want DEBUGCTLMSR_BTF */ +#define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */ +#define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */ #define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */ #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ @@ -114,7 +115,8 @@ struct thread_info { #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) #define _TIF_FREEZE (1 << TIF_FREEZE) #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) -#define _TIF_BLOCKSTEP (1 << TIF_BLOCKSTEP) +#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR) +#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR) #define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) @@ -145,7 +147,7 @@ struct thread_info { /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW \ - (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP) + (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_NOTSC) #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) diff --git a/trunk/arch/x86/kernel/Makefile b/trunk/arch/x86/kernel/Makefile index e77b22083721..4c58352209e0 100644 --- a/trunk/arch/x86/kernel/Makefile +++ b/trunk/arch/x86/kernel/Makefile @@ -47,6 +47,8 @@ obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-y += process.o obj-y += i387.o xsave.o obj-y += ptrace.o +obj-$(CONFIG_X86_DS) += ds.o +obj-$(CONFIG_X86_DS_SELFTEST) += ds_selftest.o obj-$(CONFIG_X86_32) += tls.o obj-$(CONFIG_IA32_EMULATION) += tls.o obj-y += step.o diff --git a/trunk/arch/x86/kernel/apic/io_apic.c b/trunk/arch/x86/kernel/apic/io_apic.c index 127b8718abfb..eb2789c3f721 100644 --- a/trunk/arch/x86/kernel/apic/io_apic.c +++ b/trunk/arch/x86/kernel/apic/io_apic.c @@ -2545,6 +2545,9 @@ void irq_force_complete_move(int irq) struct irq_desc *desc = irq_to_desc(irq); struct irq_cfg *cfg = desc->chip_data; + if (!cfg) + return; + __irq_complete_move(&desc, cfg->vector); } #else diff --git a/trunk/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/trunk/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index d360b56e9825..b6215b9798e2 100644 --- a/trunk/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/trunk/arch/x86/kernel/cpu/cpufreq/powernow-k8.c @@ -929,7 +929,8 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, powernow_table[i].index = index; /* Frequency may be rounded for these */ - if (boot_cpu_data.x86 == 0x10 || boot_cpu_data.x86 == 0x11) { + if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) + || boot_cpu_data.x86 == 0x11) { powernow_table[i].frequency = freq_from_fid_did(lo & 0x3f, (lo >> 6) & 7); } else diff --git a/trunk/arch/x86/kernel/cpu/intel.c b/trunk/arch/x86/kernel/cpu/intel.c index f5e5390d3459..1366c7cfd483 100644 --- a/trunk/arch/x86/kernel/cpu/intel.c +++ b/trunk/arch/x86/kernel/cpu/intel.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -387,6 +388,7 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c) set_cpu_cap(c, X86_FEATURE_BTS); if (!(l1 & (1<<12))) set_cpu_cap(c, X86_FEATURE_PEBS); + ds_init_intel(c); } if (c->x86 == 6 && c->x86_model == 29 && cpu_has_clflush) diff --git a/trunk/arch/x86/kernel/cpu/perf_event.c b/trunk/arch/x86/kernel/cpu/perf_event.c index 7de70613e6c3..db5bdc8addf8 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event.c +++ b/trunk/arch/x86/kernel/cpu/perf_event.c @@ -31,51 +31,46 @@ #include #include -#if 0 -#undef wrmsrl -#define wrmsrl(msr, val) \ -do { \ - trace_printk("wrmsrl(%lx, %lx)\n", (unsigned long)(msr),\ - (unsigned long)(val)); \ - native_write_msr((msr), (u32)((u64)(val)), \ - (u32)((u64)(val) >> 32)); \ -} while (0) -#endif +static u64 perf_event_mask __read_mostly; -/* - * best effort, GUP based copy_from_user() that assumes IRQ or NMI context - */ -static unsigned long -copy_from_user_nmi(void *to, const void __user *from, unsigned long n) -{ - unsigned long offset, addr = (unsigned long)from; - int type = in_nmi() ? KM_NMI : KM_IRQ0; - unsigned long size, len = 0; - struct page *page; - void *map; - int ret; +/* The maximal number of PEBS events: */ +#define MAX_PEBS_EVENTS 4 - do { - ret = __get_user_pages_fast(addr, 1, 0, &page); - if (!ret) - break; +/* The size of a BTS record in bytes: */ +#define BTS_RECORD_SIZE 24 - offset = addr & (PAGE_SIZE - 1); - size = min(PAGE_SIZE - offset, n - len); +/* The size of a per-cpu BTS buffer in bytes: */ +#define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 2048) - map = kmap_atomic(page, type); - memcpy(to, map+offset, size); - kunmap_atomic(map, type); - put_page(page); +/* The BTS overflow threshold in bytes from the end of the buffer: */ +#define BTS_OVFL_TH (BTS_RECORD_SIZE * 128) - len += size; - to += size; - addr += size; - } while (len < n); +/* + * Bits in the debugctlmsr controlling branch tracing. + */ +#define X86_DEBUGCTL_TR (1 << 6) +#define X86_DEBUGCTL_BTS (1 << 7) +#define X86_DEBUGCTL_BTINT (1 << 8) +#define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) +#define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) - return len; -} +/* + * A debug store configuration. + * + * We only support architectures that use 64bit fields. + */ +struct debug_store { + u64 bts_buffer_base; + u64 bts_index; + u64 bts_absolute_maximum; + u64 bts_interrupt_threshold; + u64 pebs_buffer_base; + u64 pebs_index; + u64 pebs_absolute_maximum; + u64 pebs_interrupt_threshold; + u64 pebs_event_reset[MAX_PEBS_EVENTS]; +}; struct event_constraint { union { @@ -94,39 +89,18 @@ struct amd_nb { struct event_constraint event_constraints[X86_PMC_IDX_MAX]; }; -#define MAX_LBR_ENTRIES 16 - struct cpu_hw_events { - /* - * Generic x86 PMC bits - */ struct perf_event *events[X86_PMC_IDX_MAX]; /* in counter order */ unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; + unsigned long interrupts; int enabled; + struct debug_store *ds; int n_events; int n_added; int assign[X86_PMC_IDX_MAX]; /* event to counter assignment */ u64 tags[X86_PMC_IDX_MAX]; struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ - - /* - * Intel DebugStore bits - */ - struct debug_store *ds; - u64 pebs_enabled; - - /* - * Intel LBR bits - */ - int lbr_users; - void *lbr_context; - struct perf_branch_stack lbr_stack; - struct perf_branch_entry lbr_entries[MAX_LBR_ENTRIES]; - - /* - * AMD specific bits - */ struct amd_nb *amd_nb; }; @@ -140,31 +114,11 @@ struct cpu_hw_events { #define EVENT_CONSTRAINT(c, n, m) \ __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n)) -/* - * Constraint on the Event code. - */ #define INTEL_EVENT_CONSTRAINT(c, n) \ - EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT) + EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVTSEL_MASK) -/* - * Constraint on the Event code + UMask + fixed-mask - * - * filter mask to validate fixed counter events. - * the following filters disqualify for fixed counters: - * - inv - * - edge - * - cnt-mask - * The other filters are supported by fixed counters. - * The any-thread option is supported starting with v3. - */ #define FIXED_EVENT_CONSTRAINT(c, n) \ - EVENT_CONSTRAINT(c, (1ULL << (32+n)), X86_RAW_EVENT_MASK) - -/* - * Constraint on the Event code + UMask - */ -#define PEBS_EVENT_CONSTRAINT(c, n) \ - EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) + EVENT_CONSTRAINT(c, (1ULL << (32+n)), INTEL_ARCH_FIXED_MASK) #define EVENT_CONSTRAINT_END \ EVENT_CONSTRAINT(0, 0, 0) @@ -172,43 +126,32 @@ struct cpu_hw_events { #define for_each_event_constraint(e, c) \ for ((e) = (c); (e)->cmask; (e)++) -union perf_capabilities { - struct { - u64 lbr_format : 6; - u64 pebs_trap : 1; - u64 pebs_arch_reg : 1; - u64 pebs_format : 4; - u64 smm_freeze : 1; - }; - u64 capabilities; -}; - /* * struct x86_pmu - generic x86 pmu */ struct x86_pmu { - /* - * Generic x86 PMC bits - */ const char *name; int version; int (*handle_irq)(struct pt_regs *); void (*disable_all)(void); - void (*enable_all)(int added); + void (*enable_all)(void); void (*enable)(struct perf_event *); void (*disable)(struct perf_event *); - int (*hw_config)(struct perf_event *event); - int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign); unsigned eventsel; unsigned perfctr; u64 (*event_map)(int); + u64 (*raw_event)(u64); int max_events; - int num_counters; - int num_counters_fixed; - int cntval_bits; - u64 cntval_mask; + int num_events; + int num_events_fixed; + int event_bits; + u64 event_mask; int apic; u64 max_period; + u64 intel_ctrl; + void (*enable_bts)(u64 config); + void (*disable_bts)(void); + struct event_constraint * (*get_event_constraints)(struct cpu_hw_events *cpuc, struct perf_event *event); @@ -216,32 +159,11 @@ struct x86_pmu { void (*put_event_constraints)(struct cpu_hw_events *cpuc, struct perf_event *event); struct event_constraint *event_constraints; - void (*quirks)(void); int (*cpu_prepare)(int cpu); void (*cpu_starting)(int cpu); void (*cpu_dying)(int cpu); void (*cpu_dead)(int cpu); - - /* - * Intel Arch Perfmon v2+ - */ - u64 intel_ctrl; - union perf_capabilities intel_cap; - - /* - * Intel DebugStore bits - */ - int bts, pebs; - int pebs_record_size; - void (*drain_pebs)(struct pt_regs *regs); - struct event_constraint *pebs_constraints; - - /* - * Intel LBR - */ - unsigned long lbr_tos, lbr_from, lbr_to; /* MSR base regs */ - int lbr_nr; /* hardware stack size */ }; static struct x86_pmu x86_pmu __read_mostly; @@ -276,7 +198,7 @@ static u64 x86_perf_event_update(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - int shift = 64 - x86_pmu.cntval_bits; + int shift = 64 - x86_pmu.event_bits; u64 prev_raw_count, new_raw_count; int idx = hwc->idx; s64 delta; @@ -319,32 +241,33 @@ x86_perf_event_update(struct perf_event *event) static atomic_t active_events; static DEFINE_MUTEX(pmc_reserve_mutex); -#ifdef CONFIG_X86_LOCAL_APIC - static bool reserve_pmc_hardware(void) { +#ifdef CONFIG_X86_LOCAL_APIC int i; if (nmi_watchdog == NMI_LOCAL_APIC) disable_lapic_nmi_watchdog(); - for (i = 0; i < x86_pmu.num_counters; i++) { + for (i = 0; i < x86_pmu.num_events; i++) { if (!reserve_perfctr_nmi(x86_pmu.perfctr + i)) goto perfctr_fail; } - for (i = 0; i < x86_pmu.num_counters; i++) { + for (i = 0; i < x86_pmu.num_events; i++) { if (!reserve_evntsel_nmi(x86_pmu.eventsel + i)) goto eventsel_fail; } +#endif return true; +#ifdef CONFIG_X86_LOCAL_APIC eventsel_fail: for (i--; i >= 0; i--) release_evntsel_nmi(x86_pmu.eventsel + i); - i = x86_pmu.num_counters; + i = x86_pmu.num_events; perfctr_fail: for (i--; i >= 0; i--) @@ -354,36 +277,128 @@ static bool reserve_pmc_hardware(void) enable_lapic_nmi_watchdog(); return false; +#endif } static void release_pmc_hardware(void) { +#ifdef CONFIG_X86_LOCAL_APIC int i; - for (i = 0; i < x86_pmu.num_counters; i++) { + for (i = 0; i < x86_pmu.num_events; i++) { release_perfctr_nmi(x86_pmu.perfctr + i); release_evntsel_nmi(x86_pmu.eventsel + i); } if (nmi_watchdog == NMI_LOCAL_APIC) enable_lapic_nmi_watchdog(); +#endif } -#else +static inline bool bts_available(void) +{ + return x86_pmu.enable_bts != NULL; +} -static bool reserve_pmc_hardware(void) { return true; } -static void release_pmc_hardware(void) {} +static void init_debug_store_on_cpu(int cpu) +{ + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; -#endif + if (!ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, + (u32)((u64)(unsigned long)ds), + (u32)((u64)(unsigned long)ds >> 32)); +} + +static void fini_debug_store_on_cpu(int cpu) +{ + if (!per_cpu(cpu_hw_events, cpu).ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); +} + +static void release_bts_hardware(void) +{ + int cpu; + + if (!bts_available()) + return; + + get_online_cpus(); + + for_each_online_cpu(cpu) + fini_debug_store_on_cpu(cpu); + + for_each_possible_cpu(cpu) { + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; + + if (!ds) + continue; -static int reserve_ds_buffers(void); -static void release_ds_buffers(void); + per_cpu(cpu_hw_events, cpu).ds = NULL; + + kfree((void *)(unsigned long)ds->bts_buffer_base); + kfree(ds); + } + + put_online_cpus(); +} + +static int reserve_bts_hardware(void) +{ + int cpu, err = 0; + + if (!bts_available()) + return 0; + + get_online_cpus(); + + for_each_possible_cpu(cpu) { + struct debug_store *ds; + void *buffer; + + err = -ENOMEM; + buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); + if (unlikely(!buffer)) + break; + + ds = kzalloc(sizeof(*ds), GFP_KERNEL); + if (unlikely(!ds)) { + kfree(buffer); + break; + } + + ds->bts_buffer_base = (u64)(unsigned long)buffer; + ds->bts_index = ds->bts_buffer_base; + ds->bts_absolute_maximum = + ds->bts_buffer_base + BTS_BUFFER_SIZE; + ds->bts_interrupt_threshold = + ds->bts_absolute_maximum - BTS_OVFL_TH; + + per_cpu(cpu_hw_events, cpu).ds = ds; + err = 0; + } + + if (err) + release_bts_hardware(); + else { + for_each_online_cpu(cpu) + init_debug_store_on_cpu(cpu); + } + + put_online_cpus(); + + return err; +} static void hw_perf_event_destroy(struct perf_event *event) { if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) { release_pmc_hardware(); - release_ds_buffers(); + release_bts_hardware(); mutex_unlock(&pmc_reserve_mutex); } } @@ -426,28 +441,6 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event_attr *attr) return 0; } -static int x86_pmu_hw_config(struct perf_event *event) -{ - /* - * Generate PMC IRQs: - * (keep 'enabled' bit clear for now) - */ - event->hw.config = ARCH_PERFMON_EVENTSEL_INT; - - /* - * Count user and OS events unless requested not to - */ - if (!event->attr.exclude_user) - event->hw.config |= ARCH_PERFMON_EVENTSEL_USR; - if (!event->attr.exclude_kernel) - event->hw.config |= ARCH_PERFMON_EVENTSEL_OS; - - if (event->attr.type == PERF_TYPE_RAW) - event->hw.config |= event->attr.config & X86_RAW_EVENT_MASK; - - return 0; -} - /* * Setup the hardware configuration for a given attr_type */ @@ -467,11 +460,8 @@ static int __hw_perf_event_init(struct perf_event *event) if (atomic_read(&active_events) == 0) { if (!reserve_pmc_hardware()) err = -EBUSY; - else { - err = reserve_ds_buffers(); - if (err) - release_pmc_hardware(); - } + else + err = reserve_bts_hardware(); } if (!err) atomic_inc(&active_events); @@ -482,14 +472,23 @@ static int __hw_perf_event_init(struct perf_event *event) event->destroy = hw_perf_event_destroy; + /* + * Generate PMC IRQs: + * (keep 'enabled' bit clear for now) + */ + hwc->config = ARCH_PERFMON_EVENTSEL_INT; + hwc->idx = -1; hwc->last_cpu = -1; hwc->last_tag = ~0ULL; - /* Processor specifics */ - err = x86_pmu.hw_config(event); - if (err) - return err; + /* + * Count user and OS events unless requested not to. + */ + if (!attr->exclude_user) + hwc->config |= ARCH_PERFMON_EVENTSEL_USR; + if (!attr->exclude_kernel) + hwc->config |= ARCH_PERFMON_EVENTSEL_OS; if (!hwc->sample_period) { hwc->sample_period = x86_pmu.max_period; @@ -506,8 +505,16 @@ static int __hw_perf_event_init(struct perf_event *event) return -EOPNOTSUPP; } - if (attr->type == PERF_TYPE_RAW) + /* + * Raw hw_event type provide the config in the hw_event structure + */ + if (attr->type == PERF_TYPE_RAW) { + hwc->config |= x86_pmu.raw_event(attr->config); + if ((hwc->config & ARCH_PERFMON_EVENTSEL_ANY) && + perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) + return -EACCES; return 0; + } if (attr->type == PERF_TYPE_HW_CACHE) return set_ext_hw_attr(hwc, attr); @@ -532,11 +539,11 @@ static int __hw_perf_event_init(struct perf_event *event) if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && (hwc->sample_period == 1)) { /* BTS is not supported by this architecture. */ - if (!x86_pmu.bts) + if (!bts_available()) return -EOPNOTSUPP; /* BTS is currently only allowed for user-mode. */ - if (!attr->exclude_kernel) + if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) return -EOPNOTSUPP; } @@ -550,7 +557,7 @@ static void x86_pmu_disable_all(void) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; - for (idx = 0; idx < x86_pmu.num_counters; idx++) { + for (idx = 0; idx < x86_pmu.num_events; idx++) { u64 val; if (!test_bit(idx, cpuc->active_mask)) @@ -580,12 +587,12 @@ void hw_perf_disable(void) x86_pmu.disable_all(); } -static void x86_pmu_enable_all(int added) +static void x86_pmu_enable_all(void) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; - for (idx = 0; idx < x86_pmu.num_counters; idx++) { + for (idx = 0; idx < x86_pmu.num_events; idx++) { struct perf_event *event = cpuc->events[idx]; u64 val; @@ -660,14 +667,14 @@ static int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) * assign events to counters starting with most * constrained events. */ - wmax = x86_pmu.num_counters; + wmax = x86_pmu.num_events; /* * when fixed event counters are present, * wmax is incremented by 1 to account * for one more choice */ - if (x86_pmu.num_counters_fixed) + if (x86_pmu.num_events_fixed) wmax++; for (w = 1, num = n; num && w <= wmax; w++) { @@ -717,7 +724,7 @@ static int collect_events(struct cpu_hw_events *cpuc, struct perf_event *leader, struct perf_event *event; int n, max_count; - max_count = x86_pmu.num_counters + x86_pmu.num_counters_fixed; + max_count = x86_pmu.num_events + x86_pmu.num_events_fixed; /* current number of events already accepted */ n = cpuc->n_events; @@ -788,7 +795,7 @@ void hw_perf_enable(void) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct perf_event *event; struct hw_perf_event *hwc; - int i, added = cpuc->n_added; + int i; if (!x86_pmu_initialized()) return; @@ -840,20 +847,19 @@ void hw_perf_enable(void) cpuc->enabled = 1; barrier(); - x86_pmu.enable_all(added); + x86_pmu.enable_all(); } static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc) { - wrmsrl(hwc->config_base + hwc->idx, + (void)checking_wrmsrl(hwc->config_base + hwc->idx, hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE); } static inline void x86_pmu_disable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - - wrmsrl(hwc->config_base + hwc->idx, hwc->config); + (void)checking_wrmsrl(hwc->config_base + hwc->idx, hwc->config); } static DEFINE_PER_CPU(u64 [X86_PMC_IDX_MAX], pmc_prev_left); @@ -868,7 +874,7 @@ x86_perf_event_set_period(struct perf_event *event) struct hw_perf_event *hwc = &event->hw; s64 left = atomic64_read(&hwc->period_left); s64 period = hwc->sample_period; - int ret = 0, idx = hwc->idx; + int err, ret = 0, idx = hwc->idx; if (idx == X86_PMC_IDX_FIXED_BTS) return 0; @@ -906,8 +912,8 @@ x86_perf_event_set_period(struct perf_event *event) */ atomic64_set(&hwc->prev_count, (u64)-left); - wrmsrl(hwc->event_base + idx, - (u64)(-left) & x86_pmu.cntval_mask); + err = checking_wrmsrl(hwc->event_base + idx, + (u64)(-left) & x86_pmu.event_mask); perf_event_update_userpage(event); @@ -944,7 +950,7 @@ static int x86_pmu_enable(struct perf_event *event) if (n < 0) return n; - ret = x86_pmu.schedule_events(cpuc, n, assign); + ret = x86_schedule_events(cpuc, n, assign); if (ret) return ret; /* @@ -985,12 +991,11 @@ static void x86_pmu_unthrottle(struct perf_event *event) void perf_event_print_debug(void) { u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed; - u64 pebs; struct cpu_hw_events *cpuc; unsigned long flags; int cpu, idx; - if (!x86_pmu.num_counters) + if (!x86_pmu.num_events) return; local_irq_save(flags); @@ -1003,18 +1008,16 @@ void perf_event_print_debug(void) rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow); rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed); - rdmsrl(MSR_IA32_PEBS_ENABLE, pebs); pr_info("\n"); pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl); pr_info("CPU#%d: status: %016llx\n", cpu, status); pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow); pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed); - pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs); } - pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask); + pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask); - for (idx = 0; idx < x86_pmu.num_counters; idx++) { + for (idx = 0; idx < x86_pmu.num_events; idx++) { rdmsrl(x86_pmu.eventsel + idx, pmc_ctrl); rdmsrl(x86_pmu.perfctr + idx, pmc_count); @@ -1027,7 +1030,7 @@ void perf_event_print_debug(void) pr_info("CPU#%d: gen-PMC%d left: %016llx\n", cpu, idx, prev_left); } - for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) { + for (idx = 0; idx < x86_pmu.num_events_fixed; idx++) { rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, pmc_count); pr_info("CPU#%d: fixed-PMC%d count: %016llx\n", @@ -1092,7 +1095,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs) cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx < x86_pmu.num_counters; idx++) { + for (idx = 0; idx < x86_pmu.num_events; idx++) { if (!test_bit(idx, cpuc->active_mask)) continue; @@ -1100,7 +1103,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs) hwc = &event->hw; val = x86_perf_event_update(event); - if (val & (1ULL << (x86_pmu.cntval_bits - 1))) + if (val & (1ULL << (x86_pmu.event_bits - 1))) continue; /* @@ -1143,6 +1146,7 @@ void set_perf_event_pending(void) void perf_events_lapic_init(void) { +#ifdef CONFIG_X86_LOCAL_APIC if (!x86_pmu.apic || !x86_pmu_initialized()) return; @@ -1150,6 +1154,7 @@ void perf_events_lapic_init(void) * Always use NMI for PMU */ apic_write(APIC_LVTPC, APIC_DM_NMI); +#endif } static int __kprobes @@ -1173,7 +1178,9 @@ perf_event_nmi_handler(struct notifier_block *self, regs = args->regs; +#ifdef CONFIG_X86_LOCAL_APIC apic_write(APIC_LVTPC, APIC_DM_NMI); +#endif /* * Can't rely on the handled return value to say it was our NMI, two * events could trigger 'simultaneously' raising two back-to-back NMIs. @@ -1267,15 +1274,12 @@ int hw_perf_group_sched_in(struct perf_event *leader, int assign[X86_PMC_IDX_MAX]; int n0, n1, ret; - if (!x86_pmu_initialized()) - return 0; - /* n0 = total number of events */ n0 = collect_events(cpuc, leader, true); if (n0 < 0) return n0; - ret = x86_pmu.schedule_events(cpuc, n0, assign); + ret = x86_schedule_events(cpuc, n0, assign); if (ret) return ret; @@ -1325,9 +1329,6 @@ int hw_perf_group_sched_in(struct perf_event *leader, #include "perf_event_amd.c" #include "perf_event_p6.c" -#include "perf_event_p4.c" -#include "perf_event_intel_lbr.c" -#include "perf_event_intel_ds.c" #include "perf_event_intel.c" static int __cpuinit @@ -1401,50 +1402,48 @@ void __init init_hw_perf_events(void) pr_cont("%s PMU driver.\n", x86_pmu.name); - if (x86_pmu.quirks) - x86_pmu.quirks(); - - if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) { + if (x86_pmu.num_events > X86_PMC_MAX_GENERIC) { WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!", - x86_pmu.num_counters, X86_PMC_MAX_GENERIC); - x86_pmu.num_counters = X86_PMC_MAX_GENERIC; + x86_pmu.num_events, X86_PMC_MAX_GENERIC); + x86_pmu.num_events = X86_PMC_MAX_GENERIC; } - x86_pmu.intel_ctrl = (1 << x86_pmu.num_counters) - 1; - perf_max_events = x86_pmu.num_counters; + perf_event_mask = (1 << x86_pmu.num_events) - 1; + perf_max_events = x86_pmu.num_events; - if (x86_pmu.num_counters_fixed > X86_PMC_MAX_FIXED) { + if (x86_pmu.num_events_fixed > X86_PMC_MAX_FIXED) { WARN(1, KERN_ERR "hw perf events fixed %d > max(%d), clipping!", - x86_pmu.num_counters_fixed, X86_PMC_MAX_FIXED); - x86_pmu.num_counters_fixed = X86_PMC_MAX_FIXED; + x86_pmu.num_events_fixed, X86_PMC_MAX_FIXED); + x86_pmu.num_events_fixed = X86_PMC_MAX_FIXED; } - x86_pmu.intel_ctrl |= - ((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED; + perf_event_mask |= + ((1LL << x86_pmu.num_events_fixed)-1) << X86_PMC_IDX_FIXED; + x86_pmu.intel_ctrl = perf_event_mask; perf_events_lapic_init(); register_die_notifier(&perf_event_nmi_notifier); unconstrained = (struct event_constraint) - __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, - 0, x86_pmu.num_counters); + __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_events) - 1, + 0, x86_pmu.num_events); if (x86_pmu.event_constraints) { for_each_event_constraint(c, x86_pmu.event_constraints) { - if (c->cmask != X86_RAW_EVENT_MASK) + if (c->cmask != INTEL_ARCH_FIXED_MASK) continue; - c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1; - c->weight += x86_pmu.num_counters; + c->idxmsk64 |= (1ULL << x86_pmu.num_events) - 1; + c->weight += x86_pmu.num_events; } } pr_info("... version: %d\n", x86_pmu.version); - pr_info("... bit width: %d\n", x86_pmu.cntval_bits); - pr_info("... generic registers: %d\n", x86_pmu.num_counters); - pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask); + pr_info("... bit width: %d\n", x86_pmu.event_bits); + pr_info("... generic registers: %d\n", x86_pmu.num_events); + pr_info("... value mask: %016Lx\n", x86_pmu.event_mask); pr_info("... max period: %016Lx\n", x86_pmu.max_period); - pr_info("... fixed-purpose events: %d\n", x86_pmu.num_counters_fixed); - pr_info("... event mask: %016Lx\n", x86_pmu.intel_ctrl); + pr_info("... fixed-purpose events: %d\n", x86_pmu.num_events_fixed); + pr_info("... event mask: %016Lx\n", perf_event_mask); perf_cpu_notifier(x86_pmu_notifier); } @@ -1463,32 +1462,6 @@ static const struct pmu pmu = { .unthrottle = x86_pmu_unthrottle, }; -/* - * validate that we can schedule this event - */ -static int validate_event(struct perf_event *event) -{ - struct cpu_hw_events *fake_cpuc; - struct event_constraint *c; - int ret = 0; - - fake_cpuc = kmalloc(sizeof(*fake_cpuc), GFP_KERNEL | __GFP_ZERO); - if (!fake_cpuc) - return -ENOMEM; - - c = x86_pmu.get_event_constraints(fake_cpuc, event); - - if (!c || !c->weight) - ret = -ENOSPC; - - if (x86_pmu.put_event_constraints) - x86_pmu.put_event_constraints(fake_cpuc, event); - - kfree(fake_cpuc); - - return ret; -} - /* * validate a single event group * @@ -1529,7 +1502,7 @@ static int validate_group(struct perf_event *event) fake_cpuc->n_events = n; - ret = x86_pmu.schedule_events(fake_cpuc, n, NULL); + ret = x86_schedule_events(fake_cpuc, n, NULL); out_free: kfree(fake_cpuc); @@ -1554,8 +1527,6 @@ const struct pmu *hw_perf_event_init(struct perf_event *event) if (event->group_leader != event) err = validate_group(event); - else - err = validate_event(event); event->pmu = tmp; } @@ -1603,7 +1574,8 @@ static void backtrace_address(void *data, unsigned long addr, int reliable) { struct perf_callchain_entry *entry = data; - callchain_store(entry, addr); + if (reliable) + callchain_store(entry, addr); } static const struct stacktrace_ops backtrace_ops = { @@ -1625,6 +1597,41 @@ perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) dump_trace(NULL, regs, NULL, regs->bp, &backtrace_ops, entry); } +/* + * best effort, GUP based copy_from_user() that assumes IRQ or NMI context + */ +static unsigned long +copy_from_user_nmi(void *to, const void __user *from, unsigned long n) +{ + unsigned long offset, addr = (unsigned long)from; + int type = in_nmi() ? KM_NMI : KM_IRQ0; + unsigned long size, len = 0; + struct page *page; + void *map; + int ret; + + do { + ret = __get_user_pages_fast(addr, 1, 0, &page); + if (!ret) + break; + + offset = addr & (PAGE_SIZE - 1); + size = min(PAGE_SIZE - offset, n - len); + + map = kmap_atomic(page, type); + memcpy(to, map+offset, size); + kunmap_atomic(map, type); + put_page(page); + + len += size; + to += size; + addr += size; + + } while (len < n); + + return len; +} + #ifdef CONFIG_COMPAT static inline int perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry) @@ -1720,11 +1727,6 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) { struct perf_callchain_entry *entry; - if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { - /* TODO: We don't support guest os callchain now */ - return NULL; - } - if (in_nmi()) entry = &__get_cpu_var(pmc_nmi_entry); else @@ -1748,37 +1750,3 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski regs->cs = __KERNEL_CS; local_save_flags(regs->flags); } - -unsigned long perf_instruction_pointer(struct pt_regs *regs) -{ - unsigned long ip; - - if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) - ip = perf_guest_cbs->get_guest_ip(); - else - ip = instruction_pointer(regs); - - return ip; -} - -unsigned long perf_misc_flags(struct pt_regs *regs) -{ - int misc = 0; - - if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { - if (perf_guest_cbs->is_user_mode()) - misc |= PERF_RECORD_MISC_GUEST_USER; - else - misc |= PERF_RECORD_MISC_GUEST_KERNEL; - } else { - if (user_mode(regs)) - misc |= PERF_RECORD_MISC_USER; - else - misc |= PERF_RECORD_MISC_KERNEL; - } - - if (regs->flags & PERF_EFLAGS_EXACT) - misc |= PERF_RECORD_MISC_EXACT; - - return misc; -} diff --git a/trunk/arch/x86/kernel/cpu/perf_event_amd.c b/trunk/arch/x86/kernel/cpu/perf_event_amd.c index 611df11ba15e..db6f7d4056e1 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_amd.c +++ b/trunk/arch/x86/kernel/cpu/perf_event_amd.c @@ -2,7 +2,7 @@ static DEFINE_RAW_SPINLOCK(amd_nb_lock); -static __initconst const u64 amd_hw_cache_event_ids +static __initconst u64 amd_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -111,19 +111,22 @@ static u64 amd_pmu_event_map(int hw_event) return amd_perfmon_event_map[hw_event]; } -static int amd_pmu_hw_config(struct perf_event *event) +static u64 amd_pmu_raw_event(u64 hw_event) { - int ret = x86_pmu_hw_config(event); - - if (ret) - return ret; - - if (event->attr.type != PERF_TYPE_RAW) - return 0; - - event->hw.config |= event->attr.config & AMD64_RAW_EVENT_MASK; - - return 0; +#define K7_EVNTSEL_EVENT_MASK 0xF000000FFULL +#define K7_EVNTSEL_UNIT_MASK 0x00000FF00ULL +#define K7_EVNTSEL_EDGE_MASK 0x000040000ULL +#define K7_EVNTSEL_INV_MASK 0x000800000ULL +#define K7_EVNTSEL_REG_MASK 0x0FF000000ULL + +#define K7_EVNTSEL_MASK \ + (K7_EVNTSEL_EVENT_MASK | \ + K7_EVNTSEL_UNIT_MASK | \ + K7_EVNTSEL_EDGE_MASK | \ + K7_EVNTSEL_INV_MASK | \ + K7_EVNTSEL_REG_MASK) + + return hw_event & K7_EVNTSEL_MASK; } /* @@ -162,7 +165,7 @@ static void amd_put_event_constraints(struct cpu_hw_events *cpuc, * be removed on one CPU at a time AND PMU is disabled * when we come here */ - for (i = 0; i < x86_pmu.num_counters; i++) { + for (i = 0; i < x86_pmu.num_events; i++) { if (nb->owners[i] == event) { cmpxchg(nb->owners+i, event, NULL); break; @@ -212,7 +215,7 @@ amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) struct hw_perf_event *hwc = &event->hw; struct amd_nb *nb = cpuc->amd_nb; struct perf_event *old = NULL; - int max = x86_pmu.num_counters; + int max = x86_pmu.num_events; int i, j, k = -1; /* @@ -290,7 +293,7 @@ static struct amd_nb *amd_alloc_nb(int cpu, int nb_id) /* * initialize all possible NB constraints */ - for (i = 0; i < x86_pmu.num_counters; i++) { + for (i = 0; i < x86_pmu.num_events; i++) { __set_bit(i, nb->event_constraints[i].idxmsk); nb->event_constraints[i].weight = 1; } @@ -368,22 +371,21 @@ static void amd_pmu_cpu_dead(int cpu) raw_spin_unlock(&amd_nb_lock); } -static __initconst const struct x86_pmu amd_pmu = { +static __initconst struct x86_pmu amd_pmu = { .name = "AMD", .handle_irq = x86_pmu_handle_irq, .disable_all = x86_pmu_disable_all, .enable_all = x86_pmu_enable_all, .enable = x86_pmu_enable_event, .disable = x86_pmu_disable_event, - .hw_config = amd_pmu_hw_config, - .schedule_events = x86_schedule_events, .eventsel = MSR_K7_EVNTSEL0, .perfctr = MSR_K7_PERFCTR0, .event_map = amd_pmu_event_map, + .raw_event = amd_pmu_raw_event, .max_events = ARRAY_SIZE(amd_perfmon_event_map), - .num_counters = 4, - .cntval_bits = 48, - .cntval_mask = (1ULL << 48) - 1, + .num_events = 4, + .event_bits = 48, + .event_mask = (1ULL << 48) - 1, .apic = 1, /* use highest bit to detect overflow */ .max_period = (1ULL << 47) - 1, diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel.c b/trunk/arch/x86/kernel/cpu/perf_event_intel.c index a099df96f916..9c794ac87837 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel.c +++ b/trunk/arch/x86/kernel/cpu/perf_event_intel.c @@ -88,7 +88,7 @@ static u64 intel_pmu_event_map(int hw_event) return intel_perfmon_event_map[hw_event]; } -static __initconst const u64 westmere_hw_cache_event_ids +static __initconst u64 westmere_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -179,7 +179,7 @@ static __initconst const u64 westmere_hw_cache_event_ids }, }; -static __initconst const u64 nehalem_hw_cache_event_ids +static __initconst u64 nehalem_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -270,7 +270,7 @@ static __initconst const u64 nehalem_hw_cache_event_ids }, }; -static __initconst const u64 core2_hw_cache_event_ids +static __initconst u64 core2_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -361,7 +361,7 @@ static __initconst const u64 core2_hw_cache_event_ids }, }; -static __initconst const u64 atom_hw_cache_event_ids +static __initconst u64 atom_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -452,6 +452,60 @@ static __initconst const u64 atom_hw_cache_event_ids }, }; +static u64 intel_pmu_raw_event(u64 hw_event) +{ +#define CORE_EVNTSEL_EVENT_MASK 0x000000FFULL +#define CORE_EVNTSEL_UNIT_MASK 0x0000FF00ULL +#define CORE_EVNTSEL_EDGE_MASK 0x00040000ULL +#define CORE_EVNTSEL_INV_MASK 0x00800000ULL +#define CORE_EVNTSEL_REG_MASK 0xFF000000ULL + +#define CORE_EVNTSEL_MASK \ + (INTEL_ARCH_EVTSEL_MASK | \ + INTEL_ARCH_UNIT_MASK | \ + INTEL_ARCH_EDGE_MASK | \ + INTEL_ARCH_INV_MASK | \ + INTEL_ARCH_CNT_MASK) + + return hw_event & CORE_EVNTSEL_MASK; +} + +static void intel_pmu_enable_bts(u64 config) +{ + unsigned long debugctlmsr; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr |= X86_DEBUGCTL_TR; + debugctlmsr |= X86_DEBUGCTL_BTS; + debugctlmsr |= X86_DEBUGCTL_BTINT; + + if (!(config & ARCH_PERFMON_EVENTSEL_OS)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; + + if (!(config & ARCH_PERFMON_EVENTSEL_USR)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; + + update_debugctlmsr(debugctlmsr); +} + +static void intel_pmu_disable_bts(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + unsigned long debugctlmsr; + + if (!cpuc->ds) + return; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr &= + ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | + X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); + + update_debugctlmsr(debugctlmsr); +} + static void intel_pmu_disable_all(void) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); @@ -460,17 +514,12 @@ static void intel_pmu_disable_all(void) if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) intel_pmu_disable_bts(); - - intel_pmu_pebs_disable_all(); - intel_pmu_lbr_disable_all(); } -static void intel_pmu_enable_all(int added) +static void intel_pmu_enable_all(void) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - intel_pmu_pebs_enable_all(); - intel_pmu_lbr_enable_all(); wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { @@ -484,41 +533,6 @@ static void intel_pmu_enable_all(int added) } } -/* - * Workaround for: - * Intel Errata AAK100 (model 26) - * Intel Errata AAP53 (model 30) - * Intel Errata BD53 (model 44) - * - * These chips need to be 'reset' when adding counters by programming - * the magic three (non counting) events 0x4300D2, 0x4300B1 and 0x4300B5 - * either in sequence on the same PMC or on different PMCs. - */ -static void intel_pmu_nhm_enable_all(int added) -{ - if (added) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - int i; - - wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 0, 0x4300D2); - wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 1, 0x4300B1); - wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 2, 0x4300B5); - - wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x3); - wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x0); - - for (i = 0; i < 3; i++) { - struct perf_event *event = cpuc->events[i]; - - if (!event) - continue; - - __x86_pmu_enable_event(&event->hw); - } - } - intel_pmu_enable_all(added); -} - static inline u64 intel_pmu_get_status(void) { u64 status; @@ -533,7 +547,8 @@ static inline void intel_pmu_ack_status(u64 ack) wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); } -static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) +static inline void +intel_pmu_disable_fixed(struct hw_perf_event *hwc) { int idx = hwc->idx - X86_PMC_IDX_FIXED; u64 ctrl_val, mask; @@ -542,10 +557,71 @@ static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) rdmsrl(hwc->config_base, ctrl_val); ctrl_val &= ~mask; - wrmsrl(hwc->config_base, ctrl_val); + (void)checking_wrmsrl(hwc->config_base, ctrl_val); +} + +static void intel_pmu_drain_bts_buffer(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct debug_store *ds = cpuc->ds; + struct bts_record { + u64 from; + u64 to; + u64 flags; + }; + struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS]; + struct bts_record *at, *top; + struct perf_output_handle handle; + struct perf_event_header header; + struct perf_sample_data data; + struct pt_regs regs; + + if (!event) + return; + + if (!ds) + return; + + at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; + top = (struct bts_record *)(unsigned long)ds->bts_index; + + if (top <= at) + return; + + ds->bts_index = ds->bts_buffer_base; + + perf_sample_data_init(&data, 0); + + data.period = event->hw.last_period; + regs.ip = 0; + + /* + * Prepare a generic sample, i.e. fill in the invariant fields. + * We will overwrite the from and to address before we output + * the sample. + */ + perf_prepare_sample(&header, &data, event, ®s); + + if (perf_output_begin(&handle, event, + header.size * (top - at), 1, 1)) + return; + + for (; at < top; at++) { + data.ip = at->from; + data.addr = at->to; + + perf_output_sample(&handle, &header, &data, event); + } + + perf_output_end(&handle); + + /* There's new data available. */ + event->hw.interrupts++; + event->pending_kill = POLL_IN; } -static void intel_pmu_disable_event(struct perf_event *event) +static inline void +intel_pmu_disable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -561,15 +637,14 @@ static void intel_pmu_disable_event(struct perf_event *event) } x86_pmu_disable_event(event); - - if (unlikely(event->attr.precise)) - intel_pmu_pebs_disable(event); } -static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) +static inline void +intel_pmu_enable_fixed(struct hw_perf_event *hwc) { int idx = hwc->idx - X86_PMC_IDX_FIXED; u64 ctrl_val, bits, mask; + int err; /* * Enable IRQ generation (0x8), @@ -594,7 +669,7 @@ static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) rdmsrl(hwc->config_base, ctrl_val); ctrl_val &= ~mask; ctrl_val |= bits; - wrmsrl(hwc->config_base, ctrl_val); + err = checking_wrmsrl(hwc->config_base, ctrl_val); } static void intel_pmu_enable_event(struct perf_event *event) @@ -614,9 +689,6 @@ static void intel_pmu_enable_event(struct perf_event *event) return; } - if (unlikely(event->attr.precise)) - intel_pmu_pebs_enable(event); - __x86_pmu_enable_event(hwc); } @@ -636,20 +708,20 @@ static void intel_pmu_reset(void) unsigned long flags; int idx; - if (!x86_pmu.num_counters) + if (!x86_pmu.num_events) return; local_irq_save(flags); printk("clearing PMU state on CPU#%d\n", smp_processor_id()); - for (idx = 0; idx < x86_pmu.num_counters; idx++) { + for (idx = 0; idx < x86_pmu.num_events; idx++) { checking_wrmsrl(x86_pmu.eventsel + idx, 0ull); checking_wrmsrl(x86_pmu.perfctr + idx, 0ull); } - for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) + for (idx = 0; idx < x86_pmu.num_events_fixed; idx++) { checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); - + } if (ds) ds->bts_index = ds->bts_buffer_base; @@ -675,7 +747,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) intel_pmu_drain_bts_buffer(); status = intel_pmu_get_status(); if (!status) { - intel_pmu_enable_all(0); + intel_pmu_enable_all(); return 0; } @@ -690,15 +762,6 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) inc_irq_stat(apic_perf_irqs); ack = status; - - intel_pmu_lbr_read(); - - /* - * PEBS overflow sets bit 62 in the global status register - */ - if (__test_and_clear_bit(62, (unsigned long *)&status)) - x86_pmu.drain_pebs(regs); - for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { struct perf_event *event = cpuc->events[bit]; @@ -724,22 +787,26 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) goto again; done: - intel_pmu_enable_all(0); + intel_pmu_enable_all(); return 1; } +static struct event_constraint bts_constraint = + EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); + static struct event_constraint * -intel_bts_constraints(struct perf_event *event) +intel_special_constraints(struct perf_event *event) { - struct hw_perf_event *hwc = &event->hw; - unsigned int hw_event, bts_event; + unsigned int hw_event; - hw_event = hwc->config & INTEL_ARCH_EVENT_MASK; - bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); + hw_event = event->hw.config & INTEL_ARCH_EVENT_MASK; - if (unlikely(hw_event == bts_event && hwc->sample_period == 1)) - return &bts_constraint; + if (unlikely((hw_event == + x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) && + (event->hw.sample_period == 1))) { + return &bts_constraint; + } return NULL; } @@ -748,53 +815,24 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event { struct event_constraint *c; - c = intel_bts_constraints(event); - if (c) - return c; - - c = intel_pebs_constraints(event); + c = intel_special_constraints(event); if (c) return c; return x86_get_event_constraints(cpuc, event); } -static int intel_pmu_hw_config(struct perf_event *event) -{ - int ret = x86_pmu_hw_config(event); - - if (ret) - return ret; - - if (event->attr.type != PERF_TYPE_RAW) - return 0; - - if (!(event->attr.config & ARCH_PERFMON_EVENTSEL_ANY)) - return 0; - - if (x86_pmu.version < 3) - return -EINVAL; - - if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) - return -EACCES; - - event->hw.config |= ARCH_PERFMON_EVENTSEL_ANY; - - return 0; -} - -static __initconst const struct x86_pmu core_pmu = { +static __initconst struct x86_pmu core_pmu = { .name = "core", .handle_irq = x86_pmu_handle_irq, .disable_all = x86_pmu_disable_all, .enable_all = x86_pmu_enable_all, .enable = x86_pmu_enable_event, .disable = x86_pmu_disable_event, - .hw_config = x86_pmu_hw_config, - .schedule_events = x86_schedule_events, .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, + .raw_event = intel_pmu_raw_event, .max_events = ARRAY_SIZE(intel_perfmon_event_map), .apic = 1, /* @@ -807,32 +845,17 @@ static __initconst const struct x86_pmu core_pmu = { .event_constraints = intel_core_event_constraints, }; -static void intel_pmu_cpu_starting(int cpu) -{ - init_debug_store_on_cpu(cpu); - /* - * Deal with CPUs that don't clear their LBRs on power-up. - */ - intel_pmu_lbr_reset(); -} - -static void intel_pmu_cpu_dying(int cpu) -{ - fini_debug_store_on_cpu(cpu); -} - -static __initconst const struct x86_pmu intel_pmu = { +static __initconst struct x86_pmu intel_pmu = { .name = "Intel", .handle_irq = intel_pmu_handle_irq, .disable_all = intel_pmu_disable_all, .enable_all = intel_pmu_enable_all, .enable = intel_pmu_enable_event, .disable = intel_pmu_disable_event, - .hw_config = intel_pmu_hw_config, - .schedule_events = x86_schedule_events, .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, + .raw_event = intel_pmu_raw_event, .max_events = ARRAY_SIZE(intel_perfmon_event_map), .apic = 1, /* @@ -841,38 +864,14 @@ static __initconst const struct x86_pmu intel_pmu = { * the generic event period: */ .max_period = (1ULL << 31) - 1, + .enable_bts = intel_pmu_enable_bts, + .disable_bts = intel_pmu_disable_bts, .get_event_constraints = intel_get_event_constraints, - .cpu_starting = intel_pmu_cpu_starting, - .cpu_dying = intel_pmu_cpu_dying, + .cpu_starting = init_debug_store_on_cpu, + .cpu_dying = fini_debug_store_on_cpu, }; -static void intel_clovertown_quirks(void) -{ - /* - * PEBS is unreliable due to: - * - * AJ67 - PEBS may experience CPL leaks - * AJ68 - PEBS PMI may be delayed by one event - * AJ69 - GLOBAL_STATUS[62] will only be set when DEBUGCTL[12] - * AJ106 - FREEZE_LBRS_ON_PMI doesn't work in combination with PEBS - * - * AJ67 could be worked around by restricting the OS/USR flags. - * AJ69 could be worked around by setting PMU_FREEZE_ON_PMI. - * - * AJ106 could possibly be worked around by not allowing LBR - * usage from PEBS, including the fixup. - * AJ68 could possibly be worked around by always programming - * a pebs_event_reset[0] value and coping with the lost events. - * - * But taken together it might just make sense to not enable PEBS on - * these chips. - */ - printk(KERN_WARNING "PEBS disabled due to CPU errata.\n"); - x86_pmu.pebs = 0; - x86_pmu.pebs_constraints = NULL; -} - static __init int intel_pmu_init(void) { union cpuid10_edx edx; @@ -882,13 +881,12 @@ static __init int intel_pmu_init(void) int version; if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { - switch (boot_cpu_data.x86) { - case 0x6: - return p6_pmu_init(); - case 0xf: - return p4_pmu_init(); - } + /* check for P6 processor family */ + if (boot_cpu_data.x86 == 6) { + return p6_pmu_init(); + } else { return -ENODEV; + } } /* @@ -906,28 +904,16 @@ static __init int intel_pmu_init(void) x86_pmu = intel_pmu; x86_pmu.version = version; - x86_pmu.num_counters = eax.split.num_counters; - x86_pmu.cntval_bits = eax.split.bit_width; - x86_pmu.cntval_mask = (1ULL << eax.split.bit_width) - 1; + x86_pmu.num_events = eax.split.num_events; + x86_pmu.event_bits = eax.split.bit_width; + x86_pmu.event_mask = (1ULL << eax.split.bit_width) - 1; /* * Quirk: v2 perfmon does not report fixed-purpose events, so * assume at least 3 events: */ if (version > 1) - x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3); - - /* - * v2 and above have a perf capabilities MSR - */ - if (version > 1) { - u64 capabilities; - - rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); - x86_pmu.intel_cap.capabilities = capabilities; - } - - intel_ds_init(); + x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3); /* * Install the hw-cache-events table: @@ -938,15 +924,12 @@ static __init int intel_pmu_init(void) break; case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ - x86_pmu.quirks = intel_clovertown_quirks; case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ case 29: /* six-core 45 nm xeon "Dunnington" */ memcpy(hw_cache_event_ids, core2_hw_cache_event_ids, sizeof(hw_cache_event_ids)); - intel_pmu_lbr_init_core(); - x86_pmu.event_constraints = intel_core2_event_constraints; pr_cont("Core2 events, "); break; @@ -957,19 +940,13 @@ static __init int intel_pmu_init(void) memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids, sizeof(hw_cache_event_ids)); - intel_pmu_lbr_init_nhm(); - x86_pmu.event_constraints = intel_nehalem_event_constraints; - x86_pmu.enable_all = intel_pmu_nhm_enable_all; - pr_cont("Nehalem events, "); + pr_cont("Nehalem/Corei7 events, "); break; - case 28: /* Atom */ memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, sizeof(hw_cache_event_ids)); - intel_pmu_lbr_init_atom(); - x86_pmu.event_constraints = intel_gen_event_constraints; pr_cont("Atom events, "); break; @@ -979,10 +956,7 @@ static __init int intel_pmu_init(void) memcpy(hw_cache_event_ids, westmere_hw_cache_event_ids, sizeof(hw_cache_event_ids)); - intel_pmu_lbr_init_nhm(); - x86_pmu.event_constraints = intel_westmere_event_constraints; - x86_pmu.enable_all = intel_pmu_nhm_enable_all; pr_cont("Westmere events, "); break; diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel_ds.c b/trunk/arch/x86/kernel/cpu/perf_event_intel_ds.c deleted file mode 100644 index ec8b2e12e104..000000000000 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ /dev/null @@ -1,664 +0,0 @@ -#ifdef CONFIG_CPU_SUP_INTEL - -/* The maximal number of PEBS events: */ -#define MAX_PEBS_EVENTS 4 - -/* The size of a BTS record in bytes: */ -#define BTS_RECORD_SIZE 24 - -#define BTS_BUFFER_SIZE (PAGE_SIZE << 4) -#define PEBS_BUFFER_SIZE PAGE_SIZE - -/* - * pebs_record_32 for p4 and core not supported - -struct pebs_record_32 { - u32 flags, ip; - u32 ax, bc, cx, dx; - u32 si, di, bp, sp; -}; - - */ - -struct pebs_record_core { - u64 flags, ip; - u64 ax, bx, cx, dx; - u64 si, di, bp, sp; - u64 r8, r9, r10, r11; - u64 r12, r13, r14, r15; -}; - -struct pebs_record_nhm { - u64 flags, ip; - u64 ax, bx, cx, dx; - u64 si, di, bp, sp; - u64 r8, r9, r10, r11; - u64 r12, r13, r14, r15; - u64 status, dla, dse, lat; -}; - -/* - * A debug store configuration. - * - * We only support architectures that use 64bit fields. - */ -struct debug_store { - u64 bts_buffer_base; - u64 bts_index; - u64 bts_absolute_maximum; - u64 bts_interrupt_threshold; - u64 pebs_buffer_base; - u64 pebs_index; - u64 pebs_absolute_maximum; - u64 pebs_interrupt_threshold; - u64 pebs_event_reset[MAX_PEBS_EVENTS]; -}; - -static void init_debug_store_on_cpu(int cpu) -{ - struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; - - if (!ds) - return; - - wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, - (u32)((u64)(unsigned long)ds), - (u32)((u64)(unsigned long)ds >> 32)); -} - -static void fini_debug_store_on_cpu(int cpu) -{ - if (!per_cpu(cpu_hw_events, cpu).ds) - return; - - wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); -} - -static void release_ds_buffers(void) -{ - int cpu; - - if (!x86_pmu.bts && !x86_pmu.pebs) - return; - - get_online_cpus(); - - for_each_online_cpu(cpu) - fini_debug_store_on_cpu(cpu); - - for_each_possible_cpu(cpu) { - struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; - - if (!ds) - continue; - - per_cpu(cpu_hw_events, cpu).ds = NULL; - - kfree((void *)(unsigned long)ds->pebs_buffer_base); - kfree((void *)(unsigned long)ds->bts_buffer_base); - kfree(ds); - } - - put_online_cpus(); -} - -static int reserve_ds_buffers(void) -{ - int cpu, err = 0; - - if (!x86_pmu.bts && !x86_pmu.pebs) - return 0; - - get_online_cpus(); - - for_each_possible_cpu(cpu) { - struct debug_store *ds; - void *buffer; - int max, thresh; - - err = -ENOMEM; - ds = kzalloc(sizeof(*ds), GFP_KERNEL); - if (unlikely(!ds)) - break; - per_cpu(cpu_hw_events, cpu).ds = ds; - - if (x86_pmu.bts) { - buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); - if (unlikely(!buffer)) - break; - - max = BTS_BUFFER_SIZE / BTS_RECORD_SIZE; - thresh = max / 16; - - ds->bts_buffer_base = (u64)(unsigned long)buffer; - ds->bts_index = ds->bts_buffer_base; - ds->bts_absolute_maximum = ds->bts_buffer_base + - max * BTS_RECORD_SIZE; - ds->bts_interrupt_threshold = ds->bts_absolute_maximum - - thresh * BTS_RECORD_SIZE; - } - - if (x86_pmu.pebs) { - buffer = kzalloc(PEBS_BUFFER_SIZE, GFP_KERNEL); - if (unlikely(!buffer)) - break; - - max = PEBS_BUFFER_SIZE / x86_pmu.pebs_record_size; - - ds->pebs_buffer_base = (u64)(unsigned long)buffer; - ds->pebs_index = ds->pebs_buffer_base; - ds->pebs_absolute_maximum = ds->pebs_buffer_base + - max * x86_pmu.pebs_record_size; - /* - * Always use single record PEBS - */ - ds->pebs_interrupt_threshold = ds->pebs_buffer_base + - x86_pmu.pebs_record_size; - } - - err = 0; - } - - if (err) - release_ds_buffers(); - else { - for_each_online_cpu(cpu) - init_debug_store_on_cpu(cpu); - } - - put_online_cpus(); - - return err; -} - -/* - * BTS - */ - -static struct event_constraint bts_constraint = - EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); - -static void intel_pmu_enable_bts(u64 config) -{ - unsigned long debugctlmsr; - - debugctlmsr = get_debugctlmsr(); - - debugctlmsr |= DEBUGCTLMSR_TR; - debugctlmsr |= DEBUGCTLMSR_BTS; - debugctlmsr |= DEBUGCTLMSR_BTINT; - - if (!(config & ARCH_PERFMON_EVENTSEL_OS)) - debugctlmsr |= DEBUGCTLMSR_BTS_OFF_OS; - - if (!(config & ARCH_PERFMON_EVENTSEL_USR)) - debugctlmsr |= DEBUGCTLMSR_BTS_OFF_USR; - - update_debugctlmsr(debugctlmsr); -} - -static void intel_pmu_disable_bts(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - unsigned long debugctlmsr; - - if (!cpuc->ds) - return; - - debugctlmsr = get_debugctlmsr(); - - debugctlmsr &= - ~(DEBUGCTLMSR_TR | DEBUGCTLMSR_BTS | DEBUGCTLMSR_BTINT | - DEBUGCTLMSR_BTS_OFF_OS | DEBUGCTLMSR_BTS_OFF_USR); - - update_debugctlmsr(debugctlmsr); -} - -static void intel_pmu_drain_bts_buffer(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct debug_store *ds = cpuc->ds; - struct bts_record { - u64 from; - u64 to; - u64 flags; - }; - struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS]; - struct bts_record *at, *top; - struct perf_output_handle handle; - struct perf_event_header header; - struct perf_sample_data data; - struct pt_regs regs; - - if (!event) - return; - - if (!ds) - return; - - at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; - top = (struct bts_record *)(unsigned long)ds->bts_index; - - if (top <= at) - return; - - ds->bts_index = ds->bts_buffer_base; - - perf_sample_data_init(&data, 0); - data.period = event->hw.last_period; - regs.ip = 0; - - /* - * Prepare a generic sample, i.e. fill in the invariant fields. - * We will overwrite the from and to address before we output - * the sample. - */ - perf_prepare_sample(&header, &data, event, ®s); - - if (perf_output_begin(&handle, event, header.size * (top - at), 1, 1)) - return; - - for (; at < top; at++) { - data.ip = at->from; - data.addr = at->to; - - perf_output_sample(&handle, &header, &data, event); - } - - perf_output_end(&handle); - - /* There's new data available. */ - event->hw.interrupts++; - event->pending_kill = POLL_IN; -} - -/* - * PEBS - */ - -static struct event_constraint intel_core_pebs_events[] = { - PEBS_EVENT_CONSTRAINT(0x00c0, 0x1), /* INSTR_RETIRED.ANY */ - PEBS_EVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */ - PEBS_EVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */ - PEBS_EVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */ - PEBS_EVENT_CONSTRAINT(0x01cb, 0x1), /* MEM_LOAD_RETIRED.L1D_MISS */ - PEBS_EVENT_CONSTRAINT(0x02cb, 0x1), /* MEM_LOAD_RETIRED.L1D_LINE_MISS */ - PEBS_EVENT_CONSTRAINT(0x04cb, 0x1), /* MEM_LOAD_RETIRED.L2_MISS */ - PEBS_EVENT_CONSTRAINT(0x08cb, 0x1), /* MEM_LOAD_RETIRED.L2_LINE_MISS */ - PEBS_EVENT_CONSTRAINT(0x10cb, 0x1), /* MEM_LOAD_RETIRED.DTLB_MISS */ - EVENT_CONSTRAINT_END -}; - -static struct event_constraint intel_nehalem_pebs_events[] = { - PEBS_EVENT_CONSTRAINT(0x00c0, 0xf), /* INSTR_RETIRED.ANY */ - PEBS_EVENT_CONSTRAINT(0xfec1, 0xf), /* X87_OPS_RETIRED.ANY */ - PEBS_EVENT_CONSTRAINT(0x00c5, 0xf), /* BR_INST_RETIRED.MISPRED */ - PEBS_EVENT_CONSTRAINT(0x1fc7, 0xf), /* SIMD_INST_RETURED.ANY */ - PEBS_EVENT_CONSTRAINT(0x01cb, 0xf), /* MEM_LOAD_RETIRED.L1D_MISS */ - PEBS_EVENT_CONSTRAINT(0x02cb, 0xf), /* MEM_LOAD_RETIRED.L1D_LINE_MISS */ - PEBS_EVENT_CONSTRAINT(0x04cb, 0xf), /* MEM_LOAD_RETIRED.L2_MISS */ - PEBS_EVENT_CONSTRAINT(0x08cb, 0xf), /* MEM_LOAD_RETIRED.L2_LINE_MISS */ - PEBS_EVENT_CONSTRAINT(0x10cb, 0xf), /* MEM_LOAD_RETIRED.DTLB_MISS */ - EVENT_CONSTRAINT_END -}; - -static struct event_constraint * -intel_pebs_constraints(struct perf_event *event) -{ - struct event_constraint *c; - - if (!event->attr.precise) - return NULL; - - if (x86_pmu.pebs_constraints) { - for_each_event_constraint(c, x86_pmu.pebs_constraints) { - if ((event->hw.config & c->cmask) == c->code) - return c; - } - } - - return &emptyconstraint; -} - -static void intel_pmu_pebs_enable(struct perf_event *event) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct hw_perf_event *hwc = &event->hw; - - hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; - - cpuc->pebs_enabled |= 1ULL << hwc->idx; - WARN_ON_ONCE(cpuc->enabled); - - if (x86_pmu.intel_cap.pebs_trap) - intel_pmu_lbr_enable(event); -} - -static void intel_pmu_pebs_disable(struct perf_event *event) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct hw_perf_event *hwc = &event->hw; - - cpuc->pebs_enabled &= ~(1ULL << hwc->idx); - if (cpuc->enabled) - wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); - - hwc->config |= ARCH_PERFMON_EVENTSEL_INT; - - if (x86_pmu.intel_cap.pebs_trap) - intel_pmu_lbr_disable(event); -} - -static void intel_pmu_pebs_enable_all(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - - if (cpuc->pebs_enabled) - wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); -} - -static void intel_pmu_pebs_disable_all(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - - if (cpuc->pebs_enabled) - wrmsrl(MSR_IA32_PEBS_ENABLE, 0); -} - -#include - -static inline bool kernel_ip(unsigned long ip) -{ -#ifdef CONFIG_X86_32 - return ip > PAGE_OFFSET; -#else - return (long)ip < 0; -#endif -} - -static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - unsigned long from = cpuc->lbr_entries[0].from; - unsigned long old_to, to = cpuc->lbr_entries[0].to; - unsigned long ip = regs->ip; - - /* - * We don't need to fixup if the PEBS assist is fault like - */ - if (!x86_pmu.intel_cap.pebs_trap) - return 1; - - /* - * No LBR entry, no basic block, no rewinding - */ - if (!cpuc->lbr_stack.nr || !from || !to) - return 0; - - /* - * Basic blocks should never cross user/kernel boundaries - */ - if (kernel_ip(ip) != kernel_ip(to)) - return 0; - - /* - * unsigned math, either ip is before the start (impossible) or - * the basic block is larger than 1 page (sanity) - */ - if ((ip - to) > PAGE_SIZE) - return 0; - - /* - * We sampled a branch insn, rewind using the LBR stack - */ - if (ip == to) { - regs->ip = from; - return 1; - } - - do { - struct insn insn; - u8 buf[MAX_INSN_SIZE]; - void *kaddr; - - old_to = to; - if (!kernel_ip(ip)) { - int bytes, size = MAX_INSN_SIZE; - - bytes = copy_from_user_nmi(buf, (void __user *)to, size); - if (bytes != size) - return 0; - - kaddr = buf; - } else - kaddr = (void *)to; - - kernel_insn_init(&insn, kaddr); - insn_get_length(&insn); - to += insn.length; - } while (to < ip); - - if (to == ip) { - regs->ip = old_to; - return 1; - } - - /* - * Even though we decoded the basic block, the instruction stream - * never matched the given IP, either the TO or the IP got corrupted. - */ - return 0; -} - -static int intel_pmu_save_and_restart(struct perf_event *event); - -static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct debug_store *ds = cpuc->ds; - struct perf_event *event = cpuc->events[0]; /* PMC0 only */ - struct pebs_record_core *at, *top; - struct perf_sample_data data; - struct perf_raw_record raw; - struct pt_regs regs; - int n; - - if (!ds || !x86_pmu.pebs) - return; - - at = (struct pebs_record_core *)(unsigned long)ds->pebs_buffer_base; - top = (struct pebs_record_core *)(unsigned long)ds->pebs_index; - - /* - * Whatever else happens, drain the thing - */ - ds->pebs_index = ds->pebs_buffer_base; - - if (!test_bit(0, cpuc->active_mask)) - return; - - WARN_ON_ONCE(!event); - - if (!event->attr.precise) - return; - - n = top - at; - if (n <= 0) - return; - - if (!intel_pmu_save_and_restart(event)) - return; - - /* - * Should not happen, we program the threshold at 1 and do not - * set a reset value. - */ - WARN_ON_ONCE(n > 1); - at += n - 1; - - perf_sample_data_init(&data, 0); - data.period = event->hw.last_period; - - if (event->attr.sample_type & PERF_SAMPLE_RAW) { - raw.size = x86_pmu.pebs_record_size; - raw.data = at; - data.raw = &raw; - } - - /* - * We use the interrupt regs as a base because the PEBS record - * does not contain a full regs set, specifically it seems to - * lack segment descriptors, which get used by things like - * user_mode(). - * - * In the simple case fix up only the IP and BP,SP regs, for - * PERF_SAMPLE_IP and PERF_SAMPLE_CALLCHAIN to function properly. - * A possible PERF_SAMPLE_REGS will have to transfer all regs. - */ - regs = *iregs; - regs.ip = at->ip; - regs.bp = at->bp; - regs.sp = at->sp; - - if (intel_pmu_pebs_fixup_ip(®s)) - regs.flags |= PERF_EFLAGS_EXACT; - else - regs.flags &= ~PERF_EFLAGS_EXACT; - - if (perf_event_overflow(event, 1, &data, ®s)) - x86_pmu_stop(event); -} - -static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct debug_store *ds = cpuc->ds; - struct pebs_record_nhm *at, *top; - struct perf_sample_data data; - struct perf_event *event = NULL; - struct perf_raw_record raw; - struct pt_regs regs; - u64 status = 0; - int bit, n; - - if (!ds || !x86_pmu.pebs) - return; - - at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base; - top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index; - - ds->pebs_index = ds->pebs_buffer_base; - - n = top - at; - if (n <= 0) - return; - - /* - * Should not happen, we program the threshold at 1 and do not - * set a reset value. - */ - WARN_ON_ONCE(n > MAX_PEBS_EVENTS); - - for ( ; at < top; at++) { - for_each_set_bit(bit, (unsigned long *)&at->status, MAX_PEBS_EVENTS) { - event = cpuc->events[bit]; - if (!test_bit(bit, cpuc->active_mask)) - continue; - - WARN_ON_ONCE(!event); - - if (!event->attr.precise) - continue; - - if (__test_and_set_bit(bit, (unsigned long *)&status)) - continue; - - break; - } - - if (!event || bit >= MAX_PEBS_EVENTS) - continue; - - if (!intel_pmu_save_and_restart(event)) - continue; - - perf_sample_data_init(&data, 0); - data.period = event->hw.last_period; - - if (event->attr.sample_type & PERF_SAMPLE_RAW) { - raw.size = x86_pmu.pebs_record_size; - raw.data = at; - data.raw = &raw; - } - - /* - * See the comment in intel_pmu_drain_pebs_core() - */ - regs = *iregs; - regs.ip = at->ip; - regs.bp = at->bp; - regs.sp = at->sp; - - if (intel_pmu_pebs_fixup_ip(®s)) - regs.flags |= PERF_EFLAGS_EXACT; - else - regs.flags &= ~PERF_EFLAGS_EXACT; - - if (perf_event_overflow(event, 1, &data, ®s)) - x86_pmu_stop(event); - } -} - -/* - * BTS, PEBS probe and setup - */ - -static void intel_ds_init(void) -{ - /* - * No support for 32bit formats - */ - if (!boot_cpu_has(X86_FEATURE_DTES64)) - return; - - x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS); - x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS); - if (x86_pmu.pebs) { - char pebs_type = x86_pmu.intel_cap.pebs_trap ? '+' : '-'; - int format = x86_pmu.intel_cap.pebs_format; - - switch (format) { - case 0: - printk(KERN_CONT "PEBS fmt0%c, ", pebs_type); - x86_pmu.pebs_record_size = sizeof(struct pebs_record_core); - x86_pmu.drain_pebs = intel_pmu_drain_pebs_core; - x86_pmu.pebs_constraints = intel_core_pebs_events; - break; - - case 1: - printk(KERN_CONT "PEBS fmt1%c, ", pebs_type); - x86_pmu.pebs_record_size = sizeof(struct pebs_record_nhm); - x86_pmu.drain_pebs = intel_pmu_drain_pebs_nhm; - x86_pmu.pebs_constraints = intel_nehalem_pebs_events; - break; - - default: - printk(KERN_CONT "no PEBS fmt%d%c, ", format, pebs_type); - x86_pmu.pebs = 0; - break; - } - } -} - -#else /* CONFIG_CPU_SUP_INTEL */ - -static int reserve_ds_buffers(void) -{ - return 0; -} - -static void release_ds_buffers(void) -{ -} - -#endif /* CONFIG_CPU_SUP_INTEL */ diff --git a/trunk/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/trunk/arch/x86/kernel/cpu/perf_event_intel_lbr.c deleted file mode 100644 index d202c1bece1a..000000000000 --- a/trunk/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ /dev/null @@ -1,218 +0,0 @@ -#ifdef CONFIG_CPU_SUP_INTEL - -enum { - LBR_FORMAT_32 = 0x00, - LBR_FORMAT_LIP = 0x01, - LBR_FORMAT_EIP = 0x02, - LBR_FORMAT_EIP_FLAGS = 0x03, -}; - -/* - * We only support LBR implementations that have FREEZE_LBRS_ON_PMI - * otherwise it becomes near impossible to get a reliable stack. - */ - -static void __intel_pmu_lbr_enable(void) -{ - u64 debugctl; - - rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); - debugctl |= (DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); - wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); -} - -static void __intel_pmu_lbr_disable(void) -{ - u64 debugctl; - - rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); - debugctl &= ~(DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); - wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); -} - -static void intel_pmu_lbr_reset_32(void) -{ - int i; - - for (i = 0; i < x86_pmu.lbr_nr; i++) - wrmsrl(x86_pmu.lbr_from + i, 0); -} - -static void intel_pmu_lbr_reset_64(void) -{ - int i; - - for (i = 0; i < x86_pmu.lbr_nr; i++) { - wrmsrl(x86_pmu.lbr_from + i, 0); - wrmsrl(x86_pmu.lbr_to + i, 0); - } -} - -static void intel_pmu_lbr_reset(void) -{ - if (!x86_pmu.lbr_nr) - return; - - if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32) - intel_pmu_lbr_reset_32(); - else - intel_pmu_lbr_reset_64(); -} - -static void intel_pmu_lbr_enable(struct perf_event *event) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - - if (!x86_pmu.lbr_nr) - return; - - WARN_ON_ONCE(cpuc->enabled); - - /* - * Reset the LBR stack if we changed task context to - * avoid data leaks. - */ - - if (event->ctx->task && cpuc->lbr_context != event->ctx) { - intel_pmu_lbr_reset(); - cpuc->lbr_context = event->ctx; - } - - cpuc->lbr_users++; -} - -static void intel_pmu_lbr_disable(struct perf_event *event) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - - if (!x86_pmu.lbr_nr) - return; - - cpuc->lbr_users--; - WARN_ON_ONCE(cpuc->lbr_users < 0); - - if (cpuc->enabled && !cpuc->lbr_users) - __intel_pmu_lbr_disable(); -} - -static void intel_pmu_lbr_enable_all(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - - if (cpuc->lbr_users) - __intel_pmu_lbr_enable(); -} - -static void intel_pmu_lbr_disable_all(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - - if (cpuc->lbr_users) - __intel_pmu_lbr_disable(); -} - -static inline u64 intel_pmu_lbr_tos(void) -{ - u64 tos; - - rdmsrl(x86_pmu.lbr_tos, tos); - - return tos; -} - -static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) -{ - unsigned long mask = x86_pmu.lbr_nr - 1; - u64 tos = intel_pmu_lbr_tos(); - int i; - - for (i = 0; i < x86_pmu.lbr_nr; i++) { - unsigned long lbr_idx = (tos - i) & mask; - union { - struct { - u32 from; - u32 to; - }; - u64 lbr; - } msr_lastbranch; - - rdmsrl(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr); - - cpuc->lbr_entries[i].from = msr_lastbranch.from; - cpuc->lbr_entries[i].to = msr_lastbranch.to; - cpuc->lbr_entries[i].flags = 0; - } - cpuc->lbr_stack.nr = i; -} - -#define LBR_FROM_FLAG_MISPRED (1ULL << 63) - -/* - * Due to lack of segmentation in Linux the effective address (offset) - * is the same as the linear address, allowing us to merge the LIP and EIP - * LBR formats. - */ -static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) -{ - unsigned long mask = x86_pmu.lbr_nr - 1; - int lbr_format = x86_pmu.intel_cap.lbr_format; - u64 tos = intel_pmu_lbr_tos(); - int i; - - for (i = 0; i < x86_pmu.lbr_nr; i++) { - unsigned long lbr_idx = (tos - i) & mask; - u64 from, to, flags = 0; - - rdmsrl(x86_pmu.lbr_from + lbr_idx, from); - rdmsrl(x86_pmu.lbr_to + lbr_idx, to); - - if (lbr_format == LBR_FORMAT_EIP_FLAGS) { - flags = !!(from & LBR_FROM_FLAG_MISPRED); - from = (u64)((((s64)from) << 1) >> 1); - } - - cpuc->lbr_entries[i].from = from; - cpuc->lbr_entries[i].to = to; - cpuc->lbr_entries[i].flags = flags; - } - cpuc->lbr_stack.nr = i; -} - -static void intel_pmu_lbr_read(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - - if (!cpuc->lbr_users) - return; - - if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32) - intel_pmu_lbr_read_32(cpuc); - else - intel_pmu_lbr_read_64(cpuc); -} - -static void intel_pmu_lbr_init_core(void) -{ - x86_pmu.lbr_nr = 4; - x86_pmu.lbr_tos = 0x01c9; - x86_pmu.lbr_from = 0x40; - x86_pmu.lbr_to = 0x60; -} - -static void intel_pmu_lbr_init_nhm(void) -{ - x86_pmu.lbr_nr = 16; - x86_pmu.lbr_tos = 0x01c9; - x86_pmu.lbr_from = 0x680; - x86_pmu.lbr_to = 0x6c0; -} - -static void intel_pmu_lbr_init_atom(void) -{ - x86_pmu.lbr_nr = 8; - x86_pmu.lbr_tos = 0x01c9; - x86_pmu.lbr_from = 0x40; - x86_pmu.lbr_to = 0x60; -} - -#endif /* CONFIG_CPU_SUP_INTEL */ diff --git a/trunk/arch/x86/kernel/cpu/perf_event_p4.c b/trunk/arch/x86/kernel/cpu/perf_event_p4.c deleted file mode 100644 index 15367cce66bd..000000000000 --- a/trunk/arch/x86/kernel/cpu/perf_event_p4.c +++ /dev/null @@ -1,834 +0,0 @@ -/* - * Netburst Perfomance Events (P4, old Xeon) - * - * Copyright (C) 2010 Parallels, Inc., Cyrill Gorcunov - * Copyright (C) 2010 Intel Corporation, Lin Ming - * - * For licencing details see kernel-base/COPYING - */ - -#ifdef CONFIG_CPU_SUP_INTEL - -#include - -#define P4_CNTR_LIMIT 3 -/* - * array indices: 0,1 - HT threads, used with HT enabled cpu - */ -struct p4_event_bind { - unsigned int opcode; /* Event code and ESCR selector */ - unsigned int escr_msr[2]; /* ESCR MSR for this event */ - unsigned char cntr[2][P4_CNTR_LIMIT]; /* counter index (offset), -1 on abscence */ -}; - -struct p4_cache_event_bind { - unsigned int metric_pebs; - unsigned int metric_vert; -}; - -#define P4_GEN_CACHE_EVENT_BIND(name) \ - [P4_CACHE__##name] = { \ - .metric_pebs = P4_PEBS__##name, \ - .metric_vert = P4_VERT__##name, \ - } - -static struct p4_cache_event_bind p4_cache_event_bind_map[] = { - P4_GEN_CACHE_EVENT_BIND(1stl_cache_load_miss_retired), - P4_GEN_CACHE_EVENT_BIND(2ndl_cache_load_miss_retired), - P4_GEN_CACHE_EVENT_BIND(dtlb_load_miss_retired), - P4_GEN_CACHE_EVENT_BIND(dtlb_store_miss_retired), -}; - -/* - * Note that we don't use CCCR1 here, there is an - * exception for P4_BSQ_ALLOCATION but we just have - * no workaround - * - * consider this binding as resources which particular - * event may borrow, it doesn't contain EventMask, - * Tags and friends -- they are left to a caller - */ -static struct p4_event_bind p4_event_bind_map[] = { - [P4_EVENT_TC_DELIVER_MODE] = { - .opcode = P4_OPCODE(P4_EVENT_TC_DELIVER_MODE), - .escr_msr = { MSR_P4_TC_ESCR0, MSR_P4_TC_ESCR1 }, - .cntr = { {4, 5, -1}, {6, 7, -1} }, - }, - [P4_EVENT_BPU_FETCH_REQUEST] = { - .opcode = P4_OPCODE(P4_EVENT_BPU_FETCH_REQUEST), - .escr_msr = { MSR_P4_BPU_ESCR0, MSR_P4_BPU_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_ITLB_REFERENCE] = { - .opcode = P4_OPCODE(P4_EVENT_ITLB_REFERENCE), - .escr_msr = { MSR_P4_ITLB_ESCR0, MSR_P4_ITLB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_MEMORY_CANCEL] = { - .opcode = P4_OPCODE(P4_EVENT_MEMORY_CANCEL), - .escr_msr = { MSR_P4_DAC_ESCR0, MSR_P4_DAC_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_MEMORY_COMPLETE] = { - .opcode = P4_OPCODE(P4_EVENT_MEMORY_COMPLETE), - .escr_msr = { MSR_P4_SAAT_ESCR0 , MSR_P4_SAAT_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_LOAD_PORT_REPLAY] = { - .opcode = P4_OPCODE(P4_EVENT_LOAD_PORT_REPLAY), - .escr_msr = { MSR_P4_SAAT_ESCR0, MSR_P4_SAAT_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_STORE_PORT_REPLAY] = { - .opcode = P4_OPCODE(P4_EVENT_STORE_PORT_REPLAY), - .escr_msr = { MSR_P4_SAAT_ESCR0 , MSR_P4_SAAT_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_MOB_LOAD_REPLAY] = { - .opcode = P4_OPCODE(P4_EVENT_MOB_LOAD_REPLAY), - .escr_msr = { MSR_P4_MOB_ESCR0, MSR_P4_MOB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_PAGE_WALK_TYPE] = { - .opcode = P4_OPCODE(P4_EVENT_PAGE_WALK_TYPE), - .escr_msr = { MSR_P4_PMH_ESCR0, MSR_P4_PMH_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_BSQ_CACHE_REFERENCE] = { - .opcode = P4_OPCODE(P4_EVENT_BSQ_CACHE_REFERENCE), - .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_IOQ_ALLOCATION] = { - .opcode = P4_OPCODE(P4_EVENT_IOQ_ALLOCATION), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_IOQ_ACTIVE_ENTRIES] = { /* shared ESCR */ - .opcode = P4_OPCODE(P4_EVENT_IOQ_ACTIVE_ENTRIES), - .escr_msr = { MSR_P4_FSB_ESCR1, MSR_P4_FSB_ESCR1 }, - .cntr = { {2, -1, -1}, {3, -1, -1} }, - }, - [P4_EVENT_FSB_DATA_ACTIVITY] = { - .opcode = P4_OPCODE(P4_EVENT_FSB_DATA_ACTIVITY), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_BSQ_ALLOCATION] = { /* shared ESCR, broken CCCR1 */ - .opcode = P4_OPCODE(P4_EVENT_BSQ_ALLOCATION), - .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR0 }, - .cntr = { {0, -1, -1}, {1, -1, -1} }, - }, - [P4_EVENT_BSQ_ACTIVE_ENTRIES] = { /* shared ESCR */ - .opcode = P4_OPCODE(P4_EVENT_BSQ_ACTIVE_ENTRIES), - .escr_msr = { MSR_P4_BSU_ESCR1 , MSR_P4_BSU_ESCR1 }, - .cntr = { {2, -1, -1}, {3, -1, -1} }, - }, - [P4_EVENT_SSE_INPUT_ASSIST] = { - .opcode = P4_OPCODE(P4_EVENT_SSE_INPUT_ASSIST), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_PACKED_SP_UOP] = { - .opcode = P4_OPCODE(P4_EVENT_PACKED_SP_UOP), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_PACKED_DP_UOP] = { - .opcode = P4_OPCODE(P4_EVENT_PACKED_DP_UOP), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_SCALAR_SP_UOP] = { - .opcode = P4_OPCODE(P4_EVENT_SCALAR_SP_UOP), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_SCALAR_DP_UOP] = { - .opcode = P4_OPCODE(P4_EVENT_SCALAR_DP_UOP), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_64BIT_MMX_UOP] = { - .opcode = P4_OPCODE(P4_EVENT_64BIT_MMX_UOP), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_128BIT_MMX_UOP] = { - .opcode = P4_OPCODE(P4_EVENT_128BIT_MMX_UOP), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_X87_FP_UOP] = { - .opcode = P4_OPCODE(P4_EVENT_X87_FP_UOP), - .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_TC_MISC] = { - .opcode = P4_OPCODE(P4_EVENT_TC_MISC), - .escr_msr = { MSR_P4_TC_ESCR0, MSR_P4_TC_ESCR1 }, - .cntr = { {4, 5, -1}, {6, 7, -1} }, - }, - [P4_EVENT_GLOBAL_POWER_EVENTS] = { - .opcode = P4_OPCODE(P4_EVENT_GLOBAL_POWER_EVENTS), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_TC_MS_XFER] = { - .opcode = P4_OPCODE(P4_EVENT_TC_MS_XFER), - .escr_msr = { MSR_P4_MS_ESCR0, MSR_P4_MS_ESCR1 }, - .cntr = { {4, 5, -1}, {6, 7, -1} }, - }, - [P4_EVENT_UOP_QUEUE_WRITES] = { - .opcode = P4_OPCODE(P4_EVENT_UOP_QUEUE_WRITES), - .escr_msr = { MSR_P4_MS_ESCR0, MSR_P4_MS_ESCR1 }, - .cntr = { {4, 5, -1}, {6, 7, -1} }, - }, - [P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE] = { - .opcode = P4_OPCODE(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE), - .escr_msr = { MSR_P4_TBPU_ESCR0 , MSR_P4_TBPU_ESCR0 }, - .cntr = { {4, 5, -1}, {6, 7, -1} }, - }, - [P4_EVENT_RETIRED_BRANCH_TYPE] = { - .opcode = P4_OPCODE(P4_EVENT_RETIRED_BRANCH_TYPE), - .escr_msr = { MSR_P4_TBPU_ESCR0 , MSR_P4_TBPU_ESCR1 }, - .cntr = { {4, 5, -1}, {6, 7, -1} }, - }, - [P4_EVENT_RESOURCE_STALL] = { - .opcode = P4_OPCODE(P4_EVENT_RESOURCE_STALL), - .escr_msr = { MSR_P4_ALF_ESCR0, MSR_P4_ALF_ESCR1 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_WC_BUFFER] = { - .opcode = P4_OPCODE(P4_EVENT_WC_BUFFER), - .escr_msr = { MSR_P4_DAC_ESCR0, MSR_P4_DAC_ESCR1 }, - .cntr = { {8, 9, -1}, {10, 11, -1} }, - }, - [P4_EVENT_B2B_CYCLES] = { - .opcode = P4_OPCODE(P4_EVENT_B2B_CYCLES), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_BNR] = { - .opcode = P4_OPCODE(P4_EVENT_BNR), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_SNOOP] = { - .opcode = P4_OPCODE(P4_EVENT_SNOOP), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_RESPONSE] = { - .opcode = P4_OPCODE(P4_EVENT_RESPONSE), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { {0, -1, -1}, {2, -1, -1} }, - }, - [P4_EVENT_FRONT_END_EVENT] = { - .opcode = P4_OPCODE(P4_EVENT_FRONT_END_EVENT), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_EXECUTION_EVENT] = { - .opcode = P4_OPCODE(P4_EVENT_EXECUTION_EVENT), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_REPLAY_EVENT] = { - .opcode = P4_OPCODE(P4_EVENT_REPLAY_EVENT), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_INSTR_RETIRED] = { - .opcode = P4_OPCODE(P4_EVENT_INSTR_RETIRED), - .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_UOPS_RETIRED] = { - .opcode = P4_OPCODE(P4_EVENT_UOPS_RETIRED), - .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_UOP_TYPE] = { - .opcode = P4_OPCODE(P4_EVENT_UOP_TYPE), - .escr_msr = { MSR_P4_RAT_ESCR0, MSR_P4_RAT_ESCR1 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_BRANCH_RETIRED] = { - .opcode = P4_OPCODE(P4_EVENT_BRANCH_RETIRED), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_MISPRED_BRANCH_RETIRED] = { - .opcode = P4_OPCODE(P4_EVENT_MISPRED_BRANCH_RETIRED), - .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_X87_ASSIST] = { - .opcode = P4_OPCODE(P4_EVENT_X87_ASSIST), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_MACHINE_CLEAR] = { - .opcode = P4_OPCODE(P4_EVENT_MACHINE_CLEAR), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, - [P4_EVENT_INSTR_COMPLETED] = { - .opcode = P4_OPCODE(P4_EVENT_INSTR_COMPLETED), - .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, - .cntr = { {12, 13, 16}, {14, 15, 17} }, - }, -}; - -#define P4_GEN_CACHE_EVENT(event, bit, cache_event) \ - p4_config_pack_escr(P4_ESCR_EVENT(event) | \ - P4_ESCR_EMASK_BIT(event, bit)) | \ - p4_config_pack_cccr(cache_event | \ - P4_CCCR_ESEL(P4_OPCODE_ESEL(P4_OPCODE(event)))) - -static __initconst const u64 p4_hw_cache_event_ids - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX] = -{ - [ C(L1D ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0, - [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, - P4_CACHE__1stl_cache_load_miss_retired), - }, - }, - [ C(LL ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0, - [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, - P4_CACHE__2ndl_cache_load_miss_retired), - }, -}, - [ C(DTLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0, - [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, - P4_CACHE__dtlb_load_miss_retired), - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0, - [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, - P4_CACHE__dtlb_store_miss_retired), - }, - }, - [ C(ITLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_ITLB_REFERENCE, HIT, - P4_CACHE__itlb_reference_hit), - [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_ITLB_REFERENCE, MISS, - P4_CACHE__itlb_reference_miss), - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, -}; - -static u64 p4_general_events[PERF_COUNT_HW_MAX] = { - /* non-halted CPU clocks */ - [PERF_COUNT_HW_CPU_CYCLES] = - p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS) | - P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)), - - /* - * retired instructions - * in a sake of simplicity we don't use the FSB tagging - */ - [PERF_COUNT_HW_INSTRUCTIONS] = - p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_INSTR_RETIRED) | - P4_ESCR_EMASK_BIT(P4_EVENT_INSTR_RETIRED, NBOGUSNTAG) | - P4_ESCR_EMASK_BIT(P4_EVENT_INSTR_RETIRED, BOGUSNTAG)), - - /* cache hits */ - [PERF_COUNT_HW_CACHE_REFERENCES] = - p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_BSQ_CACHE_REFERENCE) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITS) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITE) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITM) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITS) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITE) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITM)), - - /* cache misses */ - [PERF_COUNT_HW_CACHE_MISSES] = - p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_BSQ_CACHE_REFERENCE) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_MISS) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_MISS) | - P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, WR_2ndL_MISS)), - - /* branch instructions retired */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = - p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_RETIRED_BRANCH_TYPE) | - P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, CONDITIONAL) | - P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, CALL) | - P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, RETURN) | - P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, INDIRECT)), - - /* mispredicted branches retired */ - [PERF_COUNT_HW_BRANCH_MISSES] = - p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_MISPRED_BRANCH_RETIRED) | - P4_ESCR_EMASK_BIT(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS)), - - /* bus ready clocks (cpu is driving #DRDY_DRV\#DRDY_OWN): */ - [PERF_COUNT_HW_BUS_CYCLES] = - p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_FSB_DATA_ACTIVITY) | - P4_ESCR_EMASK_BIT(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_DRV) | - P4_ESCR_EMASK_BIT(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OWN)) | - p4_config_pack_cccr(P4_CCCR_EDGE | P4_CCCR_COMPARE), -}; - -static struct p4_event_bind *p4_config_get_bind(u64 config) -{ - unsigned int evnt = p4_config_unpack_event(config); - struct p4_event_bind *bind = NULL; - - if (evnt < ARRAY_SIZE(p4_event_bind_map)) - bind = &p4_event_bind_map[evnt]; - - return bind; -} - -static u64 p4_pmu_event_map(int hw_event) -{ - struct p4_event_bind *bind; - unsigned int esel; - u64 config; - - if (hw_event > ARRAY_SIZE(p4_general_events)) { - printk_once(KERN_ERR "P4 PMU: Bad index: %i\n", hw_event); - return 0; - } - - config = p4_general_events[hw_event]; - bind = p4_config_get_bind(config); - esel = P4_OPCODE_ESEL(bind->opcode); - config |= p4_config_pack_cccr(P4_CCCR_ESEL(esel)); - - return config; -} - -static int p4_hw_config(struct perf_event *event) -{ - int cpu = raw_smp_processor_id(); - u32 escr, cccr; - - /* - * the reason we use cpu that early is that: if we get scheduled - * first time on the same cpu -- we will not need swap thread - * specific flags in config (and will save some cpu cycles) - */ - - cccr = p4_default_cccr_conf(cpu); - escr = p4_default_escr_conf(cpu, event->attr.exclude_kernel, - event->attr.exclude_user); - event->hw.config = p4_config_pack_escr(escr) | - p4_config_pack_cccr(cccr); - - if (p4_ht_active() && p4_ht_thread(cpu)) - event->hw.config = p4_set_ht_bit(event->hw.config); - - if (event->attr.type != PERF_TYPE_RAW) - return 0; - - /* - * We don't control raw events so it's up to the caller - * to pass sane values (and we don't count the thread number - * on HT machine but allow HT-compatible specifics to be - * passed on) - * - * XXX: HT wide things should check perf_paranoid_cpu() && - * CAP_SYS_ADMIN - */ - event->hw.config |= event->attr.config & - (p4_config_pack_escr(P4_ESCR_MASK_HT) | - p4_config_pack_cccr(P4_CCCR_MASK_HT)); - - return 0; -} - -static inline void p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc) -{ - unsigned long dummy; - - rdmsrl(hwc->config_base + hwc->idx, dummy); - if (dummy & P4_CCCR_OVF) { - (void)checking_wrmsrl(hwc->config_base + hwc->idx, - ((u64)dummy) & ~P4_CCCR_OVF); - } -} - -static inline void p4_pmu_disable_event(struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - - /* - * If event gets disabled while counter is in overflowed - * state we need to clear P4_CCCR_OVF, otherwise interrupt get - * asserted again and again - */ - (void)checking_wrmsrl(hwc->config_base + hwc->idx, - (u64)(p4_config_unpack_cccr(hwc->config)) & - ~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED); -} - -static void p4_pmu_disable_all(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - int idx; - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - struct perf_event *event = cpuc->events[idx]; - if (!test_bit(idx, cpuc->active_mask)) - continue; - p4_pmu_disable_event(event); - } -} - -static void p4_pmu_enable_event(struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - int thread = p4_ht_config_thread(hwc->config); - u64 escr_conf = p4_config_unpack_escr(p4_clear_ht_bit(hwc->config)); - unsigned int idx = p4_config_unpack_event(hwc->config); - unsigned int idx_cache = p4_config_unpack_cache_event(hwc->config); - struct p4_event_bind *bind; - struct p4_cache_event_bind *bind_cache; - u64 escr_addr, cccr; - - bind = &p4_event_bind_map[idx]; - escr_addr = (u64)bind->escr_msr[thread]; - - /* - * - we dont support cascaded counters yet - * - and counter 1 is broken (erratum) - */ - WARN_ON_ONCE(p4_is_event_cascaded(hwc->config)); - WARN_ON_ONCE(hwc->idx == 1); - - /* we need a real Event value */ - escr_conf &= ~P4_ESCR_EVENT_MASK; - escr_conf |= P4_ESCR_EVENT(P4_OPCODE_EVNT(bind->opcode)); - - cccr = p4_config_unpack_cccr(hwc->config); - - /* - * it could be Cache event so that we need to - * set metrics into additional MSRs - */ - BUILD_BUG_ON(P4_CACHE__MAX > P4_CCCR_CACHE_OPS_MASK); - if (idx_cache > P4_CACHE__NONE && - idx_cache < ARRAY_SIZE(p4_cache_event_bind_map)) { - bind_cache = &p4_cache_event_bind_map[idx_cache]; - (void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, (u64)bind_cache->metric_pebs); - (void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, (u64)bind_cache->metric_vert); - } - - (void)checking_wrmsrl(escr_addr, escr_conf); - (void)checking_wrmsrl(hwc->config_base + hwc->idx, - (cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE); -} - -static void p4_pmu_enable_all(int added) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - int idx; - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - struct perf_event *event = cpuc->events[idx]; - if (!test_bit(idx, cpuc->active_mask)) - continue; - p4_pmu_enable_event(event); - } -} - -static int p4_pmu_handle_irq(struct pt_regs *regs) -{ - struct perf_sample_data data; - struct cpu_hw_events *cpuc; - struct perf_event *event; - struct hw_perf_event *hwc; - int idx, handled = 0; - u64 val; - - data.addr = 0; - data.raw = NULL; - - cpuc = &__get_cpu_var(cpu_hw_events); - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - - if (!test_bit(idx, cpuc->active_mask)) - continue; - - event = cpuc->events[idx]; - hwc = &event->hw; - - WARN_ON_ONCE(hwc->idx != idx); - - /* - * FIXME: Redundant call, actually not needed - * but just to check if we're screwed - */ - p4_pmu_clear_cccr_ovf(hwc); - - val = x86_perf_event_update(event); - if (val & (1ULL << (x86_pmu.cntval_bits - 1))) - continue; - - /* - * event overflow - */ - handled = 1; - data.period = event->hw.last_period; - - if (!x86_perf_event_set_period(event)) - continue; - if (perf_event_overflow(event, 1, &data, regs)) - p4_pmu_disable_event(event); - } - - if (handled) { - /* p4 quirk: unmask it again */ - apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); - inc_irq_stat(apic_perf_irqs); - } - - return handled; -} - -/* - * swap thread specific fields according to a thread - * we are going to run on - */ -static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu) -{ - u32 escr, cccr; - - /* - * we either lucky and continue on same cpu or no HT support - */ - if (!p4_should_swap_ts(hwc->config, cpu)) - return; - - /* - * the event is migrated from an another logical - * cpu, so we need to swap thread specific flags - */ - - escr = p4_config_unpack_escr(hwc->config); - cccr = p4_config_unpack_cccr(hwc->config); - - if (p4_ht_thread(cpu)) { - cccr &= ~P4_CCCR_OVF_PMI_T0; - cccr |= P4_CCCR_OVF_PMI_T1; - if (escr & P4_ESCR_T0_OS) { - escr &= ~P4_ESCR_T0_OS; - escr |= P4_ESCR_T1_OS; - } - if (escr & P4_ESCR_T0_USR) { - escr &= ~P4_ESCR_T0_USR; - escr |= P4_ESCR_T1_USR; - } - hwc->config = p4_config_pack_escr(escr); - hwc->config |= p4_config_pack_cccr(cccr); - hwc->config |= P4_CONFIG_HT; - } else { - cccr &= ~P4_CCCR_OVF_PMI_T1; - cccr |= P4_CCCR_OVF_PMI_T0; - if (escr & P4_ESCR_T1_OS) { - escr &= ~P4_ESCR_T1_OS; - escr |= P4_ESCR_T0_OS; - } - if (escr & P4_ESCR_T1_USR) { - escr &= ~P4_ESCR_T1_USR; - escr |= P4_ESCR_T0_USR; - } - hwc->config = p4_config_pack_escr(escr); - hwc->config |= p4_config_pack_cccr(cccr); - hwc->config &= ~P4_CONFIG_HT; - } -} - -/* ESCRs are not sequential in memory so we need a map */ -static const unsigned int p4_escr_map[ARCH_P4_TOTAL_ESCR] = { - MSR_P4_ALF_ESCR0, /* 0 */ - MSR_P4_ALF_ESCR1, /* 1 */ - MSR_P4_BPU_ESCR0, /* 2 */ - MSR_P4_BPU_ESCR1, /* 3 */ - MSR_P4_BSU_ESCR0, /* 4 */ - MSR_P4_BSU_ESCR1, /* 5 */ - MSR_P4_CRU_ESCR0, /* 6 */ - MSR_P4_CRU_ESCR1, /* 7 */ - MSR_P4_CRU_ESCR2, /* 8 */ - MSR_P4_CRU_ESCR3, /* 9 */ - MSR_P4_CRU_ESCR4, /* 10 */ - MSR_P4_CRU_ESCR5, /* 11 */ - MSR_P4_DAC_ESCR0, /* 12 */ - MSR_P4_DAC_ESCR1, /* 13 */ - MSR_P4_FIRM_ESCR0, /* 14 */ - MSR_P4_FIRM_ESCR1, /* 15 */ - MSR_P4_FLAME_ESCR0, /* 16 */ - MSR_P4_FLAME_ESCR1, /* 17 */ - MSR_P4_FSB_ESCR0, /* 18 */ - MSR_P4_FSB_ESCR1, /* 19 */ - MSR_P4_IQ_ESCR0, /* 20 */ - MSR_P4_IQ_ESCR1, /* 21 */ - MSR_P4_IS_ESCR0, /* 22 */ - MSR_P4_IS_ESCR1, /* 23 */ - MSR_P4_ITLB_ESCR0, /* 24 */ - MSR_P4_ITLB_ESCR1, /* 25 */ - MSR_P4_IX_ESCR0, /* 26 */ - MSR_P4_IX_ESCR1, /* 27 */ - MSR_P4_MOB_ESCR0, /* 28 */ - MSR_P4_MOB_ESCR1, /* 29 */ - MSR_P4_MS_ESCR0, /* 30 */ - MSR_P4_MS_ESCR1, /* 31 */ - MSR_P4_PMH_ESCR0, /* 32 */ - MSR_P4_PMH_ESCR1, /* 33 */ - MSR_P4_RAT_ESCR0, /* 34 */ - MSR_P4_RAT_ESCR1, /* 35 */ - MSR_P4_SAAT_ESCR0, /* 36 */ - MSR_P4_SAAT_ESCR1, /* 37 */ - MSR_P4_SSU_ESCR0, /* 38 */ - MSR_P4_SSU_ESCR1, /* 39 */ - MSR_P4_TBPU_ESCR0, /* 40 */ - MSR_P4_TBPU_ESCR1, /* 41 */ - MSR_P4_TC_ESCR0, /* 42 */ - MSR_P4_TC_ESCR1, /* 43 */ - MSR_P4_U2L_ESCR0, /* 44 */ - MSR_P4_U2L_ESCR1, /* 45 */ -}; - -static int p4_get_escr_idx(unsigned int addr) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(p4_escr_map); i++) { - if (addr == p4_escr_map[i]) - return i; - } - - return -1; -} - -static int p4_next_cntr(int thread, unsigned long *used_mask, - struct p4_event_bind *bind) -{ - int i = 0, j; - - for (i = 0; i < P4_CNTR_LIMIT; i++) { - j = bind->cntr[thread][i++]; - if (j == -1 || !test_bit(j, used_mask)) - return j; - } - - return -1; -} - -static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) -{ - unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; - unsigned long escr_mask[BITS_TO_LONGS(ARCH_P4_TOTAL_ESCR)]; - int cpu = raw_smp_processor_id(); - struct hw_perf_event *hwc; - struct p4_event_bind *bind; - unsigned int i, thread, num; - int cntr_idx, escr_idx; - - bitmap_zero(used_mask, X86_PMC_IDX_MAX); - bitmap_zero(escr_mask, ARCH_P4_TOTAL_ESCR); - - for (i = 0, num = n; i < n; i++, num--) { - - hwc = &cpuc->event_list[i]->hw; - thread = p4_ht_thread(cpu); - bind = p4_config_get_bind(hwc->config); - escr_idx = p4_get_escr_idx(bind->escr_msr[thread]); - - if (hwc->idx != -1 && !p4_should_swap_ts(hwc->config, cpu)) { - cntr_idx = hwc->idx; - if (assign) - assign[i] = hwc->idx; - goto reserve; - } - - cntr_idx = p4_next_cntr(thread, used_mask, bind); - if (cntr_idx == -1 || test_bit(escr_idx, escr_mask)) - goto done; - - p4_pmu_swap_config_ts(hwc, cpu); - if (assign) - assign[i] = cntr_idx; -reserve: - set_bit(cntr_idx, used_mask); - set_bit(escr_idx, escr_mask); - } - -done: - return num ? -ENOSPC : 0; -} - -static __initconst const struct x86_pmu p4_pmu = { - .name = "Netburst P4/Xeon", - .handle_irq = p4_pmu_handle_irq, - .disable_all = p4_pmu_disable_all, - .enable_all = p4_pmu_enable_all, - .enable = p4_pmu_enable_event, - .disable = p4_pmu_disable_event, - .eventsel = MSR_P4_BPU_CCCR0, - .perfctr = MSR_P4_BPU_PERFCTR0, - .event_map = p4_pmu_event_map, - .max_events = ARRAY_SIZE(p4_general_events), - .get_event_constraints = x86_get_event_constraints, - /* - * IF HT disabled we may need to use all - * ARCH_P4_MAX_CCCR counters simulaneously - * though leave it restricted at moment assuming - * HT is on - */ - .num_counters = ARCH_P4_MAX_CCCR, - .apic = 1, - .cntval_bits = 40, - .cntval_mask = (1ULL << 40) - 1, - .max_period = (1ULL << 39) - 1, - .hw_config = p4_hw_config, - .schedule_events = p4_pmu_schedule_events, -}; - -static __init int p4_pmu_init(void) -{ - unsigned int low, high; - - /* If we get stripped -- indexig fails */ - BUILD_BUG_ON(ARCH_P4_MAX_CCCR > X86_PMC_MAX_GENERIC); - - rdmsr(MSR_IA32_MISC_ENABLE, low, high); - if (!(low & (1 << 7))) { - pr_cont("unsupported Netburst CPU model %d ", - boot_cpu_data.x86_model); - return -ENODEV; - } - - memcpy(hw_cache_event_ids, p4_hw_cache_event_ids, - sizeof(hw_cache_event_ids)); - - pr_cont("Netburst events, "); - - x86_pmu = p4_pmu; - - return 0; -} - -#endif /* CONFIG_CPU_SUP_INTEL */ diff --git a/trunk/arch/x86/kernel/cpu/perf_event_p6.c b/trunk/arch/x86/kernel/cpu/perf_event_p6.c index 34ba07be2cda..a330485d14da 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event_p6.c +++ b/trunk/arch/x86/kernel/cpu/perf_event_p6.c @@ -27,6 +27,24 @@ static u64 p6_pmu_event_map(int hw_event) */ #define P6_NOP_EVENT 0x0000002EULL +static u64 p6_pmu_raw_event(u64 hw_event) +{ +#define P6_EVNTSEL_EVENT_MASK 0x000000FFULL +#define P6_EVNTSEL_UNIT_MASK 0x0000FF00ULL +#define P6_EVNTSEL_EDGE_MASK 0x00040000ULL +#define P6_EVNTSEL_INV_MASK 0x00800000ULL +#define P6_EVNTSEL_REG_MASK 0xFF000000ULL + +#define P6_EVNTSEL_MASK \ + (P6_EVNTSEL_EVENT_MASK | \ + P6_EVNTSEL_UNIT_MASK | \ + P6_EVNTSEL_EDGE_MASK | \ + P6_EVNTSEL_INV_MASK | \ + P6_EVNTSEL_REG_MASK) + + return hw_event & P6_EVNTSEL_MASK; +} + static struct event_constraint p6_event_constraints[] = { INTEL_EVENT_CONSTRAINT(0xc1, 0x1), /* FLOPS */ @@ -48,7 +66,7 @@ static void p6_pmu_disable_all(void) wrmsrl(MSR_P6_EVNTSEL0, val); } -static void p6_pmu_enable_all(int added) +static void p6_pmu_enable_all(void) { unsigned long val; @@ -84,23 +102,22 @@ static void p6_pmu_enable_event(struct perf_event *event) (void)checking_wrmsrl(hwc->config_base + hwc->idx, val); } -static __initconst const struct x86_pmu p6_pmu = { +static __initconst struct x86_pmu p6_pmu = { .name = "p6", .handle_irq = x86_pmu_handle_irq, .disable_all = p6_pmu_disable_all, .enable_all = p6_pmu_enable_all, .enable = p6_pmu_enable_event, .disable = p6_pmu_disable_event, - .hw_config = x86_pmu_hw_config, - .schedule_events = x86_schedule_events, .eventsel = MSR_P6_EVNTSEL0, .perfctr = MSR_P6_PERFCTR0, .event_map = p6_pmu_event_map, + .raw_event = p6_pmu_raw_event, .max_events = ARRAY_SIZE(p6_perfmon_event_map), .apic = 1, .max_period = (1ULL << 31) - 1, .version = 0, - .num_counters = 2, + .num_events = 2, /* * Events have 40 bits implemented. However they are designed such * that bits [32-39] are sign extensions of bit 31. As such the @@ -108,8 +125,8 @@ static __initconst const struct x86_pmu p6_pmu = { * * See IA-32 Intel Architecture Software developer manual Vol 3B */ - .cntval_bits = 32, - .cntval_mask = (1ULL << 32) - 1, + .event_bits = 32, + .event_mask = (1ULL << 32) - 1, .get_event_constraints = x86_get_event_constraints, .event_constraints = p6_event_constraints, }; diff --git a/trunk/arch/x86/kernel/ds.c b/trunk/arch/x86/kernel/ds.c new file mode 100644 index 000000000000..1c47390dd0e5 --- /dev/null +++ b/trunk/arch/x86/kernel/ds.c @@ -0,0 +1,1437 @@ +/* + * Debug Store support + * + * This provides a low-level interface to the hardware's Debug Store + * feature that is used for branch trace store (BTS) and + * precise-event based sampling (PEBS). + * + * It manages: + * - DS and BTS hardware configuration + * - buffer overflow handling (to be done) + * - buffer access + * + * It does not do: + * - security checking (is the caller allowed to trace the task) + * - buffer allocation (memory accounting) + * + * + * Copyright (C) 2007-2009 Intel Corporation. + * Markus Metzger , 2007-2009 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ds_selftest.h" + +/* + * The configuration for a particular DS hardware implementation: + */ +struct ds_configuration { + /* The name of the configuration: */ + const char *name; + + /* The size of pointer-typed fields in DS, BTS, and PEBS: */ + unsigned char sizeof_ptr_field; + + /* The size of a BTS/PEBS record in bytes: */ + unsigned char sizeof_rec[2]; + + /* The number of pebs counter reset values in the DS structure. */ + unsigned char nr_counter_reset; + + /* Control bit-masks indexed by enum ds_feature: */ + unsigned long ctl[dsf_ctl_max]; +}; +static struct ds_configuration ds_cfg __read_mostly; + + +/* Maximal size of a DS configuration: */ +#define MAX_SIZEOF_DS 0x80 + +/* Maximal size of a BTS record: */ +#define MAX_SIZEOF_BTS (3 * 8) + +/* BTS and PEBS buffer alignment: */ +#define DS_ALIGNMENT (1 << 3) + +/* Number of buffer pointers in DS: */ +#define NUM_DS_PTR_FIELDS 8 + +/* Size of a pebs reset value in DS: */ +#define PEBS_RESET_FIELD_SIZE 8 + +/* Mask of control bits in the DS MSR register: */ +#define BTS_CONTROL \ + ( ds_cfg.ctl[dsf_bts] | \ + ds_cfg.ctl[dsf_bts_kernel] | \ + ds_cfg.ctl[dsf_bts_user] | \ + ds_cfg.ctl[dsf_bts_overflow] ) + +/* + * A BTS or PEBS tracer. + * + * This holds the configuration of the tracer and serves as a handle + * to identify tracers. + */ +struct ds_tracer { + /* The DS context (partially) owned by this tracer. */ + struct ds_context *context; + /* The buffer provided on ds_request() and its size in bytes. */ + void *buffer; + size_t size; +}; + +struct bts_tracer { + /* The common DS part: */ + struct ds_tracer ds; + + /* The trace including the DS configuration: */ + struct bts_trace trace; + + /* Buffer overflow notification function: */ + bts_ovfl_callback_t ovfl; + + /* Active flags affecting trace collection. */ + unsigned int flags; +}; + +struct pebs_tracer { + /* The common DS part: */ + struct ds_tracer ds; + + /* The trace including the DS configuration: */ + struct pebs_trace trace; + + /* Buffer overflow notification function: */ + pebs_ovfl_callback_t ovfl; +}; + +/* + * Debug Store (DS) save area configuration (see Intel64 and IA32 + * Architectures Software Developer's Manual, section 18.5) + * + * The DS configuration consists of the following fields; different + * architetures vary in the size of those fields. + * + * - double-word aligned base linear address of the BTS buffer + * - write pointer into the BTS buffer + * - end linear address of the BTS buffer (one byte beyond the end of + * the buffer) + * - interrupt pointer into BTS buffer + * (interrupt occurs when write pointer passes interrupt pointer) + * - double-word aligned base linear address of the PEBS buffer + * - write pointer into the PEBS buffer + * - end linear address of the PEBS buffer (one byte beyond the end of + * the buffer) + * - interrupt pointer into PEBS buffer + * (interrupt occurs when write pointer passes interrupt pointer) + * - value to which counter is reset following counter overflow + * + * Later architectures use 64bit pointers throughout, whereas earlier + * architectures use 32bit pointers in 32bit mode. + * + * + * We compute the base address for the first 8 fields based on: + * - the field size stored in the DS configuration + * - the relative field position + * - an offset giving the start of the respective region + * + * This offset is further used to index various arrays holding + * information for BTS and PEBS at the respective index. + * + * On later 32bit processors, we only access the lower 32bit of the + * 64bit pointer fields. The upper halves will be zeroed out. + */ + +enum ds_field { + ds_buffer_base = 0, + ds_index, + ds_absolute_maximum, + ds_interrupt_threshold, +}; + +enum ds_qualifier { + ds_bts = 0, + ds_pebs +}; + +static inline unsigned long +ds_get(const unsigned char *base, enum ds_qualifier qual, enum ds_field field) +{ + base += (ds_cfg.sizeof_ptr_field * (field + (4 * qual))); + return *(unsigned long *)base; +} + +static inline void +ds_set(unsigned char *base, enum ds_qualifier qual, enum ds_field field, + unsigned long value) +{ + base += (ds_cfg.sizeof_ptr_field * (field + (4 * qual))); + (*(unsigned long *)base) = value; +} + + +/* + * Locking is done only for allocating BTS or PEBS resources. + */ +static DEFINE_SPINLOCK(ds_lock); + +/* + * We either support (system-wide) per-cpu or per-thread allocation. + * We distinguish the two based on the task_struct pointer, where a + * NULL pointer indicates per-cpu allocation for the current cpu. + * + * Allocations are use-counted. As soon as resources are allocated, + * further allocations must be of the same type (per-cpu or + * per-thread). We model this by counting allocations (i.e. the number + * of tracers of a certain type) for one type negatively: + * =0 no tracers + * >0 number of per-thread tracers + * <0 number of per-cpu tracers + * + * Tracers essentially gives the number of ds contexts for a certain + * type of allocation. + */ +static atomic_t tracers = ATOMIC_INIT(0); + +static inline int get_tracer(struct task_struct *task) +{ + int error; + + spin_lock_irq(&ds_lock); + + if (task) { + error = -EPERM; + if (atomic_read(&tracers) < 0) + goto out; + atomic_inc(&tracers); + } else { + error = -EPERM; + if (atomic_read(&tracers) > 0) + goto out; + atomic_dec(&tracers); + } + + error = 0; +out: + spin_unlock_irq(&ds_lock); + return error; +} + +static inline void put_tracer(struct task_struct *task) +{ + if (task) + atomic_dec(&tracers); + else + atomic_inc(&tracers); +} + +/* + * The DS context is either attached to a thread or to a cpu: + * - in the former case, the thread_struct contains a pointer to the + * attached context. + * - in the latter case, we use a static array of per-cpu context + * pointers. + * + * Contexts are use-counted. They are allocated on first access and + * deallocated when the last user puts the context. + */ +struct ds_context { + /* The DS configuration; goes into MSR_IA32_DS_AREA: */ + unsigned char ds[MAX_SIZEOF_DS]; + + /* The owner of the BTS and PEBS configuration, respectively: */ + struct bts_tracer *bts_master; + struct pebs_tracer *pebs_master; + + /* Use count: */ + unsigned long count; + + /* Pointer to the context pointer field: */ + struct ds_context **this; + + /* The traced task; NULL for cpu tracing: */ + struct task_struct *task; + + /* The traced cpu; only valid if task is NULL: */ + int cpu; +}; + +static DEFINE_PER_CPU(struct ds_context *, cpu_ds_context); + + +static struct ds_context *ds_get_context(struct task_struct *task, int cpu) +{ + struct ds_context **p_context = + (task ? &task->thread.ds_ctx : &per_cpu(cpu_ds_context, cpu)); + struct ds_context *context = NULL; + struct ds_context *new_context = NULL; + + /* Chances are small that we already have a context. */ + new_context = kzalloc(sizeof(*new_context), GFP_KERNEL); + if (!new_context) + return NULL; + + spin_lock_irq(&ds_lock); + + context = *p_context; + if (likely(!context)) { + context = new_context; + + context->this = p_context; + context->task = task; + context->cpu = cpu; + context->count = 0; + + *p_context = context; + } + + context->count++; + + spin_unlock_irq(&ds_lock); + + if (context != new_context) + kfree(new_context); + + return context; +} + +static void ds_put_context(struct ds_context *context) +{ + struct task_struct *task; + unsigned long irq; + + if (!context) + return; + + spin_lock_irqsave(&ds_lock, irq); + + if (--context->count) { + spin_unlock_irqrestore(&ds_lock, irq); + return; + } + + *(context->this) = NULL; + + task = context->task; + + if (task) + clear_tsk_thread_flag(task, TIF_DS_AREA_MSR); + + /* + * We leave the (now dangling) pointer to the DS configuration in + * the DS_AREA msr. This is as good or as bad as replacing it with + * NULL - the hardware would crash if we enabled tracing. + * + * This saves us some problems with having to write an msr on a + * different cpu while preventing others from doing the same for the + * next context for that same cpu. + */ + + spin_unlock_irqrestore(&ds_lock, irq); + + /* The context might still be in use for context switching. */ + if (task && (task != current)) + wait_task_context_switch(task); + + kfree(context); +} + +static void ds_install_ds_area(struct ds_context *context) +{ + unsigned long ds; + + ds = (unsigned long)context->ds; + + /* + * There is a race between the bts master and the pebs master. + * + * The thread/cpu access is synchronized via get/put_cpu() for + * task tracing and via wrmsr_on_cpu for cpu tracing. + * + * If bts and pebs are collected for the same task or same cpu, + * the same confiuration is written twice. + */ + if (context->task) { + get_cpu(); + if (context->task == current) + wrmsrl(MSR_IA32_DS_AREA, ds); + set_tsk_thread_flag(context->task, TIF_DS_AREA_MSR); + put_cpu(); + } else + wrmsr_on_cpu(context->cpu, MSR_IA32_DS_AREA, + (u32)((u64)ds), (u32)((u64)ds >> 32)); +} + +/* + * Call the tracer's callback on a buffer overflow. + * + * context: the ds context + * qual: the buffer type + */ +static void ds_overflow(struct ds_context *context, enum ds_qualifier qual) +{ + switch (qual) { + case ds_bts: + if (context->bts_master && + context->bts_master->ovfl) + context->bts_master->ovfl(context->bts_master); + break; + case ds_pebs: + if (context->pebs_master && + context->pebs_master->ovfl) + context->pebs_master->ovfl(context->pebs_master); + break; + } +} + + +/* + * Write raw data into the BTS or PEBS buffer. + * + * The remainder of any partially written record is zeroed out. + * + * context: the DS context + * qual: the buffer type + * record: the data to write + * size: the size of the data + */ +static int ds_write(struct ds_context *context, enum ds_qualifier qual, + const void *record, size_t size) +{ + int bytes_written = 0; + + if (!record) + return -EINVAL; + + while (size) { + unsigned long base, index, end, write_end, int_th; + unsigned long write_size, adj_write_size; + + /* + * Write as much as possible without producing an + * overflow interrupt. + * + * Interrupt_threshold must either be + * - bigger than absolute_maximum or + * - point to a record between buffer_base and absolute_maximum + * + * Index points to a valid record. + */ + base = ds_get(context->ds, qual, ds_buffer_base); + index = ds_get(context->ds, qual, ds_index); + end = ds_get(context->ds, qual, ds_absolute_maximum); + int_th = ds_get(context->ds, qual, ds_interrupt_threshold); + + write_end = min(end, int_th); + + /* + * If we are already beyond the interrupt threshold, + * we fill the entire buffer. + */ + if (write_end <= index) + write_end = end; + + if (write_end <= index) + break; + + write_size = min((unsigned long) size, write_end - index); + memcpy((void *)index, record, write_size); + + record = (const char *)record + write_size; + size -= write_size; + bytes_written += write_size; + + adj_write_size = write_size / ds_cfg.sizeof_rec[qual]; + adj_write_size *= ds_cfg.sizeof_rec[qual]; + + /* Zero out trailing bytes. */ + memset((char *)index + write_size, 0, + adj_write_size - write_size); + index += adj_write_size; + + if (index >= end) + index = base; + ds_set(context->ds, qual, ds_index, index); + + if (index >= int_th) + ds_overflow(context, qual); + } + + return bytes_written; +} + + +/* + * Branch Trace Store (BTS) uses the following format. Different + * architectures vary in the size of those fields. + * - source linear address + * - destination linear address + * - flags + * + * Later architectures use 64bit pointers throughout, whereas earlier + * architectures use 32bit pointers in 32bit mode. + * + * We compute the base address for the fields based on: + * - the field size stored in the DS configuration + * - the relative field position + * + * In order to store additional information in the BTS buffer, we use + * a special source address to indicate that the record requires + * special interpretation. + * + * Netburst indicated via a bit in the flags field whether the branch + * was predicted; this is ignored. + * + * We use two levels of abstraction: + * - the raw data level defined here + * - an arch-independent level defined in ds.h + */ + +enum bts_field { + bts_from, + bts_to, + bts_flags, + + bts_qual = bts_from, + bts_clock = bts_to, + bts_pid = bts_flags, + + bts_qual_mask = (bts_qual_max - 1), + bts_escape = ((unsigned long)-1 & ~bts_qual_mask) +}; + +static inline unsigned long bts_get(const char *base, unsigned long field) +{ + base += (ds_cfg.sizeof_ptr_field * field); + return *(unsigned long *)base; +} + +static inline void bts_set(char *base, unsigned long field, unsigned long val) +{ + base += (ds_cfg.sizeof_ptr_field * field); + (*(unsigned long *)base) = val; +} + + +/* + * The raw BTS data is architecture dependent. + * + * For higher-level users, we give an arch-independent view. + * - ds.h defines struct bts_struct + * - bts_read translates one raw bts record into a bts_struct + * - bts_write translates one bts_struct into the raw format and + * writes it into the top of the parameter tracer's buffer. + * + * return: bytes read/written on success; -Eerrno, otherwise + */ +static int +bts_read(struct bts_tracer *tracer, const void *at, struct bts_struct *out) +{ + if (!tracer) + return -EINVAL; + + if (at < tracer->trace.ds.begin) + return -EINVAL; + + if (tracer->trace.ds.end < (at + tracer->trace.ds.size)) + return -EINVAL; + + memset(out, 0, sizeof(*out)); + if ((bts_get(at, bts_qual) & ~bts_qual_mask) == bts_escape) { + out->qualifier = (bts_get(at, bts_qual) & bts_qual_mask); + out->variant.event.clock = bts_get(at, bts_clock); + out->variant.event.pid = bts_get(at, bts_pid); + } else { + out->qualifier = bts_branch; + out->variant.lbr.from = bts_get(at, bts_from); + out->variant.lbr.to = bts_get(at, bts_to); + + if (!out->variant.lbr.from && !out->variant.lbr.to) + out->qualifier = bts_invalid; + } + + return ds_cfg.sizeof_rec[ds_bts]; +} + +static int bts_write(struct bts_tracer *tracer, const struct bts_struct *in) +{ + unsigned char raw[MAX_SIZEOF_BTS]; + + if (!tracer) + return -EINVAL; + + if (MAX_SIZEOF_BTS < ds_cfg.sizeof_rec[ds_bts]) + return -EOVERFLOW; + + switch (in->qualifier) { + case bts_invalid: + bts_set(raw, bts_from, 0); + bts_set(raw, bts_to, 0); + bts_set(raw, bts_flags, 0); + break; + case bts_branch: + bts_set(raw, bts_from, in->variant.lbr.from); + bts_set(raw, bts_to, in->variant.lbr.to); + bts_set(raw, bts_flags, 0); + break; + case bts_task_arrives: + case bts_task_departs: + bts_set(raw, bts_qual, (bts_escape | in->qualifier)); + bts_set(raw, bts_clock, in->variant.event.clock); + bts_set(raw, bts_pid, in->variant.event.pid); + break; + default: + return -EINVAL; + } + + return ds_write(tracer->ds.context, ds_bts, raw, + ds_cfg.sizeof_rec[ds_bts]); +} + + +static void ds_write_config(struct ds_context *context, + struct ds_trace *cfg, enum ds_qualifier qual) +{ + unsigned char *ds = context->ds; + + ds_set(ds, qual, ds_buffer_base, (unsigned long)cfg->begin); + ds_set(ds, qual, ds_index, (unsigned long)cfg->top); + ds_set(ds, qual, ds_absolute_maximum, (unsigned long)cfg->end); + ds_set(ds, qual, ds_interrupt_threshold, (unsigned long)cfg->ith); +} + +static void ds_read_config(struct ds_context *context, + struct ds_trace *cfg, enum ds_qualifier qual) +{ + unsigned char *ds = context->ds; + + cfg->begin = (void *)ds_get(ds, qual, ds_buffer_base); + cfg->top = (void *)ds_get(ds, qual, ds_index); + cfg->end = (void *)ds_get(ds, qual, ds_absolute_maximum); + cfg->ith = (void *)ds_get(ds, qual, ds_interrupt_threshold); +} + +static void ds_init_ds_trace(struct ds_trace *trace, enum ds_qualifier qual, + void *base, size_t size, size_t ith, + unsigned int flags) { + unsigned long buffer, adj; + + /* + * Adjust the buffer address and size to meet alignment + * constraints: + * - buffer is double-word aligned + * - size is multiple of record size + * + * We checked the size at the very beginning; we have enough + * space to do the adjustment. + */ + buffer = (unsigned long)base; + + adj = ALIGN(buffer, DS_ALIGNMENT) - buffer; + buffer += adj; + size -= adj; + + trace->n = size / ds_cfg.sizeof_rec[qual]; + trace->size = ds_cfg.sizeof_rec[qual]; + + size = (trace->n * trace->size); + + trace->begin = (void *)buffer; + trace->top = trace->begin; + trace->end = (void *)(buffer + size); + /* + * The value for 'no threshold' is -1, which will set the + * threshold outside of the buffer, just like we want it. + */ + ith *= ds_cfg.sizeof_rec[qual]; + trace->ith = (void *)(buffer + size - ith); + + trace->flags = flags; +} + + +static int ds_request(struct ds_tracer *tracer, struct ds_trace *trace, + enum ds_qualifier qual, struct task_struct *task, + int cpu, void *base, size_t size, size_t th) +{ + struct ds_context *context; + int error; + size_t req_size; + + error = -EOPNOTSUPP; + if (!ds_cfg.sizeof_rec[qual]) + goto out; + + error = -EINVAL; + if (!base) + goto out; + + req_size = ds_cfg.sizeof_rec[qual]; + /* We might need space for alignment adjustments. */ + if (!IS_ALIGNED((unsigned long)base, DS_ALIGNMENT)) + req_size += DS_ALIGNMENT; + + error = -EINVAL; + if (size < req_size) + goto out; + + if (th != (size_t)-1) { + th *= ds_cfg.sizeof_rec[qual]; + + error = -EINVAL; + if (size <= th) + goto out; + } + + tracer->buffer = base; + tracer->size = size; + + error = -ENOMEM; + context = ds_get_context(task, cpu); + if (!context) + goto out; + tracer->context = context; + + /* + * Defer any tracer-specific initialization work for the context until + * context ownership has been clarified. + */ + + error = 0; + out: + return error; +} + +static struct bts_tracer *ds_request_bts(struct task_struct *task, int cpu, + void *base, size_t size, + bts_ovfl_callback_t ovfl, size_t th, + unsigned int flags) +{ + struct bts_tracer *tracer; + int error; + + /* Buffer overflow notification is not yet implemented. */ + error = -EOPNOTSUPP; + if (ovfl) + goto out; + + error = get_tracer(task); + if (error < 0) + goto out; + + error = -ENOMEM; + tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); + if (!tracer) + goto out_put_tracer; + tracer->ovfl = ovfl; + + /* Do some more error checking and acquire a tracing context. */ + error = ds_request(&tracer->ds, &tracer->trace.ds, + ds_bts, task, cpu, base, size, th); + if (error < 0) + goto out_tracer; + + /* Claim the bts part of the tracing context we acquired above. */ + spin_lock_irq(&ds_lock); + + error = -EPERM; + if (tracer->ds.context->bts_master) + goto out_unlock; + tracer->ds.context->bts_master = tracer; + + spin_unlock_irq(&ds_lock); + + /* + * Now that we own the bts part of the context, let's complete the + * initialization for that part. + */ + ds_init_ds_trace(&tracer->trace.ds, ds_bts, base, size, th, flags); + ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_bts); + ds_install_ds_area(tracer->ds.context); + + tracer->trace.read = bts_read; + tracer->trace.write = bts_write; + + /* Start tracing. */ + ds_resume_bts(tracer); + + return tracer; + + out_unlock: + spin_unlock_irq(&ds_lock); + ds_put_context(tracer->ds.context); + out_tracer: + kfree(tracer); + out_put_tracer: + put_tracer(task); + out: + return ERR_PTR(error); +} + +struct bts_tracer *ds_request_bts_task(struct task_struct *task, + void *base, size_t size, + bts_ovfl_callback_t ovfl, + size_t th, unsigned int flags) +{ + return ds_request_bts(task, 0, base, size, ovfl, th, flags); +} + +struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size, + bts_ovfl_callback_t ovfl, + size_t th, unsigned int flags) +{ + return ds_request_bts(NULL, cpu, base, size, ovfl, th, flags); +} + +static struct pebs_tracer *ds_request_pebs(struct task_struct *task, int cpu, + void *base, size_t size, + pebs_ovfl_callback_t ovfl, size_t th, + unsigned int flags) +{ + struct pebs_tracer *tracer; + int error; + + /* Buffer overflow notification is not yet implemented. */ + error = -EOPNOTSUPP; + if (ovfl) + goto out; + + error = get_tracer(task); + if (error < 0) + goto out; + + error = -ENOMEM; + tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); + if (!tracer) + goto out_put_tracer; + tracer->ovfl = ovfl; + + /* Do some more error checking and acquire a tracing context. */ + error = ds_request(&tracer->ds, &tracer->trace.ds, + ds_pebs, task, cpu, base, size, th); + if (error < 0) + goto out_tracer; + + /* Claim the pebs part of the tracing context we acquired above. */ + spin_lock_irq(&ds_lock); + + error = -EPERM; + if (tracer->ds.context->pebs_master) + goto out_unlock; + tracer->ds.context->pebs_master = tracer; + + spin_unlock_irq(&ds_lock); + + /* + * Now that we own the pebs part of the context, let's complete the + * initialization for that part. + */ + ds_init_ds_trace(&tracer->trace.ds, ds_pebs, base, size, th, flags); + ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); + ds_install_ds_area(tracer->ds.context); + + /* Start tracing. */ + ds_resume_pebs(tracer); + + return tracer; + + out_unlock: + spin_unlock_irq(&ds_lock); + ds_put_context(tracer->ds.context); + out_tracer: + kfree(tracer); + out_put_tracer: + put_tracer(task); + out: + return ERR_PTR(error); +} + +struct pebs_tracer *ds_request_pebs_task(struct task_struct *task, + void *base, size_t size, + pebs_ovfl_callback_t ovfl, + size_t th, unsigned int flags) +{ + return ds_request_pebs(task, 0, base, size, ovfl, th, flags); +} + +struct pebs_tracer *ds_request_pebs_cpu(int cpu, void *base, size_t size, + pebs_ovfl_callback_t ovfl, + size_t th, unsigned int flags) +{ + return ds_request_pebs(NULL, cpu, base, size, ovfl, th, flags); +} + +static void ds_free_bts(struct bts_tracer *tracer) +{ + struct task_struct *task; + + task = tracer->ds.context->task; + + WARN_ON_ONCE(tracer->ds.context->bts_master != tracer); + tracer->ds.context->bts_master = NULL; + + /* Make sure tracing stopped and the tracer is not in use. */ + if (task && (task != current)) + wait_task_context_switch(task); + + ds_put_context(tracer->ds.context); + put_tracer(task); + + kfree(tracer); +} + +void ds_release_bts(struct bts_tracer *tracer) +{ + might_sleep(); + + if (!tracer) + return; + + ds_suspend_bts(tracer); + ds_free_bts(tracer); +} + +int ds_release_bts_noirq(struct bts_tracer *tracer) +{ + struct task_struct *task; + unsigned long irq; + int error; + + if (!tracer) + return 0; + + task = tracer->ds.context->task; + + local_irq_save(irq); + + error = -EPERM; + if (!task && + (tracer->ds.context->cpu != smp_processor_id())) + goto out; + + error = -EPERM; + if (task && (task != current)) + goto out; + + ds_suspend_bts_noirq(tracer); + ds_free_bts(tracer); + + error = 0; + out: + local_irq_restore(irq); + return error; +} + +static void update_task_debugctlmsr(struct task_struct *task, + unsigned long debugctlmsr) +{ + task->thread.debugctlmsr = debugctlmsr; + + get_cpu(); + if (task == current) + update_debugctlmsr(debugctlmsr); + put_cpu(); +} + +void ds_suspend_bts(struct bts_tracer *tracer) +{ + struct task_struct *task; + unsigned long debugctlmsr; + int cpu; + + if (!tracer) + return; + + tracer->flags = 0; + + task = tracer->ds.context->task; + cpu = tracer->ds.context->cpu; + + WARN_ON(!task && irqs_disabled()); + + debugctlmsr = (task ? + task->thread.debugctlmsr : + get_debugctlmsr_on_cpu(cpu)); + debugctlmsr &= ~BTS_CONTROL; + + if (task) + update_task_debugctlmsr(task, debugctlmsr); + else + update_debugctlmsr_on_cpu(cpu, debugctlmsr); +} + +int ds_suspend_bts_noirq(struct bts_tracer *tracer) +{ + struct task_struct *task; + unsigned long debugctlmsr, irq; + int cpu, error = 0; + + if (!tracer) + return 0; + + tracer->flags = 0; + + task = tracer->ds.context->task; + cpu = tracer->ds.context->cpu; + + local_irq_save(irq); + + error = -EPERM; + if (!task && (cpu != smp_processor_id())) + goto out; + + debugctlmsr = (task ? + task->thread.debugctlmsr : + get_debugctlmsr()); + debugctlmsr &= ~BTS_CONTROL; + + if (task) + update_task_debugctlmsr(task, debugctlmsr); + else + update_debugctlmsr(debugctlmsr); + + error = 0; + out: + local_irq_restore(irq); + return error; +} + +static unsigned long ds_bts_control(struct bts_tracer *tracer) +{ + unsigned long control; + + control = ds_cfg.ctl[dsf_bts]; + if (!(tracer->trace.ds.flags & BTS_KERNEL)) + control |= ds_cfg.ctl[dsf_bts_kernel]; + if (!(tracer->trace.ds.flags & BTS_USER)) + control |= ds_cfg.ctl[dsf_bts_user]; + + return control; +} + +void ds_resume_bts(struct bts_tracer *tracer) +{ + struct task_struct *task; + unsigned long debugctlmsr; + int cpu; + + if (!tracer) + return; + + tracer->flags = tracer->trace.ds.flags; + + task = tracer->ds.context->task; + cpu = tracer->ds.context->cpu; + + WARN_ON(!task && irqs_disabled()); + + debugctlmsr = (task ? + task->thread.debugctlmsr : + get_debugctlmsr_on_cpu(cpu)); + debugctlmsr |= ds_bts_control(tracer); + + if (task) + update_task_debugctlmsr(task, debugctlmsr); + else + update_debugctlmsr_on_cpu(cpu, debugctlmsr); +} + +int ds_resume_bts_noirq(struct bts_tracer *tracer) +{ + struct task_struct *task; + unsigned long debugctlmsr, irq; + int cpu, error = 0; + + if (!tracer) + return 0; + + tracer->flags = tracer->trace.ds.flags; + + task = tracer->ds.context->task; + cpu = tracer->ds.context->cpu; + + local_irq_save(irq); + + error = -EPERM; + if (!task && (cpu != smp_processor_id())) + goto out; + + debugctlmsr = (task ? + task->thread.debugctlmsr : + get_debugctlmsr()); + debugctlmsr |= ds_bts_control(tracer); + + if (task) + update_task_debugctlmsr(task, debugctlmsr); + else + update_debugctlmsr(debugctlmsr); + + error = 0; + out: + local_irq_restore(irq); + return error; +} + +static void ds_free_pebs(struct pebs_tracer *tracer) +{ + struct task_struct *task; + + task = tracer->ds.context->task; + + WARN_ON_ONCE(tracer->ds.context->pebs_master != tracer); + tracer->ds.context->pebs_master = NULL; + + ds_put_context(tracer->ds.context); + put_tracer(task); + + kfree(tracer); +} + +void ds_release_pebs(struct pebs_tracer *tracer) +{ + might_sleep(); + + if (!tracer) + return; + + ds_suspend_pebs(tracer); + ds_free_pebs(tracer); +} + +int ds_release_pebs_noirq(struct pebs_tracer *tracer) +{ + struct task_struct *task; + unsigned long irq; + int error; + + if (!tracer) + return 0; + + task = tracer->ds.context->task; + + local_irq_save(irq); + + error = -EPERM; + if (!task && + (tracer->ds.context->cpu != smp_processor_id())) + goto out; + + error = -EPERM; + if (task && (task != current)) + goto out; + + ds_suspend_pebs_noirq(tracer); + ds_free_pebs(tracer); + + error = 0; + out: + local_irq_restore(irq); + return error; +} + +void ds_suspend_pebs(struct pebs_tracer *tracer) +{ + +} + +int ds_suspend_pebs_noirq(struct pebs_tracer *tracer) +{ + return 0; +} + +void ds_resume_pebs(struct pebs_tracer *tracer) +{ + +} + +int ds_resume_pebs_noirq(struct pebs_tracer *tracer) +{ + return 0; +} + +const struct bts_trace *ds_read_bts(struct bts_tracer *tracer) +{ + if (!tracer) + return NULL; + + ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_bts); + return &tracer->trace; +} + +const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer) +{ + if (!tracer) + return NULL; + + ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); + + tracer->trace.counters = ds_cfg.nr_counter_reset; + memcpy(tracer->trace.counter_reset, + tracer->ds.context->ds + + (NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field), + ds_cfg.nr_counter_reset * PEBS_RESET_FIELD_SIZE); + + return &tracer->trace; +} + +int ds_reset_bts(struct bts_tracer *tracer) +{ + if (!tracer) + return -EINVAL; + + tracer->trace.ds.top = tracer->trace.ds.begin; + + ds_set(tracer->ds.context->ds, ds_bts, ds_index, + (unsigned long)tracer->trace.ds.top); + + return 0; +} + +int ds_reset_pebs(struct pebs_tracer *tracer) +{ + if (!tracer) + return -EINVAL; + + tracer->trace.ds.top = tracer->trace.ds.begin; + + ds_set(tracer->ds.context->ds, ds_pebs, ds_index, + (unsigned long)tracer->trace.ds.top); + + return 0; +} + +int ds_set_pebs_reset(struct pebs_tracer *tracer, + unsigned int counter, u64 value) +{ + if (!tracer) + return -EINVAL; + + if (ds_cfg.nr_counter_reset < counter) + return -EINVAL; + + *(u64 *)(tracer->ds.context->ds + + (NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field) + + (counter * PEBS_RESET_FIELD_SIZE)) = value; + + return 0; +} + +static const struct ds_configuration ds_cfg_netburst = { + .name = "Netburst", + .ctl[dsf_bts] = (1 << 2) | (1 << 3), + .ctl[dsf_bts_kernel] = (1 << 5), + .ctl[dsf_bts_user] = (1 << 6), + .nr_counter_reset = 1, +}; +static const struct ds_configuration ds_cfg_pentium_m = { + .name = "Pentium M", + .ctl[dsf_bts] = (1 << 6) | (1 << 7), + .nr_counter_reset = 1, +}; +static const struct ds_configuration ds_cfg_core2_atom = { + .name = "Core 2/Atom", + .ctl[dsf_bts] = (1 << 6) | (1 << 7), + .ctl[dsf_bts_kernel] = (1 << 9), + .ctl[dsf_bts_user] = (1 << 10), + .nr_counter_reset = 1, +}; +static const struct ds_configuration ds_cfg_core_i7 = { + .name = "Core i7", + .ctl[dsf_bts] = (1 << 6) | (1 << 7), + .ctl[dsf_bts_kernel] = (1 << 9), + .ctl[dsf_bts_user] = (1 << 10), + .nr_counter_reset = 4, +}; + +static void +ds_configure(const struct ds_configuration *cfg, + struct cpuinfo_x86 *cpu) +{ + unsigned long nr_pebs_fields = 0; + + printk(KERN_INFO "[ds] using %s configuration\n", cfg->name); + +#ifdef __i386__ + nr_pebs_fields = 10; +#else + nr_pebs_fields = 18; +#endif + + /* + * Starting with version 2, architectural performance + * monitoring supports a format specifier. + */ + if ((cpuid_eax(0xa) & 0xff) > 1) { + unsigned long perf_capabilities, format; + + rdmsrl(MSR_IA32_PERF_CAPABILITIES, perf_capabilities); + + format = (perf_capabilities >> 8) & 0xf; + + switch (format) { + case 0: + nr_pebs_fields = 18; + break; + case 1: + nr_pebs_fields = 22; + break; + default: + printk(KERN_INFO + "[ds] unknown PEBS format: %lu\n", format); + nr_pebs_fields = 0; + break; + } + } + + memset(&ds_cfg, 0, sizeof(ds_cfg)); + ds_cfg = *cfg; + + ds_cfg.sizeof_ptr_field = + (cpu_has(cpu, X86_FEATURE_DTES64) ? 8 : 4); + + ds_cfg.sizeof_rec[ds_bts] = ds_cfg.sizeof_ptr_field * 3; + ds_cfg.sizeof_rec[ds_pebs] = ds_cfg.sizeof_ptr_field * nr_pebs_fields; + + if (!cpu_has(cpu, X86_FEATURE_BTS)) { + ds_cfg.sizeof_rec[ds_bts] = 0; + printk(KERN_INFO "[ds] bts not available\n"); + } + if (!cpu_has(cpu, X86_FEATURE_PEBS)) { + ds_cfg.sizeof_rec[ds_pebs] = 0; + printk(KERN_INFO "[ds] pebs not available\n"); + } + + printk(KERN_INFO "[ds] sizes: address: %u bit, ", + 8 * ds_cfg.sizeof_ptr_field); + printk("bts/pebs record: %u/%u bytes\n", + ds_cfg.sizeof_rec[ds_bts], ds_cfg.sizeof_rec[ds_pebs]); + + WARN_ON_ONCE(MAX_PEBS_COUNTERS < ds_cfg.nr_counter_reset); +} + +void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) +{ + /* Only configure the first cpu. Others are identical. */ + if (ds_cfg.name) + return; + + switch (c->x86) { + case 0x6: + switch (c->x86_model) { + case 0x9: + case 0xd: /* Pentium M */ + ds_configure(&ds_cfg_pentium_m, c); + break; + case 0xf: + case 0x17: /* Core2 */ + case 0x1c: /* Atom */ + ds_configure(&ds_cfg_core2_atom, c); + break; + case 0x1a: /* Core i7 */ + ds_configure(&ds_cfg_core_i7, c); + break; + default: + /* Sorry, don't know about them. */ + break; + } + break; + case 0xf: + switch (c->x86_model) { + case 0x0: + case 0x1: + case 0x2: /* Netburst */ + ds_configure(&ds_cfg_netburst, c); + break; + default: + /* Sorry, don't know about them. */ + break; + } + break; + default: + /* Sorry, don't know about them. */ + break; + } +} + +static inline void ds_take_timestamp(struct ds_context *context, + enum bts_qualifier qualifier, + struct task_struct *task) +{ + struct bts_tracer *tracer = context->bts_master; + struct bts_struct ts; + + /* Prevent compilers from reading the tracer pointer twice. */ + barrier(); + + if (!tracer || !(tracer->flags & BTS_TIMESTAMPS)) + return; + + memset(&ts, 0, sizeof(ts)); + ts.qualifier = qualifier; + ts.variant.event.clock = trace_clock_global(); + ts.variant.event.pid = task->pid; + + bts_write(tracer, &ts); +} + +/* + * Change the DS configuration from tracing prev to tracing next. + */ +void ds_switch_to(struct task_struct *prev, struct task_struct *next) +{ + struct ds_context *prev_ctx = prev->thread.ds_ctx; + struct ds_context *next_ctx = next->thread.ds_ctx; + unsigned long debugctlmsr = next->thread.debugctlmsr; + + /* Make sure all data is read before we start. */ + barrier(); + + if (prev_ctx) { + update_debugctlmsr(0); + + ds_take_timestamp(prev_ctx, bts_task_departs, prev); + } + + if (next_ctx) { + ds_take_timestamp(next_ctx, bts_task_arrives, next); + + wrmsrl(MSR_IA32_DS_AREA, (unsigned long)next_ctx->ds); + } + + update_debugctlmsr(debugctlmsr); +} + +static __init int ds_selftest(void) +{ + if (ds_cfg.sizeof_rec[ds_bts]) { + int error; + + error = ds_selftest_bts(); + if (error) { + WARN(1, "[ds] selftest failed. disabling bts.\n"); + ds_cfg.sizeof_rec[ds_bts] = 0; + } + } + + if (ds_cfg.sizeof_rec[ds_pebs]) { + int error; + + error = ds_selftest_pebs(); + if (error) { + WARN(1, "[ds] selftest failed. disabling pebs.\n"); + ds_cfg.sizeof_rec[ds_pebs] = 0; + } + } + + return 0; +} +device_initcall(ds_selftest); diff --git a/trunk/arch/x86/kernel/ds_selftest.c b/trunk/arch/x86/kernel/ds_selftest.c new file mode 100644 index 000000000000..6bc7c199ab99 --- /dev/null +++ b/trunk/arch/x86/kernel/ds_selftest.c @@ -0,0 +1,408 @@ +/* + * Debug Store support - selftest + * + * + * Copyright (C) 2009 Intel Corporation. + * Markus Metzger , 2009 + */ + +#include "ds_selftest.h" + +#include +#include +#include +#include + +#include + + +#define BUFFER_SIZE 521 /* Intentionally chose an odd size. */ +#define SMALL_BUFFER_SIZE 24 /* A single bts entry. */ + +struct ds_selftest_bts_conf { + struct bts_tracer *tracer; + int error; + int (*suspend)(struct bts_tracer *); + int (*resume)(struct bts_tracer *); +}; + +static int ds_selftest_bts_consistency(const struct bts_trace *trace) +{ + int error = 0; + + if (!trace) { + printk(KERN_CONT "failed to access trace..."); + /* Bail out. Other tests are pointless. */ + return -1; + } + + if (!trace->read) { + printk(KERN_CONT "bts read not available..."); + error = -1; + } + + /* Do some sanity checks on the trace configuration. */ + if (!trace->ds.n) { + printk(KERN_CONT "empty bts buffer..."); + error = -1; + } + if (!trace->ds.size) { + printk(KERN_CONT "bad bts trace setup..."); + error = -1; + } + if (trace->ds.end != + (char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) { + printk(KERN_CONT "bad bts buffer setup..."); + error = -1; + } + /* + * We allow top in [begin; end], since its not clear when the + * overflow adjustment happens: after the increment or before the + * write. + */ + if ((trace->ds.top < trace->ds.begin) || + (trace->ds.end < trace->ds.top)) { + printk(KERN_CONT "bts top out of bounds..."); + error = -1; + } + + return error; +} + +static int ds_selftest_bts_read(struct bts_tracer *tracer, + const struct bts_trace *trace, + const void *from, const void *to) +{ + const unsigned char *at; + + /* + * Check a few things which do not belong to this test. + * They should be covered by other tests. + */ + if (!trace) + return -1; + + if (!trace->read) + return -1; + + if (to < from) + return -1; + + if (from < trace->ds.begin) + return -1; + + if (trace->ds.end < to) + return -1; + + if (!trace->ds.size) + return -1; + + /* Now to the test itself. */ + for (at = from; (void *)at < to; at += trace->ds.size) { + struct bts_struct bts; + unsigned long index; + int error; + + if (((void *)at - trace->ds.begin) % trace->ds.size) { + printk(KERN_CONT + "read from non-integer index..."); + return -1; + } + index = ((void *)at - trace->ds.begin) / trace->ds.size; + + memset(&bts, 0, sizeof(bts)); + error = trace->read(tracer, at, &bts); + if (error < 0) { + printk(KERN_CONT + "error reading bts trace at [%lu] (0x%p)...", + index, at); + return error; + } + + switch (bts.qualifier) { + case BTS_BRANCH: + break; + default: + printk(KERN_CONT + "unexpected bts entry %llu at [%lu] (0x%p)...", + bts.qualifier, index, at); + return -1; + } + } + + return 0; +} + +static void ds_selftest_bts_cpu(void *arg) +{ + struct ds_selftest_bts_conf *conf = arg; + const struct bts_trace *trace; + void *top; + + if (IS_ERR(conf->tracer)) { + conf->error = PTR_ERR(conf->tracer); + conf->tracer = NULL; + + printk(KERN_CONT + "initialization failed (err: %d)...", conf->error); + return; + } + + /* We should meanwhile have enough trace. */ + conf->error = conf->suspend(conf->tracer); + if (conf->error < 0) + return; + + /* Let's see if we can access the trace. */ + trace = ds_read_bts(conf->tracer); + + conf->error = ds_selftest_bts_consistency(trace); + if (conf->error < 0) + return; + + /* If everything went well, we should have a few trace entries. */ + if (trace->ds.top == trace->ds.begin) { + /* + * It is possible but highly unlikely that we got a + * buffer overflow and end up at exactly the same + * position we started from. + * Let's issue a warning, but continue. + */ + printk(KERN_CONT "no trace/overflow..."); + } + + /* Let's try to read the trace we collected. */ + conf->error = + ds_selftest_bts_read(conf->tracer, trace, + trace->ds.begin, trace->ds.top); + if (conf->error < 0) + return; + + /* + * Let's read the trace again. + * Since we suspended tracing, we should get the same result. + */ + top = trace->ds.top; + + trace = ds_read_bts(conf->tracer); + conf->error = ds_selftest_bts_consistency(trace); + if (conf->error < 0) + return; + + if (top != trace->ds.top) { + printk(KERN_CONT "suspend not working..."); + conf->error = -1; + return; + } + + /* Let's collect some more trace - see if resume is working. */ + conf->error = conf->resume(conf->tracer); + if (conf->error < 0) + return; + + conf->error = conf->suspend(conf->tracer); + if (conf->error < 0) + return; + + trace = ds_read_bts(conf->tracer); + + conf->error = ds_selftest_bts_consistency(trace); + if (conf->error < 0) + return; + + if (trace->ds.top == top) { + /* + * It is possible but highly unlikely that we got a + * buffer overflow and end up at exactly the same + * position we started from. + * Let's issue a warning and check the full trace. + */ + printk(KERN_CONT + "no resume progress/overflow..."); + + conf->error = + ds_selftest_bts_read(conf->tracer, trace, + trace->ds.begin, trace->ds.end); + } else if (trace->ds.top < top) { + /* + * We had a buffer overflow - the entire buffer should + * contain trace records. + */ + conf->error = + ds_selftest_bts_read(conf->tracer, trace, + trace->ds.begin, trace->ds.end); + } else { + /* + * It is quite likely that the buffer did not overflow. + * Let's just check the delta trace. + */ + conf->error = + ds_selftest_bts_read(conf->tracer, trace, top, + trace->ds.top); + } + if (conf->error < 0) + return; + + conf->error = 0; +} + +static int ds_suspend_bts_wrap(struct bts_tracer *tracer) +{ + ds_suspend_bts(tracer); + return 0; +} + +static int ds_resume_bts_wrap(struct bts_tracer *tracer) +{ + ds_resume_bts(tracer); + return 0; +} + +static void ds_release_bts_noirq_wrap(void *tracer) +{ + (void)ds_release_bts_noirq(tracer); +} + +static int ds_selftest_bts_bad_release_noirq(int cpu, + struct bts_tracer *tracer) +{ + int error = -EPERM; + + /* Try to release the tracer on the wrong cpu. */ + get_cpu(); + if (cpu != smp_processor_id()) { + error = ds_release_bts_noirq(tracer); + if (error != -EPERM) + printk(KERN_CONT "release on wrong cpu..."); + } + put_cpu(); + + return error ? 0 : -1; +} + +static int ds_selftest_bts_bad_request_cpu(int cpu, void *buffer) +{ + struct bts_tracer *tracer; + int error; + + /* Try to request cpu tracing while task tracing is active. */ + tracer = ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, NULL, + (size_t)-1, BTS_KERNEL); + error = PTR_ERR(tracer); + if (!IS_ERR(tracer)) { + ds_release_bts(tracer); + error = 0; + } + + if (error != -EPERM) + printk(KERN_CONT "cpu/task tracing overlap..."); + + return error ? 0 : -1; +} + +static int ds_selftest_bts_bad_request_task(void *buffer) +{ + struct bts_tracer *tracer; + int error; + + /* Try to request cpu tracing while task tracing is active. */ + tracer = ds_request_bts_task(current, buffer, BUFFER_SIZE, NULL, + (size_t)-1, BTS_KERNEL); + error = PTR_ERR(tracer); + if (!IS_ERR(tracer)) { + error = 0; + ds_release_bts(tracer); + } + + if (error != -EPERM) + printk(KERN_CONT "task/cpu tracing overlap..."); + + return error ? 0 : -1; +} + +int ds_selftest_bts(void) +{ + struct ds_selftest_bts_conf conf; + unsigned char buffer[BUFFER_SIZE], *small_buffer; + unsigned long irq; + int cpu; + + printk(KERN_INFO "[ds] bts selftest..."); + conf.error = 0; + + small_buffer = (unsigned char *)ALIGN((unsigned long)buffer, 8) + 8; + + get_online_cpus(); + for_each_online_cpu(cpu) { + conf.suspend = ds_suspend_bts_wrap; + conf.resume = ds_resume_bts_wrap; + conf.tracer = + ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, + NULL, (size_t)-1, BTS_KERNEL); + ds_selftest_bts_cpu(&conf); + if (conf.error >= 0) + conf.error = ds_selftest_bts_bad_request_task(buffer); + ds_release_bts(conf.tracer); + if (conf.error < 0) + goto out; + + conf.suspend = ds_suspend_bts_noirq; + conf.resume = ds_resume_bts_noirq; + conf.tracer = + ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, + NULL, (size_t)-1, BTS_KERNEL); + smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1); + if (conf.error >= 0) { + conf.error = + ds_selftest_bts_bad_release_noirq(cpu, + conf.tracer); + /* We must not release the tracer twice. */ + if (conf.error < 0) + conf.tracer = NULL; + } + if (conf.error >= 0) + conf.error = ds_selftest_bts_bad_request_task(buffer); + smp_call_function_single(cpu, ds_release_bts_noirq_wrap, + conf.tracer, 1); + if (conf.error < 0) + goto out; + } + + conf.suspend = ds_suspend_bts_wrap; + conf.resume = ds_resume_bts_wrap; + conf.tracer = + ds_request_bts_task(current, buffer, BUFFER_SIZE, + NULL, (size_t)-1, BTS_KERNEL); + ds_selftest_bts_cpu(&conf); + if (conf.error >= 0) + conf.error = ds_selftest_bts_bad_request_cpu(0, buffer); + ds_release_bts(conf.tracer); + if (conf.error < 0) + goto out; + + conf.suspend = ds_suspend_bts_noirq; + conf.resume = ds_resume_bts_noirq; + conf.tracer = + ds_request_bts_task(current, small_buffer, SMALL_BUFFER_SIZE, + NULL, (size_t)-1, BTS_KERNEL); + local_irq_save(irq); + ds_selftest_bts_cpu(&conf); + if (conf.error >= 0) + conf.error = ds_selftest_bts_bad_request_cpu(0, buffer); + ds_release_bts_noirq(conf.tracer); + local_irq_restore(irq); + if (conf.error < 0) + goto out; + + conf.error = 0; + out: + put_online_cpus(); + printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed")); + + return conf.error; +} + +int ds_selftest_pebs(void) +{ + return 0; +} diff --git a/trunk/arch/x86/kernel/ds_selftest.h b/trunk/arch/x86/kernel/ds_selftest.h new file mode 100644 index 000000000000..2ba8745c6663 --- /dev/null +++ b/trunk/arch/x86/kernel/ds_selftest.h @@ -0,0 +1,15 @@ +/* + * Debug Store support - selftest + * + * + * Copyright (C) 2009 Intel Corporation. + * Markus Metzger , 2009 + */ + +#ifdef CONFIG_X86_DS_SELFTEST +extern int ds_selftest_bts(void); +extern int ds_selftest_pebs(void); +#else +static inline int ds_selftest_bts(void) { return 0; } +static inline int ds_selftest_pebs(void) { return 0; } +#endif diff --git a/trunk/arch/x86/kernel/dumpstack.c b/trunk/arch/x86/kernel/dumpstack.c index c89a386930b7..6d817554780a 100644 --- a/trunk/arch/x86/kernel/dumpstack.c +++ b/trunk/arch/x86/kernel/dumpstack.c @@ -224,6 +224,11 @@ unsigned __kprobes long oops_begin(void) int cpu; unsigned long flags; + /* notify the hw-branch tracer so it may disable tracing and + add the last trace to the trace buffer - + the earlier this happens, the more useful the trace. */ + trace_hw_branch_oops(); + oops_enter(); /* racy, but better than risking deadlock. */ diff --git a/trunk/arch/x86/kernel/hw_breakpoint.c b/trunk/arch/x86/kernel/hw_breakpoint.c index a8f1b803d2fd..d6cc065f519f 100644 --- a/trunk/arch/x86/kernel/hw_breakpoint.c +++ b/trunk/arch/x86/kernel/hw_breakpoint.c @@ -188,17 +188,26 @@ static int get_hbp_len(u8 hbp_len) return len_in_bytes; } +/* + * Check for virtual address in user space. + */ +int arch_check_va_in_userspace(unsigned long va, u8 hbp_len) +{ + unsigned int len; + + len = get_hbp_len(hbp_len); + + return (va <= TASK_SIZE - len); +} + /* * Check for virtual address in kernel space. */ -int arch_check_bp_in_kernelspace(struct perf_event *bp) +static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) { unsigned int len; - unsigned long va; - struct arch_hw_breakpoint *info = counter_arch_bp(bp); - va = info->address; - len = get_hbp_len(info->len); + len = get_hbp_len(hbp_len); return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); } @@ -291,7 +300,8 @@ static int arch_build_bp_info(struct perf_event *bp) /* * Validate the arch-specific HW Breakpoint register settings */ -int arch_validate_hwbkpt_settings(struct perf_event *bp) +int arch_validate_hwbkpt_settings(struct perf_event *bp, + struct task_struct *tsk) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); unsigned int align; @@ -304,6 +314,16 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) ret = -EINVAL; + if (info->type == X86_BREAKPOINT_EXECUTE) + /* + * Ptrace-refactoring code + * For now, we'll allow instruction breakpoint only for user-space + * addresses + */ + if ((!arch_check_va_in_userspace(info->address, info->len)) && + info->len != X86_BREAKPOINT_EXECUTE) + return ret; + switch (info->len) { case X86_BREAKPOINT_LEN_1: align = 0; @@ -330,6 +350,15 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) if (info->address & align) return -EINVAL; + /* Check that the virtual address is in the proper range */ + if (tsk) { + if (!arch_check_va_in_userspace(info->address, info->len)) + return -EFAULT; + } else { + if (!arch_check_va_in_kernelspace(info->address, info->len)) + return -EFAULT; + } + return 0; } diff --git a/trunk/arch/x86/kernel/kprobes.c b/trunk/arch/x86/kernel/kprobes.c index f2f56c0967b6..b43bbaebe2c0 100644 --- a/trunk/arch/x86/kernel/kprobes.c +++ b/trunk/arch/x86/kernel/kprobes.c @@ -422,22 +422,14 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, static void __kprobes clear_btf(void) { - if (test_thread_flag(TIF_BLOCKSTEP)) { - unsigned long debugctl = get_debugctlmsr(); - - debugctl &= ~DEBUGCTLMSR_BTF; - update_debugctlmsr(debugctl); - } + if (test_thread_flag(TIF_DEBUGCTLMSR)) + update_debugctlmsr(0); } static void __kprobes restore_btf(void) { - if (test_thread_flag(TIF_BLOCKSTEP)) { - unsigned long debugctl = get_debugctlmsr(); - - debugctl |= DEBUGCTLMSR_BTF; - update_debugctlmsr(debugctl); - } + if (test_thread_flag(TIF_DEBUGCTLMSR)) + update_debugctlmsr(current->thread.debugctlmsr); } void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, diff --git a/trunk/arch/x86/kernel/process.c b/trunk/arch/x86/kernel/process.c index eccdb57094e3..28ad9f4d8b94 100644 --- a/trunk/arch/x86/kernel/process.c +++ b/trunk/arch/x86/kernel/process.c @@ -20,6 +20,7 @@ #include #include #include +#include #include unsigned long idle_halt; @@ -49,6 +50,8 @@ void free_thread_xstate(struct task_struct *tsk) kmem_cache_free(task_xstate_cachep, tsk->thread.xstate); tsk->thread.xstate = NULL; } + + WARN(tsk->thread.ds_ctx, "leaking DS context\n"); } void free_thread_info(struct thread_info *ti) @@ -195,16 +198,11 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, prev = &prev_p->thread; next = &next_p->thread; - if (test_tsk_thread_flag(prev_p, TIF_BLOCKSTEP) ^ - test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) { - unsigned long debugctl = get_debugctlmsr(); - - debugctl &= ~DEBUGCTLMSR_BTF; - if (test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) - debugctl |= DEBUGCTLMSR_BTF; - - update_debugctlmsr(debugctl); - } + if (test_tsk_thread_flag(next_p, TIF_DS_AREA_MSR) || + test_tsk_thread_flag(prev_p, TIF_DS_AREA_MSR)) + ds_switch_to(prev_p, next_p); + else if (next->debugctlmsr != prev->debugctlmsr) + update_debugctlmsr(next->debugctlmsr); if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ test_tsk_thread_flag(next_p, TIF_NOTSC)) { diff --git a/trunk/arch/x86/kernel/process_32.c b/trunk/arch/x86/kernel/process_32.c index 75090c589b7a..f6c62667e30c 100644 --- a/trunk/arch/x86/kernel/process_32.c +++ b/trunk/arch/x86/kernel/process_32.c @@ -55,6 +55,7 @@ #include #include #include +#include #include asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); @@ -237,6 +238,13 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, kfree(p->thread.io_bitmap_ptr); p->thread.io_bitmap_max = 0; } + + clear_tsk_thread_flag(p, TIF_DS_AREA_MSR); + p->thread.ds_ctx = NULL; + + clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR); + p->thread.debugctlmsr = 0; + return err; } diff --git a/trunk/arch/x86/kernel/process_64.c b/trunk/arch/x86/kernel/process_64.c index 50cc84ac0a0d..17cb3295cbf7 100644 --- a/trunk/arch/x86/kernel/process_64.c +++ b/trunk/arch/x86/kernel/process_64.c @@ -49,6 +49,7 @@ #include #include #include +#include #include asmlinkage extern void ret_from_fork(void); @@ -312,6 +313,13 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, if (err) goto out; } + + clear_tsk_thread_flag(p, TIF_DS_AREA_MSR); + p->thread.ds_ctx = NULL; + + clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR); + p->thread.debugctlmsr = 0; + err = 0; out: if (err && p->thread.io_bitmap_ptr) { diff --git a/trunk/arch/x86/kernel/ptrace.c b/trunk/arch/x86/kernel/ptrace.c index 70c4872cd8aa..2e9b55027b7e 100644 --- a/trunk/arch/x86/kernel/ptrace.c +++ b/trunk/arch/x86/kernel/ptrace.c @@ -2,6 +2,9 @@ /* * Pentium III FXSR, SSE support * Gareth Hughes , May 2000 + * + * BTS tracing + * Markus Metzger , Dec 2007 */ #include @@ -19,6 +22,7 @@ #include #include #include +#include #include #include @@ -32,6 +36,7 @@ #include #include #include +#include #include #include "tls.h" @@ -688,7 +693,7 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, struct perf_event_attr attr; if (!t->ptrace_bps[nr]) { - ptrace_breakpoint_init(&attr); + hw_breakpoint_init(&attr); /* * Put stub len and type to register (reserve) an inactive but * correct bp @@ -784,6 +789,342 @@ static int ioperm_get(struct task_struct *target, 0, IO_BITMAP_BYTES); } +#ifdef CONFIG_X86_PTRACE_BTS +/* + * A branch trace store context. + * + * Contexts may only be installed by ptrace_bts_config() and only for + * ptraced tasks. + * + * Contexts are destroyed when the tracee is detached from the tracer. + * The actual destruction work requires interrupts enabled, so the + * work is deferred and will be scheduled during __ptrace_unlink(). + * + * Contexts hold an additional task_struct reference on the traced + * task, as well as a reference on the tracer's mm. + * + * Ptrace already holds a task_struct for the duration of ptrace operations, + * but since destruction is deferred, it may be executed after both + * tracer and tracee exited. + */ +struct bts_context { + /* The branch trace handle. */ + struct bts_tracer *tracer; + + /* The buffer used to store the branch trace and its size. */ + void *buffer; + unsigned int size; + + /* The mm that paid for the above buffer. */ + struct mm_struct *mm; + + /* The task this context belongs to. */ + struct task_struct *task; + + /* The signal to send on a bts buffer overflow. */ + unsigned int bts_ovfl_signal; + + /* The work struct to destroy a context. */ + struct work_struct work; +}; + +static int alloc_bts_buffer(struct bts_context *context, unsigned int size) +{ + void *buffer = NULL; + int err = -ENOMEM; + + err = account_locked_memory(current->mm, current->signal->rlim, size); + if (err < 0) + return err; + + buffer = kzalloc(size, GFP_KERNEL); + if (!buffer) + goto out_refund; + + context->buffer = buffer; + context->size = size; + context->mm = get_task_mm(current); + + return 0; + + out_refund: + refund_locked_memory(current->mm, size); + return err; +} + +static inline void free_bts_buffer(struct bts_context *context) +{ + if (!context->buffer) + return; + + kfree(context->buffer); + context->buffer = NULL; + + refund_locked_memory(context->mm, context->size); + context->size = 0; + + mmput(context->mm); + context->mm = NULL; +} + +static void free_bts_context_work(struct work_struct *w) +{ + struct bts_context *context; + + context = container_of(w, struct bts_context, work); + + ds_release_bts(context->tracer); + put_task_struct(context->task); + free_bts_buffer(context); + kfree(context); +} + +static inline void free_bts_context(struct bts_context *context) +{ + INIT_WORK(&context->work, free_bts_context_work); + schedule_work(&context->work); +} + +static inline struct bts_context *alloc_bts_context(struct task_struct *task) +{ + struct bts_context *context = kzalloc(sizeof(*context), GFP_KERNEL); + if (context) { + context->task = task; + task->bts = context; + + get_task_struct(task); + } + + return context; +} + +static int ptrace_bts_read_record(struct task_struct *child, size_t index, + struct bts_struct __user *out) +{ + struct bts_context *context; + const struct bts_trace *trace; + struct bts_struct bts; + const unsigned char *at; + int error; + + context = child->bts; + if (!context) + return -ESRCH; + + trace = ds_read_bts(context->tracer); + if (!trace) + return -ESRCH; + + at = trace->ds.top - ((index + 1) * trace->ds.size); + if ((void *)at < trace->ds.begin) + at += (trace->ds.n * trace->ds.size); + + if (!trace->read) + return -EOPNOTSUPP; + + error = trace->read(context->tracer, at, &bts); + if (error < 0) + return error; + + if (copy_to_user(out, &bts, sizeof(bts))) + return -EFAULT; + + return sizeof(bts); +} + +static int ptrace_bts_drain(struct task_struct *child, + long size, + struct bts_struct __user *out) +{ + struct bts_context *context; + const struct bts_trace *trace; + const unsigned char *at; + int error, drained = 0; + + context = child->bts; + if (!context) + return -ESRCH; + + trace = ds_read_bts(context->tracer); + if (!trace) + return -ESRCH; + + if (!trace->read) + return -EOPNOTSUPP; + + if (size < (trace->ds.top - trace->ds.begin)) + return -EIO; + + for (at = trace->ds.begin; (void *)at < trace->ds.top; + out++, drained++, at += trace->ds.size) { + struct bts_struct bts; + + error = trace->read(context->tracer, at, &bts); + if (error < 0) + return error; + + if (copy_to_user(out, &bts, sizeof(bts))) + return -EFAULT; + } + + memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size); + + error = ds_reset_bts(context->tracer); + if (error < 0) + return error; + + return drained; +} + +static int ptrace_bts_config(struct task_struct *child, + long cfg_size, + const struct ptrace_bts_config __user *ucfg) +{ + struct bts_context *context; + struct ptrace_bts_config cfg; + unsigned int flags = 0; + + if (cfg_size < sizeof(cfg)) + return -EIO; + + if (copy_from_user(&cfg, ucfg, sizeof(cfg))) + return -EFAULT; + + context = child->bts; + if (!context) + context = alloc_bts_context(child); + if (!context) + return -ENOMEM; + + if (cfg.flags & PTRACE_BTS_O_SIGNAL) { + if (!cfg.signal) + return -EINVAL; + + return -EOPNOTSUPP; + context->bts_ovfl_signal = cfg.signal; + } + + ds_release_bts(context->tracer); + context->tracer = NULL; + + if ((cfg.flags & PTRACE_BTS_O_ALLOC) && (cfg.size != context->size)) { + int err; + + free_bts_buffer(context); + if (!cfg.size) + return 0; + + err = alloc_bts_buffer(context, cfg.size); + if (err < 0) + return err; + } + + if (cfg.flags & PTRACE_BTS_O_TRACE) + flags |= BTS_USER; + + if (cfg.flags & PTRACE_BTS_O_SCHED) + flags |= BTS_TIMESTAMPS; + + context->tracer = + ds_request_bts_task(child, context->buffer, context->size, + NULL, (size_t)-1, flags); + if (unlikely(IS_ERR(context->tracer))) { + int error = PTR_ERR(context->tracer); + + free_bts_buffer(context); + context->tracer = NULL; + return error; + } + + return sizeof(cfg); +} + +static int ptrace_bts_status(struct task_struct *child, + long cfg_size, + struct ptrace_bts_config __user *ucfg) +{ + struct bts_context *context; + const struct bts_trace *trace; + struct ptrace_bts_config cfg; + + context = child->bts; + if (!context) + return -ESRCH; + + if (cfg_size < sizeof(cfg)) + return -EIO; + + trace = ds_read_bts(context->tracer); + if (!trace) + return -ESRCH; + + memset(&cfg, 0, sizeof(cfg)); + cfg.size = trace->ds.end - trace->ds.begin; + cfg.signal = context->bts_ovfl_signal; + cfg.bts_size = sizeof(struct bts_struct); + + if (cfg.signal) + cfg.flags |= PTRACE_BTS_O_SIGNAL; + + if (trace->ds.flags & BTS_USER) + cfg.flags |= PTRACE_BTS_O_TRACE; + + if (trace->ds.flags & BTS_TIMESTAMPS) + cfg.flags |= PTRACE_BTS_O_SCHED; + + if (copy_to_user(ucfg, &cfg, sizeof(cfg))) + return -EFAULT; + + return sizeof(cfg); +} + +static int ptrace_bts_clear(struct task_struct *child) +{ + struct bts_context *context; + const struct bts_trace *trace; + + context = child->bts; + if (!context) + return -ESRCH; + + trace = ds_read_bts(context->tracer); + if (!trace) + return -ESRCH; + + memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size); + + return ds_reset_bts(context->tracer); +} + +static int ptrace_bts_size(struct task_struct *child) +{ + struct bts_context *context; + const struct bts_trace *trace; + + context = child->bts; + if (!context) + return -ESRCH; + + trace = ds_read_bts(context->tracer); + if (!trace) + return -ESRCH; + + return (trace->ds.top - trace->ds.begin) / trace->ds.size; +} + +/* + * Called from __ptrace_unlink() after the child has been moved back + * to its original parent. + */ +void ptrace_bts_untrace(struct task_struct *child) +{ + if (unlikely(child->bts)) { + free_bts_context(child->bts); + child->bts = NULL; + } +} +#endif /* CONFIG_X86_PTRACE_BTS */ + /* * Called by kernel/ptrace.c when detaching.. * @@ -911,6 +1252,39 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; #endif + /* + * These bits need more cooking - not enabled yet: + */ +#ifdef CONFIG_X86_PTRACE_BTS + case PTRACE_BTS_CONFIG: + ret = ptrace_bts_config + (child, data, (struct ptrace_bts_config __user *)addr); + break; + + case PTRACE_BTS_STATUS: + ret = ptrace_bts_status + (child, data, (struct ptrace_bts_config __user *)addr); + break; + + case PTRACE_BTS_SIZE: + ret = ptrace_bts_size(child); + break; + + case PTRACE_BTS_GET: + ret = ptrace_bts_read_record + (child, data, (struct bts_struct __user *) addr); + break; + + case PTRACE_BTS_CLEAR: + ret = ptrace_bts_clear(child); + break; + + case PTRACE_BTS_DRAIN: + ret = ptrace_bts_drain + (child, data, (struct bts_struct __user *) addr); + break; +#endif /* CONFIG_X86_PTRACE_BTS */ + default: ret = ptrace_request(child, request, addr, data); break; @@ -1170,6 +1544,14 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, case PTRACE_GET_THREAD_AREA: case PTRACE_SET_THREAD_AREA: +#ifdef CONFIG_X86_PTRACE_BTS + case PTRACE_BTS_CONFIG: + case PTRACE_BTS_STATUS: + case PTRACE_BTS_SIZE: + case PTRACE_BTS_GET: + case PTRACE_BTS_CLEAR: + case PTRACE_BTS_DRAIN: +#endif /* CONFIG_X86_PTRACE_BTS */ return arch_ptrace(child, request, addr, data); default: diff --git a/trunk/arch/x86/kernel/step.c b/trunk/arch/x86/kernel/step.c index 58de45ee08b6..3149032ff107 100644 --- a/trunk/arch/x86/kernel/step.c +++ b/trunk/arch/x86/kernel/step.c @@ -157,6 +157,22 @@ static int enable_single_step(struct task_struct *child) return 1; } +/* + * Install this value in MSR_IA32_DEBUGCTLMSR whenever child is running. + */ +static void write_debugctlmsr(struct task_struct *child, unsigned long val) +{ + if (child->thread.debugctlmsr == val) + return; + + child->thread.debugctlmsr = val; + + if (child != current) + return; + + update_debugctlmsr(val); +} + /* * Enable single or block step. */ @@ -170,17 +186,15 @@ static void enable_step(struct task_struct *child, bool block) * that uses user-mode single stepping itself. */ if (enable_single_step(child) && block) { - unsigned long debugctl = get_debugctlmsr(); - - debugctl |= DEBUGCTLMSR_BTF; - update_debugctlmsr(debugctl); - set_tsk_thread_flag(child, TIF_BLOCKSTEP); - } else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { - unsigned long debugctl = get_debugctlmsr(); - - debugctl &= ~DEBUGCTLMSR_BTF; - update_debugctlmsr(debugctl); - clear_tsk_thread_flag(child, TIF_BLOCKSTEP); + set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); + write_debugctlmsr(child, + child->thread.debugctlmsr | DEBUGCTLMSR_BTF); + } else { + write_debugctlmsr(child, + child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF); + + if (!child->thread.debugctlmsr) + clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); } } @@ -199,13 +213,11 @@ void user_disable_single_step(struct task_struct *child) /* * Make sure block stepping (BTF) is disabled. */ - if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { - unsigned long debugctl = get_debugctlmsr(); + write_debugctlmsr(child, + child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF); - debugctl &= ~DEBUGCTLMSR_BTF; - update_debugctlmsr(debugctl); - clear_tsk_thread_flag(child, TIF_BLOCKSTEP); - } + if (!child->thread.debugctlmsr) + clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); /* Always clear TIF_SINGLESTEP... */ clear_tsk_thread_flag(child, TIF_SINGLESTEP); diff --git a/trunk/arch/x86/kernel/traps.c b/trunk/arch/x86/kernel/traps.c index 36f1bd9f8e76..1168e4454188 100644 --- a/trunk/arch/x86/kernel/traps.c +++ b/trunk/arch/x86/kernel/traps.c @@ -543,11 +543,11 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) /* DR6 may or may not be cleared by the CPU */ set_debugreg(0, 6); - /* * The processor cleared BTF, so don't mark that we need it set. */ - clear_tsk_thread_flag(tsk, TIF_BLOCKSTEP); + clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); + tsk->thread.debugctlmsr = 0; /* Store the virtualized DR6 value */ tsk->thread.debugreg6 = dr6; diff --git a/trunk/arch/x86/kvm/vmx.c b/trunk/arch/x86/kvm/vmx.c index 32022a8a5c3b..bc933cfb4e66 100644 --- a/trunk/arch/x86/kvm/vmx.c +++ b/trunk/arch/x86/kvm/vmx.c @@ -3660,11 +3660,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx) /* We need to handle NMIs before interrupts are enabled */ if ((exit_intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR && - (exit_intr_info & INTR_INFO_VALID_MASK)) { - kvm_before_handle_nmi(&vmx->vcpu); + (exit_intr_info & INTR_INFO_VALID_MASK)) asm("int $2"); - kvm_after_handle_nmi(&vmx->vcpu); - } idtv_info_valid = idt_vectoring_info & VECTORING_INFO_VALID_MASK; diff --git a/trunk/arch/x86/kvm/x86.c b/trunk/arch/x86/kvm/x86.c index 73d854c36e39..3c4ca98ad27f 100644 --- a/trunk/arch/x86/kvm/x86.c +++ b/trunk/arch/x86/kvm/x86.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #undef TRACE_INCLUDE_FILE #define CREATE_TRACE_POINTS @@ -3744,51 +3743,6 @@ static void kvm_timer_init(void) } } -static DEFINE_PER_CPU(struct kvm_vcpu *, current_vcpu); - -static int kvm_is_in_guest(void) -{ - return percpu_read(current_vcpu) != NULL; -} - -static int kvm_is_user_mode(void) -{ - int user_mode = 3; - - if (percpu_read(current_vcpu)) - user_mode = kvm_x86_ops->get_cpl(percpu_read(current_vcpu)); - - return user_mode != 0; -} - -static unsigned long kvm_get_guest_ip(void) -{ - unsigned long ip = 0; - - if (percpu_read(current_vcpu)) - ip = kvm_rip_read(percpu_read(current_vcpu)); - - return ip; -} - -static struct perf_guest_info_callbacks kvm_guest_cbs = { - .is_in_guest = kvm_is_in_guest, - .is_user_mode = kvm_is_user_mode, - .get_guest_ip = kvm_get_guest_ip, -}; - -void kvm_before_handle_nmi(struct kvm_vcpu *vcpu) -{ - percpu_write(current_vcpu, vcpu); -} -EXPORT_SYMBOL_GPL(kvm_before_handle_nmi); - -void kvm_after_handle_nmi(struct kvm_vcpu *vcpu) -{ - percpu_write(current_vcpu, NULL); -} -EXPORT_SYMBOL_GPL(kvm_after_handle_nmi); - int kvm_arch_init(void *opaque) { int r; @@ -3825,8 +3779,6 @@ int kvm_arch_init(void *opaque) kvm_timer_init(); - perf_register_guest_info_callbacks(&kvm_guest_cbs); - return 0; out: @@ -3835,8 +3787,6 @@ int kvm_arch_init(void *opaque) void kvm_arch_exit(void) { - perf_unregister_guest_info_callbacks(&kvm_guest_cbs); - if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) cpufreq_unregister_notifier(&kvmclock_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); diff --git a/trunk/arch/x86/kvm/x86.h b/trunk/arch/x86/kvm/x86.h index b7a404722d2b..2d101639bd8d 100644 --- a/trunk/arch/x86/kvm/x86.h +++ b/trunk/arch/x86/kvm/x86.h @@ -65,7 +65,4 @@ static inline int is_paging(struct kvm_vcpu *vcpu) return kvm_read_cr0_bits(vcpu, X86_CR0_PG); } -void kvm_before_handle_nmi(struct kvm_vcpu *vcpu); -void kvm_after_handle_nmi(struct kvm_vcpu *vcpu); - #endif diff --git a/trunk/arch/x86/lib/Makefile b/trunk/arch/x86/lib/Makefile index cbaf8f2b83df..419386c24b82 100644 --- a/trunk/arch/x86/lib/Makefile +++ b/trunk/arch/x86/lib/Makefile @@ -20,7 +20,7 @@ lib-y := delay.o lib-y += thunk_$(BITS).o lib-y += usercopy_$(BITS).o getuser.o putuser.o lib-y += memcpy_$(BITS).o -lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o +lib-$(CONFIG_KPROBES) += insn.o inat.o obj-y += msr.o msr-reg.o msr-reg-export.o diff --git a/trunk/arch/x86/lib/rwsem_64.S b/trunk/arch/x86/lib/rwsem_64.S index 15acecf0d7aa..41fcf00e49df 100644 --- a/trunk/arch/x86/lib/rwsem_64.S +++ b/trunk/arch/x86/lib/rwsem_64.S @@ -60,7 +60,7 @@ ENTRY(call_rwsem_down_write_failed) ENDPROC(call_rwsem_down_write_failed) ENTRY(call_rwsem_wake) - decw %dx /* do nothing if still outstanding active readers */ + decl %edx /* do nothing if still outstanding active readers */ jnz 1f save_common_regs movq %rax,%rdi diff --git a/trunk/arch/x86/mm/ioremap.c b/trunk/arch/x86/mm/ioremap.c index 5eb1ba74a3a9..12e4d2d3c110 100644 --- a/trunk/arch/x86/mm/ioremap.c +++ b/trunk/arch/x86/mm/ioremap.c @@ -448,6 +448,20 @@ static inline void __init early_clear_fixmap(enum fixed_addresses idx) static void __iomem *prev_map[FIX_BTMAPS_SLOTS] __initdata; static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata; +void __init fixup_early_ioremap(void) +{ + int i; + + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { + if (prev_map[i]) { + WARN_ON(1); + break; + } + } + + early_ioremap_init(); +} + static int __init check_early_ioremap_leak(void) { int count = 0; diff --git a/trunk/arch/x86/mm/pgtable_32.c b/trunk/arch/x86/mm/pgtable_32.c index 1a8faf09afed..792854003ed3 100644 --- a/trunk/arch/x86/mm/pgtable_32.c +++ b/trunk/arch/x86/mm/pgtable_32.c @@ -18,6 +18,7 @@ #include #include #include +#include unsigned int __VMALLOC_RESERVE = 128 << 20; @@ -128,6 +129,7 @@ static int __init parse_reservetop(char *arg) address = memparse(arg, &arg); reserve_top_address(address); + fixup_early_ioremap(); return 0; } early_param("reservetop", parse_reservetop); diff --git a/trunk/arch/x86/oprofile/op_model_ppro.c b/trunk/arch/x86/oprofile/op_model_ppro.c index c8abc4d1bf35..2bf90fafa7b5 100644 --- a/trunk/arch/x86/oprofile/op_model_ppro.c +++ b/trunk/arch/x86/oprofile/op_model_ppro.c @@ -239,11 +239,11 @@ static void arch_perfmon_setup_counters(void) if (eax.split.version_id == 0 && current_cpu_data.x86 == 6 && current_cpu_data.x86_model == 15) { eax.split.version_id = 2; - eax.split.num_counters = 2; + eax.split.num_events = 2; eax.split.bit_width = 40; } - num_counters = eax.split.num_counters; + num_counters = eax.split.num_events; op_arch_perfmon_spec.num_counters = num_counters; op_arch_perfmon_spec.num_controls = num_counters; diff --git a/trunk/crypto/async_tx/async_raid6_recov.c b/trunk/crypto/async_tx/async_raid6_recov.c index 943f2abac9b4..ce038d861eb9 100644 --- a/trunk/crypto/async_tx/async_raid6_recov.c +++ b/trunk/crypto/async_tx/async_raid6_recov.c @@ -324,6 +324,7 @@ struct dma_async_tx_descriptor * async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, struct page **blocks, struct async_submit_ctl *submit) { + void *scribble = submit->scribble; int non_zero_srcs, i; BUG_ON(faila == failb); @@ -332,11 +333,13 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); - /* we need to preserve the contents of 'blocks' for the async - * case, so punt to synchronous if a scribble buffer is not available + /* if a dma resource is not available or a scribble buffer is not + * available punt to the synchronous path. In the 'dma not + * available' case be sure to use the scribble buffer to + * preserve the content of 'blocks' as the caller intended. */ - if (!submit->scribble) { - void **ptrs = (void **) blocks; + if (!async_dma_find_channel(DMA_PQ) || !scribble) { + void **ptrs = scribble ? scribble : (void **) blocks; async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) @@ -406,11 +409,13 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila, pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); - /* we need to preserve the contents of 'blocks' for the async - * case, so punt to synchronous if a scribble buffer is not available + /* if a dma resource is not available or a scribble buffer is not + * available punt to the synchronous path. In the 'dma not + * available' case be sure to use the scribble buffer to + * preserve the content of 'blocks' as the caller intended. */ - if (!scribble) { - void **ptrs = (void **) blocks; + if (!async_dma_find_channel(DMA_PQ) || !scribble) { + void **ptrs = scribble ? scribble : (void **) blocks; async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) diff --git a/trunk/drivers/ata/pata_pcmcia.c b/trunk/drivers/ata/pata_pcmcia.c index 4164dd244dd0..d94b8f0bd743 100644 --- a/trunk/drivers/ata/pata_pcmcia.c +++ b/trunk/drivers/ata/pata_pcmcia.c @@ -424,7 +424,7 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420), PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178), PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753), - PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 1GB", 0x2e6d1829, 0x3e520e17), + PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 1GB", 0x2e6d1829, 0x55d5bffb), PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 4GB", 0x2e6d1829, 0x531e7d10), PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF8GB", 0x2e6d1829, 0xacbe682e), PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2 ", 0x547e66dc, 0x8671043b), @@ -446,7 +446,7 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS2GCF120", 0x709b1bf1, 0x969aa4f2), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8), - PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF133", 0x709b1bf1, 0x9351e59d), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF133", 0x709b1bf1, 0x7558f133), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS8GCF133", 0x709b1bf1, 0xb2f89b47), PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852), PCMCIA_DEVICE_PROD_ID12("WEIDA", "TWTTI", 0xcc7cf69c, 0x212bb918), diff --git a/trunk/drivers/char/isicom.c b/trunk/drivers/char/isicom.c index 0fa2e4a0835d..c1ab303455cf 100644 --- a/trunk/drivers/char/isicom.c +++ b/trunk/drivers/char/isicom.c @@ -879,8 +879,8 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) if (tport == NULL) return -ENODEV; port = container_of(tport, struct isi_port, port); - card = &isi_card[BOARD(tty->index)]; + tty->driver_data = port; return tty_port_open(tport, tty, filp); } @@ -936,7 +936,12 @@ static void isicom_shutdown(struct tty_port *port) static void isicom_close(struct tty_struct *tty, struct file *filp) { struct isi_port *ip = tty->driver_data; - struct tty_port *port = &ip->port; + struct tty_port *port; + + if (ip == NULL) + return; + + port = &ip->port; if (isicom_paranoia_check(ip, tty->name, "isicom_close")) return; tty_port_close(port, tty, filp); diff --git a/trunk/drivers/char/istallion.c b/trunk/drivers/char/istallion.c index 4cd6c527ee41..4e395c956a09 100644 --- a/trunk/drivers/char/istallion.c +++ b/trunk/drivers/char/istallion.c @@ -827,6 +827,8 @@ static int stli_open(struct tty_struct *tty, struct file *filp) return -ENODEV; if (portp->devnr < 1) return -ENODEV; + + tty->driver_data = portp; return tty_port_open(&portp->port, tty, filp); } diff --git a/trunk/drivers/char/mxser.c b/trunk/drivers/char/mxser.c index 47023053ee85..d2692d443f7b 100644 --- a/trunk/drivers/char/mxser.c +++ b/trunk/drivers/char/mxser.c @@ -1011,6 +1011,7 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) if (!info->ioaddr) return -ENODEV; + tty->driver_data = info; return tty_port_open(&info->port, tty, filp); } @@ -1074,7 +1075,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) struct mxser_port *info = tty->driver_data; struct tty_port *port = &info->port; - if (tty->index == MXSER_PORTS) + if (tty->index == MXSER_PORTS || info == NULL) return; if (tty_port_close_start(port, tty, filp) == 0) return; diff --git a/trunk/drivers/char/riscom8.c b/trunk/drivers/char/riscom8.c index 0a8d1e56c993..b02332a5412f 100644 --- a/trunk/drivers/char/riscom8.c +++ b/trunk/drivers/char/riscom8.c @@ -909,6 +909,7 @@ static int rc_open(struct tty_struct *tty, struct file *filp) if (error) return error; + tty->driver_data = port; return tty_port_open(&port->port, tty, filp); } diff --git a/trunk/drivers/char/stallion.c b/trunk/drivers/char/stallion.c index 0e511d61f544..6049fd731924 100644 --- a/trunk/drivers/char/stallion.c +++ b/trunk/drivers/char/stallion.c @@ -724,7 +724,6 @@ static int stl_open(struct tty_struct *tty, struct file *filp) { struct stlport *portp; struct stlbrd *brdp; - struct tty_port *port; unsigned int minordev, brdnr, panelnr; int portnr; @@ -754,7 +753,8 @@ static int stl_open(struct tty_struct *tty, struct file *filp) portp = brdp->panels[panelnr]->ports[portnr]; if (portp == NULL) return -ENODEV; - port = &portp->port; + + tty->driver_data = portp; return tty_port_open(&portp->port, tty, filp); } @@ -841,7 +841,8 @@ static void stl_close(struct tty_struct *tty, struct file *filp) pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp); portp = tty->driver_data; - BUG_ON(portp == NULL); + if(portp == NULL) + return; tty_port_close(&portp->port, tty, filp); } diff --git a/trunk/drivers/dma/txx9dmac.c b/trunk/drivers/dma/txx9dmac.c index 3ebc61067e54..75fcf1ac8bb7 100644 --- a/trunk/drivers/dma/txx9dmac.c +++ b/trunk/drivers/dma/txx9dmac.c @@ -1359,3 +1359,5 @@ module_exit(txx9dmac_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("TXx9 DMA Controller driver"); MODULE_AUTHOR("Atsushi Nemoto "); +MODULE_ALIAS("platform:txx9dmac"); +MODULE_ALIAS("platform:txx9dmac-chan"); diff --git a/trunk/drivers/edac/edac_mce_amd.c b/trunk/drivers/edac/edac_mce_amd.c index f5b6d9fe4def..97e64bcdbc06 100644 --- a/trunk/drivers/edac/edac_mce_amd.c +++ b/trunk/drivers/edac/edac_mce_amd.c @@ -294,7 +294,6 @@ static void amd_decode_ls_mce(u64 mc3_status) void amd_decode_nb_mce(int node_id, struct err_regs *regs, int handle_errors) { u32 ec = ERROR_CODE(regs->nbsl); - u32 xec = EXT_ERROR_CODE(regs->nbsl); if (!handle_errors) return; @@ -324,7 +323,7 @@ void amd_decode_nb_mce(int node_id, struct err_regs *regs, int handle_errors) pr_cont("\n"); } - pr_emerg("%s.\n", EXT_ERR_MSG(xec)); + pr_emerg("%s.\n", EXT_ERR_MSG(regs->nbsl)); if (BUS_ERROR(ec) && nb_bus_decoder) nb_bus_decoder(node_id, regs); @@ -374,7 +373,7 @@ static int amd_decode_mce(struct notifier_block *nb, unsigned long val, ((m->status & MCI_STATUS_PCC) ? "yes" : "no")); /* do the two bits[14:13] together */ - ecc = m->status & (3ULL << 45); + ecc = (m->status >> 45) & 0x3; if (ecc) pr_cont(", %sECC Error", ((ecc == 2) ? "C" : "U")); diff --git a/trunk/drivers/gpio/gpiolib.c b/trunk/drivers/gpio/gpiolib.c index 76be229c814d..eb0c3fe44b29 100644 --- a/trunk/drivers/gpio/gpiolib.c +++ b/trunk/drivers/gpio/gpiolib.c @@ -416,7 +416,8 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, return 0; free_sd: - sysfs_put(pdesc->value_sd); + if (pdesc) + sysfs_put(pdesc->value_sd); free_id: idr_remove(&pdesc_idr, id); desc->flags &= GPIO_FLAGS_MASK; diff --git a/trunk/drivers/gpu/drm/drm_memory.c b/trunk/drivers/gpu/drm/drm_memory.c index e4865f99989c..7732268eced2 100644 --- a/trunk/drivers/gpu/drm/drm_memory.c +++ b/trunk/drivers/gpu/drm/drm_memory.c @@ -77,7 +77,7 @@ static void *agp_remap(unsigned long offset, unsigned long size, && (agpmem->bound + (agpmem->pages << PAGE_SHIFT)) >= (offset + size)) break; - if (!agpmem) + if (&agpmem->head == &dev->agp->memory) return NULL; /* diff --git a/trunk/drivers/gpu/drm/drm_sysfs.c b/trunk/drivers/gpu/drm/drm_sysfs.c index 1a1825b29f5f..25bbd30ed7af 100644 --- a/trunk/drivers/gpu/drm/drm_sysfs.c +++ b/trunk/drivers/gpu/drm/drm_sysfs.c @@ -354,7 +354,10 @@ static struct bin_attribute edid_attr = { int drm_sysfs_connector_add(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - int ret = 0, i, j; + int attr_cnt = 0; + int opt_cnt = 0; + int i; + int ret = 0; /* We shouldn't get called more than once for the same connector */ BUG_ON(device_is_registered(&connector->kdev)); @@ -377,8 +380,8 @@ int drm_sysfs_connector_add(struct drm_connector *connector) /* Standard attributes */ - for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) { - ret = device_create_file(&connector->kdev, &connector_attrs[i]); + for (attr_cnt = 0; attr_cnt < ARRAY_SIZE(connector_attrs); attr_cnt++) { + ret = device_create_file(&connector->kdev, &connector_attrs[attr_cnt]); if (ret) goto err_out_files; } @@ -394,8 +397,8 @@ int drm_sysfs_connector_add(struct drm_connector *connector) case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_Component: case DRM_MODE_CONNECTOR_TV: - for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) { - ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]); + for (opt_cnt = 0; opt_cnt < ARRAY_SIZE(connector_attrs_opt1); opt_cnt++) { + ret = device_create_file(&connector->kdev, &connector_attrs_opt1[opt_cnt]); if (ret) goto err_out_files; } @@ -414,10 +417,10 @@ int drm_sysfs_connector_add(struct drm_connector *connector) return 0; err_out_files: - if (i > 0) - for (j = 0; j < i; j++) - device_remove_file(&connector->kdev, - &connector_attrs[i]); + for (i = 0; i < opt_cnt; i++) + device_remove_file(&connector->kdev, &connector_attrs_opt1[i]); + for (i = 0; i < attr_cnt; i++) + device_remove_file(&connector->kdev, &connector_attrs[i]); device_unregister(&connector->kdev); out: diff --git a/trunk/drivers/gpu/drm/radeon/atombios.h b/trunk/drivers/gpu/drm/radeon/atombios.h index 6732b5dd8ff4..27e2c715be11 100644 --- a/trunk/drivers/gpu/drm/radeon/atombios.h +++ b/trunk/drivers/gpu/drm/radeon/atombios.h @@ -2912,7 +2912,7 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 UCHAR ucTV_BootUpDefaultStandard; UCHAR ucExt_TV_ASIC_ID; UCHAR ucExt_TV_ASIC_SlaveAddr; - ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_TV_TIMING]; + ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_TV_TIMING_V1_2]; }ATOM_ANALOG_TV_INFO_V1_2; typedef struct _ATOM_DPCD_INFO diff --git a/trunk/drivers/gpu/drm/radeon/r100.c b/trunk/drivers/gpu/drm/radeon/r100.c index d7388fdb6d0b..cf60c0b3ef15 100644 --- a/trunk/drivers/gpu/drm/radeon/r100.c +++ b/trunk/drivers/gpu/drm/radeon/r100.c @@ -2975,7 +2975,7 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track) for (i = 0; i < track->num_cb; i++) { if (track->cb[i].robj == NULL) { - if (!(track->fastfill || track->color_channel_mask || + if (!(track->zb_cb_clear || track->color_channel_mask || track->blend_read_enable)) { continue; } diff --git a/trunk/drivers/gpu/drm/radeon/r100_track.h b/trunk/drivers/gpu/drm/radeon/r100_track.h index fadfe68de9cc..f47cdca1c004 100644 --- a/trunk/drivers/gpu/drm/radeon/r100_track.h +++ b/trunk/drivers/gpu/drm/radeon/r100_track.h @@ -75,7 +75,7 @@ struct r100_cs_track { struct r100_cs_track_texture textures[R300_TRACK_MAX_TEXTURE]; bool z_enabled; bool separate_cube; - bool fastfill; + bool zb_cb_clear; bool blend_read_enable; }; diff --git a/trunk/drivers/gpu/drm/radeon/r300.c b/trunk/drivers/gpu/drm/radeon/r300.c index eaf1f6bc44f1..a5ff8076b423 100644 --- a/trunk/drivers/gpu/drm/radeon/r300.c +++ b/trunk/drivers/gpu/drm/radeon/r300.c @@ -1044,7 +1044,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p, break; case 0x4d1c: /* ZB_BW_CNTL */ - track->fastfill = !!(idx_value & (1 << 2)); + track->zb_cb_clear = !!(idx_value & (1 << 5)); break; case 0x4e04: /* RB3D_BLENDCNTL */ diff --git a/trunk/drivers/gpu/drm/radeon/radeon_agp.c b/trunk/drivers/gpu/drm/radeon/radeon_agp.c index c4457791dff1..28e473f1f56f 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_agp.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_agp.c @@ -134,12 +134,10 @@ int radeon_agp_init(struct radeon_device *rdev) int ret; /* Acquire AGP. */ - if (!rdev->ddev->agp->acquired) { - ret = drm_agp_acquire(rdev->ddev); - if (ret) { - DRM_ERROR("Unable to acquire AGP: %d\n", ret); - return ret; - } + ret = drm_agp_acquire(rdev->ddev); + if (ret) { + DRM_ERROR("Unable to acquire AGP: %d\n", ret); + return ret; } ret = drm_agp_info(rdev->ddev, &info); diff --git a/trunk/drivers/gpu/drm/radeon/radeon_atombios.c b/trunk/drivers/gpu/drm/radeon/radeon_atombios.c index 5673665ff216..9916d825401c 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1264,7 +1264,7 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, switch (crev) { case 1: tv_info = (ATOM_ANALOG_TV_INFO *)(mode_info->atom_context->bios + data_offset); - if (index > MAX_SUPPORTED_TV_TIMING) + if (index >= MAX_SUPPORTED_TV_TIMING) return false; mode->crtc_htotal = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Total); @@ -1302,7 +1302,7 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, break; case 2: tv_info_v1_2 = (ATOM_ANALOG_TV_INFO_V1_2 *)(mode_info->atom_context->bios + data_offset); - if (index > MAX_SUPPORTED_TV_TIMING_V1_2) + if (index >= MAX_SUPPORTED_TV_TIMING_V1_2) return false; dtd_timings = &tv_info_v1_2->aModeTimings[index]; diff --git a/trunk/drivers/gpu/drm/radeon/radeon_connectors.c b/trunk/drivers/gpu/drm/radeon/radeon_connectors.c index 1331351c5178..4559a53d5e57 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_connectors.c @@ -1316,6 +1316,8 @@ radeon_add_legacy_connector(struct drm_device *dev, radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); if (!radeon_connector->ddc_bus) goto failed; + } + if (connector_type == DRM_MODE_CONNECTOR_DVII) { radeon_connector->dac_load_detect = true; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, diff --git a/trunk/drivers/gpu/drm/radeon/radeon_encoders.c b/trunk/drivers/gpu/drm/radeon/radeon_encoders.c index fed7b8084779..c5ddaf58563a 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_encoders.c @@ -254,6 +254,53 @@ radeon_get_atom_connector_priv_from_encoder(struct drm_encoder *encoder) return dig_connector; } +void radeon_panel_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + unsigned hblank = native_mode->htotal - native_mode->hdisplay; + unsigned vblank = native_mode->vtotal - native_mode->vdisplay; + unsigned hover = native_mode->hsync_start - native_mode->hdisplay; + unsigned vover = native_mode->vsync_start - native_mode->vdisplay; + unsigned hsync_width = native_mode->hsync_end - native_mode->hsync_start; + unsigned vsync_width = native_mode->vsync_end - native_mode->vsync_start; + + adjusted_mode->clock = native_mode->clock; + adjusted_mode->flags = native_mode->flags; + + if (ASIC_IS_AVIVO(rdev)) { + adjusted_mode->hdisplay = native_mode->hdisplay; + adjusted_mode->vdisplay = native_mode->vdisplay; + } + + adjusted_mode->htotal = native_mode->hdisplay + hblank; + adjusted_mode->hsync_start = native_mode->hdisplay + hover; + adjusted_mode->hsync_end = adjusted_mode->hsync_start + hsync_width; + + adjusted_mode->vtotal = native_mode->vdisplay + vblank; + adjusted_mode->vsync_start = native_mode->vdisplay + vover; + adjusted_mode->vsync_end = adjusted_mode->vsync_start + vsync_width; + + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + + if (ASIC_IS_AVIVO(rdev)) { + adjusted_mode->crtc_hdisplay = native_mode->hdisplay; + adjusted_mode->crtc_vdisplay = native_mode->vdisplay; + } + + adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + hblank; + adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + hover; + adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + hsync_width; + + adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + vblank; + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + vover; + adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + vsync_width; + +} + static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -275,18 +322,8 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; /* get the native mode for LVDS */ - if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) { - struct drm_display_mode *native_mode = &radeon_encoder->native_mode; - int mode_id = adjusted_mode->base.id; - *adjusted_mode = *native_mode; - if (!ASIC_IS_AVIVO(rdev)) { - adjusted_mode->hdisplay = mode->hdisplay; - adjusted_mode->vdisplay = mode->vdisplay; - adjusted_mode->crtc_hdisplay = mode->hdisplay; - adjusted_mode->crtc_vdisplay = mode->vdisplay; - } - adjusted_mode->base.id = mode_id; - } + if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) + radeon_panel_mode_fixup(encoder, adjusted_mode); /* get the native mode for TV */ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) { diff --git a/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index 2441cca7d775..0274abe17ad9 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -228,16 +228,8 @@ static bool radeon_legacy_mode_fixup(struct drm_encoder *encoder, drm_mode_set_crtcinfo(adjusted_mode, 0); /* get the native mode for LVDS */ - if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) { - struct drm_display_mode *native_mode = &radeon_encoder->native_mode; - int mode_id = adjusted_mode->base.id; - *adjusted_mode = *native_mode; - adjusted_mode->hdisplay = mode->hdisplay; - adjusted_mode->vdisplay = mode->vdisplay; - adjusted_mode->crtc_hdisplay = mode->hdisplay; - adjusted_mode->crtc_vdisplay = mode->vdisplay; - adjusted_mode->base.id = mode_id; - } + if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) + radeon_panel_mode_fixup(encoder, adjusted_mode); return true; } diff --git a/trunk/drivers/gpu/drm/radeon/radeon_mode.h b/trunk/drivers/gpu/drm/radeon/radeon_mode.h index 0b8e32776b10..5413fcd63086 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_mode.h +++ b/trunk/drivers/gpu/drm/radeon/radeon_mode.h @@ -558,6 +558,8 @@ extern int radeon_static_clocks_init(struct drm_device *dev); bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); +void radeon_panel_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode); void atom_rv515_force_tv_scaler(struct radeon_device *rdev, struct radeon_crtc *radeon_crtc); /* legacy tv */ diff --git a/trunk/drivers/gpu/drm/via/via_video.c b/trunk/drivers/gpu/drm/via/via_video.c index 6ec04ac12459..6efac8117c93 100644 --- a/trunk/drivers/gpu/drm/via/via_video.c +++ b/trunk/drivers/gpu/drm/via/via_video.c @@ -75,7 +75,7 @@ int via_decoder_futex(struct drm_device *dev, void *data, struct drm_file *file_ DRM_DEBUG("\n"); - if (fx->lock > VIA_NR_XVMC_LOCKS) + if (fx->lock >= VIA_NR_XVMC_LOCKS) return -EFAULT; lock = (volatile int *)XVMCLOCKPTR(sAPriv, fx->lock); diff --git a/trunk/drivers/gpu/vga/vga_switcheroo.c b/trunk/drivers/gpu/vga/vga_switcheroo.c index d6d1149d525d..c8768f38511e 100644 --- a/trunk/drivers/gpu/vga/vga_switcheroo.c +++ b/trunk/drivers/gpu/vga/vga_switcheroo.c @@ -276,8 +276,10 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, mutex_lock(&vgasr_mutex); - if (!vgasr_priv.active) - return -EINVAL; + if (!vgasr_priv.active) { + cnt = -EINVAL; + goto out; + } /* pwr off the device not in use */ if (strncmp(usercmd, "OFF", 3) == 0) { diff --git a/trunk/drivers/i2c/busses/i2c-octeon.c b/trunk/drivers/i2c/busses/i2c-octeon.c index a2481f40ea1c..0e9f85d0a835 100644 --- a/trunk/drivers/i2c/busses/i2c-octeon.c +++ b/trunk/drivers/i2c/busses/i2c-octeon.c @@ -447,7 +447,7 @@ static struct i2c_adapter octeon_i2c_ops = { /** * octeon_i2c_setclock - Calculate and set clock divisors. */ -static int __init octeon_i2c_setclock(struct octeon_i2c *i2c) +static int __devinit octeon_i2c_setclock(struct octeon_i2c *i2c) { int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; @@ -490,7 +490,7 @@ static int __init octeon_i2c_setclock(struct octeon_i2c *i2c) return 0; } -static int __init octeon_i2c_initlowlevel(struct octeon_i2c *i2c) +static int __devinit octeon_i2c_initlowlevel(struct octeon_i2c *i2c) { u8 status; int tries; diff --git a/trunk/drivers/i2c/i2c-core.c b/trunk/drivers/i2c/i2c-core.c index 3202a86f420e..c2258a51fe0c 100644 --- a/trunk/drivers/i2c/i2c-core.c +++ b/trunk/drivers/i2c/i2c-core.c @@ -40,12 +40,11 @@ #include "i2c-core.h" -/* core_lock protects i2c_adapter_idr, userspace_devices, and guarantees +/* core_lock protects i2c_adapter_idr, and guarantees that device detection, deletion of detected devices, and attach_adapter and detach_adapter calls are serialized */ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); -static LIST_HEAD(userspace_devices); static struct device_type i2c_client_type; static int i2c_check_addr(struct i2c_adapter *adapter, int addr); @@ -117,8 +116,10 @@ static int i2c_device_probe(struct device *dev) dev_dbg(dev, "probe\n"); status = driver->probe(client, i2c_match_id(driver->id_table, client)); - if (status) + if (status) { client->driver = NULL; + i2c_set_clientdata(client, NULL); + } return status; } @@ -139,8 +140,10 @@ static int i2c_device_remove(struct device *dev) dev->driver = NULL; status = 0; } - if (status == 0) + if (status == 0) { client->driver = NULL; + i2c_set_clientdata(client, NULL); + } return status; } @@ -538,9 +541,9 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr, return -EEXIST; /* Keep track of the added device */ - mutex_lock(&core_lock); - list_add_tail(&client->detected, &userspace_devices); - mutex_unlock(&core_lock); + i2c_lock_adapter(adap); + list_add_tail(&client->detected, &adap->userspace_clients); + i2c_unlock_adapter(adap); dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", info.type, info.addr); @@ -579,9 +582,10 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, /* Make sure the device was added through sysfs */ res = -ENOENT; - mutex_lock(&core_lock); - list_for_each_entry_safe(client, next, &userspace_devices, detected) { - if (client->addr == addr && client->adapter == adap) { + i2c_lock_adapter(adap); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + if (client->addr == addr) { dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", "delete_device", client->name, client->addr); @@ -591,7 +595,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, break; } } - mutex_unlock(&core_lock); + i2c_unlock_adapter(adap); if (res < 0) dev_err(dev, "%s: Can't find device in list\n", @@ -673,6 +677,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) } rt_mutex_init(&adap->bus_lock); + INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) @@ -875,14 +880,15 @@ int i2c_del_adapter(struct i2c_adapter *adap) return res; /* Remove devices instantiated from sysfs */ - list_for_each_entry_safe(client, next, &userspace_devices, detected) { - if (client->adapter == adap) { - dev_dbg(&adap->dev, "Removing %s at 0x%x\n", - client->name, client->addr); - list_del(&client->detected); - i2c_unregister_device(client); - } + i2c_lock_adapter(adap); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, + client->addr); + list_del(&client->detected); + i2c_unregister_device(client); } + i2c_unlock_adapter(adap); /* Detach any active clients. This can't fail, thus we do not checking the returned value. */ @@ -1260,12 +1266,23 @@ static int i2c_detect_address(struct i2c_client *temp_client, return 0; /* Make sure there is something at this address */ - if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0) - return 0; + if (addr == 0x73 && (adapter->class & I2C_CLASS_HWMON)) { + /* Special probe for FSC hwmon chips */ + union i2c_smbus_data dummy; - /* Prevent 24RF08 corruption */ - if ((addr & ~0x0f) == 0x50) - i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL); + if (i2c_smbus_xfer(adapter, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE_DATA, &dummy) < 0) + return 0; + } else { + if (i2c_smbus_xfer(adapter, addr, 0, I2C_SMBUS_WRITE, 0, + I2C_SMBUS_QUICK, NULL) < 0) + return 0; + + /* Prevent 24RF08 corruption */ + if ((addr & ~0x0f) == 0x50) + i2c_smbus_xfer(adapter, addr, 0, I2C_SMBUS_WRITE, 0, + I2C_SMBUS_QUICK, NULL); + } /* Finally call the custom detection function */ memset(&info, 0, sizeof(struct i2c_board_info)); diff --git a/trunk/drivers/ide/ide-cs.c b/trunk/drivers/ide/ide-cs.c index defce2877eef..b85450865ff0 100644 --- a/trunk/drivers/ide/ide-cs.c +++ b/trunk/drivers/ide/ide-cs.c @@ -409,7 +409,7 @@ static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420), PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178), PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753), - PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 1GB", 0x2e6d1829, 0x3e520e17), + PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 1GB", 0x2e6d1829, 0x55d5bffb), PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 4GB", 0x2e6d1829, 0x531e7d10), PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF8GB", 0x2e6d1829, 0xacbe682e), PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2 ", 0x547e66dc, 0x8671043b), @@ -431,7 +431,7 @@ static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS2GCF120", 0x709b1bf1, 0x969aa4f2), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8), - PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF133", 0x709b1bf1, 0x9351e59d), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF133", 0x709b1bf1, 0x7558f133), PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS8GCF133", 0x709b1bf1, 0xb2f89b47), PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852), PCMCIA_DEVICE_PROD_ID12("WEIDA", "TWTTI", 0xcc7cf69c, 0x212bb918), diff --git a/trunk/drivers/input/joydev.c b/trunk/drivers/input/joydev.c index c52bec4d0530..423e0e6031ab 100644 --- a/trunk/drivers/input/joydev.c +++ b/trunk/drivers/input/joydev.c @@ -929,6 +929,24 @@ static const struct input_device_id joydev_ids[] = { .evbit = { BIT_MASK(EV_ABS) }, .absbit = { BIT_MASK(ABS_THROTTLE) }, }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) }, + }, { } /* Terminating entry */ }; diff --git a/trunk/drivers/input/misc/ati_remote.c b/trunk/drivers/input/misc/ati_remote.c index 614b65d78fe9..e8bbc619f6df 100644 --- a/trunk/drivers/input/misc/ati_remote.c +++ b/trunk/drivers/input/misc/ati_remote.c @@ -98,10 +98,12 @@ * Module and Version Information, Module Parameters */ -#define ATI_REMOTE_VENDOR_ID 0x0bc7 -#define ATI_REMOTE_PRODUCT_ID 0x004 -#define LOLA_REMOTE_PRODUCT_ID 0x002 -#define MEDION_REMOTE_PRODUCT_ID 0x006 +#define ATI_REMOTE_VENDOR_ID 0x0bc7 +#define LOLA_REMOTE_PRODUCT_ID 0x0002 +#define LOLA2_REMOTE_PRODUCT_ID 0x0003 +#define ATI_REMOTE_PRODUCT_ID 0x0004 +#define NVIDIA_REMOTE_PRODUCT_ID 0x0005 +#define MEDION_REMOTE_PRODUCT_ID 0x0006 #define DRIVER_VERSION "2.2.1" #define DRIVER_AUTHOR "Torrey Hoffman " @@ -142,8 +144,10 @@ MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec #define err(format, arg...) printk(KERN_ERR format , ## arg) static struct usb_device_id ati_remote_table[] = { - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) }, { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID) }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID) }, { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) }, {} /* Terminating entry */ }; diff --git a/trunk/drivers/input/mouse/alps.c b/trunk/drivers/input/mouse/alps.c index 0d22cb9ce42e..99d58764ef03 100644 --- a/trunk/drivers/input/mouse/alps.c +++ b/trunk/drivers/input/mouse/alps.c @@ -64,7 +64,6 @@ static const struct alps_model_info alps_model_data[] = { { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ - { { 0x73, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, /* HP Pavilion dm3 */ { { 0x52, 0x01, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ }; diff --git a/trunk/drivers/input/mouse/elantech.c b/trunk/drivers/input/mouse/elantech.c index a138b5da79f9..0520c2e19927 100644 --- a/trunk/drivers/input/mouse/elantech.c +++ b/trunk/drivers/input/mouse/elantech.c @@ -25,6 +25,10 @@ printk(KERN_DEBUG format, ##arg); \ } while (0) +static bool force_elantech; +module_param_named(force_elantech, force_elantech, bool, 0644); +MODULE_PARM_DESC(force_elantech, "Force the Elantech PS/2 protocol extension to be used, 1 = enabled, 0 = disabled (default)."); + /* * Send a Synaptics style sliced query command */ @@ -182,13 +186,17 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) static int old_fingers; if (etd->fw_version_maj == 0x01) { - /* byte 0: D U p1 p2 1 p3 R L - byte 1: f 0 th tw x9 x8 y9 y8 */ + /* + * byte 0: D U p1 p2 1 p3 R L + * byte 1: f 0 th tw x9 x8 y9 y8 + */ fingers = ((packet[1] & 0x80) >> 7) + ((packet[1] & 0x30) >> 4); } else { - /* byte 0: n1 n0 p2 p1 1 p3 R L - byte 1: 0 0 0 0 x9 x8 y9 y8 */ + /* + * byte 0: n1 n0 p2 p1 1 p3 R L + * byte 1: 0 0 0 0 x9 x8 y9 y8 + */ fingers = (packet[0] & 0xc0) >> 6; } @@ -202,13 +210,15 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) input_report_key(dev, BTN_TOUCH, fingers != 0); - /* byte 2: x7 x6 x5 x4 x3 x2 x1 x0 - byte 3: y7 y6 y5 y4 y3 y2 y1 y0 */ + /* + * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 + * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 + */ if (fingers) { input_report_abs(dev, ABS_X, ((packet[1] & 0x0c) << 6) | packet[2]); - input_report_abs(dev, ABS_Y, ETP_YMAX_V1 - - (((packet[1] & 0x03) << 8) | packet[3])); + input_report_abs(dev, ABS_Y, + ETP_YMAX_V1 - (((packet[1] & 0x03) << 8) | packet[3])); } input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); @@ -247,34 +257,47 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) switch (fingers) { case 1: - /* byte 1: x15 x14 x13 x12 x11 x10 x9 x8 - byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */ - input_report_abs(dev, ABS_X, (packet[1] << 8) | packet[2]); - /* byte 4: y15 y14 y13 y12 y11 y10 y8 y8 - byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */ - input_report_abs(dev, ABS_Y, ETP_YMAX_V2 - - ((packet[4] << 8) | packet[5])); + /* + * byte 1: . . . . . x10 x9 x8 + * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 + */ + input_report_abs(dev, ABS_X, + ((packet[1] & 0x07) << 8) | packet[2]); + /* + * byte 4: . . . . . . y9 y8 + * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 + */ + input_report_abs(dev, ABS_Y, + ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5])); break; case 2: - /* The coordinate of each finger is reported separately with - a lower resolution for two finger touches */ - /* byte 0: . . ay8 ax8 . . . . - byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 */ + /* + * The coordinate of each finger is reported separately + * with a lower resolution for two finger touches: + * byte 0: . . ay8 ax8 . . . . + * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 + */ x1 = ((packet[0] & 0x10) << 4) | packet[1]; /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]); - /* byte 3: . . by8 bx8 . . . . - byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 */ + /* + * byte 3: . . by8 bx8 . . . . + * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 + */ x2 = ((packet[3] & 0x10) << 4) | packet[4]; /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]); - /* For compatibility with the X Synaptics driver scale up one - coordinate and report as ordinary mouse movent */ + /* + * For compatibility with the X Synaptics driver scale up + * one coordinate and report as ordinary mouse movent + */ input_report_abs(dev, ABS_X, x1 << 2); input_report_abs(dev, ABS_Y, y1 << 2); - /* For compatibility with the proprietary X Elantech driver - report both coordinates as hat coordinates */ + /* + * For compatibility with the proprietary X Elantech driver + * report both coordinates as hat coordinates + */ input_report_abs(dev, ABS_HAT0X, x1); input_report_abs(dev, ABS_HAT0Y, y1); input_report_abs(dev, ABS_HAT1X, x2); @@ -596,8 +619,12 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) param[0], param[1], param[2]); if (param[0] == 0 || param[1] != 0) { - pr_debug("elantech.c: Probably not a real Elantech touchpad. Aborting.\n"); - return -1; + if (!force_elantech) { + pr_debug("elantech.c: Probably not a real Elantech touchpad. Aborting.\n"); + return -1; + } + + pr_debug("elantech.c: Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n"); } if (set_properties) { @@ -666,7 +693,8 @@ int elantech_init(struct psmouse *psmouse) * Assume every version greater than this is new EeePC style * hardware with 6 byte packets */ - if (etd->fw_version_maj >= 0x02 && etd->fw_version_min >= 0x30) { + if ((etd->fw_version_maj == 0x02 && etd->fw_version_min >= 0x30) || + etd->fw_version_maj > 0x02) { etd->hw_version = 2; /* For now show extra debug information */ etd->debug = 1; diff --git a/trunk/drivers/input/mouse/psmouse-base.c b/trunk/drivers/input/mouse/psmouse-base.c index d8c0c8d6992c..cbc807264940 100644 --- a/trunk/drivers/input/mouse/psmouse-base.c +++ b/trunk/drivers/input/mouse/psmouse-base.c @@ -110,6 +110,7 @@ static struct workqueue_struct *kpsmoused_wq; struct psmouse_protocol { enum psmouse_type type; bool maxproto; + bool ignore_parity; /* Protocol should ignore parity errors from KBC */ const char *name; const char *alias; int (*detect)(struct psmouse *, bool); @@ -288,7 +289,9 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, if (psmouse->state == PSMOUSE_IGNORE) goto out; - if (flags & (SERIO_PARITY|SERIO_TIMEOUT)) { + if (unlikely((flags & SERIO_TIMEOUT) || + ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) { + if (psmouse->state == PSMOUSE_ACTIVATED) printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n", flags & SERIO_TIMEOUT ? " timeout" : "", @@ -759,6 +762,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "PS/2", .alias = "bare", .maxproto = true, + .ignore_parity = true, .detect = ps2bare_detect, }, #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP @@ -786,6 +790,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "ImPS/2", .alias = "imps", .maxproto = true, + .ignore_parity = true, .detect = intellimouse_detect, }, { @@ -793,6 +798,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "ImExPS/2", .alias = "exps", .maxproto = true, + .ignore_parity = true, .detect = im_explorer_detect, }, #ifdef CONFIG_MOUSE_PS2_SYNAPTICS @@ -1222,6 +1228,7 @@ static void psmouse_disconnect(struct serio *serio) static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto) { + const struct psmouse_protocol *selected_proto; struct input_dev *input_dev = psmouse->dev; input_dev->dev.parent = &psmouse->ps2dev.serio->dev; @@ -1245,9 +1252,14 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, return -1; psmouse->type = proto->type; - } else + selected_proto = proto; + } else { psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, true); + selected_proto = psmouse_protocol_by_type(psmouse->type); + } + + psmouse->ignore_parity = selected_proto->ignore_parity; /* * If mouse's packet size is 3 there is no point in polling the @@ -1267,7 +1279,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, psmouse->resync_time = 0; snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s", - psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name); + selected_proto->name, psmouse->vendor, psmouse->name); input_dev->name = psmouse->devname; input_dev->phys = psmouse->phys; diff --git a/trunk/drivers/input/mouse/psmouse.h b/trunk/drivers/input/mouse/psmouse.h index e053bdd137ff..593e910bfc7a 100644 --- a/trunk/drivers/input/mouse/psmouse.h +++ b/trunk/drivers/input/mouse/psmouse.h @@ -47,6 +47,7 @@ struct psmouse { unsigned char pktcnt; unsigned char pktsize; unsigned char type; + bool ignore_parity; bool acks_disable_command; unsigned int model; unsigned long last; diff --git a/trunk/drivers/input/mouse/synaptics.c b/trunk/drivers/input/mouse/synaptics.c index 026df6010161..ebd7a99efeae 100644 --- a/trunk/drivers/input/mouse/synaptics.c +++ b/trunk/drivers/input/mouse/synaptics.c @@ -137,7 +137,8 @@ static int synaptics_capability(struct psmouse *psmouse) if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) return -1; priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; - priv->ext_cap = 0; + priv->ext_cap = priv->ext_cap_0c = 0; + if (!SYN_CAP_VALID(priv->capabilities)) return -1; @@ -150,7 +151,7 @@ static int synaptics_capability(struct psmouse *psmouse) if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { printk(KERN_ERR "Synaptics claims to have extended capabilities," - " but I'm not able to read them."); + " but I'm not able to read them.\n"); } else { priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; @@ -162,6 +163,16 @@ static int synaptics_capability(struct psmouse *psmouse) priv->ext_cap &= 0xff0fff; } } + + if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) { + printk(KERN_ERR "Synaptics claims to have extended capability 0x0c," + " but I'm not able to read it.\n"); + } else { + priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2]; + } + } + return 0; } @@ -348,7 +359,15 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; - if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { + if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + /* + * Clickpad's button is transmitted as middle button, + * however, since it is primary button, we will report + * it as BTN_LEFT. + */ + hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; + + } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; if (hw->w == 2) hw->scroll = (signed char)(buf[1]); @@ -593,6 +612,12 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) dev->absres[ABS_X] = priv->x_res; dev->absres[ABS_Y] = priv->y_res; + + if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + /* Clickpads report only left button */ + __clear_bit(BTN_RIGHT, dev->keybit); + __clear_bit(BTN_MIDDLE, dev->keybit); + } } static void synaptics_disconnect(struct psmouse *psmouse) @@ -697,10 +722,10 @@ int synaptics_init(struct psmouse *psmouse) priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; - printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx\n", + printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n", SYN_ID_MODEL(priv->identity), SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), - priv->model_id, priv->capabilities, priv->ext_cap); + priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c); set_input_params(psmouse->dev, priv); diff --git a/trunk/drivers/input/mouse/synaptics.h b/trunk/drivers/input/mouse/synaptics.h index f0f40a331dc8..ae37c5d162a4 100644 --- a/trunk/drivers/input/mouse/synaptics.h +++ b/trunk/drivers/input/mouse/synaptics.h @@ -18,6 +18,7 @@ #define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07 #define SYN_QUE_RESOLUTION 0x08 #define SYN_QUE_EXT_CAPAB 0x09 +#define SYN_QUE_EXT_CAPAB_0C 0x0c /* synatics modes */ #define SYN_BIT_ABSOLUTE_MODE (1 << 7) @@ -48,6 +49,8 @@ #define SYN_CAP_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47) #define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) +#define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) +#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100) /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) @@ -96,6 +99,7 @@ struct synaptics_data { unsigned long int model_id; /* Model-ID */ unsigned long int capabilities; /* Capabilities */ unsigned long int ext_cap; /* Extended Capabilities */ + unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ unsigned long int identity; /* Identification */ int x_res; /* X resolution in units/mm */ int y_res; /* Y resolution in units/mm */ diff --git a/trunk/drivers/input/touchscreen/eeti_ts.c b/trunk/drivers/input/touchscreen/eeti_ts.c index 204b8a1a601c..75f8b73010fa 100644 --- a/trunk/drivers/input/touchscreen/eeti_ts.c +++ b/trunk/drivers/input/touchscreen/eeti_ts.c @@ -124,14 +124,25 @@ static irqreturn_t eeti_ts_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int eeti_ts_open(struct input_dev *dev) +static void eeti_ts_start(struct eeti_ts_priv *priv) { - struct eeti_ts_priv *priv = input_get_drvdata(dev); - enable_irq(priv->irq); /* Read the events once to arm the IRQ */ eeti_ts_read(&priv->work); +} + +static void eeti_ts_stop(struct eeti_ts_priv *priv) +{ + disable_irq(priv->irq); + cancel_work_sync(&priv->work); +} + +static int eeti_ts_open(struct input_dev *dev) +{ + struct eeti_ts_priv *priv = input_get_drvdata(dev); + + eeti_ts_start(priv); return 0; } @@ -140,8 +151,7 @@ static void eeti_ts_close(struct input_dev *dev) { struct eeti_ts_priv *priv = input_get_drvdata(dev); - disable_irq(priv->irq); - cancel_work_sync(&priv->work); + eeti_ts_stop(priv); } static int __devinit eeti_ts_probe(struct i2c_client *client, @@ -153,10 +163,12 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, unsigned int irq_flags; int err = -ENOMEM; - /* In contrast to what's described in the datasheet, there seems + /* + * In contrast to what's described in the datasheet, there seems * to be no way of probing the presence of that device using I2C * commands. So we need to blindly believe it is there, and wait - * for interrupts to occur. */ + * for interrupts to occur. + */ priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -212,9 +224,11 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, goto err2; } - /* Disable the irq for now. It will be enabled once the input device - * is opened. */ - disable_irq(priv->irq); + /* + * Disable the device for now. It will be enabled once the + * input device is opened. + */ + eeti_ts_stop(priv); device_init_wakeup(&client->dev, 0); return 0; @@ -235,6 +249,12 @@ static int __devexit eeti_ts_remove(struct i2c_client *client) struct eeti_ts_priv *priv = i2c_get_clientdata(client); free_irq(priv->irq, priv); + /* + * eeti_ts_stop() leaves IRQ disabled. We need to re-enable it + * so that device still works if we reload the driver. + */ + enable_irq(priv->irq); + input_unregister_device(priv->input); i2c_set_clientdata(client, NULL); kfree(priv); @@ -246,6 +266,14 @@ static int __devexit eeti_ts_remove(struct i2c_client *client) static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg) { struct eeti_ts_priv *priv = i2c_get_clientdata(client); + struct input_dev *input_dev = priv->input; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + eeti_ts_stop(priv); + + mutex_unlock(&input_dev->mutex); if (device_may_wakeup(&client->dev)) enable_irq_wake(priv->irq); @@ -256,10 +284,18 @@ static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg) static int eeti_ts_resume(struct i2c_client *client) { struct eeti_ts_priv *priv = i2c_get_clientdata(client); + struct input_dev *input_dev = priv->input; if (device_may_wakeup(&client->dev)) disable_irq_wake(priv->irq); + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + eeti_ts_start(priv); + + mutex_unlock(&input_dev->mutex); + return 0; } #else diff --git a/trunk/drivers/mtd/internal.h b/trunk/drivers/mtd/internal.h deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/trunk/drivers/mtd/mtdbdi.c b/trunk/drivers/mtd/mtdbdi.c deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/trunk/drivers/net/arm/ep93xx_eth.c b/trunk/drivers/net/arm/ep93xx_eth.c index 6995169d285a..cd17d09f385c 100644 --- a/trunk/drivers/net/arm/ep93xx_eth.c +++ b/trunk/drivers/net/arm/ep93xx_eth.c @@ -311,11 +311,6 @@ static int ep93xx_rx(struct net_device *dev, int processed, int budget) processed++; } - if (processed) { - wrw(ep, REG_RXDENQ, processed); - wrw(ep, REG_RXSTSENQ, processed); - } - return processed; } @@ -350,6 +345,11 @@ static int ep93xx_poll(struct napi_struct *napi, int budget) goto poll_some_more; } + if (rx) { + wrw(ep, REG_RXDENQ, rx); + wrw(ep, REG_RXSTSENQ, rx); + } + return rx; } diff --git a/trunk/drivers/net/e1000e/netdev.c b/trunk/drivers/net/e1000e/netdev.c index fb8fc7d1b50d..dbf81788bb40 100644 --- a/trunk/drivers/net/e1000e/netdev.c +++ b/trunk/drivers/net/e1000e/netdev.c @@ -4633,6 +4633,9 @@ static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state) reg16 &= ~state; pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); + if (!pdev->bus->self) + return; + pos = pci_pcie_cap(pdev->bus->self); pci_read_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, ®16); reg16 &= ~state; diff --git a/trunk/drivers/net/fec.c b/trunk/drivers/net/fec.c index 9f98c1c4a344..9b4e8f797a7a 100644 --- a/trunk/drivers/net/fec.c +++ b/trunk/drivers/net/fec.c @@ -1653,7 +1653,7 @@ fec_set_mac_address(struct net_device *dev, void *p) (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24), fep->hwp + FEC_ADDR_LOW); writel((dev->dev_addr[5] << 16) | (dev->dev_addr[4] << 24), - fep + FEC_ADDR_HIGH); + fep->hwp + FEC_ADDR_HIGH); return 0; } diff --git a/trunk/drivers/net/phy/Kconfig b/trunk/drivers/net/phy/Kconfig index fc5938ba3d78..a527e37728cd 100644 --- a/trunk/drivers/net/phy/Kconfig +++ b/trunk/drivers/net/phy/Kconfig @@ -88,6 +88,11 @@ config LSI_ET1011C_PHY ---help--- Supports the LSI ET1011C PHY. +config MICREL_PHY + tristate "Driver for Micrel PHYs" + ---help--- + Supports the KSZ9021, VSC8201, KS8001 PHYs. + config FIXED_PHY bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" depends on PHYLIB=y diff --git a/trunk/drivers/net/phy/Makefile b/trunk/drivers/net/phy/Makefile index 1342585af381..13bebab65d02 100644 --- a/trunk/drivers/net/phy/Makefile +++ b/trunk/drivers/net/phy/Makefile @@ -20,4 +20,5 @@ obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_STE10XP) += ste10Xp.o +obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o diff --git a/trunk/drivers/net/phy/mdio-octeon.c b/trunk/drivers/net/phy/mdio-octeon.c index a872aea4ed74..f443d43edd80 100644 --- a/trunk/drivers/net/phy/mdio-octeon.c +++ b/trunk/drivers/net/phy/mdio-octeon.c @@ -88,6 +88,7 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id, static int __init octeon_mdiobus_probe(struct platform_device *pdev) { struct octeon_mdiobus *bus; + union cvmx_smix_en smi_en; int i; int err = -ENOENT; @@ -103,6 +104,10 @@ static int __init octeon_mdiobus_probe(struct platform_device *pdev) if (!bus->mii_bus) goto err; + smi_en.u64 = 0; + smi_en.s.en = 1; + cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); + /* * Standard Octeon evaluation boards don't support phy * interrupts, we need to poll. @@ -133,17 +138,22 @@ static int __init octeon_mdiobus_probe(struct platform_device *pdev) err: devm_kfree(&pdev->dev, bus); + smi_en.u64 = 0; + cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); return err; } static int __exit octeon_mdiobus_remove(struct platform_device *pdev) { struct octeon_mdiobus *bus; + union cvmx_smix_en smi_en; bus = dev_get_drvdata(&pdev->dev); mdiobus_unregister(bus->mii_bus); mdiobus_free(bus->mii_bus); + smi_en.u64 = 0; + cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); return 0; } diff --git a/trunk/drivers/net/phy/micrel.c b/trunk/drivers/net/phy/micrel.c new file mode 100644 index 000000000000..0cd80e4d71d9 --- /dev/null +++ b/trunk/drivers/net/phy/micrel.c @@ -0,0 +1,104 @@ +/* + * drivers/net/phy/micrel.c + * + * Driver for Micrel PHYs + * + * Author: David J. Choi + * + * Copyright (c) 2010 Micrel, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Support : ksz9021 , vsc8201, ks8001 + */ + +#include +#include +#include + +#define PHY_ID_KSZ9021 0x00221611 +#define PHY_ID_VSC8201 0x000FC413 +#define PHY_ID_KS8001 0x0022161A + + +static int kszphy_config_init(struct phy_device *phydev) +{ + return 0; +} + + +static struct phy_driver ks8001_driver = { + .phy_id = PHY_ID_KS8001, + .phy_id_mask = 0x00fffff0, + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .driver = { .owner = THIS_MODULE,}, +}; + +static struct phy_driver vsc8201_driver = { + .phy_id = PHY_ID_VSC8201, + .name = "Micrel VSC8201", + .phy_id_mask = 0x00fffff0, + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .driver = { .owner = THIS_MODULE,}, +}; + +static struct phy_driver ksz9021_driver = { + .phy_id = PHY_ID_KSZ9021, + .phy_id_mask = 0x000fff10, + .name = "Micrel KSZ9021 Gigabit PHY", + .features = PHY_GBIT_FEATURES | SUPPORTED_Pause, + .flags = PHY_POLL, + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .driver = { .owner = THIS_MODULE, }, +}; + +static int __init ksphy_init(void) +{ + int ret; + + ret = phy_driver_register(&ks8001_driver); + if (ret) + goto err1; + ret = phy_driver_register(&vsc8201_driver); + if (ret) + goto err2; + + ret = phy_driver_register(&ksz9021_driver); + if (ret) + goto err3; + return 0; + +err3: + phy_driver_unregister(&vsc8201_driver); +err2: + phy_driver_unregister(&ks8001_driver); +err1: + return ret; +} + +static void __exit ksphy_exit(void) +{ + phy_driver_unregister(&ks8001_driver); + phy_driver_unregister(&vsc8201_driver); + phy_driver_unregister(&ksz9021_driver); +} + +module_init(ksphy_init); +module_exit(ksphy_exit); + +MODULE_DESCRIPTION("Micrel PHY driver"); +MODULE_AUTHOR("David J. Choi"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/net/ppp_generic.c b/trunk/drivers/net/ppp_generic.c index 6e281bc825e5..8518a2e58e53 100644 --- a/trunk/drivers/net/ppp_generic.c +++ b/trunk/drivers/net/ppp_generic.c @@ -405,6 +405,7 @@ static ssize_t ppp_read(struct file *file, char __user *buf, DECLARE_WAITQUEUE(wait, current); ssize_t ret; struct sk_buff *skb = NULL; + struct iovec iov; ret = count; @@ -448,7 +449,9 @@ static ssize_t ppp_read(struct file *file, char __user *buf, if (skb->len > count) goto outf; ret = -EFAULT; - if (copy_to_user(buf, skb->data, skb->len)) + iov.iov_base = buf; + iov.iov_len = count; + if (skb_copy_datagram_iovec(skb, 0, &iov, skb->len)) goto outf; ret = skb->len; @@ -1567,13 +1570,22 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) struct channel *pch = chan->ppp; int proto; - if (!pch || skb->len == 0) { + if (!pch) { kfree_skb(skb); return; } - proto = PPP_PROTO(skb); read_lock_bh(&pch->upl); + if (!pskb_may_pull(skb, 2)) { + kfree_skb(skb); + if (pch->ppp) { + ++pch->ppp->dev->stats.rx_length_errors; + ppp_receive_error(pch->ppp); + } + goto done; + } + + proto = PPP_PROTO(skb); if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { /* put it on the channel queue */ skb_queue_tail(&pch->file.rq, skb); @@ -1585,6 +1597,8 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) } else { ppp_do_recv(pch->ppp, skb, pch); } + +done: read_unlock_bh(&pch->upl); } @@ -1617,7 +1631,8 @@ ppp_input_error(struct ppp_channel *chan, int code) static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) { - if (pskb_may_pull(skb, 2)) { + /* note: a 0-length skb is used as an error indication */ + if (skb->len > 0) { #ifdef CONFIG_PPP_MULTILINK /* XXX do channel-level decompression here */ if (PPP_PROTO(skb) == PPP_MP) @@ -1625,15 +1640,10 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) else #endif /* CONFIG_PPP_MULTILINK */ ppp_receive_nonmp_frame(ppp, skb); - return; + } else { + kfree_skb(skb); + ppp_receive_error(ppp); } - - if (skb->len > 0) - /* note: a 0-length skb is used as an error indication */ - ++ppp->dev->stats.rx_length_errors; - - kfree_skb(skb); - ppp_receive_error(ppp); } static void diff --git a/trunk/drivers/net/r8169.c b/trunk/drivers/net/r8169.c index 4748c21eb72e..dd8106ff35aa 100644 --- a/trunk/drivers/net/r8169.c +++ b/trunk/drivers/net/r8169.c @@ -1042,14 +1042,14 @@ static void rtl8169_vlan_rx_register(struct net_device *dev, } static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, - struct sk_buff *skb) + struct sk_buff *skb, int polling) { u32 opts2 = le32_to_cpu(desc->opts2); struct vlan_group *vlgrp = tp->vlgrp; int ret; if (vlgrp && (opts2 & RxVlanTag)) { - vlan_hwaccel_receive_skb(skb, vlgrp, swab16(opts2 & 0xffff)); + __vlan_hwaccel_rx(skb, vlgrp, swab16(opts2 & 0xffff), polling); ret = 0; } else ret = -1; @@ -1066,7 +1066,7 @@ static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp, } static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, - struct sk_buff *skb) + struct sk_buff *skb, int polling) { return -1; } @@ -4445,12 +4445,20 @@ static inline bool rtl8169_try_rx_copy(struct sk_buff **sk_buff, return done; } +/* + * Warning : rtl8169_rx_interrupt() might be called : + * 1) from NAPI (softirq) context + * (polling = 1 : we should call netif_receive_skb()) + * 2) from process context (rtl8169_reset_task()) + * (polling = 0 : we must call netif_rx() instead) + */ static int rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp, void __iomem *ioaddr, u32 budget) { unsigned int cur_rx, rx_left; unsigned int delta, count; + int polling = (budget != ~(u32)0) ? 1 : 0; cur_rx = tp->cur_rx; rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; @@ -4512,8 +4520,12 @@ static int rtl8169_rx_interrupt(struct net_device *dev, skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); - if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0) - netif_receive_skb(skb); + if (rtl8169_rx_vlan_skb(tp, desc, skb, polling) < 0) { + if (likely(polling)) + netif_receive_skb(skb); + else + netif_rx(skb); + } dev->stats.rx_bytes += pkt_size; dev->stats.rx_packets++; diff --git a/trunk/drivers/net/sb1250-mac.c b/trunk/drivers/net/sb1250-mac.c index 9944e5d662c0..04efc0c1bda9 100644 --- a/trunk/drivers/net/sb1250-mac.c +++ b/trunk/drivers/net/sb1250-mac.c @@ -2353,17 +2353,36 @@ static int sbmac_init(struct platform_device *pldev, long long base) sc->mii_bus = mdiobus_alloc(); if (sc->mii_bus == NULL) { - sbmac_uninitctx(sc); - return -ENOMEM; + err = -ENOMEM; + goto uninit_ctx; } + sc->mii_bus->name = sbmac_mdio_string; + snprintf(sc->mii_bus->id, MII_BUS_ID_SIZE, "%x", idx); + sc->mii_bus->priv = sc; + sc->mii_bus->read = sbmac_mii_read; + sc->mii_bus->write = sbmac_mii_write; + sc->mii_bus->irq = sc->phy_irq; + for (i = 0; i < PHY_MAX_ADDR; ++i) + sc->mii_bus->irq[i] = SBMAC_PHY_INT; + + sc->mii_bus->parent = &pldev->dev; + /* + * Probe PHY address + */ + err = mdiobus_register(sc->mii_bus); + if (err) { + printk(KERN_ERR "%s: unable to register MDIO bus\n", + dev->name); + goto free_mdio; + } + dev_set_drvdata(&pldev->dev, sc->mii_bus); + err = register_netdev(dev); if (err) { printk(KERN_ERR "%s.%d: unable to register netdev\n", sbmac_string, idx); - mdiobus_free(sc->mii_bus); - sbmac_uninitctx(sc); - return err; + goto unreg_mdio; } pr_info("%s.%d: registered as %s\n", sbmac_string, idx, dev->name); @@ -2379,19 +2398,15 @@ static int sbmac_init(struct platform_device *pldev, long long base) pr_info("%s: SiByte Ethernet at 0x%08Lx, address: %pM\n", dev->name, base, eaddr); - sc->mii_bus->name = sbmac_mdio_string; - snprintf(sc->mii_bus->id, MII_BUS_ID_SIZE, "%x", idx); - sc->mii_bus->priv = sc; - sc->mii_bus->read = sbmac_mii_read; - sc->mii_bus->write = sbmac_mii_write; - sc->mii_bus->irq = sc->phy_irq; - for (i = 0; i < PHY_MAX_ADDR; ++i) - sc->mii_bus->irq[i] = SBMAC_PHY_INT; - - sc->mii_bus->parent = &pldev->dev; - dev_set_drvdata(&pldev->dev, sc->mii_bus); - return 0; +unreg_mdio: + mdiobus_unregister(sc->mii_bus); + dev_set_drvdata(&pldev->dev, NULL); +free_mdio: + mdiobus_free(sc->mii_bus); +uninit_ctx: + sbmac_uninitctx(sc); + return err; } @@ -2417,16 +2432,6 @@ static int sbmac_open(struct net_device *dev) goto out_err; } - /* - * Probe PHY address - */ - err = mdiobus_register(sc->mii_bus); - if (err) { - printk(KERN_ERR "%s: unable to register MDIO bus\n", - dev->name); - goto out_unirq; - } - sc->sbm_speed = sbmac_speed_none; sc->sbm_duplex = sbmac_duplex_none; sc->sbm_fc = sbmac_fc_none; @@ -2457,11 +2462,7 @@ static int sbmac_open(struct net_device *dev) return 0; out_unregister: - mdiobus_unregister(sc->mii_bus); - -out_unirq: free_irq(dev->irq, dev); - out_err: return err; } @@ -2650,9 +2651,6 @@ static int sbmac_close(struct net_device *dev) phy_disconnect(sc->phy_dev); sc->phy_dev = NULL; - - mdiobus_unregister(sc->mii_bus); - free_irq(dev->irq, dev); sbdma_emptyring(&(sc->sbm_txdma)); @@ -2760,6 +2758,7 @@ static int __exit sbmac_remove(struct platform_device *pldev) unregister_netdev(dev); sbmac_uninitctx(sc); + mdiobus_unregister(sc->mii_bus); mdiobus_free(sc->mii_bus); iounmap(sc->sbm_base); free_netdev(dev); diff --git a/trunk/drivers/net/usb/Kconfig b/trunk/drivers/net/usb/Kconfig index 5d58abc224f4..d7b7018a1de1 100644 --- a/trunk/drivers/net/usb/Kconfig +++ b/trunk/drivers/net/usb/Kconfig @@ -400,7 +400,6 @@ config USB_IPHETH config USB_SIERRA_NET tristate "USB-to-WWAN Driver for Sierra Wireless modems" depends on USB_USBNET - default y help Choose this option if you have a Sierra Wireless USB-to-WWAN device. diff --git a/trunk/drivers/net/usb/dm9601.c b/trunk/drivers/net/usb/dm9601.c index 04b281002a76..5dfed9297b22 100644 --- a/trunk/drivers/net/usb/dm9601.c +++ b/trunk/drivers/net/usb/dm9601.c @@ -240,7 +240,7 @@ static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 valu goto out; dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); - dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1c : 0x14); + dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1a : 0x12); for (i = 0; i < DM_TIMEOUT; i++) { u8 tmp; diff --git a/trunk/drivers/net/usb/sierra_net.c b/trunk/drivers/net/usb/sierra_net.c index a44f9e0ea098..f1942d69a0d5 100644 --- a/trunk/drivers/net/usb/sierra_net.c +++ b/trunk/drivers/net/usb/sierra_net.c @@ -789,6 +789,9 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf) /* prepare sync message from template */ memcpy(priv->sync_msg, sync_tmplate, sizeof(priv->sync_msg)); + /* initiate the sync sequence */ + sierra_net_dosync(dev); + return 0; } diff --git a/trunk/drivers/net/wireless/p54/p54pci.c b/trunk/drivers/net/wireless/p54/p54pci.c index 269fda362836..c24067f1a0cb 100644 --- a/trunk/drivers/net/wireless/p54/p54pci.c +++ b/trunk/drivers/net/wireless/p54/p54pci.c @@ -246,7 +246,7 @@ static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, u32 idx, i; i = (*index) % ring_limit; - (*index) = idx = le32_to_cpu(ring_control->device_idx[1]); + (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); idx %= ring_limit; while (i != idx) { diff --git a/trunk/drivers/of/of_mdio.c b/trunk/drivers/of/of_mdio.c index 18ecae4a4375..b4748337223b 100644 --- a/trunk/drivers/of/of_mdio.c +++ b/trunk/drivers/of/of_mdio.c @@ -69,7 +69,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) } phy = get_phy_device(mdio, be32_to_cpup(addr)); - if (!phy) { + if (!phy || IS_ERR(phy)) { dev_err(&mdio->dev, "error probing PHY at address %i\n", *addr); continue; diff --git a/trunk/drivers/pcmcia/db1xxx_ss.c b/trunk/drivers/pcmcia/db1xxx_ss.c index 2d48196a48cd..0f4cc3f00028 100644 --- a/trunk/drivers/pcmcia/db1xxx_ss.c +++ b/trunk/drivers/pcmcia/db1xxx_ss.c @@ -146,7 +146,6 @@ static irqreturn_t db1200_pcmcia_cdirq(int irq, void *data) static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock) { int ret; - unsigned long flags; if (sock->stschg_irq != -1) { ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq, @@ -162,30 +161,23 @@ static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock) * active one disabled. */ if (sock->board_type == BOARD_TYPE_DB1200) { - local_irq_save(flags); - ret = request_irq(sock->insert_irq, db1200_pcmcia_cdirq, IRQF_DISABLED, "pcmcia_insert", sock); - if (ret) { - local_irq_restore(flags); + if (ret) goto out1; - } ret = request_irq(sock->eject_irq, db1200_pcmcia_cdirq, IRQF_DISABLED, "pcmcia_eject", sock); if (ret) { free_irq(sock->insert_irq, sock); - local_irq_restore(flags); goto out1; } - /* disable the currently active one */ + /* enable the currently silent one */ if (db1200_card_inserted(sock)) - disable_irq_nosync(sock->insert_irq); + enable_irq(sock->eject_irq); else - disable_irq_nosync(sock->eject_irq); - - local_irq_restore(flags); + enable_irq(sock->insert_irq); } else { /* all other (older) Db1x00 boards use a GPIO to show * card detection status: use both-edge triggers. diff --git a/trunk/drivers/serial/8250_pnp.c b/trunk/drivers/serial/8250_pnp.c index 24485cc62ff8..4822cb50cd0f 100644 --- a/trunk/drivers/serial/8250_pnp.c +++ b/trunk/drivers/serial/8250_pnp.c @@ -348,6 +348,8 @@ static const struct pnp_device_id pnp_dev_table[] = { { "FUJ02E6", 0 }, /* Fujitsu Wacom 2FGT Tablet PC device */ { "FUJ02E7", 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { "FUJ02E9", 0 }, /* * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in * disguise) diff --git a/trunk/drivers/serial/mpc52xx_uart.c b/trunk/drivers/serial/mpc52xx_uart.c index 3119fddaedb5..a176ab4bd65b 100644 --- a/trunk/drivers/serial/mpc52xx_uart.c +++ b/trunk/drivers/serial/mpc52xx_uart.c @@ -29,39 +29,6 @@ * kind, whether express or implied. */ -/* Platform device Usage : - * - * Since PSCs can have multiple function, the correct driver for each one - * is selected by calling mpc52xx_match_psc_function(...). The function - * handled by this driver is "uart". - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - * - * The idx field must be equal to the PSC index (e.g. 0 for PSC1, 1 for PSC2, - * and so on). So the PSC1 is mapped to /dev/ttyPSC0, PSC2 to /dev/ttyPSC1 and - * so on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly - * fpr the console code : without this 1:1 mapping, at early boot time, when we - * are parsing the kernel args console=ttyPSC?, we wouldn't know which PSC it - * will be mapped to. - */ - -/* OF Platform device Usage : - * - * This driver is only used for PSCs configured in uart mode. The device - * tree will have a node for each PSC with "mpc52xx-psc-uart" in the compatible - * list. - * - * By default, PSC devices are enumerated in the order they are found. However - * a particular PSC number can be forces by adding 'device_no = ' - * to the device node. - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - */ - #undef DEBUG #include diff --git a/trunk/drivers/serial/pmac_zilog.c b/trunk/drivers/serial/pmac_zilog.c index 4eaa043ca2a8..700e10833bf9 100644 --- a/trunk/drivers/serial/pmac_zilog.c +++ b/trunk/drivers/serial/pmac_zilog.c @@ -752,8 +752,10 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) uap->curregs[R5] = new_reg; /* NOTE: Not subject to 'transmitter active' rule. */ - if (ZS_IS_ASLEEP(uap)) + if (ZS_IS_ASLEEP(uap)) { + spin_unlock_irqrestore(&port->lock, flags); return; + } write_zsreg(uap, R5, uap->curregs[R5]); } diff --git a/trunk/drivers/spi/omap2_mcspi.c b/trunk/drivers/spi/omap2_mcspi.c index d8356af118a8..e0de0d0eedea 100644 --- a/trunk/drivers/spi/omap2_mcspi.c +++ b/trunk/drivers/spi/omap2_mcspi.c @@ -204,6 +204,7 @@ static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val) cs->chconf0 = val; mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, val); + mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); } static void omap2_mcspi_set_dma_req(const struct spi_device *spi, @@ -532,7 +533,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) goto out; } #ifdef VERBOSE - dev_dbg(&spi->dev, "write-%d %04x\n", + dev_dbg(&spi->dev, "write-%d %08x\n", word_len, *tx); #endif __raw_writel(*tx++, tx_reg); @@ -550,7 +551,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) mcspi_write_chconf0(spi, l); *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE - dev_dbg(&spi->dev, "read-%d %04x\n", + dev_dbg(&spi->dev, "read-%d %08x\n", word_len, *(rx - 1)); #endif } diff --git a/trunk/drivers/spi/spi.c b/trunk/drivers/spi/spi.c index 9ffb0fdbd6fe..b3a1f9259b62 100644 --- a/trunk/drivers/spi/spi.c +++ b/trunk/drivers/spi/spi.c @@ -41,7 +41,7 @@ static void spidev_release(struct device *dev) spi->master->cleanup(spi); spi_master_put(spi->master); - kfree(dev); + kfree(spi); } static ssize_t @@ -257,6 +257,7 @@ int spi_add_device(struct spi_device *spi) { static DEFINE_MUTEX(spi_add_lock); struct device *dev = spi->master->dev.parent; + struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ @@ -278,10 +279,11 @@ int spi_add_device(struct spi_device *spi) */ mutex_lock(&spi_add_lock); - if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)) - != NULL) { + d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); + if (d != NULL) { dev_err(dev, "chipselect %d already in use\n", spi->chip_select); + put_device(d); status = -EBUSY; goto done; } diff --git a/trunk/drivers/staging/dt3155/dt3155_drv.c b/trunk/drivers/staging/dt3155/dt3155_drv.c index e2c44ec6fc45..7ac2c6d8e9a3 100644 --- a/trunk/drivers/staging/dt3155/dt3155_drv.c +++ b/trunk/drivers/staging/dt3155/dt3155_drv.c @@ -464,9 +464,9 @@ static void dt3155_init_isr(int minor) /* 50/60 Hz should be set before this point but let's make sure it is */ /* right anyway */ - ReadI2C(dt3155_lbase[ minor ], CONFIG, &i2c_csr2.reg); + ReadI2C(dt3155_lbase[ minor ], CSR2, &i2c_csr2.reg); i2c_csr2.fld.HZ50 = FORMAT50HZ; - WriteI2C(dt3155_lbase[ minor ], CONFIG, i2c_config.reg); + WriteI2C(dt3155_lbase[ minor ], CSR2, i2c_csr2.reg); /* enable busmaster chip, clear flags */ diff --git a/trunk/drivers/staging/hv/Hv.c b/trunk/drivers/staging/hv/Hv.c index 5d53889fb4a4..3a1112d29aeb 100644 --- a/trunk/drivers/staging/hv/Hv.c +++ b/trunk/drivers/staging/hv/Hv.c @@ -306,9 +306,9 @@ void HvCleanup(void) DPRINT_ENTER(VMBUS); if (gHvContext.SignalEventBuffer) { + kfree(gHvContext.SignalEventBuffer); gHvContext.SignalEventBuffer = NULL; gHvContext.SignalEventParam = NULL; - kfree(gHvContext.SignalEventBuffer); } if (gHvContext.HypercallPage) { diff --git a/trunk/drivers/staging/hv/RndisFilter.c b/trunk/drivers/staging/hv/RndisFilter.c index cd2930de2176..6704f64c93f0 100644 --- a/trunk/drivers/staging/hv/RndisFilter.c +++ b/trunk/drivers/staging/hv/RndisFilter.c @@ -751,6 +751,7 @@ static int RndisFilterOpenDevice(struct rndis_device *Device) ret = RndisFilterSetPacketFilter(Device, NDIS_PACKET_TYPE_BROADCAST | + NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_DIRECTED); if (ret == 0) Device->State = RNDIS_DEV_DATAINITIALIZED; diff --git a/trunk/drivers/staging/hv/netvsc_drv.c b/trunk/drivers/staging/hv/netvsc_drv.c index 2ccb6b93fe47..ab27d9a4446d 100644 --- a/trunk/drivers/staging/hv/netvsc_drv.c +++ b/trunk/drivers/staging/hv/netvsc_drv.c @@ -403,8 +403,7 @@ static int netvsc_probe(struct device *device) if (!net_drv_obj->Base.OnDeviceAdd) return -1; - net = alloc_netdev(sizeof(struct net_device_context), "seth%d", - ether_setup); + net = alloc_etherdev(sizeof(struct net_device_context)); if (!net) return -1; diff --git a/trunk/drivers/staging/iio/accel/lis3l02dq_core.c b/trunk/drivers/staging/iio/accel/lis3l02dq_core.c index ea76902797bb..82e43588e8a5 100644 --- a/trunk/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/trunk/drivers/staging/iio/accel/lis3l02dq_core.c @@ -618,7 +618,7 @@ static int lis3l02dq_thresh_handler_th(struct iio_dev *dev_info, static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s) { struct iio_work_cont *wc - = container_of(work_s, struct iio_work_cont, ws_nocheck); + = container_of(work_s, struct iio_work_cont, ws); struct lis3l02dq_state *st = wc->st; u8 t; diff --git a/trunk/drivers/staging/iio/accel/lis3l02dq_ring.c b/trunk/drivers/staging/iio/accel/lis3l02dq_ring.c index 93712430e579..a4d97ea0df3d 100644 --- a/trunk/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/trunk/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -493,6 +493,9 @@ int lis3l02dq_probe_trigger(struct iio_dev *indio_dev) struct lis3l02dq_state *state = indio_dev->dev_data; state->trig = iio_allocate_trigger(); + if (!state->trig) + return -ENOMEM; + state->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); if (!state->trig->name) { ret = -ENOMEM; diff --git a/trunk/drivers/staging/iio/adc/max1363_core.c b/trunk/drivers/staging/iio/adc/max1363_core.c index 790d1cc9cdc3..773f1d1d9c6e 100644 --- a/trunk/drivers/staging/iio/adc/max1363_core.c +++ b/trunk/drivers/staging/iio/adc/max1363_core.c @@ -557,6 +557,7 @@ static int __devinit max1363_probe(struct i2c_client *client, if (!IS_ERR(st->reg)) regulator_put(st->reg); error_free_st: + i2c_set_clientdata(client, NULL); kfree(st); error_ret: @@ -574,6 +575,7 @@ static int max1363_remove(struct i2c_client *client) regulator_disable(st->reg); regulator_put(st->reg); } + i2c_set_clientdata(client, NULL); kfree(st); return 0; diff --git a/trunk/drivers/staging/iio/industrialio-core.c b/trunk/drivers/staging/iio/industrialio-core.c index 37f58f66e491..1d77082c8531 100644 --- a/trunk/drivers/staging/iio/industrialio-core.c +++ b/trunk/drivers/staging/iio/industrialio-core.c @@ -537,6 +537,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *dev_info) sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs); } +/* Return a negative errno on failure */ int iio_get_new_idr_val(struct idr *this_idr) { int ret; @@ -660,7 +661,7 @@ static int iio_device_register_eventset(struct iio_dev *dev_info) for (i = 0; i < dev_info->num_interrupt_lines; i++) { dev_info->event_interfaces[i].owner = dev_info->driver_module; ret = iio_get_new_idr_val(&iio_event_idr); - if (ret) + if (ret < 0) goto error_free_setup_ev_ints; else dev_info->event_interfaces[i].id = ret; diff --git a/trunk/drivers/staging/iio/light/tsl2563.c b/trunk/drivers/staging/iio/light/tsl2563.c index 1ba4aa392f6e..8770a00e3652 100644 --- a/trunk/drivers/staging/iio/light/tsl2563.c +++ b/trunk/drivers/staging/iio/light/tsl2563.c @@ -682,6 +682,7 @@ static int __devinit tsl2563_probe(struct i2c_client *client, fail2: iio_device_unregister(chip->indio_dev); fail1: + i2c_set_clientdata(client, NULL); kfree(chip); return err; } @@ -692,6 +693,7 @@ static int tsl2563_remove(struct i2c_client *client) iio_device_unregister(chip->indio_dev); + i2c_set_clientdata(client, NULL); kfree(chip); return 0; } diff --git a/trunk/drivers/staging/iio/ring_sw.c b/trunk/drivers/staging/iio/ring_sw.c index b104c3d9c35e..cf22c091668c 100644 --- a/trunk/drivers/staging/iio/ring_sw.c +++ b/trunk/drivers/staging/iio/ring_sw.c @@ -293,7 +293,7 @@ int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring, return -EAGAIN; memcpy(data, last_written_p_copy, ring->buf.bpd); - if (unlikely(ring->last_written_p >= last_written_p_copy)) + if (unlikely(ring->last_written_p != last_written_p_copy)) goto again; iio_unmark_sw_rb_in_use(&ring->buf); diff --git a/trunk/drivers/staging/octeon/cvmx-helper-board.c b/trunk/drivers/staging/octeon/cvmx-helper-board.c index 3085e38a6f99..00a555b83354 100644 --- a/trunk/drivers/staging/octeon/cvmx-helper-board.c +++ b/trunk/drivers/staging/octeon/cvmx-helper-board.c @@ -153,6 +153,14 @@ int cvmx_helper_board_get_mii_address(int ipd_port) * through switch. */ return -1; + + case CVMX_BOARD_TYPE_CUST_WSX16: + if (ipd_port >= 0 && ipd_port <= 3) + return ipd_port; + else if (ipd_port >= 16 && ipd_port <= 19) + return ipd_port - 16 + 4; + else + return -1; } /* Some unknown board. Somebody forgot to update this function... */ diff --git a/trunk/drivers/staging/rt2860/usb_main_dev.c b/trunk/drivers/staging/rt2860/usb_main_dev.c index 1873a79bb033..740db0c1ac01 100644 --- a/trunk/drivers/staging/rt2860/usb_main_dev.c +++ b/trunk/drivers/staging/rt2860/usb_main_dev.c @@ -63,6 +63,7 @@ struct usb_device_id rtusb_usb_id[] = { {USB_DEVICE(0x07D1, 0x3C11)}, /* D-Link */ {USB_DEVICE(0x14B2, 0x3C07)}, /* AL */ {USB_DEVICE(0x050D, 0x8053)}, /* Belkin */ + {USB_DEVICE(0x050D, 0x825B)}, /* Belkin */ {USB_DEVICE(0x14B2, 0x3C23)}, /* Airlink */ {USB_DEVICE(0x14B2, 0x3C27)}, /* Airlink */ {USB_DEVICE(0x07AA, 0x002F)}, /* Corega */ diff --git a/trunk/drivers/staging/rtl8192su/r8192U_core.c b/trunk/drivers/staging/rtl8192su/r8192U_core.c index e16256fe595a..04d9b85f3d4c 100644 --- a/trunk/drivers/staging/rtl8192su/r8192U_core.c +++ b/trunk/drivers/staging/rtl8192su/r8192U_core.c @@ -113,14 +113,17 @@ u32 rt_global_debug_component = \ static const struct usb_device_id rtl8192_usb_id_tbl[] = { /* Realtek */ + {USB_DEVICE(0x0bda, 0x8171)}, {USB_DEVICE(0x0bda, 0x8192)}, {USB_DEVICE(0x0bda, 0x8709)}, /* Corega */ {USB_DEVICE(0x07aa, 0x0043)}, /* Belkin */ {USB_DEVICE(0x050d, 0x805E)}, + {USB_DEVICE(0x050d, 0x815F)}, /* Belkin F5D8053 v6 */ /* Sitecom */ {USB_DEVICE(0x0df6, 0x0031)}, + {USB_DEVICE(0x0df6, 0x004b)}, /* WL-349 */ /* EnGenius */ {USB_DEVICE(0x1740, 0x9201)}, /* Dlink */ diff --git a/trunk/drivers/staging/usbip/usbip_event.c b/trunk/drivers/staging/usbip/usbip_event.c index 6da1021e8a65..a2566f1075d5 100644 --- a/trunk/drivers/staging/usbip/usbip_event.c +++ b/trunk/drivers/staging/usbip/usbip_event.c @@ -117,6 +117,9 @@ void usbip_stop_eh(struct usbip_device *ud) { struct usbip_task *eh = &ud->eh; + if (eh->thread == current) + return; /* do not wait for myself */ + wait_for_completion(&eh->thread_done); usbip_dbg_eh("usbip_eh has finished\n"); } diff --git a/trunk/drivers/staging/vme/bridges/vme_tsi148.c b/trunk/drivers/staging/vme/bridges/vme_tsi148.c index 68f24425977f..783051f59f19 100644 --- a/trunk/drivers/staging/vme/bridges/vme_tsi148.c +++ b/trunk/drivers/staging/vme/bridges/vme_tsi148.c @@ -2455,9 +2455,10 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_info(&pdev->dev, "VME Write and flush and error check is %s\n", err_chk ? "enabled" : "disabled"); - if (tsi148_crcsr_init(tsi148_bridge, pdev)) + if (tsi148_crcsr_init(tsi148_bridge, pdev)) { dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); goto err_crcsr; + } retval = vme_register_bridge(tsi148_bridge); if (retval != 0) { diff --git a/trunk/drivers/usb/class/cdc-acm.c b/trunk/drivers/usb/class/cdc-acm.c index be6331e2c276..5e1a253b08a0 100644 --- a/trunk/drivers/usb/class/cdc-acm.c +++ b/trunk/drivers/usb/class/cdc-acm.c @@ -1542,6 +1542,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */ + .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ + }, /* Nokia S60 phones expose two ACM channels. The first is * a modem and is picked up by the standard AT-command diff --git a/trunk/drivers/usb/core/Kconfig b/trunk/drivers/usb/core/Kconfig index 97a819c23ef3..7e594449600e 100644 --- a/trunk/drivers/usb/core/Kconfig +++ b/trunk/drivers/usb/core/Kconfig @@ -109,7 +109,7 @@ config USB_SUSPEND config USB_OTG bool depends on USB && EXPERIMENTAL - select USB_SUSPEND + depends on USB_SUSPEND default n diff --git a/trunk/drivers/usb/core/generic.c b/trunk/drivers/usb/core/generic.c index bdf87a8414a1..2c95153c0f24 100644 --- a/trunk/drivers/usb/core/generic.c +++ b/trunk/drivers/usb/core/generic.c @@ -120,7 +120,7 @@ int usb_choose_configuration(struct usb_device *udev) * than a vendor-specific driver. */ else if (udev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC && - (!desc || desc->bInterfaceClass != + (desc && desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC)) { best = c; break; diff --git a/trunk/drivers/usb/core/inode.c b/trunk/drivers/usb/core/inode.c index 97b40ce133f0..4a6366a42129 100644 --- a/trunk/drivers/usb/core/inode.c +++ b/trunk/drivers/usb/core/inode.c @@ -515,13 +515,13 @@ static int fs_create_by_name (const char *name, mode_t mode, *dentry = NULL; mutex_lock(&parent->d_inode->i_mutex); *dentry = lookup_one_len(name, parent, strlen(name)); - if (!IS_ERR(dentry)) { + if (!IS_ERR(*dentry)) { if ((mode & S_IFMT) == S_IFDIR) error = usbfs_mkdir (parent->d_inode, *dentry, mode); else error = usbfs_create (parent->d_inode, *dentry, mode); } else - error = PTR_ERR(dentry); + error = PTR_ERR(*dentry); mutex_unlock(&parent->d_inode->i_mutex); return error; diff --git a/trunk/drivers/usb/core/usb.c b/trunk/drivers/usb/core/usb.c index 1297e9b16a51..0561430f2ede 100644 --- a/trunk/drivers/usb/core/usb.c +++ b/trunk/drivers/usb/core/usb.c @@ -718,7 +718,7 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size, EXPORT_SYMBOL_GPL(__usb_get_extra_descriptor); /** - * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP + * usb_alloc_coherent - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP * @dev: device the buffer will be used with * @size: requested buffer size * @mem_flags: affect whether allocation may block @@ -737,30 +737,30 @@ EXPORT_SYMBOL_GPL(__usb_get_extra_descriptor); * architectures where CPU caches are not DMA-coherent. On systems without * bus-snooping caches, these buffers are uncached. * - * When the buffer is no longer used, free it with usb_buffer_free(). + * When the buffer is no longer used, free it with usb_free_coherent(). */ -void *usb_buffer_alloc(struct usb_device *dev, size_t size, gfp_t mem_flags, - dma_addr_t *dma) +void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, + dma_addr_t *dma) { if (!dev || !dev->bus) return NULL; return hcd_buffer_alloc(dev->bus, size, mem_flags, dma); } -EXPORT_SYMBOL_GPL(usb_buffer_alloc); +EXPORT_SYMBOL_GPL(usb_alloc_coherent); /** - * usb_buffer_free - free memory allocated with usb_buffer_alloc() + * usb_free_coherent - free memory allocated with usb_alloc_coherent() * @dev: device the buffer was used with * @size: requested buffer size * @addr: CPU address of buffer * @dma: DMA address of buffer * * This reclaims an I/O buffer, letting it be reused. The memory must have - * been allocated using usb_buffer_alloc(), and the parameters must match + * been allocated using usb_alloc_coherent(), and the parameters must match * those provided in that allocation request. */ -void usb_buffer_free(struct usb_device *dev, size_t size, void *addr, - dma_addr_t dma) +void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, + dma_addr_t dma) { if (!dev || !dev->bus) return; @@ -768,7 +768,7 @@ void usb_buffer_free(struct usb_device *dev, size_t size, void *addr, return; hcd_buffer_free(dev->bus, size, addr, dma); } -EXPORT_SYMBOL_GPL(usb_buffer_free); +EXPORT_SYMBOL_GPL(usb_free_coherent); /** * usb_buffer_map - create DMA mapping(s) for an urb diff --git a/trunk/drivers/usb/gadget/s3c-hsotg.c b/trunk/drivers/usb/gadget/s3c-hsotg.c index 124a8ccfdcda..1f73b485732d 100644 --- a/trunk/drivers/usb/gadget/s3c-hsotg.c +++ b/trunk/drivers/usb/gadget/s3c-hsotg.c @@ -2145,6 +2145,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, u32 epctrl; u32 mps; int dir_in; + int ret = 0; dev_dbg(hsotg->dev, "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", @@ -2196,7 +2197,8 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_ISOC: dev_err(hsotg->dev, "no current ISOC support\n"); - return -EINVAL; + ret = -EINVAL; + goto out; case USB_ENDPOINT_XFER_BULK: epctrl |= S3C_DxEPCTL_EPType_Bulk; @@ -2235,8 +2237,9 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, /* enable the endpoint interrupt */ s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); +out: spin_unlock_irqrestore(&hs_ep->lock, flags); - return 0; + return ret; } static int s3c_hsotg_ep_disable(struct usb_ep *ep) diff --git a/trunk/drivers/usb/host/ohci-at91.c b/trunk/drivers/usb/host/ohci-at91.c index 68b83ab70719..944291e10f97 100644 --- a/trunk/drivers/usb/host/ohci-at91.c +++ b/trunk/drivers/usb/host/ohci-at91.c @@ -331,6 +331,8 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) */ if (at91_suspend_entering_slow_clock()) { ohci_usb_reset (ohci); + /* flush the writes */ + (void) ohci_readl (ohci, &ohci->regs->control); at91_stop_clock(); } diff --git a/trunk/drivers/usb/host/ohci-hub.c b/trunk/drivers/usb/host/ohci-hub.c index 32bbce9718f0..65cac8cc8921 100644 --- a/trunk/drivers/usb/host/ohci-hub.c +++ b/trunk/drivers/usb/host/ohci-hub.c @@ -697,7 +697,7 @@ static int ohci_hub_control ( u16 wLength ) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ports = hcd_to_bus (hcd)->root_hub->maxchild; + int ports = ohci->num_ports; u32 temp; int retval = 0; diff --git a/trunk/drivers/usb/host/oxu210hp-hcd.c b/trunk/drivers/usb/host/oxu210hp-hcd.c index 50f57f468836..e62b30b3e429 100644 --- a/trunk/drivers/usb/host/oxu210hp-hcd.c +++ b/trunk/drivers/usb/host/oxu210hp-hcd.c @@ -660,13 +660,13 @@ static struct ehci_qh *oxu_qh_alloc(struct oxu_hcd *oxu) if (qh->dummy == NULL) { oxu_dbg(oxu, "no dummy td\n"); oxu->qh_used[i] = 0; - - return NULL; + qh = NULL; + goto unlock; } oxu->qh_used[i] = 1; } - +unlock: spin_unlock(&oxu->mem_lock); return qh; diff --git a/trunk/drivers/usb/host/sl811-hcd.c b/trunk/drivers/usb/host/sl811-hcd.c index e11cc3aa4b82..3b867a8af7b2 100644 --- a/trunk/drivers/usb/host/sl811-hcd.c +++ b/trunk/drivers/usb/host/sl811-hcd.c @@ -720,10 +720,10 @@ static irqreturn_t sl811h_irq(struct usb_hcd *hcd) /* port status seems weird until after reset, so * force the reset and make khubd clean up later. */ - if (sl811->stat_insrmv & 1) - sl811->port1 |= 1 << USB_PORT_FEAT_CONNECTION; - else + if (irqstat & SL11H_INTMASK_RD) sl811->port1 &= ~(1 << USB_PORT_FEAT_CONNECTION); + else + sl811->port1 |= 1 << USB_PORT_FEAT_CONNECTION; sl811->port1 |= 1 << USB_PORT_FEAT_C_CONNECTION; diff --git a/trunk/drivers/usb/host/xhci-mem.c b/trunk/drivers/usb/host/xhci-mem.c index c09539bad1ee..d64f5724bfc4 100644 --- a/trunk/drivers/usb/host/xhci-mem.c +++ b/trunk/drivers/usb/host/xhci-mem.c @@ -582,6 +582,19 @@ static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev, return EP_INTERVAL(interval); } +/* The "Mult" field in the endpoint context is only set for SuperSpeed devices. + * High speed endpoint descriptors can define "the number of additional + * transaction opportunities per microframe", but that goes in the Max Burst + * endpoint context field. + */ +static inline u32 xhci_get_endpoint_mult(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + if (udev->speed != USB_SPEED_SUPER || !ep->ss_ep_comp) + return 0; + return ep->ss_ep_comp->desc.bmAttributes; +} + static inline u32 xhci_get_endpoint_type(struct usb_device *udev, struct usb_host_endpoint *ep) { @@ -612,6 +625,36 @@ static inline u32 xhci_get_endpoint_type(struct usb_device *udev, return type; } +/* Return the maximum endpoint service interval time (ESIT) payload. + * Basically, this is the maxpacket size, multiplied by the burst size + * and mult size. + */ +static inline u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + int max_burst; + int max_packet; + + /* Only applies for interrupt or isochronous endpoints */ + if (usb_endpoint_xfer_control(&ep->desc) || + usb_endpoint_xfer_bulk(&ep->desc)) + return 0; + + if (udev->speed == USB_SPEED_SUPER) { + if (ep->ss_ep_comp) + return ep->ss_ep_comp->desc.wBytesPerInterval; + xhci_warn(xhci, "WARN no SS endpoint companion descriptor.\n"); + /* Assume no bursts, no multiple opportunities to send. */ + return ep->desc.wMaxPacketSize; + } + + max_packet = ep->desc.wMaxPacketSize & 0x3ff; + max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11; + /* A 0 in max burst means 1 transfer per ESIT */ + return max_packet * (max_burst + 1); +} + int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, @@ -623,6 +666,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_ring *ep_ring; unsigned int max_packet; unsigned int max_burst; + u32 max_esit_payload; ep_index = xhci_get_endpoint_index(&ep->desc); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); @@ -644,6 +688,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep); + ep_ctx->ep_info |= EP_MULT(xhci_get_endpoint_mult(udev, ep)); /* FIXME dig Mult and streams info out of ep companion desc */ @@ -689,6 +734,26 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, default: BUG(); } + max_esit_payload = xhci_get_max_esit_payload(xhci, udev, ep); + ep_ctx->tx_info = MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload); + + /* + * XXX no idea how to calculate the average TRB buffer length for bulk + * endpoints, as the driver gives us no clue how big each scatter gather + * list entry (or buffer) is going to be. + * + * For isochronous and interrupt endpoints, we set it to the max + * available, until we have new API in the USB core to allow drivers to + * declare how much bandwidth they actually need. + * + * Normally, it would be calculated by taking the total of the buffer + * lengths in the TD and then dividing by the number of TRBs in a TD, + * including link TRBs, No-op TRBs, and Event data TRBs. Since we don't + * use Event Data TRBs, and we don't chain in a link TRB on short + * transfers, we're basically dividing by 1. + */ + ep_ctx->tx_info |= AVG_TRB_LENGTH_FOR_EP(max_esit_payload); + /* FIXME Debug endpoint context */ return 0; } diff --git a/trunk/drivers/usb/host/xhci.h b/trunk/drivers/usb/host/xhci.h index e5eb09b2f38e..ea389e9a4931 100644 --- a/trunk/drivers/usb/host/xhci.h +++ b/trunk/drivers/usb/host/xhci.h @@ -609,6 +609,10 @@ struct xhci_ep_ctx { #define MAX_PACKET_MASK (0xffff << 16) #define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) +/* tx_info bitmasks */ +#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) +#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) + /** * struct xhci_input_control_context diff --git a/trunk/drivers/usb/musb/Kconfig b/trunk/drivers/usb/musb/Kconfig index b4c783c284ba..07fe490b44d8 100644 --- a/trunk/drivers/usb/musb/Kconfig +++ b/trunk/drivers/usb/musb/Kconfig @@ -42,7 +42,7 @@ config USB_MUSB_SOC default y if (BF52x && !BF522 && !BF523) comment "DaVinci 35x and 644x USB support" - depends on USB_MUSB_HDRC && ARCH_DAVINCI + depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx comment "OMAP 243x high speed USB support" depends on USB_MUSB_HDRC && ARCH_OMAP2430 diff --git a/trunk/drivers/usb/musb/Makefile b/trunk/drivers/usb/musb/Makefile index 85710ccc1887..3a485dabebbb 100644 --- a/trunk/drivers/usb/musb/Makefile +++ b/trunk/drivers/usb/musb/Makefile @@ -6,7 +6,7 @@ musb_hdrc-objs := musb_core.o obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o -ifeq ($(CONFIG_ARCH_DAVINCI),y) +ifeq ($(CONFIG_ARCH_DAVINCI_DMx),y) musb_hdrc-objs += davinci.o endif diff --git a/trunk/drivers/usb/musb/blackfin.c b/trunk/drivers/usb/musb/blackfin.c index 719a22d664ef..ec8d324237f6 100644 --- a/trunk/drivers/usb/musb/blackfin.c +++ b/trunk/drivers/usb/musb/blackfin.c @@ -172,13 +172,7 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci) spin_unlock_irqrestore(&musb->lock, flags); - /* REVISIT we sometimes get spurious IRQs on g_ep0 - * not clear why... fall in BF54x too. - */ - if (retval != IRQ_HANDLED) - DBG(5, "spurious?\n"); - - return IRQ_HANDLED; + return retval; } static void musb_conn_timer_handler(unsigned long _musb) diff --git a/trunk/drivers/usb/musb/davinci.c b/trunk/drivers/usb/musb/davinci.c index 29bce5c0fd10..ce2e16fee0df 100644 --- a/trunk/drivers/usb/musb/davinci.c +++ b/trunk/drivers/usb/musb/davinci.c @@ -444,6 +444,8 @@ int __init musb_platform_init(struct musb *musb) return 0; fail: + clk_disable(musb->clock); + usb_nop_xceiv_unregister(); return -ENODEV; } diff --git a/trunk/drivers/usb/musb/musb_core.c b/trunk/drivers/usb/musb/musb_core.c index 0e8b8ab1d168..705cc4ad8737 100644 --- a/trunk/drivers/usb/musb/musb_core.c +++ b/trunk/drivers/usb/musb/musb_core.c @@ -965,10 +965,8 @@ static void musb_shutdown(struct platform_device *pdev) spin_lock_irqsave(&musb->lock, flags); musb_platform_disable(musb); musb_generic_disable(musb); - if (musb->clock) { + if (musb->clock) clk_put(musb->clock); - musb->clock = NULL; - } spin_unlock_irqrestore(&musb->lock, flags); /* FIXME power down */ @@ -1853,15 +1851,6 @@ static void musb_free(struct musb *musb) put_device(musb->xceiv->dev); #endif - musb_writeb(musb->mregs, MUSB_DEVCTL, 0); - musb_platform_exit(musb); - musb_writeb(musb->mregs, MUSB_DEVCTL, 0); - - if (musb->clock) { - clk_disable(musb->clock); - clk_put(musb->clock); - } - #ifdef CONFIG_USB_MUSB_HDRC_HCD usb_put_hcd(musb_to_hcd(musb)); #else @@ -1889,8 +1878,10 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) */ if (!plat) { dev_dbg(dev, "no platform_data?\n"); - return -ENODEV; + status = -ENODEV; + goto fail0; } + switch (plat->mode) { case MUSB_HOST: #ifdef CONFIG_USB_MUSB_HDRC_HCD @@ -1912,13 +1903,16 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) #endif default: dev_err(dev, "incompatible Kconfig role setting\n"); - return -EINVAL; + status = -EINVAL; + goto fail0; } /* allocate */ musb = allocate_instance(dev, plat->config, ctrl); - if (!musb) - return -ENOMEM; + if (!musb) { + status = -ENOMEM; + goto fail0; + } spin_lock_init(&musb->lock); musb->board_mode = plat->mode; @@ -1936,7 +1930,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (IS_ERR(musb->clock)) { status = PTR_ERR(musb->clock); musb->clock = NULL; - goto fail; + goto fail1; } } @@ -1955,12 +1949,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) */ musb->isr = generic_interrupt; status = musb_platform_init(musb); - if (status < 0) - goto fail; + goto fail2; + if (!musb->isr) { status = -ENODEV; - goto fail2; + goto fail3; } #ifndef CONFIG_MUSB_PIO_ONLY @@ -1986,7 +1980,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) ? MUSB_CONTROLLER_MHDRC : MUSB_CONTROLLER_HDRC, musb); if (status < 0) - goto fail2; + goto fail3; #ifdef CONFIG_USB_MUSB_OTG setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); @@ -1999,7 +1993,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) { dev_err(dev, "request_irq %d failed!\n", nIrq); status = -ENODEV; - goto fail2; + goto fail3; } musb->nIrq = nIrq; /* FIXME this handles wakeup irqs wrong */ @@ -2039,8 +2033,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->xceiv->state = OTG_STATE_A_IDLE; status = usb_add_hcd(musb_to_hcd(musb), -1, 0); - if (status) - goto fail; DBG(1, "%s mode, status %d, devctl %02x %c\n", "HOST", status, @@ -2055,8 +2047,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->xceiv->state = OTG_STATE_B_IDLE; status = musb_gadget_setup(musb); - if (status) - goto fail; DBG(1, "%s mode, status %d, dev%02x\n", is_otg_enabled(musb) ? "OTG" : "PERIPHERAL", @@ -2064,12 +2054,14 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_readb(musb->mregs, MUSB_DEVCTL)); } + if (status < 0) + goto fail3; #ifdef CONFIG_SYSFS status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group); -#endif if (status) - goto fail2; + goto fail4; +#endif dev_info(dev, "USB %s mode controller at %p using %s, IRQ %d\n", ({char *s; @@ -2085,17 +2077,29 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) return 0; -fail2: +fail4: + if (!is_otg_enabled(musb) && is_host_enabled(musb)) + usb_remove_hcd(musb_to_hcd(musb)); + else + musb_gadget_cleanup(musb); + +fail3: + if (musb->irq_wake) + device_init_wakeup(dev, 0); musb_platform_exit(musb); -fail: - dev_err(musb->controller, - "musb_init_controller failed with status %d\n", status); +fail2: if (musb->clock) clk_put(musb->clock); - device_init_wakeup(dev, 0); + +fail1: + dev_err(musb->controller, + "musb_init_controller failed with status %d\n", status); + musb_free(musb); +fail0: + return status; } @@ -2132,7 +2136,6 @@ static int __init musb_probe(struct platform_device *pdev) /* clobbered by use_dma=n */ orig_dma_mask = dev->dma_mask; #endif - status = musb_init_controller(dev, irq, base); if (status < 0) iounmap(base); @@ -2155,6 +2158,10 @@ static int __exit musb_remove(struct platform_device *pdev) if (musb->board_mode == MUSB_HOST) usb_remove_hcd(musb_to_hcd(musb)); #endif + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_platform_exit(musb); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_free(musb); iounmap(ctrl_base); device_init_wakeup(&pdev->dev, 0); @@ -2176,6 +2183,7 @@ void musb_save_context(struct musb *musb) if (is_host_enabled(musb)) { musb_context.frame = musb_readw(musb_base, MUSB_FRAME); musb_context.testmode = musb_readb(musb_base, MUSB_TESTMODE); + musb_context.busctl = musb_read_ulpi_buscontrol(musb->mregs); } musb_context.power = musb_readb(musb_base, MUSB_POWER); musb_context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE); @@ -2247,6 +2255,7 @@ void musb_restore_context(struct musb *musb) if (is_host_enabled(musb)) { musb_writew(musb_base, MUSB_FRAME, musb_context.frame); musb_writeb(musb_base, MUSB_TESTMODE, musb_context.testmode); + musb_write_ulpi_buscontrol(musb->mregs, musb_context.busctl); } musb_writeb(musb_base, MUSB_POWER, musb_context.power); musb_writew(musb_base, MUSB_INTRTXE, musb_context.intrtxe); diff --git a/trunk/drivers/usb/musb/musb_core.h b/trunk/drivers/usb/musb/musb_core.h index cd9f4a9a06c6..ac17b004909b 100644 --- a/trunk/drivers/usb/musb/musb_core.h +++ b/trunk/drivers/usb/musb/musb_core.h @@ -478,7 +478,7 @@ struct musb_context_registers { u16 frame; u8 index, testmode; - u8 devctl, misc; + u8 devctl, busctl, misc; struct musb_csr_regs index_regs[MUSB_C_NUM_EPS]; }; diff --git a/trunk/drivers/usb/musb/musb_host.c b/trunk/drivers/usb/musb/musb_host.c index dec896e888db..877d20b1dff9 100644 --- a/trunk/drivers/usb/musb/musb_host.c +++ b/trunk/drivers/usb/musb/musb_host.c @@ -2042,6 +2042,7 @@ static int musb_urb_enqueue( * odd, rare, error prone, but legal. */ kfree(qh); + qh = NULL; ret = 0; } else ret = musb_schedule(musb, qh, diff --git a/trunk/drivers/usb/musb/omap2430.c b/trunk/drivers/usb/musb/omap2430.c index 490cdf15ccb6..82592633502f 100644 --- a/trunk/drivers/usb/musb/omap2430.c +++ b/trunk/drivers/usb/musb/omap2430.c @@ -331,8 +331,5 @@ int musb_platform_exit(struct musb *musb) musb_platform_suspend(musb); - clk_put(musb->clock); - musb->clock = NULL; - return 0; } diff --git a/trunk/drivers/usb/musb/tusb6010.c b/trunk/drivers/usb/musb/tusb6010.c index ab776a8d98ca..60d3938cafcf 100644 --- a/trunk/drivers/usb/musb/tusb6010.c +++ b/trunk/drivers/usb/musb/tusb6010.c @@ -29,6 +29,19 @@ static void tusb_source_power(struct musb *musb, int is_on); #define TUSB_REV_MAJOR(reg_val) ((reg_val >> 4) & 0xf) #define TUSB_REV_MINOR(reg_val) (reg_val & 0xf) +#ifdef CONFIG_PM +/* REVISIT: These should be only needed if somebody implements off idle */ +void musb_platform_save_context(struct musb *musb, + struct musb_context_registers *musb_context) +{ +} + +void musb_platform_restore_context(struct musb *musb, + struct musb_context_registers *musb_context) +{ +} +#endif + /* * Checks the revision. We need to use the DMA register as 3.0 does not * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV. diff --git a/trunk/drivers/usb/musb/tusb6010_omap.c b/trunk/drivers/usb/musb/tusb6010_omap.c index 5afa070d7dc9..c061a88f2b0f 100644 --- a/trunk/drivers/usb/musb/tusb6010_omap.c +++ b/trunk/drivers/usb/musb/tusb6010_omap.c @@ -39,7 +39,7 @@ struct tusb_omap_dma_ch { struct tusb_omap_dma *tusb_dma; - void __iomem *dma_addr; + dma_addr_t dma_addr; u32 len; u16 packet_sz; @@ -126,6 +126,7 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) struct tusb_omap_dma_ch *chdat = to_chdat(channel); struct tusb_omap_dma *tusb_dma = chdat->tusb_dma; struct musb *musb = chdat->musb; + struct device *dev = musb->controller; struct musb_hw_ep *hw_ep = chdat->hw_ep; void __iomem *ep_conf = hw_ep->conf; void __iomem *mbase = musb->mregs; @@ -173,13 +174,15 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) DBG(3, "Using PIO for remaining %lu bytes\n", pio); buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len; if (chdat->tx) { - dma_cache_maint(phys_to_virt((u32)chdat->dma_addr), - chdat->transfer_len, DMA_TO_DEVICE); + dma_unmap_single(dev, chdat->dma_addr, + chdat->transfer_len, + DMA_TO_DEVICE); musb_write_fifo(hw_ep, pio, buf); } else { + dma_unmap_single(dev, chdat->dma_addr, + chdat->transfer_len, + DMA_FROM_DEVICE); musb_read_fifo(hw_ep, pio, buf); - dma_cache_maint(phys_to_virt((u32)chdat->dma_addr), - chdat->transfer_len, DMA_FROM_DEVICE); } channel->actual_len += pio; } @@ -224,6 +227,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, struct tusb_omap_dma_ch *chdat = to_chdat(channel); struct tusb_omap_dma *tusb_dma = chdat->tusb_dma; struct musb *musb = chdat->musb; + struct device *dev = musb->controller; struct musb_hw_ep *hw_ep = chdat->hw_ep; void __iomem *mbase = musb->mregs; void __iomem *ep_conf = hw_ep->conf; @@ -299,14 +303,16 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, chdat->packet_sz = packet_sz; chdat->len = len; channel->actual_len = 0; - chdat->dma_addr = (void __iomem *)dma_addr; + chdat->dma_addr = dma_addr; channel->status = MUSB_DMA_STATUS_BUSY; /* Since we're recycling dma areas, we need to clean or invalidate */ if (chdat->tx) - dma_cache_maint(phys_to_virt(dma_addr), len, DMA_TO_DEVICE); + dma_map_single(dev, phys_to_virt(dma_addr), len, + DMA_TO_DEVICE); else - dma_cache_maint(phys_to_virt(dma_addr), len, DMA_FROM_DEVICE); + dma_map_single(dev, phys_to_virt(dma_addr), len, + DMA_FROM_DEVICE); /* Use 16-bit transfer if dma_addr is not 32-bit aligned */ if ((dma_addr & 0x3) == 0) { diff --git a/trunk/drivers/usb/serial/option.c b/trunk/drivers/usb/serial/option.c index ca9d866672aa..84d0edad8e4f 100644 --- a/trunk/drivers/usb/serial/option.c +++ b/trunk/drivers/usb/serial/option.c @@ -305,6 +305,11 @@ static int option_resume(struct usb_serial *serial); #define ZTE_PRODUCT_CDMA_TECH 0xfffe #define ZTE_PRODUCT_AC8710 0xfff1 #define ZTE_PRODUCT_AC2726 0xfff5 +#define ZTE_PRODUCT_AC8710T 0xffff + +/* ZTE PRODUCTS -- alternate vendor ID */ +#define ZTE_VENDOR_ID2 0x1d6b +#define ZTE_PRODUCT_MF_330 0x0002 #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 @@ -373,6 +378,8 @@ static int option_resume(struct usb_serial *serial); #define HAIER_VENDOR_ID 0x201e #define HAIER_PRODUCT_CE100 0x2009 +#define CINTERION_VENDOR_ID 0x0681 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -679,6 +686,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) }, + { USB_DEVICE(ZTE_VENDOR_ID2, ZTE_PRODUCT_MF_330) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, { USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5) }, /* Yes, ALINK_VENDOR_ID */ @@ -716,6 +725,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1011)}, { USB_DEVICE(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_1012)}, + { USB_DEVICE(CINTERION_VENDOR_ID, 0x0047) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/trunk/drivers/usb/serial/pl2303.c b/trunk/drivers/usb/serial/pl2303.c index c97a0bb5b6db..c28b1607eacc 100644 --- a/trunk/drivers/usb/serial/pl2303.c +++ b/trunk/drivers/usb/serial/pl2303.c @@ -59,6 +59,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, diff --git a/trunk/drivers/usb/serial/pl2303.h b/trunk/drivers/usb/serial/pl2303.h index a352d5f3a59c..23c09b38b9ec 100644 --- a/trunk/drivers/usb/serial/pl2303.h +++ b/trunk/drivers/usb/serial/pl2303.h @@ -20,6 +20,7 @@ #define PL2303_PRODUCT_ID_ALDIGA 0x0611 #define PL2303_PRODUCT_ID_MMX 0x0612 #define PL2303_PRODUCT_ID_GPRS 0x0609 +#define PL2303_PRODUCT_ID_HCR331 0x331a #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 diff --git a/trunk/drivers/usb/serial/ti_usb_3410_5052.c b/trunk/drivers/usb/serial/ti_usb_3410_5052.c index 880e990abb07..e1bfda33f5b9 100644 --- a/trunk/drivers/usb/serial/ti_usb_3410_5052.c +++ b/trunk/drivers/usb/serial/ti_usb_3410_5052.c @@ -1735,7 +1735,7 @@ static int ti_download_firmware(struct ti_device *tdev) return -ENOENT; } if (fw_p->size > TI_FIRMWARE_BUF_SIZE) { - dev_err(&dev->dev, "%s - firmware too large %d \n", __func__, fw_p->size); + dev_err(&dev->dev, "%s - firmware too large %zu\n", __func__, fw_p->size); return -ENOENT; } diff --git a/trunk/drivers/watchdog/ep93xx_wdt.c b/trunk/drivers/watchdog/ep93xx_wdt.c index 88ed54e50f74..59359c9a5e01 100644 --- a/trunk/drivers/watchdog/ep93xx_wdt.c +++ b/trunk/drivers/watchdog/ep93xx_wdt.c @@ -244,7 +244,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" - __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + __MODULE_STRING(WDT_TIMEOUT) ")"); MODULE_AUTHOR("Ray Lehtiniemi ," "Alessandro Zummo "); diff --git a/trunk/fs/ceph/addr.c b/trunk/fs/ceph/addr.c index 412593703d1e..4b42c2bb603f 100644 --- a/trunk/fs/ceph/addr.c +++ b/trunk/fs/ceph/addr.c @@ -509,7 +509,7 @@ static void writepages_finish(struct ceph_osd_request *req, u64 bytes = 0; struct ceph_client *client = ceph_inode_to_client(inode); long writeback_stat; - unsigned issued = __ceph_caps_issued(ci, NULL); + unsigned issued = ceph_caps_issued(ci); /* parse reply */ replyhead = msg->front.iov_base; diff --git a/trunk/fs/ceph/auth.c b/trunk/fs/ceph/auth.c index f6394b94b866..818afe72e6c7 100644 --- a/trunk/fs/ceph/auth.c +++ b/trunk/fs/ceph/auth.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "types.h" #include "auth_none.h" diff --git a/trunk/fs/ceph/auth_none.h b/trunk/fs/ceph/auth_none.h index 56c05533a31c..8164df1a08be 100644 --- a/trunk/fs/ceph/auth_none.h +++ b/trunk/fs/ceph/auth_none.h @@ -1,6 +1,8 @@ #ifndef _FS_CEPH_AUTH_NONE_H #define _FS_CEPH_AUTH_NONE_H +#include + #include "auth.h" /* diff --git a/trunk/fs/ceph/auth_x.c b/trunk/fs/ceph/auth_x.c index d9001a4dc8cc..fee5a08da881 100644 --- a/trunk/fs/ceph/auth_x.c +++ b/trunk/fs/ceph/auth_x.c @@ -12,8 +12,6 @@ #include "auth.h" #include "decode.h" -struct kmem_cache *ceph_x_ticketbuf_cachep; - #define TEMP_TICKET_BUF_LEN 256 static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed); @@ -131,13 +129,12 @@ static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, char *ticket_buf; u8 struct_v; - dbuf = kmem_cache_alloc(ceph_x_ticketbuf_cachep, GFP_NOFS | GFP_ATOMIC); + dbuf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); if (!dbuf) return -ENOMEM; ret = -ENOMEM; - ticket_buf = kmem_cache_alloc(ceph_x_ticketbuf_cachep, - GFP_NOFS | GFP_ATOMIC); + ticket_buf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); if (!ticket_buf) goto out_dbuf; @@ -251,9 +248,9 @@ static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, ret = 0; out: - kmem_cache_free(ceph_x_ticketbuf_cachep, ticket_buf); + kfree(ticket_buf); out_dbuf: - kmem_cache_free(ceph_x_ticketbuf_cachep, dbuf); + kfree(dbuf); return ret; bad: @@ -605,8 +602,6 @@ static void ceph_x_destroy(struct ceph_auth_client *ac) remove_ticket_handler(ac, th); } - kmem_cache_destroy(ceph_x_ticketbuf_cachep); - kfree(ac->private); ac->private = NULL; } @@ -641,26 +636,20 @@ int ceph_x_init(struct ceph_auth_client *ac) int ret; dout("ceph_x_init %p\n", ac); + ret = -ENOMEM; xi = kzalloc(sizeof(*xi), GFP_NOFS); if (!xi) - return -ENOMEM; + goto out; - ret = -ENOMEM; - ceph_x_ticketbuf_cachep = kmem_cache_create("ceph_x_ticketbuf", - TEMP_TICKET_BUF_LEN, 8, - (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), - NULL); - if (!ceph_x_ticketbuf_cachep) - goto done_nomem; ret = -EINVAL; if (!ac->secret) { pr_err("no secret set (for auth_x protocol)\n"); - goto done_nomem; + goto out_nomem; } ret = ceph_crypto_key_unarmor(&xi->secret, ac->secret); if (ret) - goto done_nomem; + goto out_nomem; xi->starting = true; xi->ticket_handlers = RB_ROOT; @@ -670,10 +659,9 @@ int ceph_x_init(struct ceph_auth_client *ac) ac->ops = &ceph_x_ops; return 0; -done_nomem: +out_nomem: kfree(xi); - if (ceph_x_ticketbuf_cachep) - kmem_cache_destroy(ceph_x_ticketbuf_cachep); +out: return ret; } diff --git a/trunk/fs/ceph/caps.c b/trunk/fs/ceph/caps.c index aa2239fa9a3b..0c1681806867 100644 --- a/trunk/fs/ceph/caps.c +++ b/trunk/fs/ceph/caps.c @@ -1861,8 +1861,8 @@ static void kick_flushing_capsnaps(struct ceph_mds_client *mdsc, } else { pr_err("%p auth cap %p not mds%d ???\n", inode, cap, session->s_mds); - spin_unlock(&inode->i_lock); } + spin_unlock(&inode->i_lock); } } diff --git a/trunk/fs/ceph/dir.c b/trunk/fs/ceph/dir.c index ea8ee2e526aa..650d2db5ed26 100644 --- a/trunk/fs/ceph/dir.c +++ b/trunk/fs/ceph/dir.c @@ -880,7 +880,16 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry, * do_request, above). If there is no trace, we need * to do it here. */ + + /* d_move screws up d_subdirs order */ + ceph_i_clear(new_dir, CEPH_I_COMPLETE); + d_move(old_dentry, new_dentry); + + /* ensure target dentry is invalidated, despite + rehashing bug in vfs_rename_dir */ + new_dentry->d_time = jiffies; + ceph_dentry(new_dentry)->lease_shared_gen = 0; } ceph_mdsc_put_request(req); return err; diff --git a/trunk/fs/ceph/file.c b/trunk/fs/ceph/file.c index 4add3d5da2c1..ed6f19721d6e 100644 --- a/trunk/fs/ceph/file.c +++ b/trunk/fs/ceph/file.c @@ -665,7 +665,8 @@ static ssize_t ceph_sync_write(struct file *file, const char __user *data, * throw out any page cache pages in this range. this * may block. */ - truncate_inode_pages_range(inode->i_mapping, pos, pos+len); + truncate_inode_pages_range(inode->i_mapping, pos, + (pos+len) | (PAGE_CACHE_SIZE-1)); } else { pages = alloc_page_vector(num_pages); if (IS_ERR(pages)) { diff --git a/trunk/fs/ceph/inode.c b/trunk/fs/ceph/inode.c index 26f883c275e8..261f3e6c0bcf 100644 --- a/trunk/fs/ceph/inode.c +++ b/trunk/fs/ceph/inode.c @@ -997,6 +997,10 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, dn, dn->d_name.len, dn->d_name.name); dout("fill_trace doing d_move %p -> %p\n", req->r_old_dentry, dn); + + /* d_move screws up d_subdirs order */ + ceph_i_clear(dir, CEPH_I_COMPLETE); + d_move(req->r_old_dentry, dn); dout(" src %p '%.*s' dst %p '%.*s'\n", req->r_old_dentry, diff --git a/trunk/fs/ceph/messenger.c b/trunk/fs/ceph/messenger.c index cdaaa131add3..509f57d9ccb3 100644 --- a/trunk/fs/ceph/messenger.c +++ b/trunk/fs/ceph/messenger.c @@ -1334,6 +1334,7 @@ static int read_partial_message(struct ceph_connection *con) unsigned front_len, middle_len, data_len, data_off; int datacrc = con->msgr->nocrc; int skip; + u64 seq; dout("read_partial_message con %p msg %p\n", con, m); @@ -1368,6 +1369,25 @@ static int read_partial_message(struct ceph_connection *con) return -EIO; data_off = le16_to_cpu(con->in_hdr.data_off); + /* verify seq# */ + seq = le64_to_cpu(con->in_hdr.seq); + if ((s64)seq - (s64)con->in_seq < 1) { + pr_info("skipping %s%lld %s seq %lld, expected %lld\n", + ENTITY_NAME(con->peer_name), + pr_addr(&con->peer_addr.in_addr), + seq, con->in_seq + 1); + con->in_base_pos = -front_len - middle_len - data_len - + sizeof(m->footer); + con->in_tag = CEPH_MSGR_TAG_READY; + con->in_seq++; + return 0; + } else if ((s64)seq - (s64)con->in_seq > 1) { + pr_err("read_partial_message bad seq %lld expected %lld\n", + seq, con->in_seq + 1); + con->error_msg = "bad message sequence # for incoming message"; + return -EBADMSG; + } + /* allocate message? */ if (!con->in_msg) { dout("got hdr type %d front %d data %d\n", con->in_hdr.type, @@ -1379,6 +1399,7 @@ static int read_partial_message(struct ceph_connection *con) con->in_base_pos = -front_len - middle_len - data_len - sizeof(m->footer); con->in_tag = CEPH_MSGR_TAG_READY; + con->in_seq++; return 0; } if (IS_ERR(con->in_msg)) { @@ -2030,6 +2051,7 @@ void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) ceph_msg_put(con->in_msg); con->in_msg = NULL; con->in_tag = CEPH_MSGR_TAG_READY; + con->in_seq++; } else { dout("con_revoke_pages %p msg %p pages %p no-op\n", con, con->in_msg, msg); diff --git a/trunk/fs/ceph/snap.c b/trunk/fs/ceph/snap.c index 2b881262ef67..d5114db70453 100644 --- a/trunk/fs/ceph/snap.c +++ b/trunk/fs/ceph/snap.c @@ -869,16 +869,20 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc, continue; ci = ceph_inode(inode); spin_lock(&inode->i_lock); - if (!ci->i_snap_realm) - goto split_skip_inode; - ceph_put_snap_realm(mdsc, ci->i_snap_realm); - spin_lock(&realm->inodes_with_caps_lock); - list_add(&ci->i_snap_realm_item, - &realm->inodes_with_caps); - ci->i_snap_realm = realm; - spin_unlock(&realm->inodes_with_caps_lock); - ceph_get_snap_realm(mdsc, realm); -split_skip_inode: + if (list_empty(&ci->i_snap_realm_item)) { + struct ceph_snap_realm *oldrealm = + ci->i_snap_realm; + + dout(" moving %p to split realm %llx %p\n", + inode, realm->ino, realm); + spin_lock(&realm->inodes_with_caps_lock); + list_add(&ci->i_snap_realm_item, + &realm->inodes_with_caps); + ci->i_snap_realm = realm; + spin_unlock(&realm->inodes_with_caps_lock); + ceph_get_snap_realm(mdsc, realm); + ceph_put_snap_realm(mdsc, oldrealm); + } spin_unlock(&inode->i_lock); iput(inode); } diff --git a/trunk/fs/ceph/super.c b/trunk/fs/ceph/super.c index 75d02eaa1279..f888cf487b7c 100644 --- a/trunk/fs/ceph/super.c +++ b/trunk/fs/ceph/super.c @@ -996,9 +996,10 @@ static int __init init_ceph(void) if (ret) goto out_icache; - pr_info("loaded %d.%d.%d (mon/mds/osd proto %d/%d/%d)\n", - CEPH_VERSION_MAJOR, CEPH_VERSION_MINOR, CEPH_VERSION_PATCH, - CEPH_MONC_PROTOCOL, CEPH_MDSC_PROTOCOL, CEPH_OSDC_PROTOCOL); + pr_info("loaded (mon/mds/osd proto %d/%d/%d, osdmap %d/%d %d/%d)\n", + CEPH_MONC_PROTOCOL, CEPH_MDSC_PROTOCOL, CEPH_OSDC_PROTOCOL, + CEPH_OSDMAP_VERSION, CEPH_OSDMAP_VERSION_EXT, + CEPH_OSDMAP_INC_VERSION, CEPH_OSDMAP_INC_VERSION_EXT); return 0; out_icache: diff --git a/trunk/fs/ceph/super.h b/trunk/fs/ceph/super.h index e30dfbb056c3..13513b80d87f 100644 --- a/trunk/fs/ceph/super.h +++ b/trunk/fs/ceph/super.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/trunk/fs/nilfs2/super.c b/trunk/fs/nilfs2/super.c index 0cdbc5e7655a..48145f505a6a 100644 --- a/trunk/fs/nilfs2/super.c +++ b/trunk/fs/nilfs2/super.c @@ -749,6 +749,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, sb->s_export_op = &nilfs_export_ops; sb->s_root = NULL; sb->s_time_gran = 1; + sb->s_bdi = nilfs->ns_bdi; err = load_nilfs(nilfs, sbi); if (err) diff --git a/trunk/fs/notify/inotify/Kconfig b/trunk/fs/notify/inotify/Kconfig index 3e56dbffe729..b3a159b21cfd 100644 --- a/trunk/fs/notify/inotify/Kconfig +++ b/trunk/fs/notify/inotify/Kconfig @@ -15,6 +15,7 @@ config INOTIFY config INOTIFY_USER bool "Inotify support for userspace" + select ANON_INODES select FSNOTIFY default y ---help--- diff --git a/trunk/fs/ocfs2/buffer_head_io.c b/trunk/fs/ocfs2/buffer_head_io.c index ecebb2276790..f9d5d3ffc75a 100644 --- a/trunk/fs/ocfs2/buffer_head_io.c +++ b/trunk/fs/ocfs2/buffer_head_io.c @@ -406,6 +406,7 @@ int ocfs2_write_super_or_backup(struct ocfs2_super *osb, struct buffer_head *bh) { int ret = 0; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; mlog_entry_void(); @@ -425,6 +426,7 @@ int ocfs2_write_super_or_backup(struct ocfs2_super *osb, get_bh(bh); /* for end_buffer_write_sync() */ bh->b_end_io = end_buffer_write_sync; + ocfs2_compute_meta_ecc(osb->sb, bh->b_data, &di->i_check); submit_bh(WRITE, bh); wait_on_buffer(bh); diff --git a/trunk/fs/ocfs2/dlm/dlmast.c b/trunk/fs/ocfs2/dlm/dlmast.c index a795eb91f4ea..12d5eb78a11a 100644 --- a/trunk/fs/ocfs2/dlm/dlmast.c +++ b/trunk/fs/ocfs2/dlm/dlmast.c @@ -184,9 +184,8 @@ static void dlm_update_lvb(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, BUG_ON(!lksb); /* only updates if this node masters the lockres */ + spin_lock(&res->spinlock); if (res->owner == dlm->node_num) { - - spin_lock(&res->spinlock); /* check the lksb flags for the direction */ if (lksb->flags & DLM_LKSB_GET_LVB) { mlog(0, "getting lvb from lockres for %s node\n", @@ -201,8 +200,8 @@ static void dlm_update_lvb(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, * here. In the future we might want to clear it at the time * the put is actually done. */ - spin_unlock(&res->spinlock); } + spin_unlock(&res->spinlock); /* reset any lvb flags on the lksb */ lksb->flags &= ~(DLM_LKSB_PUT_LVB|DLM_LKSB_GET_LVB); diff --git a/trunk/fs/ocfs2/dlmfs/dlmfs.c b/trunk/fs/ocfs2/dlmfs/dlmfs.c index 1b0de157a08c..b83d6107a1f5 100644 --- a/trunk/fs/ocfs2/dlmfs/dlmfs.c +++ b/trunk/fs/ocfs2/dlmfs/dlmfs.c @@ -112,20 +112,20 @@ MODULE_PARM_DESC(capabilities, DLMFS_CAPABILITIES); * O_RDONLY -> PRMODE level * O_WRONLY -> EXMODE level * - * O_NONBLOCK -> LKM_NOQUEUE + * O_NONBLOCK -> NOQUEUE */ static int dlmfs_decode_open_flags(int open_flags, int *level, int *flags) { if (open_flags & (O_WRONLY|O_RDWR)) - *level = LKM_EXMODE; + *level = DLM_LOCK_EX; else - *level = LKM_PRMODE; + *level = DLM_LOCK_PR; *flags = 0; if (open_flags & O_NONBLOCK) - *flags |= LKM_NOQUEUE; + *flags |= DLM_LKF_NOQUEUE; return 0; } @@ -166,7 +166,7 @@ static int dlmfs_file_open(struct inode *inode, * to be able userspace to be able to distinguish a * valid lock request from one that simply couldn't be * granted. */ - if (flags & LKM_NOQUEUE && status == -EAGAIN) + if (flags & DLM_LKF_NOQUEUE && status == -EAGAIN) status = -ETXTBSY; kfree(fp); goto bail; @@ -193,7 +193,7 @@ static int dlmfs_file_release(struct inode *inode, status = 0; if (fp) { level = fp->fp_lock_level; - if (level != LKM_IVMODE) + if (level != DLM_LOCK_IV) user_dlm_cluster_unlock(&ip->ip_lockres, level); kfree(fp); @@ -262,7 +262,7 @@ static ssize_t dlmfs_file_read(struct file *filp, if ((count + *ppos) > i_size_read(inode)) readlen = i_size_read(inode) - *ppos; else - readlen = count - *ppos; + readlen = count; lvb_buf = kmalloc(readlen, GFP_NOFS); if (!lvb_buf) diff --git a/trunk/fs/ocfs2/file.c b/trunk/fs/ocfs2/file.c index 17947dc8341e..a5fbd9cea968 100644 --- a/trunk/fs/ocfs2/file.c +++ b/trunk/fs/ocfs2/file.c @@ -684,6 +684,7 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start, if (why == RESTART_META) { mlog(0, "restarting function.\n"); restart_func = 1; + status = 0; } else { BUG_ON(why != RESTART_TRANS); @@ -1981,18 +1982,18 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, /* communicate with ocfs2_dio_end_io */ ocfs2_iocb_set_rw_locked(iocb, rw_level); - if (direct_io) { - ret = generic_segment_checks(iov, &nr_segs, &ocount, - VERIFY_READ); - if (ret) - goto out_dio; + ret = generic_segment_checks(iov, &nr_segs, &ocount, + VERIFY_READ); + if (ret) + goto out_dio; - count = ocount; - ret = generic_write_checks(file, ppos, &count, - S_ISBLK(inode->i_mode)); - if (ret) - goto out_dio; + count = ocount; + ret = generic_write_checks(file, ppos, &count, + S_ISBLK(inode->i_mode)); + if (ret) + goto out_dio; + if (direct_io) { written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos, ppos, count, ocount); if (written < 0) { @@ -2007,7 +2008,10 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, goto out_dio; } } else { - written = __generic_file_aio_write(iocb, iov, nr_segs, ppos); + current->backing_dev_info = file->f_mapping->backing_dev_info; + written = generic_file_buffered_write(iocb, iov, nr_segs, *ppos, + ppos, count, 0); + current->backing_dev_info = NULL; } out_dio: @@ -2021,9 +2025,9 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, if (ret < 0) written = ret; - if (!ret && (old_size != i_size_read(inode) || - old_clusters != OCFS2_I(inode)->ip_clusters || - has_refcount)) { + if (!ret && ((old_size != i_size_read(inode)) || + (old_clusters != OCFS2_I(inode)->ip_clusters) || + has_refcount)) { ret = jbd2_journal_force_commit(osb->journal->j_journal); if (ret < 0) written = ret; diff --git a/trunk/fs/ocfs2/inode.c b/trunk/fs/ocfs2/inode.c index 07cc8bb68b6d..af189887201c 100644 --- a/trunk/fs/ocfs2/inode.c +++ b/trunk/fs/ocfs2/inode.c @@ -558,6 +558,7 @@ static int ocfs2_truncate_for_delete(struct ocfs2_super *osb, handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); if (IS_ERR(handle)) { status = PTR_ERR(handle); + handle = NULL; mlog_errno(status); goto out; } @@ -639,11 +640,13 @@ static int ocfs2_remove_inode(struct inode *inode, goto bail_unlock; } - status = ocfs2_orphan_del(osb, handle, orphan_dir_inode, inode, - orphan_dir_bh); - if (status < 0) { - mlog_errno(status); - goto bail_commit; + if (!(OCFS2_I(inode)->ip_flags & OCFS2_INODE_SKIP_ORPHAN_DIR)) { + status = ocfs2_orphan_del(osb, handle, orphan_dir_inode, inode, + orphan_dir_bh); + if (status < 0) { + mlog_errno(status); + goto bail_commit; + } } /* set the inodes dtime */ @@ -722,38 +725,39 @@ static void ocfs2_signal_wipe_completion(struct ocfs2_super *osb, static int ocfs2_wipe_inode(struct inode *inode, struct buffer_head *di_bh) { - int status, orphaned_slot; + int status, orphaned_slot = -1; struct inode *orphan_dir_inode = NULL; struct buffer_head *orphan_dir_bh = NULL; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - struct ocfs2_dinode *di; + struct ocfs2_dinode *di = (struct ocfs2_dinode *) di_bh->b_data; - di = (struct ocfs2_dinode *) di_bh->b_data; - orphaned_slot = le16_to_cpu(di->i_orphaned_slot); + if (!(OCFS2_I(inode)->ip_flags & OCFS2_INODE_SKIP_ORPHAN_DIR)) { + orphaned_slot = le16_to_cpu(di->i_orphaned_slot); - status = ocfs2_check_orphan_recovery_state(osb, orphaned_slot); - if (status) - return status; + status = ocfs2_check_orphan_recovery_state(osb, orphaned_slot); + if (status) + return status; - orphan_dir_inode = ocfs2_get_system_file_inode(osb, - ORPHAN_DIR_SYSTEM_INODE, - orphaned_slot); - if (!orphan_dir_inode) { - status = -EEXIST; - mlog_errno(status); - goto bail; - } + orphan_dir_inode = ocfs2_get_system_file_inode(osb, + ORPHAN_DIR_SYSTEM_INODE, + orphaned_slot); + if (!orphan_dir_inode) { + status = -EEXIST; + mlog_errno(status); + goto bail; + } - /* Lock the orphan dir. The lock will be held for the entire - * delete_inode operation. We do this now to avoid races with - * recovery completion on other nodes. */ - mutex_lock(&orphan_dir_inode->i_mutex); - status = ocfs2_inode_lock(orphan_dir_inode, &orphan_dir_bh, 1); - if (status < 0) { - mutex_unlock(&orphan_dir_inode->i_mutex); + /* Lock the orphan dir. The lock will be held for the entire + * delete_inode operation. We do this now to avoid races with + * recovery completion on other nodes. */ + mutex_lock(&orphan_dir_inode->i_mutex); + status = ocfs2_inode_lock(orphan_dir_inode, &orphan_dir_bh, 1); + if (status < 0) { + mutex_unlock(&orphan_dir_inode->i_mutex); - mlog_errno(status); - goto bail; + mlog_errno(status); + goto bail; + } } /* we do this while holding the orphan dir lock because we @@ -794,6 +798,9 @@ static int ocfs2_wipe_inode(struct inode *inode, mlog_errno(status); bail_unlock_dir: + if (OCFS2_I(inode)->ip_flags & OCFS2_INODE_SKIP_ORPHAN_DIR) + return status; + ocfs2_inode_unlock(orphan_dir_inode, 1); mutex_unlock(&orphan_dir_inode->i_mutex); brelse(orphan_dir_bh); @@ -889,7 +896,8 @@ static int ocfs2_query_inode_wipe(struct inode *inode, /* Do some basic inode verification... */ di = (struct ocfs2_dinode *) di_bh->b_data; - if (!(di->i_flags & cpu_to_le32(OCFS2_ORPHANED_FL))) { + if (!(di->i_flags & cpu_to_le32(OCFS2_ORPHANED_FL)) && + !(oi->ip_flags & OCFS2_INODE_SKIP_ORPHAN_DIR)) { /* * Inodes in the orphan dir must have ORPHANED_FL. The only * inodes that come back out of the orphan dir are reflink diff --git a/trunk/fs/ocfs2/inode.h b/trunk/fs/ocfs2/inode.h index ba4fe07b293c..0b28e1921a39 100644 --- a/trunk/fs/ocfs2/inode.h +++ b/trunk/fs/ocfs2/inode.h @@ -100,6 +100,8 @@ struct ocfs2_inode_info #define OCFS2_INODE_MAYBE_ORPHANED 0x00000020 /* Does someone have the file open O_DIRECT */ #define OCFS2_INODE_OPEN_DIRECT 0x00000040 +/* Tell the inode wipe code it's not in orphan dir */ +#define OCFS2_INODE_SKIP_ORPHAN_DIR 0x00000080 static inline struct ocfs2_inode_info *OCFS2_I(struct inode *inode) { diff --git a/trunk/fs/ocfs2/namei.c b/trunk/fs/ocfs2/namei.c index b1eb50ae4097..4cbb18f26c5f 100644 --- a/trunk/fs/ocfs2/namei.c +++ b/trunk/fs/ocfs2/namei.c @@ -408,23 +408,28 @@ static int ocfs2_mknod(struct inode *dir, } } - status = ocfs2_add_entry(handle, dentry, inode, - OCFS2_I(inode)->ip_blkno, parent_fe_bh, - &lookup); - if (status < 0) { + /* + * Do this before adding the entry to the directory. We add + * also set d_op after success so that ->d_iput() will cleanup + * the dentry lock even if ocfs2_add_entry() fails below. + */ + status = ocfs2_dentry_attach_lock(dentry, inode, + OCFS2_I(dir)->ip_blkno); + if (status) { mlog_errno(status); goto leave; } + dentry->d_op = &ocfs2_dentry_ops; - status = ocfs2_dentry_attach_lock(dentry, inode, - OCFS2_I(dir)->ip_blkno); - if (status) { + status = ocfs2_add_entry(handle, dentry, inode, + OCFS2_I(inode)->ip_blkno, parent_fe_bh, + &lookup); + if (status < 0) { mlog_errno(status); goto leave; } insert_inode_hash(inode); - dentry->d_op = &ocfs2_dentry_ops; d_instantiate(dentry, inode); status = 0; leave: @@ -445,11 +450,6 @@ static int ocfs2_mknod(struct inode *dir, ocfs2_free_dir_lookup_result(&lookup); - if ((status < 0) && inode) { - clear_nlink(inode); - iput(inode); - } - if (inode_ac) ocfs2_free_alloc_context(inode_ac); @@ -459,6 +459,17 @@ static int ocfs2_mknod(struct inode *dir, if (meta_ac) ocfs2_free_alloc_context(meta_ac); + /* + * We should call iput after the i_mutex of the bitmap been + * unlocked in ocfs2_free_alloc_context, or the + * ocfs2_delete_inode will mutex_lock again. + */ + if ((status < 0) && inode) { + OCFS2_I(inode)->ip_flags |= OCFS2_INODE_SKIP_ORPHAN_DIR; + clear_nlink(inode); + iput(inode); + } + mlog_exit(status); return status; @@ -1771,22 +1782,27 @@ static int ocfs2_symlink(struct inode *dir, } } - status = ocfs2_add_entry(handle, dentry, inode, - le64_to_cpu(fe->i_blkno), parent_fe_bh, - &lookup); - if (status < 0) { + /* + * Do this before adding the entry to the directory. We add + * also set d_op after success so that ->d_iput() will cleanup + * the dentry lock even if ocfs2_add_entry() fails below. + */ + status = ocfs2_dentry_attach_lock(dentry, inode, OCFS2_I(dir)->ip_blkno); + if (status) { mlog_errno(status); goto bail; } + dentry->d_op = &ocfs2_dentry_ops; - status = ocfs2_dentry_attach_lock(dentry, inode, OCFS2_I(dir)->ip_blkno); - if (status) { + status = ocfs2_add_entry(handle, dentry, inode, + le64_to_cpu(fe->i_blkno), parent_fe_bh, + &lookup); + if (status < 0) { mlog_errno(status); goto bail; } insert_inode_hash(inode); - dentry->d_op = &ocfs2_dentry_ops; d_instantiate(dentry, inode); bail: if (status < 0 && did_quota) @@ -1811,6 +1827,7 @@ static int ocfs2_symlink(struct inode *dir, if (xattr_ac) ocfs2_free_alloc_context(xattr_ac); if ((status < 0) && inode) { + OCFS2_I(inode)->ip_flags |= OCFS2_INODE_SKIP_ORPHAN_DIR; clear_nlink(inode); iput(inode); } @@ -1976,6 +1993,7 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, } le32_add_cpu(&fe->i_flags, OCFS2_ORPHANED_FL); + OCFS2_I(inode)->ip_flags &= ~OCFS2_INODE_SKIP_ORPHAN_DIR; /* Record which orphan dir our inode now resides * in. delete_inode will use this to determine which orphan diff --git a/trunk/fs/ocfs2/refcounttree.c b/trunk/fs/ocfs2/refcounttree.c index bd96f6c7877e..5cbcd0f008fc 100644 --- a/trunk/fs/ocfs2/refcounttree.c +++ b/trunk/fs/ocfs2/refcounttree.c @@ -4083,6 +4083,9 @@ static int ocfs2_complete_reflink(struct inode *s_inode, di->i_attr = s_di->i_attr; if (preserve) { + t_inode->i_uid = s_inode->i_uid; + t_inode->i_gid = s_inode->i_gid; + t_inode->i_mode = s_inode->i_mode; di->i_uid = s_di->i_uid; di->i_gid = s_di->i_gid; di->i_mode = s_di->i_mode; diff --git a/trunk/include/linux/ftrace.h b/trunk/include/linux/ftrace.h index cc12b3c556b3..01e6adea07ec 100644 --- a/trunk/include/linux/ftrace.h +++ b/trunk/include/linux/ftrace.h @@ -504,6 +504,18 @@ extern int ftrace_dump_on_oops; #define INIT_TRACE_RECURSION #endif +#ifdef CONFIG_HW_BRANCH_TRACER + +void trace_hw_branch(u64 from, u64 to); +void trace_hw_branch_oops(void); + +#else /* CONFIG_HW_BRANCH_TRACER */ + +static inline void trace_hw_branch(u64 from, u64 to) {} +static inline void trace_hw_branch_oops(void) {} + +#endif /* CONFIG_HW_BRANCH_TRACER */ + #ifdef CONFIG_FTRACE_SYSCALLS unsigned long arch_syscall_addr(int nr); diff --git a/trunk/include/linux/hw_breakpoint.h b/trunk/include/linux/hw_breakpoint.h index a2d6ea49ec56..c70d27af03f9 100644 --- a/trunk/include/linux/hw_breakpoint.h +++ b/trunk/include/linux/hw_breakpoint.h @@ -9,22 +9,9 @@ enum { }; enum { - HW_BREAKPOINT_EMPTY = 0, - HW_BREAKPOINT_R = 1, - HW_BREAKPOINT_W = 2, - HW_BREAKPOINT_RW = HW_BREAKPOINT_R | HW_BREAKPOINT_W, - HW_BREAKPOINT_X = 4, - HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X, -}; - -enum bp_type_idx { - TYPE_INST = 0, -#ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS - TYPE_DATA = 0, -#else - TYPE_DATA = 1, -#endif - TYPE_MAX + HW_BREAKPOINT_R = 1, + HW_BREAKPOINT_W = 2, + HW_BREAKPOINT_X = 4, }; #ifdef __KERNEL__ @@ -47,12 +34,6 @@ static inline void hw_breakpoint_init(struct perf_event_attr *attr) attr->sample_period = 1; } -static inline void ptrace_breakpoint_init(struct perf_event_attr *attr) -{ - hw_breakpoint_init(attr); - attr->exclude_kernel = 1; -} - static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) { return bp->attr.bp_addr; diff --git a/trunk/include/linux/i2c.h b/trunk/include/linux/i2c.h index 0a5da639b327..6ed1d59bfb1e 100644 --- a/trunk/include/linux/i2c.h +++ b/trunk/include/linux/i2c.h @@ -355,6 +355,8 @@ struct i2c_adapter { int nr; char name[48]; struct completion dev_released; + + struct list_head userspace_clients; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) diff --git a/trunk/include/linux/mm.h b/trunk/include/linux/mm.h index fb19bb92b809..462acaf36f3a 100644 --- a/trunk/include/linux/mm.h +++ b/trunk/include/linux/mm.h @@ -19,6 +19,7 @@ struct anon_vma; struct file_ra_state; struct user_struct; struct writeback_control; +struct rlimit; #ifndef CONFIG_DISCONTIGMEM /* Don't use mapnrs, do it properly */ extern unsigned long max_mapnr; @@ -1448,6 +1449,9 @@ int vmemmap_populate_basepages(struct page *start_page, int vmemmap_populate(struct page *start_page, unsigned long pages, int node); void vmemmap_populate_print_last(void); +extern int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim, + size_t size); +extern void refund_locked_memory(struct mm_struct *mm, size_t size); enum mf_flags { MF_COUNT_INCREASED = 1 << 0, diff --git a/trunk/include/linux/perf_event.h b/trunk/include/linux/perf_event.h index ace31fbac513..bf8f3c003297 100644 --- a/trunk/include/linux/perf_event.h +++ b/trunk/include/linux/perf_event.h @@ -203,9 +203,8 @@ struct perf_event_attr { enable_on_exec : 1, /* next exec enables */ task : 1, /* trace fork/exit */ watermark : 1, /* wakeup_watermark */ - precise : 1, /* OoO invariant counter */ - __reserved_1 : 48; + __reserved_1 : 49; union { __u32 wakeup_events; /* wakeup every n events */ @@ -288,19 +287,11 @@ struct perf_event_mmap_page { __u64 data_tail; /* user-space written tail */ }; -#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) +#define PERF_RECORD_MISC_CPUMODE_MASK (3 << 0) #define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) #define PERF_RECORD_MISC_KERNEL (1 << 0) #define PERF_RECORD_MISC_USER (2 << 0) #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) -#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) -#define PERF_RECORD_MISC_GUEST_USER (5 << 0) - -#define PERF_RECORD_MISC_EXACT (1 << 14) -/* - * Reserve the last bit to indicate some extended misc field - */ -#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) struct perf_event_header { __u32 type; @@ -448,12 +439,6 @@ enum perf_callchain_context { # include #endif -struct perf_guest_info_callbacks { - int (*is_in_guest) (void); - int (*is_user_mode) (void); - unsigned long (*get_guest_ip) (void); -}; - #ifdef CONFIG_HAVE_HW_BREAKPOINT #include #endif @@ -483,17 +468,6 @@ struct perf_raw_record { void *data; }; -struct perf_branch_entry { - __u64 from; - __u64 to; - __u64 flags; -}; - -struct perf_branch_stack { - __u64 nr; - struct perf_branch_entry entries[0]; -}; - struct task_struct; /** @@ -548,6 +522,7 @@ struct pmu { * enum perf_event_active_state - the states of a event */ enum perf_event_active_state { + PERF_EVENT_STATE_FREE = -3, PERF_EVENT_STATE_ERROR = -2, PERF_EVENT_STATE_OFF = -1, PERF_EVENT_STATE_INACTIVE = 0, @@ -597,14 +572,6 @@ enum perf_group_flag { PERF_GROUP_SOFTWARE = 0x1, }; -#define SWEVENT_HLIST_BITS 8 -#define SWEVENT_HLIST_SIZE (1 << SWEVENT_HLIST_BITS) - -struct swevent_hlist { - struct hlist_head heads[SWEVENT_HLIST_SIZE]; - struct rcu_head rcu_head; -}; - /** * struct perf_event - performance event kernel representation: */ @@ -613,7 +580,6 @@ struct perf_event { struct list_head group_entry; struct list_head event_entry; struct list_head sibling_list; - struct hlist_node hlist_entry; int nr_siblings; int group_flags; struct perf_event *group_leader; @@ -761,9 +727,6 @@ struct perf_cpu_context { int active_oncpu; int max_pertask; int exclusive; - struct swevent_hlist *swevent_hlist; - struct mutex hlist_mutex; - int hlist_refcount; /* * Recursion avoidance: @@ -940,10 +903,6 @@ static inline void perf_event_mmap(struct vm_area_struct *vma) __perf_event_mmap(vma); } -extern struct perf_guest_info_callbacks *perf_guest_cbs; -extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); -extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); - extern void perf_event_comm(struct task_struct *tsk); extern void perf_event_fork(struct task_struct *tsk); @@ -1013,11 +972,6 @@ perf_sw_event(u32 event_id, u64 nr, int nmi, static inline void perf_bp_event(struct perf_event *event, void *data) { } -static inline int perf_register_guest_info_callbacks -(struct perf_guest_info_callbacks *callbacks) { return 0; } -static inline int perf_unregister_guest_info_callbacks -(struct perf_guest_info_callbacks *callbacks) { return 0; } - static inline void perf_event_mmap(struct vm_area_struct *vma) { } static inline void perf_event_comm(struct task_struct *tsk) { } static inline void perf_event_fork(struct task_struct *tsk) { } diff --git a/trunk/include/linux/ptrace.h b/trunk/include/linux/ptrace.h index 4272521e29e9..e1fb60729979 100644 --- a/trunk/include/linux/ptrace.h +++ b/trunk/include/linux/ptrace.h @@ -345,6 +345,18 @@ static inline void user_single_step_siginfo(struct task_struct *tsk, #define arch_ptrace_stop(code, info) do { } while (0) #endif +#ifndef arch_ptrace_untrace +/* + * Do machine-specific work before untracing child. + * + * This is called for a normal detach as well as from ptrace_exit() + * when the tracing task dies. + * + * Called with write_lock(&tasklist_lock) held. + */ +#define arch_ptrace_untrace(task) do { } while (0) +#endif + extern int task_current_syscall(struct task_struct *target, long *callno, unsigned long args[6], unsigned int maxargs, unsigned long *sp, unsigned long *pc); diff --git a/trunk/include/linux/sched.h b/trunk/include/linux/sched.h index e0447c64af6a..dad7f668ebf7 100644 --- a/trunk/include/linux/sched.h +++ b/trunk/include/linux/sched.h @@ -99,6 +99,7 @@ struct futex_pi_state; struct robust_list_head; struct bio_list; struct fs_struct; +struct bts_context; struct perf_event_context; /* @@ -1271,6 +1272,12 @@ struct task_struct { struct list_head ptraced; struct list_head ptrace_entry; + /* + * This is the tracer handle for the ptrace BTS extension. + * This field actually belongs to the ptracer task. + */ + struct bts_context *bts; + /* PID/PID hash table linkage. */ struct pid_link pids[PIDTYPE_MAX]; struct list_head thread_group; @@ -2116,8 +2123,10 @@ extern void set_task_comm(struct task_struct *tsk, char *from); extern char *get_task_comm(char *to, struct task_struct *tsk); #ifdef CONFIG_SMP +extern void wait_task_context_switch(struct task_struct *p); extern unsigned long wait_task_inactive(struct task_struct *, long match_state); #else +static inline void wait_task_context_switch(struct task_struct *p) {} static inline unsigned long wait_task_inactive(struct task_struct *p, long match_state) { diff --git a/trunk/include/linux/usb.h b/trunk/include/linux/usb.h index ce1323c4e47c..739f1fd1cc15 100644 --- a/trunk/include/linux/usb.h +++ b/trunk/include/linux/usb.h @@ -1085,7 +1085,7 @@ typedef void (*usb_complete_t)(struct urb *); * Alternatively, drivers may pass the URB_NO_xxx_DMA_MAP transfer flags, * which tell the host controller driver that no such mapping is needed since * the device driver is DMA-aware. For example, a device driver might - * allocate a DMA buffer with usb_buffer_alloc() or call usb_buffer_map(). + * allocate a DMA buffer with usb_alloc_coherent() or call usb_buffer_map(). * When these transfer flags are provided, host controller drivers will * attempt to use the dma addresses found in the transfer_dma and/or * setup_dma fields rather than determining a dma address themselves. @@ -1366,11 +1366,23 @@ static inline int usb_urb_dir_out(struct urb *urb) return (urb->transfer_flags & URB_DIR_MASK) == URB_DIR_OUT; } -void *usb_buffer_alloc(struct usb_device *dev, size_t size, +void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma); -void usb_buffer_free(struct usb_device *dev, size_t size, +void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, dma_addr_t dma); +/* Compatible macros while we switch over */ +static inline void *usb_buffer_alloc(struct usb_device *dev, size_t size, + gfp_t mem_flags, dma_addr_t *dma) +{ + return usb_alloc_coherent(dev, size, mem_flags, dma); +} +static inline void usb_buffer_free(struct usb_device *dev, size_t size, + void *addr, dma_addr_t dma) +{ + return usb_free_coherent(dev, size, addr, dma); +} + #if 0 struct urb *usb_buffer_map(struct urb *urb); void usb_buffer_dmasync(struct urb *urb); diff --git a/trunk/include/net/sctp/structs.h b/trunk/include/net/sctp/structs.h index ff3017744711..597f8e27aaf6 100644 --- a/trunk/include/net/sctp/structs.h +++ b/trunk/include/net/sctp/structs.h @@ -778,6 +778,7 @@ int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len, struct iovec *data); void sctp_chunk_free(struct sctp_chunk *); void *sctp_addto_chunk(struct sctp_chunk *, int len, const void *data); +void *sctp_addto_chunk_fixed(struct sctp_chunk *, int len, const void *data); struct sctp_chunk *sctp_chunkify(struct sk_buff *, const struct sctp_association *, struct sock *); diff --git a/trunk/include/net/sock.h b/trunk/include/net/sock.h index b4603cd54fcd..1ad6435f252e 100644 --- a/trunk/include/net/sock.h +++ b/trunk/include/net/sock.h @@ -74,7 +74,7 @@ printk(KERN_DEBUG msg); } while (0) #else /* Validate arguments and do nothing */ -static void inline int __attribute__ ((format (printf, 2, 3))) +static inline void __attribute__ ((format (printf, 2, 3))) SOCK_DEBUG(struct sock *sk, const char *msg, ...) { } diff --git a/trunk/include/trace/ftrace.h b/trunk/include/trace/ftrace.h index 882c64832ffe..ea6f9d4a20e9 100644 --- a/trunk/include/trace/ftrace.h +++ b/trunk/include/trace/ftrace.h @@ -758,12 +758,13 @@ __attribute__((section("_ftrace_events"))) event_##call = { \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ static notrace void \ perf_trace_templ_##call(struct ftrace_event_call *event_call, \ - struct pt_regs *__regs, proto) \ + proto) \ { \ struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ struct ftrace_raw_##call *entry; \ u64 __addr = 0, __count = 1; \ unsigned long irq_flags; \ + struct pt_regs *__regs; \ int __entry_size; \ int __data_size; \ int rctx; \ @@ -784,22 +785,20 @@ perf_trace_templ_##call(struct ftrace_event_call *event_call, \ \ { assign; } \ \ + __regs = &__get_cpu_var(perf_trace_regs); \ + perf_fetch_caller_regs(__regs, 2); \ + \ perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \ __count, irq_flags, __regs); \ } #undef DEFINE_EVENT -#define DEFINE_EVENT(template, call, proto, args) \ -static notrace void perf_trace_##call(proto) \ -{ \ - struct ftrace_event_call *event_call = &event_##call; \ - struct pt_regs *__regs = &get_cpu_var(perf_trace_regs); \ - \ - perf_fetch_caller_regs(__regs, 1); \ - \ - perf_trace_templ_##template(event_call, __regs, args); \ - \ - put_cpu_var(perf_trace_regs); \ +#define DEFINE_EVENT(template, call, proto, args) \ +static notrace void perf_trace_##call(proto) \ +{ \ + struct ftrace_event_call *event_call = &event_##call; \ + \ + perf_trace_templ_##template(event_call, args); \ } #undef DEFINE_EVENT_PRINT diff --git a/trunk/kernel/cgroup_freezer.c b/trunk/kernel/cgroup_freezer.c index da5e13975531..e5c0244962b0 100644 --- a/trunk/kernel/cgroup_freezer.c +++ b/trunk/kernel/cgroup_freezer.c @@ -205,9 +205,12 @@ static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task) * No lock is needed, since the task isn't on tasklist yet, * so it can't be moved to another cgroup, which means the * freezer won't be removed and will be valid during this - * function call. + * function call. Nevertheless, apply RCU read-side critical + * section to suppress RCU lockdep false positives. */ + rcu_read_lock(); freezer = task_freezer(task); + rcu_read_unlock(); /* * The root cgroup is non-freezable, so we can skip the diff --git a/trunk/kernel/fork.c b/trunk/kernel/fork.c index 5d3592deaf71..44b0791b0a2e 100644 --- a/trunk/kernel/fork.c +++ b/trunk/kernel/fork.c @@ -1111,6 +1111,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->memcg_batch.do_batch = 0; p->memcg_batch.memcg = NULL; #endif + + p->bts = NULL; + p->stack_start = stack_start; /* Perform scheduler related setup. Assign this task to a CPU. */ diff --git a/trunk/kernel/hw_breakpoint.c b/trunk/kernel/hw_breakpoint.c index 7a56b22e0602..03808ed342a6 100644 --- a/trunk/kernel/hw_breakpoint.c +++ b/trunk/kernel/hw_breakpoint.c @@ -40,29 +40,23 @@ #include #include #include -#include #include #include #include - /* * Constraints data */ /* Number of pinned cpu breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); +static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned); /* Number of pinned task breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int *, nr_task_bp_pinned[TYPE_MAX]); +static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[HBP_NUM]); /* Number of non-pinned cpu/task breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); - -static int nr_slots[TYPE_MAX]; - -static int constraints_initialized; +static DEFINE_PER_CPU(unsigned int, nr_bp_flexible); /* Gather the number of total pinned and un-pinned bp in a cpuset */ struct bp_busy_slots { @@ -73,29 +67,16 @@ struct bp_busy_slots { /* Serialize accesses to the above constraints */ static DEFINE_MUTEX(nr_bp_mutex); -__weak int hw_breakpoint_weight(struct perf_event *bp) -{ - return 1; -} - -static inline enum bp_type_idx find_slot_idx(struct perf_event *bp) -{ - if (bp->attr.bp_type & HW_BREAKPOINT_RW) - return TYPE_DATA; - - return TYPE_INST; -} - /* * Report the maximum number of pinned breakpoints a task * have in this cpu */ -static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) +static unsigned int max_task_bp_pinned(int cpu) { int i; - unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); + unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); - for (i = nr_slots[type] - 1; i >= 0; i--) { + for (i = HBP_NUM -1; i >= 0; i--) { if (tsk_pinned[i] > 0) return i + 1; } @@ -103,7 +84,7 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) return 0; } -static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type) +static int task_bp_pinned(struct task_struct *tsk) { struct perf_event_context *ctx = tsk->perf_event_ctxp; struct list_head *list; @@ -124,8 +105,7 @@ static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type) */ list_for_each_entry(bp, list, event_entry) { if (bp->attr.type == PERF_TYPE_BREAKPOINT) - if (find_slot_idx(bp) == type) - count += hw_breakpoint_weight(bp); + count++; } raw_spin_unlock_irqrestore(&ctx->lock, flags); @@ -138,19 +118,18 @@ static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type) * a given cpu (cpu > -1) or in all of them (cpu = -1). */ static void -fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, - enum bp_type_idx type) +fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp) { int cpu = bp->cpu; struct task_struct *tsk = bp->ctx->task; if (cpu >= 0) { - slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu); + slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu); if (!tsk) - slots->pinned += max_task_bp_pinned(cpu, type); + slots->pinned += max_task_bp_pinned(cpu); else - slots->pinned += task_bp_pinned(tsk, type); - slots->flexible = per_cpu(nr_bp_flexible[type], cpu); + slots->pinned += task_bp_pinned(tsk); + slots->flexible = per_cpu(nr_bp_flexible, cpu); return; } @@ -158,66 +137,48 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, for_each_online_cpu(cpu) { unsigned int nr; - nr = per_cpu(nr_cpu_bp_pinned[type], cpu); + nr = per_cpu(nr_cpu_bp_pinned, cpu); if (!tsk) - nr += max_task_bp_pinned(cpu, type); + nr += max_task_bp_pinned(cpu); else - nr += task_bp_pinned(tsk, type); + nr += task_bp_pinned(tsk); if (nr > slots->pinned) slots->pinned = nr; - nr = per_cpu(nr_bp_flexible[type], cpu); + nr = per_cpu(nr_bp_flexible, cpu); if (nr > slots->flexible) slots->flexible = nr; } } -/* - * For now, continue to consider flexible as pinned, until we can - * ensure no flexible event can ever be scheduled before a pinned event - * in a same cpu. - */ -static void -fetch_this_slot(struct bp_busy_slots *slots, int weight) -{ - slots->pinned += weight; -} - /* * Add a pinned breakpoint for the given task in our constraint table */ -static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable, - enum bp_type_idx type, int weight) +static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable) { unsigned int *tsk_pinned; - int old_count = 0; - int old_idx = 0; - int idx = 0; + int count = 0; - old_count = task_bp_pinned(tsk, type); - old_idx = old_count - 1; - idx = old_idx + weight; + count = task_bp_pinned(tsk); - tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); + tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); if (enable) { - tsk_pinned[idx]++; - if (old_count > 0) - tsk_pinned[old_idx]--; + tsk_pinned[count]++; + if (count > 0) + tsk_pinned[count-1]--; } else { - tsk_pinned[idx]--; - if (old_count > 0) - tsk_pinned[old_idx]++; + tsk_pinned[count]--; + if (count > 0) + tsk_pinned[count-1]++; } } /* * Add/remove the given breakpoint in our constraint table */ -static void -toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, - int weight) +static void toggle_bp_slot(struct perf_event *bp, bool enable) { int cpu = bp->cpu; struct task_struct *tsk = bp->ctx->task; @@ -225,20 +186,20 @@ toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, /* Pinned counter task profiling */ if (tsk) { if (cpu >= 0) { - toggle_bp_task_slot(tsk, cpu, enable, type, weight); + toggle_bp_task_slot(tsk, cpu, enable); return; } for_each_online_cpu(cpu) - toggle_bp_task_slot(tsk, cpu, enable, type, weight); + toggle_bp_task_slot(tsk, cpu, enable); return; } /* Pinned counter cpu profiling */ if (enable) - per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight; + per_cpu(nr_cpu_bp_pinned, bp->cpu)++; else - per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight; + per_cpu(nr_cpu_bp_pinned, bp->cpu)--; } /* @@ -285,29 +246,14 @@ toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, static int __reserve_bp_slot(struct perf_event *bp) { struct bp_busy_slots slots = {0}; - enum bp_type_idx type; - int weight; - /* We couldn't initialize breakpoint constraints on boot */ - if (!constraints_initialized) - return -ENOMEM; - - /* Basic checks */ - if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY || - bp->attr.bp_type == HW_BREAKPOINT_INVALID) - return -EINVAL; - - type = find_slot_idx(bp); - weight = hw_breakpoint_weight(bp); - - fetch_bp_busy_slots(&slots, bp, type); - fetch_this_slot(&slots, weight); + fetch_bp_busy_slots(&slots, bp); /* Flexible counters need to keep at least one slot */ - if (slots.pinned + (!!slots.flexible) > nr_slots[type]) + if (slots.pinned + (!!slots.flexible) == HBP_NUM) return -ENOSPC; - toggle_bp_slot(bp, true, type, weight); + toggle_bp_slot(bp, true); return 0; } @@ -327,12 +273,7 @@ int reserve_bp_slot(struct perf_event *bp) static void __release_bp_slot(struct perf_event *bp) { - enum bp_type_idx type; - int weight; - - type = find_slot_idx(bp); - weight = hw_breakpoint_weight(bp); - toggle_bp_slot(bp, false, type, weight); + toggle_bp_slot(bp, false); } void release_bp_slot(struct perf_event *bp) @@ -367,28 +308,6 @@ int dbg_release_bp_slot(struct perf_event *bp) return 0; } -static int validate_hw_breakpoint(struct perf_event *bp) -{ - int ret; - - ret = arch_validate_hwbkpt_settings(bp); - if (ret) - return ret; - - if (arch_check_bp_in_kernelspace(bp)) { - if (bp->attr.exclude_kernel) - return -EINVAL; - /* - * Don't let unprivileged users set a breakpoint in the trap - * path to avoid trap recursion attacks. - */ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - } - - return 0; -} - int register_perf_hw_breakpoint(struct perf_event *bp) { int ret; @@ -397,7 +316,17 @@ int register_perf_hw_breakpoint(struct perf_event *bp) if (ret) return ret; - ret = validate_hw_breakpoint(bp); + /* + * Ptrace breakpoints can be temporary perf events only + * meant to reserve a slot. In this case, it is created disabled and + * we don't want to check the params right now (as we put a null addr) + * But perf tools create events as disabled and we want to check + * the params for them. + * This is a quick hack that will be removed soon, once we remove + * the tmp breakpoints from ptrace + */ + if (!bp->attr.disabled || !bp->overflow_handler) + ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); /* if arch_validate_hwbkpt_settings() fails then release bp slot */ if (ret) @@ -444,7 +373,7 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att if (attr->disabled) goto end; - err = validate_hw_breakpoint(bp); + err = arch_validate_hwbkpt_settings(bp, bp->ctx->task); if (!err) perf_event_enable(bp); @@ -551,36 +480,7 @@ static struct notifier_block hw_breakpoint_exceptions_nb = { static int __init init_hw_breakpoint(void) { - unsigned int **task_bp_pinned; - int cpu, err_cpu; - int i; - - for (i = 0; i < TYPE_MAX; i++) - nr_slots[i] = hw_breakpoint_slots(i); - - for_each_possible_cpu(cpu) { - for (i = 0; i < TYPE_MAX; i++) { - task_bp_pinned = &per_cpu(nr_task_bp_pinned[i], cpu); - *task_bp_pinned = kzalloc(sizeof(int) * nr_slots[i], - GFP_KERNEL); - if (!*task_bp_pinned) - goto err_alloc; - } - } - - constraints_initialized = 1; - return register_die_notifier(&hw_breakpoint_exceptions_nb); - - err_alloc: - for_each_possible_cpu(err_cpu) { - if (err_cpu == cpu) - break; - for (i = 0; i < TYPE_MAX; i++) - kfree(per_cpu(nr_task_bp_pinned[i], cpu)); - } - - return -ENOMEM; } core_initcall(init_hw_breakpoint); diff --git a/trunk/kernel/perf_event.c b/trunk/kernel/perf_event.c index 9dbe8cdaf145..f13c3db765f4 100644 --- a/trunk/kernel/perf_event.c +++ b/trunk/kernel/perf_event.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -342,6 +341,9 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) if (event->state > PERF_EVENT_STATE_OFF) event->state = PERF_EVENT_STATE_OFF; + if (event->state > PERF_EVENT_STATE_FREE) + return; + /* * If this was a group event with sibling events then * upgrade the siblings to singleton events by adding them @@ -1368,8 +1370,6 @@ void perf_event_task_sched_in(struct task_struct *task) if (cpuctx->task_ctx == ctx) return; - perf_disable(); - /* * We want to keep the following priority order: * cpu pinned (that don't need to move), task pinned, @@ -1382,8 +1382,6 @@ void perf_event_task_sched_in(struct task_struct *task) ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE); cpuctx->task_ctx = ctx; - - perf_enable(); } #define MAX_INTERRUPTS (~0ULL) @@ -1861,6 +1859,8 @@ int perf_event_release_kernel(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; + event->state = PERF_EVENT_STATE_FREE; + WARN_ON_ONCE(ctx->parent_ctx); mutex_lock(&ctx->mutex); perf_event_remove_from_context(event); @@ -2647,7 +2647,6 @@ static int perf_fasync(int fd, struct file *filp, int on) } static const struct file_operations perf_fops = { - .llseek = no_llseek, .release = perf_release, .read = perf_read, .poll = perf_poll, @@ -2797,27 +2796,6 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski } -/* - * We assume there is only KVM supporting the callbacks. - * Later on, we might change it to a list if there is - * another virtualization implementation supporting the callbacks. - */ -struct perf_guest_info_callbacks *perf_guest_cbs; - -int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) -{ - perf_guest_cbs = cbs; - return 0; -} -EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks); - -int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) -{ - perf_guest_cbs = NULL; - return 0; -} -EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); - /* * Output */ @@ -3770,7 +3748,7 @@ void __perf_event_mmap(struct vm_area_struct *vma) .event_id = { .header = { .type = PERF_RECORD_MMAP, - .misc = PERF_RECORD_MISC_USER, + .misc = 0, /* .size */ }, /* .pid */ @@ -3988,6 +3966,36 @@ static void perf_swevent_add(struct perf_event *event, u64 nr, perf_swevent_overflow(event, 0, nmi, data, regs); } +static int perf_swevent_is_counting(struct perf_event *event) +{ + /* + * The event is active, we're good! + */ + if (event->state == PERF_EVENT_STATE_ACTIVE) + return 1; + + /* + * The event is off/error, not counting. + */ + if (event->state != PERF_EVENT_STATE_INACTIVE) + return 0; + + /* + * The event is inactive, if the context is active + * we're part of a group that didn't make it on the 'pmu', + * not counting. + */ + if (event->ctx->is_active) + return 0; + + /* + * We're inactive and the context is too, this means the + * task is scheduled out, we're counting events that happen + * to us, like migration events. + */ + return 1; +} + static int perf_tp_event_match(struct perf_event *event, struct perf_sample_data *data); @@ -4011,6 +4019,12 @@ static int perf_swevent_match(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) { + if (event->cpu != -1 && event->cpu != smp_processor_id()) + return 0; + + if (!perf_swevent_is_counting(event)) + return 0; + if (event->attr.type != type) return 0; @@ -4027,53 +4041,18 @@ static int perf_swevent_match(struct perf_event *event, return 1; } -static inline u64 swevent_hash(u64 type, u32 event_id) -{ - u64 val = event_id | (type << 32); - - return hash_64(val, SWEVENT_HLIST_BITS); -} - -static struct hlist_head * -find_swevent_head(struct perf_cpu_context *ctx, u64 type, u32 event_id) -{ - u64 hash; - struct swevent_hlist *hlist; - - hash = swevent_hash(type, event_id); - - hlist = rcu_dereference(ctx->swevent_hlist); - if (!hlist) - return NULL; - - return &hlist->heads[hash]; -} - -static void do_perf_sw_event(enum perf_type_id type, u32 event_id, - u64 nr, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) +static void perf_swevent_ctx_event(struct perf_event_context *ctx, + enum perf_type_id type, + u32 event_id, u64 nr, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) { - struct perf_cpu_context *cpuctx; struct perf_event *event; - struct hlist_node *node; - struct hlist_head *head; - cpuctx = &__get_cpu_var(perf_cpu_context); - - rcu_read_lock(); - - head = find_swevent_head(cpuctx, type, event_id); - - if (!head) - goto end; - - hlist_for_each_entry_rcu(event, node, head, hlist_entry) { + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { if (perf_swevent_match(event, type, event_id, data, regs)) perf_swevent_add(event, nr, nmi, data, regs); } -end: - rcu_read_unlock(); } int perf_swevent_get_recursion_context(void) @@ -4111,6 +4090,27 @@ void perf_swevent_put_recursion_context(int rctx) } EXPORT_SYMBOL_GPL(perf_swevent_put_recursion_context); +static void do_perf_sw_event(enum perf_type_id type, u32 event_id, + u64 nr, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct perf_cpu_context *cpuctx; + struct perf_event_context *ctx; + + cpuctx = &__get_cpu_var(perf_cpu_context); + rcu_read_lock(); + perf_swevent_ctx_event(&cpuctx->ctx, type, event_id, + nr, nmi, data, regs); + /* + * doesn't really matter which of the child contexts the + * events ends up in. + */ + ctx = rcu_dereference(current->perf_event_ctxp); + if (ctx) + perf_swevent_ctx_event(ctx, type, event_id, nr, nmi, data, regs); + rcu_read_unlock(); +} void __perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr) @@ -4136,28 +4136,16 @@ static void perf_swevent_read(struct perf_event *event) static int perf_swevent_enable(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - struct perf_cpu_context *cpuctx; - struct hlist_head *head; - - cpuctx = &__get_cpu_var(perf_cpu_context); if (hwc->sample_period) { hwc->last_period = hwc->sample_period; perf_swevent_set_period(event); } - - head = find_swevent_head(cpuctx, event->attr.type, event->attr.config); - if (WARN_ON_ONCE(!head)) - return -EINVAL; - - hlist_add_head_rcu(&event->hlist_entry, head); - return 0; } static void perf_swevent_disable(struct perf_event *event) { - hlist_del_rcu(&event->hlist_entry); } static const struct pmu perf_ops_generic = { @@ -4185,8 +4173,15 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) perf_sample_data_init(&data, 0); data.period = event->hw.last_period; regs = get_irq_regs(); + /* + * In case we exclude kernel IPs or are somehow not in interrupt + * context, provide the next best thing, the user IP. + */ + if ((event->attr.exclude_kernel || !regs) && + !event->attr.exclude_user) + regs = task_pt_regs(current); - if (regs && !perf_exclude_event(event, regs)) { + if (regs) { if (!(event->attr.exclude_idle && current->pid == 0)) if (perf_event_overflow(event, 0, &data, regs)) ret = HRTIMER_NORESTART; @@ -4334,105 +4329,6 @@ static const struct pmu perf_ops_task_clock = { .read = task_clock_perf_event_read, }; -static void swevent_hlist_release_rcu(struct rcu_head *rcu_head) -{ - struct swevent_hlist *hlist; - - hlist = container_of(rcu_head, struct swevent_hlist, rcu_head); - kfree(hlist); -} - -static void swevent_hlist_release(struct perf_cpu_context *cpuctx) -{ - struct swevent_hlist *hlist; - - if (!cpuctx->swevent_hlist) - return; - - hlist = cpuctx->swevent_hlist; - rcu_assign_pointer(cpuctx->swevent_hlist, NULL); - call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu); -} - -static void swevent_hlist_put_cpu(struct perf_event *event, int cpu) -{ - struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); - - mutex_lock(&cpuctx->hlist_mutex); - - if (!--cpuctx->hlist_refcount) - swevent_hlist_release(cpuctx); - - mutex_unlock(&cpuctx->hlist_mutex); -} - -static void swevent_hlist_put(struct perf_event *event) -{ - int cpu; - - if (event->cpu != -1) { - swevent_hlist_put_cpu(event, event->cpu); - return; - } - - for_each_possible_cpu(cpu) - swevent_hlist_put_cpu(event, cpu); -} - -static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) -{ - struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); - int err = 0; - - mutex_lock(&cpuctx->hlist_mutex); - - if (!cpuctx->swevent_hlist && cpu_online(cpu)) { - struct swevent_hlist *hlist; - - hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); - if (!hlist) { - err = -ENOMEM; - goto exit; - } - rcu_assign_pointer(cpuctx->swevent_hlist, hlist); - } - cpuctx->hlist_refcount++; - exit: - mutex_unlock(&cpuctx->hlist_mutex); - - return err; -} - -static int swevent_hlist_get(struct perf_event *event) -{ - int err; - int cpu, failed_cpu; - - if (event->cpu != -1) - return swevent_hlist_get_cpu(event, event->cpu); - - get_online_cpus(); - for_each_possible_cpu(cpu) { - err = swevent_hlist_get_cpu(event, cpu); - if (err) { - failed_cpu = cpu; - goto fail; - } - } - put_online_cpus(); - - return 0; - fail: - for_each_possible_cpu(cpu) { - if (cpu == failed_cpu) - break; - swevent_hlist_put_cpu(event, cpu); - } - - put_online_cpus(); - return err; -} - #ifdef CONFIG_EVENT_TRACING void perf_tp_event(int event_id, u64 addr, u64 count, void *record, @@ -4466,13 +4362,10 @@ static int perf_tp_event_match(struct perf_event *event, static void tp_perf_event_destroy(struct perf_event *event) { perf_trace_disable(event->attr.config); - swevent_hlist_put(event); } static const struct pmu *tp_perf_event_init(struct perf_event *event) { - int err; - /* * Raw tracepoint data is a severe data leak, only allow root to * have these. @@ -4486,11 +4379,6 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event) return NULL; event->destroy = tp_perf_event_destroy; - err = swevent_hlist_get(event); - if (err) { - perf_trace_disable(event->attr.config); - return ERR_PTR(err); - } return &perf_ops_generic; } @@ -4591,7 +4479,6 @@ static void sw_perf_event_destroy(struct perf_event *event) WARN_ON(event->parent); atomic_dec(&perf_swevent_enabled[event_id]); - swevent_hlist_put(event); } static const struct pmu *sw_perf_event_init(struct perf_event *event) @@ -4630,12 +4517,6 @@ static const struct pmu *sw_perf_event_init(struct perf_event *event) case PERF_COUNT_SW_ALIGNMENT_FAULTS: case PERF_COUNT_SW_EMULATION_FAULTS: if (!event->parent) { - int err; - - err = swevent_hlist_get(event); - if (err) - return ERR_PTR(err); - atomic_inc(&perf_swevent_enabled[event_id]); event->destroy = sw_perf_event_destroy; } @@ -5021,7 +4902,7 @@ SYSCALL_DEFINE5(perf_event_open, err_free_put_context: if (err < 0) - kfree(event); + free_event(event); err_put_context: if (err < 0) @@ -5508,7 +5389,6 @@ static void __init perf_event_init_all_cpus(void) for_each_possible_cpu(cpu) { cpuctx = &per_cpu(perf_cpu_context, cpu); - mutex_init(&cpuctx->hlist_mutex); __perf_event_init_context(&cpuctx->ctx, NULL); } } @@ -5522,16 +5402,6 @@ static void __cpuinit perf_event_init_cpu(int cpu) spin_lock(&perf_resource_lock); cpuctx->max_pertask = perf_max_events - perf_reserved_percpu; spin_unlock(&perf_resource_lock); - - mutex_lock(&cpuctx->hlist_mutex); - if (cpuctx->hlist_refcount > 0) { - struct swevent_hlist *hlist; - - hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); - WARN_ON_ONCE(!hlist); - rcu_assign_pointer(cpuctx->swevent_hlist, hlist); - } - mutex_unlock(&cpuctx->hlist_mutex); } #ifdef CONFIG_HOTPLUG_CPU @@ -5551,10 +5421,6 @@ static void perf_event_exit_cpu(int cpu) struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); struct perf_event_context *ctx = &cpuctx->ctx; - mutex_lock(&cpuctx->hlist_mutex); - swevent_hlist_release(cpuctx); - mutex_unlock(&cpuctx->hlist_mutex); - mutex_lock(&ctx->mutex); smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1); mutex_unlock(&ctx->mutex); diff --git a/trunk/kernel/ptrace.c b/trunk/kernel/ptrace.c index 9fb51237b18c..42ad8ae729a0 100644 --- a/trunk/kernel/ptrace.c +++ b/trunk/kernel/ptrace.c @@ -76,6 +76,7 @@ void __ptrace_unlink(struct task_struct *child) child->parent = child->real_parent; list_del_init(&child->ptrace_entry); + arch_ptrace_untrace(child); if (task_is_traced(child)) ptrace_untrace(child); } diff --git a/trunk/kernel/sched.c b/trunk/kernel/sched.c index b0bbadc24955..3c2a54f70ffe 100644 --- a/trunk/kernel/sched.c +++ b/trunk/kernel/sched.c @@ -323,6 +323,15 @@ static inline struct task_group *task_group(struct task_struct *p) /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */ static inline void set_task_rq(struct task_struct *p, unsigned int cpu) { + /* + * Strictly speaking this rcu_read_lock() is not needed since the + * task_group is tied to the cgroup, which in turn can never go away + * as long as there are tasks attached to it. + * + * However since task_group() uses task_subsys_state() which is an + * rcu_dereference() user, this quiets CONFIG_PROVE_RCU. + */ + rcu_read_lock(); #ifdef CONFIG_FAIR_GROUP_SCHED p->se.cfs_rq = task_group(p)->cfs_rq[cpu]; p->se.parent = task_group(p)->se[cpu]; @@ -332,6 +341,7 @@ static inline void set_task_rq(struct task_struct *p, unsigned int cpu) p->rt.rt_rq = task_group(p)->rt_rq[cpu]; p->rt.parent = task_group(p)->rt_se[cpu]; #endif + rcu_read_unlock(); } #else @@ -2077,6 +2087,49 @@ migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req) return 1; } +/* + * wait_task_context_switch - wait for a thread to complete at least one + * context switch. + * + * @p must not be current. + */ +void wait_task_context_switch(struct task_struct *p) +{ + unsigned long nvcsw, nivcsw, flags; + int running; + struct rq *rq; + + nvcsw = p->nvcsw; + nivcsw = p->nivcsw; + for (;;) { + /* + * The runqueue is assigned before the actual context + * switch. We need to take the runqueue lock. + * + * We could check initially without the lock but it is + * very likely that we need to take the lock in every + * iteration. + */ + rq = task_rq_lock(p, &flags); + running = task_running(rq, p); + task_rq_unlock(rq, &flags); + + if (likely(!running)) + break; + /* + * The switch count is incremented before the actual + * context switch. We thus wait for two switches to be + * sure at least one completed. + */ + if ((p->nvcsw - nvcsw) > 1) + break; + if ((p->nivcsw - nivcsw) > 1) + break; + + cpu_relax(); + } +} + /* * wait_task_inactive - wait for a thread to unschedule. * @@ -3737,7 +3790,7 @@ int mutex_spin_on_owner(struct mutex *lock, struct thread_info *owner) * the mutex owner just released it and exited. */ if (probe_kernel_address(&owner->cpu, cpu)) - goto out; + return 0; #else cpu = owner->cpu; #endif @@ -3747,14 +3800,14 @@ int mutex_spin_on_owner(struct mutex *lock, struct thread_info *owner) * the cpu field may no longer be valid. */ if (cpu >= nr_cpumask_bits) - goto out; + return 0; /* * We need to validate that we can do a * get_cpu() and that we have the percpu area. */ if (!cpu_online(cpu)) - goto out; + return 0; rq = cpu_rq(cpu); @@ -3773,7 +3826,7 @@ int mutex_spin_on_owner(struct mutex *lock, struct thread_info *owner) cpu_relax(); } -out: + return 1; } #endif diff --git a/trunk/kernel/trace/Kconfig b/trunk/kernel/trace/Kconfig index 8b1797c4545b..13e13d428cd3 100644 --- a/trunk/kernel/trace/Kconfig +++ b/trunk/kernel/trace/Kconfig @@ -44,6 +44,9 @@ config HAVE_FTRACE_MCOUNT_RECORD help See Documentation/trace/ftrace-design.txt +config HAVE_HW_BRANCH_TRACER + bool + config HAVE_SYSCALL_TRACEPOINTS bool help @@ -371,6 +374,14 @@ config STACK_TRACER Say N if unsure. +config HW_BRANCH_TRACER + depends on HAVE_HW_BRANCH_TRACER + bool "Trace hw branches" + select GENERIC_TRACER + help + This tracer records all branches on the system in a circular + buffer, giving access to the last N branches for each cpu. + config KMEMTRACE bool "Trace SLAB allocations" select GENERIC_TRACER diff --git a/trunk/kernel/trace/Makefile b/trunk/kernel/trace/Makefile index ffb1a5b0550e..78edc6490038 100644 --- a/trunk/kernel/trace/Makefile +++ b/trunk/kernel/trace/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o obj-$(CONFIG_BOOT_TRACER) += trace_boot.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o +obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o obj-$(CONFIG_KMEMTRACE) += kmemtrace.o obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o diff --git a/trunk/kernel/trace/trace.h b/trunk/kernel/trace/trace.h index 3ebdb6bd2362..2825ef2c0b15 100644 --- a/trunk/kernel/trace/trace.h +++ b/trunk/kernel/trace/trace.h @@ -34,6 +34,7 @@ enum trace_type { TRACE_GRAPH_RET, TRACE_GRAPH_ENT, TRACE_USER_STACK, + TRACE_HW_BRANCHES, TRACE_KMEM_ALLOC, TRACE_KMEM_FREE, TRACE_BLK, @@ -102,17 +103,29 @@ struct syscall_trace_exit { long ret; }; -struct kprobe_trace_entry_head { +struct kprobe_trace_entry { struct trace_entry ent; unsigned long ip; + int nargs; + unsigned long args[]; }; -struct kretprobe_trace_entry_head { +#define SIZEOF_KPROBE_TRACE_ENTRY(n) \ + (offsetof(struct kprobe_trace_entry, args) + \ + (sizeof(unsigned long) * (n))) + +struct kretprobe_trace_entry { struct trace_entry ent; unsigned long func; unsigned long ret_ip; + int nargs; + unsigned long args[]; }; +#define SIZEOF_KRETPROBE_TRACE_ENTRY(n) \ + (offsetof(struct kretprobe_trace_entry, args) + \ + (sizeof(unsigned long) * (n))) + /* * trace_flag_type is an enumeration that holds different * states when a trace occurs. These are: @@ -216,6 +229,7 @@ extern void __ftrace_bad_type(void); TRACE_GRAPH_ENT); \ IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \ TRACE_GRAPH_RET); \ + IF_ASSIGN(var, ent, struct hw_branch_entry, TRACE_HW_BRANCHES);\ IF_ASSIGN(var, ent, struct kmemtrace_alloc_entry, \ TRACE_KMEM_ALLOC); \ IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \ @@ -453,6 +467,8 @@ extern int trace_selftest_startup_sysprof(struct tracer *trace, struct trace_array *tr); extern int trace_selftest_startup_branch(struct tracer *trace, struct trace_array *tr); +extern int trace_selftest_startup_hw_branches(struct tracer *trace, + struct trace_array *tr); extern int trace_selftest_startup_ksym(struct tracer *trace, struct trace_array *tr); #endif /* CONFIG_FTRACE_STARTUP_TEST */ diff --git a/trunk/kernel/trace/trace_entries.h b/trunk/kernel/trace/trace_entries.h index dc008c1240da..c16a08f399df 100644 --- a/trunk/kernel/trace/trace_entries.h +++ b/trunk/kernel/trace/trace_entries.h @@ -318,6 +318,18 @@ FTRACE_ENTRY(branch, trace_branch, __entry->func, __entry->file, __entry->correct) ); +FTRACE_ENTRY(hw_branch, hw_branch_entry, + + TRACE_HW_BRANCHES, + + F_STRUCT( + __field( u64, from ) + __field( u64, to ) + ), + + F_printk("from: %llx to: %llx", __entry->from, __entry->to) +); + FTRACE_ENTRY(kmem_alloc, kmemtrace_alloc_entry, TRACE_KMEM_ALLOC, diff --git a/trunk/kernel/trace/trace_events_filter.c b/trunk/kernel/trace/trace_events_filter.c index 58092d844a1f..88c0b6dbd7fe 100644 --- a/trunk/kernel/trace/trace_events_filter.c +++ b/trunk/kernel/trace/trace_events_filter.c @@ -1398,7 +1398,7 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id, } err = -EINVAL; - if (&call->list == &ftrace_events) + if (!call) goto out_unlock; err = -EEXIST; diff --git a/trunk/kernel/trace/trace_hw_branches.c b/trunk/kernel/trace/trace_hw_branches.c new file mode 100644 index 000000000000..7b97000745f5 --- /dev/null +++ b/trunk/kernel/trace/trace_hw_branches.c @@ -0,0 +1,312 @@ +/* + * h/w branch tracer for x86 based on BTS + * + * Copyright (C) 2008-2009 Intel Corporation. + * Markus Metzger , 2008-2009 + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "trace_output.h" +#include "trace.h" + + +#define BTS_BUFFER_SIZE (1 << 13) + +static DEFINE_PER_CPU(struct bts_tracer *, hwb_tracer); +static DEFINE_PER_CPU(unsigned char[BTS_BUFFER_SIZE], hwb_buffer); + +#define this_tracer per_cpu(hwb_tracer, smp_processor_id()) + +static int trace_hw_branches_enabled __read_mostly; +static int trace_hw_branches_suspended __read_mostly; +static struct trace_array *hw_branch_trace __read_mostly; + + +static void bts_trace_init_cpu(int cpu) +{ + per_cpu(hwb_tracer, cpu) = + ds_request_bts_cpu(cpu, per_cpu(hwb_buffer, cpu), + BTS_BUFFER_SIZE, NULL, (size_t)-1, + BTS_KERNEL); + + if (IS_ERR(per_cpu(hwb_tracer, cpu))) + per_cpu(hwb_tracer, cpu) = NULL; +} + +static int bts_trace_init(struct trace_array *tr) +{ + int cpu; + + hw_branch_trace = tr; + trace_hw_branches_enabled = 0; + + get_online_cpus(); + for_each_online_cpu(cpu) { + bts_trace_init_cpu(cpu); + + if (likely(per_cpu(hwb_tracer, cpu))) + trace_hw_branches_enabled = 1; + } + trace_hw_branches_suspended = 0; + put_online_cpus(); + + /* If we could not enable tracing on a single cpu, we fail. */ + return trace_hw_branches_enabled ? 0 : -EOPNOTSUPP; +} + +static void bts_trace_reset(struct trace_array *tr) +{ + int cpu; + + get_online_cpus(); + for_each_online_cpu(cpu) { + if (likely(per_cpu(hwb_tracer, cpu))) { + ds_release_bts(per_cpu(hwb_tracer, cpu)); + per_cpu(hwb_tracer, cpu) = NULL; + } + } + trace_hw_branches_enabled = 0; + trace_hw_branches_suspended = 0; + put_online_cpus(); +} + +static void bts_trace_start(struct trace_array *tr) +{ + int cpu; + + get_online_cpus(); + for_each_online_cpu(cpu) + if (likely(per_cpu(hwb_tracer, cpu))) + ds_resume_bts(per_cpu(hwb_tracer, cpu)); + trace_hw_branches_suspended = 0; + put_online_cpus(); +} + +static void bts_trace_stop(struct trace_array *tr) +{ + int cpu; + + get_online_cpus(); + for_each_online_cpu(cpu) + if (likely(per_cpu(hwb_tracer, cpu))) + ds_suspend_bts(per_cpu(hwb_tracer, cpu)); + trace_hw_branches_suspended = 1; + put_online_cpus(); +} + +static int __cpuinit bts_hotcpu_handler(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + int cpu = (long)hcpu; + + switch (action) { + case CPU_ONLINE: + case CPU_DOWN_FAILED: + /* The notification is sent with interrupts enabled. */ + if (trace_hw_branches_enabled) { + bts_trace_init_cpu(cpu); + + if (trace_hw_branches_suspended && + likely(per_cpu(hwb_tracer, cpu))) + ds_suspend_bts(per_cpu(hwb_tracer, cpu)); + } + break; + + case CPU_DOWN_PREPARE: + /* The notification is sent with interrupts enabled. */ + if (likely(per_cpu(hwb_tracer, cpu))) { + ds_release_bts(per_cpu(hwb_tracer, cpu)); + per_cpu(hwb_tracer, cpu) = NULL; + } + } + + return NOTIFY_DONE; +} + +static struct notifier_block bts_hotcpu_notifier __cpuinitdata = { + .notifier_call = bts_hotcpu_handler +}; + +static void bts_trace_print_header(struct seq_file *m) +{ + seq_puts(m, "# CPU# TO <- FROM\n"); +} + +static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) +{ + unsigned long symflags = TRACE_ITER_SYM_OFFSET; + struct trace_entry *entry = iter->ent; + struct trace_seq *seq = &iter->seq; + struct hw_branch_entry *it; + + trace_assign_type(it, entry); + + if (entry->type == TRACE_HW_BRANCHES) { + if (trace_seq_printf(seq, "%4d ", iter->cpu) && + seq_print_ip_sym(seq, it->to, symflags) && + trace_seq_printf(seq, "\t <- ") && + seq_print_ip_sym(seq, it->from, symflags) && + trace_seq_printf(seq, "\n")) + return TRACE_TYPE_HANDLED; + return TRACE_TYPE_PARTIAL_LINE; + } + return TRACE_TYPE_UNHANDLED; +} + +void trace_hw_branch(u64 from, u64 to) +{ + struct ftrace_event_call *call = &event_hw_branch; + struct trace_array *tr = hw_branch_trace; + struct ring_buffer_event *event; + struct ring_buffer *buf; + struct hw_branch_entry *entry; + unsigned long irq1; + int cpu; + + if (unlikely(!tr)) + return; + + if (unlikely(!trace_hw_branches_enabled)) + return; + + local_irq_save(irq1); + cpu = raw_smp_processor_id(); + if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) + goto out; + + buf = tr->buffer; + event = trace_buffer_lock_reserve(buf, TRACE_HW_BRANCHES, + sizeof(*entry), 0, 0); + if (!event) + goto out; + entry = ring_buffer_event_data(event); + tracing_generic_entry_update(&entry->ent, 0, from); + entry->ent.type = TRACE_HW_BRANCHES; + entry->from = from; + entry->to = to; + if (!filter_check_discard(call, entry, buf, event)) + trace_buffer_unlock_commit(buf, event, 0, 0); + + out: + atomic_dec(&tr->data[cpu]->disabled); + local_irq_restore(irq1); +} + +static void trace_bts_at(const struct bts_trace *trace, void *at) +{ + struct bts_struct bts; + int err = 0; + + WARN_ON_ONCE(!trace->read); + if (!trace->read) + return; + + err = trace->read(this_tracer, at, &bts); + if (err < 0) + return; + + switch (bts.qualifier) { + case BTS_BRANCH: + trace_hw_branch(bts.variant.lbr.from, bts.variant.lbr.to); + break; + } +} + +/* + * Collect the trace on the current cpu and write it into the ftrace buffer. + * + * pre: tracing must be suspended on the current cpu + */ +static void trace_bts_cpu(void *arg) +{ + struct trace_array *tr = (struct trace_array *)arg; + const struct bts_trace *trace; + unsigned char *at; + + if (unlikely(!tr)) + return; + + if (unlikely(atomic_read(&tr->data[raw_smp_processor_id()]->disabled))) + return; + + if (unlikely(!this_tracer)) + return; + + trace = ds_read_bts(this_tracer); + if (!trace) + return; + + for (at = trace->ds.top; (void *)at < trace->ds.end; + at += trace->ds.size) + trace_bts_at(trace, at); + + for (at = trace->ds.begin; (void *)at < trace->ds.top; + at += trace->ds.size) + trace_bts_at(trace, at); +} + +static void trace_bts_prepare(struct trace_iterator *iter) +{ + int cpu; + + get_online_cpus(); + for_each_online_cpu(cpu) + if (likely(per_cpu(hwb_tracer, cpu))) + ds_suspend_bts(per_cpu(hwb_tracer, cpu)); + /* + * We need to collect the trace on the respective cpu since ftrace + * implicitly adds the record for the current cpu. + * Once that is more flexible, we could collect the data from any cpu. + */ + on_each_cpu(trace_bts_cpu, iter->tr, 1); + + for_each_online_cpu(cpu) + if (likely(per_cpu(hwb_tracer, cpu))) + ds_resume_bts(per_cpu(hwb_tracer, cpu)); + put_online_cpus(); +} + +static void trace_bts_close(struct trace_iterator *iter) +{ + tracing_reset_online_cpus(iter->tr); +} + +void trace_hw_branch_oops(void) +{ + if (this_tracer) { + ds_suspend_bts_noirq(this_tracer); + trace_bts_cpu(hw_branch_trace); + ds_resume_bts_noirq(this_tracer); + } +} + +struct tracer bts_tracer __read_mostly = +{ + .name = "hw-branch-tracer", + .init = bts_trace_init, + .reset = bts_trace_reset, + .print_header = bts_trace_print_header, + .print_line = bts_trace_print_line, + .start = bts_trace_start, + .stop = bts_trace_stop, + .open = trace_bts_prepare, + .close = trace_bts_close, +#ifdef CONFIG_FTRACE_SELFTEST + .selftest = trace_selftest_startup_hw_branches, +#endif /* CONFIG_FTRACE_SELFTEST */ +}; + +__init static int init_bts_trace(void) +{ + register_hotcpu_notifier(&bts_hotcpu_notifier); + return register_tracer(&bts_tracer); +} +device_initcall(init_bts_trace); diff --git a/trunk/kernel/trace/trace_kprobe.c b/trunk/kernel/trace/trace_kprobe.c index a7514326052b..1251e367bae9 100644 --- a/trunk/kernel/trace/trace_kprobe.c +++ b/trunk/kernel/trace/trace_kprobe.c @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include "trace.h" #include "trace_output.h" @@ -42,6 +40,7 @@ /* Reserved field names */ #define FIELD_STRING_IP "__probe_ip" +#define FIELD_STRING_NARGS "__probe_nargs" #define FIELD_STRING_RETIP "__probe_ret_ip" #define FIELD_STRING_FUNC "__probe_func" @@ -53,102 +52,56 @@ const char *reserved_field_names[] = { "common_tgid", "common_lock_depth", FIELD_STRING_IP, + FIELD_STRING_NARGS, FIELD_STRING_RETIP, FIELD_STRING_FUNC, }; -/* Printing function type */ -typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *); -#define PRINT_TYPE_FUNC_NAME(type) print_type_##type -#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type - -/* Printing in basic type function template */ -#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \ -static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ - const char *name, void *data)\ -{ \ - return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\ -} \ -static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; - -DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int) -DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int) -DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long) -DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long) -DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int) -DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int) -DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long) -DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) - -/* Data fetch function type */ -typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); - -struct fetch_param { - fetch_func_t fn; +struct fetch_func { + unsigned long (*func)(struct pt_regs *, void *); void *data; }; -static __kprobes void call_fetch(struct fetch_param *fprm, - struct pt_regs *regs, void *dest) +static __kprobes unsigned long call_fetch(struct fetch_func *f, + struct pt_regs *regs) { - return fprm->fn(regs, fprm->data, dest); + return f->func(regs, f->data); } -#define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type -/* - * Define macro for basic types - we don't need to define s* types, because - * we have to care only about bitwidth at recording time. - */ -#define DEFINE_BASIC_FETCH_FUNCS(kind) \ -DEFINE_FETCH_##kind(u8) \ -DEFINE_FETCH_##kind(u16) \ -DEFINE_FETCH_##kind(u32) \ -DEFINE_FETCH_##kind(u64) - -#define CHECK_BASIC_FETCH_FUNCS(kind, fn) \ - ((FETCH_FUNC_NAME(kind, u8) == fn) || \ - (FETCH_FUNC_NAME(kind, u16) == fn) || \ - (FETCH_FUNC_NAME(kind, u32) == fn) || \ - (FETCH_FUNC_NAME(kind, u64) == fn)) - -/* Data fetch function templates */ -#define DEFINE_FETCH_reg(type) \ -static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ - void *offset, void *dest) \ -{ \ - *(type *)dest = (type)regs_get_register(regs, \ - (unsigned int)((unsigned long)offset)); \ +/* fetch handlers */ +static __kprobes unsigned long fetch_register(struct pt_regs *regs, + void *offset) +{ + return regs_get_register(regs, (unsigned int)((unsigned long)offset)); } -DEFINE_BASIC_FETCH_FUNCS(reg) - -#define DEFINE_FETCH_stack(type) \ -static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ - void *offset, void *dest) \ -{ \ - *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ - (unsigned int)((unsigned long)offset)); \ + +static __kprobes unsigned long fetch_stack(struct pt_regs *regs, + void *num) +{ + return regs_get_kernel_stack_nth(regs, + (unsigned int)((unsigned long)num)); } -DEFINE_BASIC_FETCH_FUNCS(stack) -#define DEFINE_FETCH_retval(type) \ -static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ - void *dummy, void *dest) \ -{ \ - *(type *)dest = (type)regs_return_value(regs); \ +static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr) +{ + unsigned long retval; + + if (probe_kernel_address(addr, retval)) + return 0; + return retval; } -DEFINE_BASIC_FETCH_FUNCS(retval) - -#define DEFINE_FETCH_memory(type) \ -static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ - void *addr, void *dest) \ -{ \ - type retval; \ - if (probe_kernel_address(addr, retval)) \ - *(type *)dest = 0; \ - else \ - *(type *)dest = retval; \ + +static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs, + void *dummy) +{ + return regs_return_value(regs); +} + +static __kprobes unsigned long fetch_stack_address(struct pt_regs *regs, + void *dummy) +{ + return kernel_stack_pointer(regs); } -DEFINE_BASIC_FETCH_FUNCS(memory) /* Memory fetching by symbol */ struct symbol_cache { @@ -192,114 +145,42 @@ static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) return sc; } -#define DEFINE_FETCH_symbol(type) \ -static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ - void *data, void *dest) \ -{ \ - struct symbol_cache *sc = data; \ - if (sc->addr) \ - fetch_memory_##type(regs, (void *)sc->addr, dest); \ - else \ - *(type *)dest = 0; \ -} -DEFINE_BASIC_FETCH_FUNCS(symbol) - -/* Dereference memory access function */ -struct deref_fetch_param { - struct fetch_param orig; - long offset; -}; - -#define DEFINE_FETCH_deref(type) \ -static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ - void *data, void *dest) \ -{ \ - struct deref_fetch_param *dprm = data; \ - unsigned long addr; \ - call_fetch(&dprm->orig, regs, &addr); \ - if (addr) { \ - addr += dprm->offset; \ - fetch_memory_##type(regs, (void *)addr, dest); \ - } else \ - *(type *)dest = 0; \ -} -DEFINE_BASIC_FETCH_FUNCS(deref) - -static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) +static __kprobes unsigned long fetch_symbol(struct pt_regs *regs, void *data) { - if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn)) - free_deref_fetch_param(data->orig.data); - else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn)) - free_symbol_cache(data->orig.data); - kfree(data); -} + struct symbol_cache *sc = data; -/* Default (unsigned long) fetch type */ -#define __DEFAULT_FETCH_TYPE(t) u##t -#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) -#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) -#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) - -#define ASSIGN_FETCH_FUNC(kind, type) \ - .kind = FETCH_FUNC_NAME(kind, type) - -#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ - {.name = #ptype, \ - .size = sizeof(ftype), \ - .is_signed = sign, \ - .print = PRINT_TYPE_FUNC_NAME(ptype), \ - .fmt = PRINT_TYPE_FMT_NAME(ptype), \ -ASSIGN_FETCH_FUNC(reg, ftype), \ -ASSIGN_FETCH_FUNC(stack, ftype), \ -ASSIGN_FETCH_FUNC(retval, ftype), \ -ASSIGN_FETCH_FUNC(memory, ftype), \ -ASSIGN_FETCH_FUNC(symbol, ftype), \ -ASSIGN_FETCH_FUNC(deref, ftype), \ - } + if (sc->addr) + return fetch_memory(regs, (void *)sc->addr); + else + return 0; +} -/* Fetch type information table */ -static const struct fetch_type { - const char *name; /* Name of type */ - size_t size; /* Byte size of type */ - int is_signed; /* Signed flag */ - print_type_func_t print; /* Print functions */ - const char *fmt; /* Fromat string */ - /* Fetch functions */ - fetch_func_t reg; - fetch_func_t stack; - fetch_func_t retval; - fetch_func_t memory; - fetch_func_t symbol; - fetch_func_t deref; -} fetch_type_table[] = { - ASSIGN_FETCH_TYPE(u8, u8, 0), - ASSIGN_FETCH_TYPE(u16, u16, 0), - ASSIGN_FETCH_TYPE(u32, u32, 0), - ASSIGN_FETCH_TYPE(u64, u64, 0), - ASSIGN_FETCH_TYPE(s8, u8, 1), - ASSIGN_FETCH_TYPE(s16, u16, 1), - ASSIGN_FETCH_TYPE(s32, u32, 1), - ASSIGN_FETCH_TYPE(s64, u64, 1), +/* Special indirect memory access interface */ +struct indirect_fetch_data { + struct fetch_func orig; + long offset; }; -static const struct fetch_type *find_fetch_type(const char *type) +static __kprobes unsigned long fetch_indirect(struct pt_regs *regs, void *data) { - int i; - - if (!type) - type = DEFAULT_FETCH_TYPE_STR; + struct indirect_fetch_data *ind = data; + unsigned long addr; - for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) - if (strcmp(type, fetch_type_table[i].name) == 0) - return &fetch_type_table[i]; - return NULL; + addr = call_fetch(&ind->orig, regs); + if (addr) { + addr += ind->offset; + return fetch_memory(regs, (void *)addr); + } else + return 0; } -/* Special function : only accept unsigned long */ -static __kprobes void fetch_stack_address(struct pt_regs *regs, - void *dummy, void *dest) +static __kprobes void free_indirect_fetch_data(struct indirect_fetch_data *data) { - *(unsigned long *)dest = kernel_stack_pointer(regs); + if (data->orig.func == fetch_indirect) + free_indirect_fetch_data(data->orig.data); + else if (data->orig.func == fetch_symbol) + free_symbol_cache(data->orig.data); + kfree(data); } /** @@ -307,11 +188,8 @@ static __kprobes void fetch_stack_address(struct pt_regs *regs, */ struct probe_arg { - struct fetch_param fetch; - unsigned int offset; /* Offset from argument entry */ - const char *name; /* Name of this argument */ - const char *comm; /* Command of this argument */ - const struct fetch_type *type; /* Type of this argument */ + struct fetch_func fetch; + const char *name; }; /* Flags for trace_probe */ @@ -326,7 +204,6 @@ struct trace_probe { const char *symbol; /* symbol name */ struct ftrace_event_call call; struct trace_event event; - ssize_t size; /* trace entry size */ unsigned int nr_args; struct probe_arg args[]; }; @@ -335,7 +212,6 @@ struct trace_probe { (offsetof(struct trace_probe, args) + \ (sizeof(struct probe_arg) * (n))) - static __kprobes int probe_is_return(struct trace_probe *tp) { return tp->rp.handler != NULL; @@ -346,6 +222,49 @@ static __kprobes const char *probe_symbol(struct trace_probe *tp) return tp->symbol ? tp->symbol : "unknown"; } +static int probe_arg_string(char *buf, size_t n, struct fetch_func *ff) +{ + int ret = -EINVAL; + + if (ff->func == fetch_register) { + const char *name; + name = regs_query_register_name((unsigned int)((long)ff->data)); + ret = snprintf(buf, n, "%%%s", name); + } else if (ff->func == fetch_stack) + ret = snprintf(buf, n, "$stack%lu", (unsigned long)ff->data); + else if (ff->func == fetch_memory) + ret = snprintf(buf, n, "@0x%p", ff->data); + else if (ff->func == fetch_symbol) { + struct symbol_cache *sc = ff->data; + if (sc->offset) + ret = snprintf(buf, n, "@%s%+ld", sc->symbol, + sc->offset); + else + ret = snprintf(buf, n, "@%s", sc->symbol); + } else if (ff->func == fetch_retvalue) + ret = snprintf(buf, n, "$retval"); + else if (ff->func == fetch_stack_address) + ret = snprintf(buf, n, "$stack"); + else if (ff->func == fetch_indirect) { + struct indirect_fetch_data *id = ff->data; + size_t l = 0; + ret = snprintf(buf, n, "%+ld(", id->offset); + if (ret >= n) + goto end; + l += ret; + ret = probe_arg_string(buf + l, n - l, &id->orig); + if (ret < 0) + goto end; + l += ret; + ret = snprintf(buf + l, n - l, ")"); + ret += l; + } +end: + if (ret >= n) + return -ENOSPC; + return ret; +} + static int register_probe_event(struct trace_probe *tp); static void unregister_probe_event(struct trace_probe *tp); @@ -428,12 +347,11 @@ static struct trace_probe *alloc_trace_probe(const char *group, static void free_probe_arg(struct probe_arg *arg) { - if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn)) - free_deref_fetch_param(arg->fetch.data); - else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn)) + if (arg->fetch.func == fetch_symbol) free_symbol_cache(arg->fetch.data); + else if (arg->fetch.func == fetch_indirect) + free_indirect_fetch_data(arg->fetch.data); kfree(arg->name); - kfree(arg->comm); } static void free_trace_probe(struct trace_probe *tp) @@ -539,30 +457,28 @@ static int split_symbol_offset(char *symbol, unsigned long *offset) #define PARAM_MAX_ARGS 16 #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) -static int parse_probe_vars(char *arg, const struct fetch_type *t, - struct fetch_param *f, int is_return) +static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return) { int ret = 0; unsigned long param; if (strcmp(arg, "retval") == 0) { - if (is_return) - f->fn = t->retval; - else + if (is_return) { + ff->func = fetch_retvalue; + ff->data = NULL; + } else ret = -EINVAL; } else if (strncmp(arg, "stack", 5) == 0) { if (arg[5] == '\0') { - if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0) - f->fn = fetch_stack_address; - else - ret = -EINVAL; + ff->func = fetch_stack_address; + ff->data = NULL; } else if (isdigit(arg[5])) { ret = strict_strtoul(arg + 5, 10, ¶m); if (ret || param > PARAM_MAX_STACK) ret = -EINVAL; else { - f->fn = t->stack; - f->data = (void *)param; + ff->func = fetch_stack; + ff->data = (void *)param; } } else ret = -EINVAL; @@ -572,8 +488,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, } /* Recursive argument parser */ -static int __parse_probe_arg(char *arg, const struct fetch_type *t, - struct fetch_param *f, int is_return) +static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) { int ret = 0; unsigned long param; @@ -582,13 +497,13 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t, switch (arg[0]) { case '$': - ret = parse_probe_vars(arg + 1, t, f, is_return); + ret = parse_probe_vars(arg + 1, ff, is_return); break; case '%': /* named register */ ret = regs_query_register_offset(arg + 1); if (ret >= 0) { - f->fn = t->reg; - f->data = (void *)(unsigned long)ret; + ff->func = fetch_register; + ff->data = (void *)(unsigned long)ret; ret = 0; } break; @@ -597,22 +512,26 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t, ret = strict_strtoul(arg + 1, 0, ¶m); if (ret) break; - f->fn = t->memory; - f->data = (void *)param; + ff->func = fetch_memory; + ff->data = (void *)param; } else { ret = split_symbol_offset(arg + 1, &offset); if (ret) break; - f->data = alloc_symbol_cache(arg + 1, offset); - if (f->data) - f->fn = t->symbol; + ff->data = alloc_symbol_cache(arg + 1, offset); + if (ff->data) + ff->func = fetch_symbol; + else + ret = -EINVAL; } break; - case '+': /* deref memory */ + case '+': /* indirect memory */ case '-': tmp = strchr(arg, '('); - if (!tmp) + if (!tmp) { + ret = -EINVAL; break; + } *tmp = '\0'; ret = strict_strtol(arg + 1, 0, &offset); if (ret) @@ -622,58 +541,38 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t, arg = tmp + 1; tmp = strrchr(arg, ')'); if (tmp) { - struct deref_fetch_param *dprm; - const struct fetch_type *t2 = find_fetch_type(NULL); + struct indirect_fetch_data *id; *tmp = '\0'; - dprm = kzalloc(sizeof(struct deref_fetch_param), - GFP_KERNEL); - if (!dprm) + id = kzalloc(sizeof(struct indirect_fetch_data), + GFP_KERNEL); + if (!id) return -ENOMEM; - dprm->offset = offset; - ret = __parse_probe_arg(arg, t2, &dprm->orig, - is_return); + id->offset = offset; + ret = __parse_probe_arg(arg, &id->orig, is_return); if (ret) - kfree(dprm); + kfree(id); else { - f->fn = t->deref; - f->data = (void *)dprm; + ff->func = fetch_indirect; + ff->data = (void *)id; } - } + } else + ret = -EINVAL; break; - } - if (!ret && !f->fn) + default: + /* TODO: support custom handler */ ret = -EINVAL; + } return ret; } /* String length checking wrapper */ -static int parse_probe_arg(char *arg, struct trace_probe *tp, - struct probe_arg *parg, int is_return) +static int parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) { - const char *t; - if (strlen(arg) > MAX_ARGSTR_LEN) { pr_info("Argument is too long.: %s\n", arg); return -ENOSPC; } - parg->comm = kstrdup(arg, GFP_KERNEL); - if (!parg->comm) { - pr_info("Failed to allocate memory for command '%s'.\n", arg); - return -ENOMEM; - } - t = strchr(parg->comm, ':'); - if (t) { - arg[t - parg->comm] = '\0'; - t++; - } - parg->type = find_fetch_type(t); - if (!parg->type) { - pr_info("Unsupported type: %s\n", t); - return -EINVAL; - } - parg->offset = tp->size; - tp->size += parg->type->size; - return __parse_probe_arg(arg, parg->type, &parg->fetch, is_return); + return __parse_probe_arg(arg, ff, is_return); } /* Return 1 if name is reserved or already used by another argument */ @@ -703,18 +602,15 @@ static int create_trace_probe(int argc, char **argv) * @ADDR : fetch memory at ADDR (ADDR should be in kernel) * @SYM[+|-offs] : fetch memory at SYM +|- offs (SYM is a data symbol) * %REG : fetch register REG - * Dereferencing memory fetch: + * Indirect memory fetch: * +|-offs(ARG) : fetch memory at ARG +|- offs address. * Alias name of args: * NAME=FETCHARG : set NAME as alias of FETCHARG. - * Type of args: - * FETCHARG:TYPE : use TYPE instead of unsigned long. */ struct trace_probe *tp; int i, ret = 0; int is_return = 0, is_delete = 0; - char *symbol = NULL, *event = NULL, *group = NULL; - char *arg, *tmp; + char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL; unsigned long offset = 0; void *addr = NULL; char buf[MAX_EVENT_NAME_LEN]; @@ -827,6 +723,13 @@ static int create_trace_probe(int argc, char **argv) else arg = argv[i]; + if (conflict_field_name(argv[i], tp->args, i)) { + pr_info("Argument%d name '%s' conflicts with " + "another field.\n", i, argv[i]); + ret = -EINVAL; + goto error; + } + tp->args[i].name = kstrdup(argv[i], GFP_KERNEL); if (!tp->args[i].name) { pr_info("Failed to allocate argument%d name '%s'.\n", @@ -834,19 +737,9 @@ static int create_trace_probe(int argc, char **argv) ret = -ENOMEM; goto error; } - tmp = strchr(tp->args[i].name, ':'); - if (tmp) - *tmp = '_'; /* convert : to _ */ - - if (conflict_field_name(tp->args[i].name, tp->args, i)) { - pr_info("Argument%d name '%s' conflicts with " - "another field.\n", i, argv[i]); - ret = -EINVAL; - goto error; - } /* Parse fetch argument */ - ret = parse_probe_arg(arg, tp, &tp->args[i], is_return); + ret = parse_probe_arg(arg, &tp->args[i].fetch, is_return); if (ret) { pr_info("Parse error at argument%d. (%d)\n", i, ret); kfree(tp->args[i].name); @@ -901,7 +794,8 @@ static void probes_seq_stop(struct seq_file *m, void *v) static int probes_seq_show(struct seq_file *m, void *v) { struct trace_probe *tp = v; - int i; + int i, ret; + char buf[MAX_ARGSTR_LEN + 1]; seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p'); seq_printf(m, ":%s/%s", tp->call.system, tp->call.name); @@ -913,10 +807,15 @@ static int probes_seq_show(struct seq_file *m, void *v) else seq_printf(m, " %s", probe_symbol(tp)); - for (i = 0; i < tp->nr_args; i++) - seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm); + for (i = 0; i < tp->nr_args; i++) { + ret = probe_arg_string(buf, MAX_ARGSTR_LEN, &tp->args[i].fetch); + if (ret < 0) { + pr_warning("Argument%d decoding error(%d).\n", i, ret); + return ret; + } + seq_printf(m, " %s=%s", tp->args[i].name, buf); + } seq_printf(m, "\n"); - return 0; } @@ -1046,10 +945,9 @@ static const struct file_operations kprobe_profile_ops = { static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) { struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); - struct kprobe_trace_entry_head *entry; + struct kprobe_trace_entry *entry; struct ring_buffer_event *event; struct ring_buffer *buffer; - u8 *data; int size, i, pc; unsigned long irq_flags; struct ftrace_event_call *call = &tp->call; @@ -1059,7 +957,7 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) local_save_flags(irq_flags); pc = preempt_count(); - size = sizeof(*entry) + tp->size; + size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args); event = trace_current_buffer_lock_reserve(&buffer, call->id, size, irq_flags, pc); @@ -1067,10 +965,10 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) return; entry = ring_buffer_event_data(event); + entry->nargs = tp->nr_args; entry->ip = (unsigned long)kp->addr; - data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); + entry->args[i] = call_fetch(&tp->args[i].fetch, regs); if (!filter_current_check_discard(buffer, call, entry, event)) trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); @@ -1081,10 +979,9 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, struct pt_regs *regs) { struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); - struct kretprobe_trace_entry_head *entry; + struct kretprobe_trace_entry *entry; struct ring_buffer_event *event; struct ring_buffer *buffer; - u8 *data; int size, i, pc; unsigned long irq_flags; struct ftrace_event_call *call = &tp->call; @@ -1092,7 +989,7 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, local_save_flags(irq_flags); pc = preempt_count(); - size = sizeof(*entry) + tp->size; + size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args); event = trace_current_buffer_lock_reserve(&buffer, call->id, size, irq_flags, pc); @@ -1100,11 +997,11 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, return; entry = ring_buffer_event_data(event); + entry->nargs = tp->nr_args; entry->func = (unsigned long)tp->rp.kp.addr; entry->ret_ip = (unsigned long)ri->ret_addr; - data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); + entry->args[i] = call_fetch(&tp->args[i].fetch, regs); if (!filter_current_check_discard(buffer, call, entry, event)) trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); @@ -1114,14 +1011,13 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, enum print_line_t print_kprobe_event(struct trace_iterator *iter, int flags) { - struct kprobe_trace_entry_head *field; + struct kprobe_trace_entry *field; struct trace_seq *s = &iter->seq; struct trace_event *event; struct trace_probe *tp; - u8 *data; int i; - field = (struct kprobe_trace_entry_head *)iter->ent; + field = (struct kprobe_trace_entry *)iter->ent; event = ftrace_find_event(field->ent.type); tp = container_of(event, struct trace_probe, event); @@ -1134,10 +1030,9 @@ print_kprobe_event(struct trace_iterator *iter, int flags) if (!trace_seq_puts(s, ")")) goto partial; - data = (u8 *)&field[1]; - for (i = 0; i < tp->nr_args; i++) - if (!tp->args[i].type->print(s, tp->args[i].name, - data + tp->args[i].offset)) + for (i = 0; i < field->nargs; i++) + if (!trace_seq_printf(s, " %s=%lx", + tp->args[i].name, field->args[i])) goto partial; if (!trace_seq_puts(s, "\n")) @@ -1151,14 +1046,13 @@ print_kprobe_event(struct trace_iterator *iter, int flags) enum print_line_t print_kretprobe_event(struct trace_iterator *iter, int flags) { - struct kretprobe_trace_entry_head *field; + struct kretprobe_trace_entry *field; struct trace_seq *s = &iter->seq; struct trace_event *event; struct trace_probe *tp; - u8 *data; int i; - field = (struct kretprobe_trace_entry_head *)iter->ent; + field = (struct kretprobe_trace_entry *)iter->ent; event = ftrace_find_event(field->ent.type); tp = container_of(event, struct trace_probe, event); @@ -1177,10 +1071,9 @@ print_kretprobe_event(struct trace_iterator *iter, int flags) if (!trace_seq_puts(s, ")")) goto partial; - data = (u8 *)&field[1]; - for (i = 0; i < tp->nr_args; i++) - if (!tp->args[i].type->print(s, tp->args[i].name, - data + tp->args[i].offset)) + for (i = 0; i < field->nargs; i++) + if (!trace_seq_printf(s, " %s=%lx", + tp->args[i].name, field->args[i])) goto partial; if (!trace_seq_puts(s, "\n")) @@ -1236,43 +1129,29 @@ static int probe_event_raw_init(struct ftrace_event_call *event_call) static int kprobe_event_define_fields(struct ftrace_event_call *event_call) { int ret, i; - struct kprobe_trace_entry_head field; + struct kprobe_trace_entry field; struct trace_probe *tp = (struct trace_probe *)event_call->data; DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); + DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1); /* Set argument names as fields */ - for (i = 0; i < tp->nr_args; i++) { - ret = trace_define_field(event_call, tp->args[i].type->name, - tp->args[i].name, - sizeof(field) + tp->args[i].offset, - tp->args[i].type->size, - tp->args[i].type->is_signed, - FILTER_OTHER); - if (ret) - return ret; - } + for (i = 0; i < tp->nr_args; i++) + DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0); return 0; } static int kretprobe_event_define_fields(struct ftrace_event_call *event_call) { int ret, i; - struct kretprobe_trace_entry_head field; + struct kretprobe_trace_entry field; struct trace_probe *tp = (struct trace_probe *)event_call->data; DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0); DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0); + DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1); /* Set argument names as fields */ - for (i = 0; i < tp->nr_args; i++) { - ret = trace_define_field(event_call, tp->args[i].type->name, - tp->args[i].name, - sizeof(field) + tp->args[i].offset, - tp->args[i].type->size, - tp->args[i].type->is_signed, - FILTER_OTHER); - if (ret) - return ret; - } + for (i = 0; i < tp->nr_args; i++) + DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0); return 0; } @@ -1297,8 +1176,8 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len) pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt); for (i = 0; i < tp->nr_args; i++) { - pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s", - tp->args[i].name, tp->args[i].type->fmt); + pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%%lx", + tp->args[i].name); } pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg); @@ -1340,13 +1219,12 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp, { struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); struct ftrace_event_call *call = &tp->call; - struct kprobe_trace_entry_head *entry; - u8 *data; + struct kprobe_trace_entry *entry; int size, __size, i; unsigned long irq_flags; int rctx; - __size = sizeof(*entry) + tp->size; + __size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args); size = ALIGN(__size + sizeof(u32), sizeof(u64)); size -= sizeof(u32); if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, @@ -1357,10 +1235,10 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp, if (!entry) return; + entry->nargs = tp->nr_args; entry->ip = (unsigned long)kp->addr; - data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); + entry->args[i] = call_fetch(&tp->args[i].fetch, regs); perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, irq_flags, regs); } @@ -1371,13 +1249,12 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, { struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); struct ftrace_event_call *call = &tp->call; - struct kretprobe_trace_entry_head *entry; - u8 *data; + struct kretprobe_trace_entry *entry; int size, __size, i; unsigned long irq_flags; int rctx; - __size = sizeof(*entry) + tp->size; + __size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args); size = ALIGN(__size + sizeof(u32), sizeof(u64)); size -= sizeof(u32); if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, @@ -1388,11 +1265,11 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, if (!entry) return; + entry->nargs = tp->nr_args; entry->func = (unsigned long)tp->rp.kp.addr; entry->ret_ip = (unsigned long)ri->ret_addr; - data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); + entry->args[i] = call_fetch(&tp->args[i].fetch, regs); perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, irq_flags, regs); diff --git a/trunk/kernel/trace/trace_ksym.c b/trunk/kernel/trace/trace_ksym.c index 8eaf00749b65..d59cd6879477 100644 --- a/trunk/kernel/trace/trace_ksym.c +++ b/trunk/kernel/trace/trace_ksym.c @@ -34,6 +34,12 @@ #include +/* + * For now, let us restrict the no. of symbols traced simultaneously to number + * of available hardware breakpoint registers. + */ +#define KSYM_TRACER_MAX HBP_NUM + #define KSYM_TRACER_OP_LEN 3 /* rw- */ struct trace_ksym { @@ -47,6 +53,7 @@ struct trace_ksym { static struct trace_array *ksym_trace_array; +static unsigned int ksym_filter_entry_count; static unsigned int ksym_tracing_enabled; static HLIST_HEAD(ksym_filter_head); @@ -174,6 +181,13 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) struct trace_ksym *entry; int ret = -ENOMEM; + if (ksym_filter_entry_count >= KSYM_TRACER_MAX) { + printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No" + " new requests for tracing can be accepted now.\n", + KSYM_TRACER_MAX); + return -ENOSPC; + } + entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -189,17 +203,13 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) if (IS_ERR(entry->ksym_hbp)) { ret = PTR_ERR(entry->ksym_hbp); - if (ret == -ENOSPC) { - printk(KERN_ERR "ksym_tracer: Maximum limit reached." - " No new requests for tracing can be accepted now.\n"); - } else { - printk(KERN_INFO "ksym_tracer request failed. Try again" - " later!!\n"); - } + printk(KERN_INFO "ksym_tracer request failed. Try again" + " later!!\n"); goto err; } hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head); + ksym_filter_entry_count++; return 0; @@ -255,6 +265,7 @@ static void __ksym_trace_reset(void) hlist_for_each_entry_safe(entry, node, node1, &ksym_filter_head, ksym_hlist) { unregister_wide_hw_breakpoint(entry->ksym_hbp); + ksym_filter_entry_count--; hlist_del_rcu(&(entry->ksym_hlist)); synchronize_rcu(); kfree(entry); @@ -327,6 +338,7 @@ static ssize_t ksym_trace_filter_write(struct file *file, goto out_unlock; } /* Error or "symbol:---" case: drop it */ + ksym_filter_entry_count--; hlist_del_rcu(&(entry->ksym_hlist)); synchronize_rcu(); kfree(entry); diff --git a/trunk/kernel/trace/trace_selftest.c b/trunk/kernel/trace/trace_selftest.c index 1cc9858258b3..81003b4d617f 100644 --- a/trunk/kernel/trace/trace_selftest.c +++ b/trunk/kernel/trace/trace_selftest.c @@ -17,6 +17,7 @@ static inline int trace_valid_entry(struct trace_entry *entry) case TRACE_BRANCH: case TRACE_GRAPH_ENT: case TRACE_GRAPH_RET: + case TRACE_HW_BRANCHES: case TRACE_KSYM: return 1; } @@ -754,6 +755,62 @@ trace_selftest_startup_branch(struct tracer *trace, struct trace_array *tr) } #endif /* CONFIG_BRANCH_TRACER */ +#ifdef CONFIG_HW_BRANCH_TRACER +int +trace_selftest_startup_hw_branches(struct tracer *trace, + struct trace_array *tr) +{ + struct trace_iterator *iter; + struct tracer tracer; + unsigned long count; + int ret; + + if (!trace->open) { + printk(KERN_CONT "missing open function..."); + return -1; + } + + ret = tracer_init(trace, tr); + if (ret) { + warn_failed_init_tracer(trace, ret); + return ret; + } + + /* + * The hw-branch tracer needs to collect the trace from the various + * cpu trace buffers - before tracing is stopped. + */ + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + memcpy(&tracer, trace, sizeof(tracer)); + + iter->trace = &tracer; + iter->tr = tr; + iter->pos = -1; + mutex_init(&iter->mutex); + + trace->open(iter); + + mutex_destroy(&iter->mutex); + kfree(iter); + + tracing_stop(); + + ret = trace_test_buffer(tr, &count); + trace->reset(tr); + tracing_start(); + + if (!ret && !count) { + printk(KERN_CONT "no entries found.."); + ret = -1; + } + + return ret; +} +#endif /* CONFIG_HW_BRANCH_TRACER */ + #ifdef CONFIG_KSYM_TRACER static int ksym_selftest_dummy; diff --git a/trunk/kernel/workqueue.c b/trunk/kernel/workqueue.c index dee48658805c..5bfb213984b2 100644 --- a/trunk/kernel/workqueue.c +++ b/trunk/kernel/workqueue.c @@ -774,7 +774,7 @@ void flush_delayed_work(struct delayed_work *dwork) { if (del_timer_sync(&dwork->timer)) { struct cpu_workqueue_struct *cwq; - cwq = wq_per_cpu(keventd_wq, get_cpu()); + cwq = wq_per_cpu(get_wq_data(&dwork->work)->wq, get_cpu()); __queue_work(cwq, &dwork->work); put_cpu(); } diff --git a/trunk/mm/mlock.c b/trunk/mm/mlock.c index 3f82720e0515..8f4e2dfceec1 100644 --- a/trunk/mm/mlock.c +++ b/trunk/mm/mlock.c @@ -607,3 +607,44 @@ void user_shm_unlock(size_t size, struct user_struct *user) spin_unlock(&shmlock_user_lock); free_uid(user); } + +int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim, + size_t size) +{ + unsigned long lim, vm, pgsz; + int error = -ENOMEM; + + pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT; + + down_write(&mm->mmap_sem); + + lim = ACCESS_ONCE(rlim[RLIMIT_AS].rlim_cur) >> PAGE_SHIFT; + vm = mm->total_vm + pgsz; + if (lim < vm) + goto out; + + lim = ACCESS_ONCE(rlim[RLIMIT_MEMLOCK].rlim_cur) >> PAGE_SHIFT; + vm = mm->locked_vm + pgsz; + if (lim < vm) + goto out; + + mm->total_vm += pgsz; + mm->locked_vm += pgsz; + + error = 0; + out: + up_write(&mm->mmap_sem); + return error; +} + +void refund_locked_memory(struct mm_struct *mm, size_t size) +{ + unsigned long pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT; + + down_write(&mm->mmap_sem); + + mm->total_vm -= pgsz; + mm->locked_vm -= pgsz; + + up_write(&mm->mmap_sem); +} diff --git a/trunk/mm/slub.c b/trunk/mm/slub.c index 7d6c8b1ccf63..d2a54fe71ea2 100644 --- a/trunk/mm/slub.c +++ b/trunk/mm/slub.c @@ -2153,7 +2153,7 @@ static int init_kmem_cache_nodes(struct kmem_cache *s, gfp_t gfpflags) int local_node; if (slab_state >= UP && (s < kmalloc_caches || - s > kmalloc_caches + KMALLOC_CACHES)) + s >= kmalloc_caches + KMALLOC_CACHES)) local_node = page_to_nid(virt_to_page(s)); else local_node = 0; diff --git a/trunk/net/ipv6/af_inet6.c b/trunk/net/ipv6/af_inet6.c index 3192aa02ba5d..3f9e86b15e0d 100644 --- a/trunk/net/ipv6/af_inet6.c +++ b/trunk/net/ipv6/af_inet6.c @@ -200,7 +200,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, inet_sk(sk)->pinet6 = np = inet6_sk_generic(sk); np->hop_limit = -1; - np->mcast_hops = -1; + np->mcast_hops = IPV6_DEFAULT_MCASTHOPS; np->mc_loop = 1; np->pmtudisc = IPV6_PMTUDISC_WANT; np->ipv6only = net->ipv6.sysctl.bindv6only; diff --git a/trunk/net/sctp/sm_make_chunk.c b/trunk/net/sctp/sm_make_chunk.c index 0fd5b4c88358..30c1767186b8 100644 --- a/trunk/net/sctp/sm_make_chunk.c +++ b/trunk/net/sctp/sm_make_chunk.c @@ -108,7 +108,7 @@ static const struct sctp_paramhdr prsctp_param = { cpu_to_be16(sizeof(struct sctp_paramhdr)), }; -/* A helper to initialize to initialize an op error inside a +/* A helper to initialize an op error inside a * provided chunk, as most cause codes will be embedded inside an * abort chunk. */ @@ -125,6 +125,29 @@ void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err); } +/* A helper to initialize an op error inside a + * provided chunk, as most cause codes will be embedded inside an + * abort chunk. Differs from sctp_init_cause in that it won't oops + * if there isn't enough space in the op error chunk + */ +int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code, + size_t paylen) +{ + sctp_errhdr_t err; + __u16 len; + + /* Cause code constants are now defined in network order. */ + err.cause = cause_code; + len = sizeof(sctp_errhdr_t) + paylen; + err.length = htons(len); + + if (skb_tailroom(chunk->skb) > len) + return -ENOSPC; + chunk->subh.err_hdr = sctp_addto_chunk_fixed(chunk, + sizeof(sctp_errhdr_t), + &err); + return 0; +} /* 3.3.2 Initiation (INIT) (1) * * This chunk is used to initiate a SCTP association between two @@ -1132,6 +1155,24 @@ static struct sctp_chunk *sctp_make_op_error_space( return retval; } +/* Create an Operation Error chunk of a fixed size, + * specifically, max(asoc->pathmtu, SCTP_DEFAULT_MAXSEGMENT) + * This is a helper function to allocate an error chunk for + * for those invalid parameter codes in which we may not want + * to report all the errors, if the incomming chunk is large + */ +static inline struct sctp_chunk *sctp_make_op_error_fixed( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) +{ + size_t size = asoc ? asoc->pathmtu : 0; + + if (!size) + size = SCTP_DEFAULT_MAXSEGMENT; + + return sctp_make_op_error_space(asoc, chunk, size); +} + /* Create an Operation Error chunk. */ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, const struct sctp_chunk *chunk, @@ -1374,6 +1415,18 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data) return target; } +/* Append bytes to the end of a chunk. Returns NULL if there isn't sufficient + * space in the chunk + */ +void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk, + int len, const void *data) +{ + if (skb_tailroom(chunk->skb) > len) + return sctp_addto_chunk(chunk, len, data); + else + return NULL; +} + /* Append bytes from user space to the end of a chunk. Will panic if * chunk is not big enough. * Returns a kernel err value. @@ -1977,13 +2030,12 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, * returning multiple unknown parameters. */ if (NULL == *errp) - *errp = sctp_make_op_error_space(asoc, chunk, - ntohs(chunk->chunk_hdr->length)); + *errp = sctp_make_op_error_fixed(asoc, chunk); if (*errp) { - sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM, + sctp_init_cause_fixed(*errp, SCTP_ERROR_UNKNOWN_PARAM, WORD_ROUND(ntohs(param.p->length))); - sctp_addto_chunk(*errp, + sctp_addto_chunk_fixed(*errp, WORD_ROUND(ntohs(param.p->length)), param.v); } else { diff --git a/trunk/security/keys/gc.c b/trunk/security/keys/gc.c index 19902319d097..a46e825cbf02 100644 --- a/trunk/security/keys/gc.c +++ b/trunk/security/keys/gc.c @@ -77,10 +77,10 @@ static bool key_gc_keyring(struct key *keyring, time_t limit) goto dont_gc; /* scan the keyring looking for dead keys */ - klist = rcu_dereference_check(keyring->payload.subscriptions, - lockdep_is_held(&key_serial_lock)); + rcu_read_lock(); + klist = rcu_dereference(keyring->payload.subscriptions); if (!klist) - goto dont_gc; + goto unlock_dont_gc; for (loop = klist->nkeys - 1; loop >= 0; loop--) { key = klist->keys[loop]; @@ -89,11 +89,14 @@ static bool key_gc_keyring(struct key *keyring, time_t limit) goto do_gc; } +unlock_dont_gc: + rcu_read_unlock(); dont_gc: kleave(" = false"); return false; do_gc: + rcu_read_unlock(); key_gc_cursor = keyring->serial; key_get(keyring); spin_unlock(&key_serial_lock); diff --git a/trunk/security/keys/keyring.c b/trunk/security/keys/keyring.c index dd7cd0f8e13c..1e4b0037935c 100644 --- a/trunk/security/keys/keyring.c +++ b/trunk/security/keys/keyring.c @@ -20,6 +20,11 @@ #include #include "internal.h" +#define rcu_dereference_locked_keyring(keyring) \ + (rcu_dereference_protected( \ + (keyring)->payload.subscriptions, \ + rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) + /* * when plumbing the depths of the key tree, this sets a hard limit set on how * deep we're willing to go @@ -201,8 +206,7 @@ static long keyring_read(const struct key *keyring, int loop, ret; ret = 0; - klist = keyring->payload.subscriptions; - + klist = rcu_dereference_locked_keyring(keyring); if (klist) { /* calculate how much data we could return */ qty = klist->nkeys * sizeof(key_serial_t); @@ -526,9 +530,8 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) struct key *keyring; int bucket; - keyring = ERR_PTR(-EINVAL); if (!name) - goto error; + return ERR_PTR(-EINVAL); bucket = keyring_hash(name); @@ -555,17 +558,18 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) KEY_SEARCH) < 0) continue; - /* we've got a match */ - atomic_inc(&keyring->usage); - read_unlock(&keyring_name_lock); - goto error; + /* we've got a match but we might end up racing with + * key_cleanup() if the keyring is currently 'dead' + * (ie. it has a zero usage count) */ + if (!atomic_inc_not_zero(&keyring->usage)) + continue; + goto out; } } - read_unlock(&keyring_name_lock); keyring = ERR_PTR(-ENOKEY); - - error: +out: + read_unlock(&keyring_name_lock); return keyring; } /* end find_keyring_by_name() */ @@ -720,8 +724,7 @@ int __key_link(struct key *keyring, struct key *key) } /* see if there's a matching key we can displace */ - klist = keyring->payload.subscriptions; - + klist = rcu_dereference_locked_keyring(keyring); if (klist && klist->nkeys > 0) { struct key_type *type = key->type; @@ -765,8 +768,6 @@ int __key_link(struct key *keyring, struct key *key) if (ret < 0) goto error2; - klist = keyring->payload.subscriptions; - if (klist && klist->nkeys < klist->maxkeys) { /* there's sufficient slack space to add directly */ atomic_inc(&key->usage); @@ -868,7 +869,7 @@ int key_unlink(struct key *keyring, struct key *key) down_write(&keyring->sem); - klist = keyring->payload.subscriptions; + klist = rcu_dereference_locked_keyring(keyring); if (klist) { /* search the keyring for the key */ for (loop = 0; loop < klist->nkeys; loop++) @@ -959,7 +960,7 @@ int keyring_clear(struct key *keyring) /* detach the pointer block with the locks held */ down_write(&keyring->sem); - klist = keyring->payload.subscriptions; + klist = rcu_dereference_locked_keyring(keyring); if (klist) { /* adjust the quota */ key_payload_reserve(keyring, @@ -991,7 +992,9 @@ EXPORT_SYMBOL(keyring_clear); */ static void keyring_revoke(struct key *keyring) { - struct keyring_list *klist = keyring->payload.subscriptions; + struct keyring_list *klist; + + klist = rcu_dereference_locked_keyring(keyring); /* adjust the quota */ key_payload_reserve(keyring, 0); @@ -1025,7 +1028,7 @@ void keyring_gc(struct key *keyring, time_t limit) down_write(&keyring->sem); - klist = keyring->payload.subscriptions; + klist = rcu_dereference_locked_keyring(keyring); if (!klist) goto no_klist; diff --git a/trunk/security/keys/request_key.c b/trunk/security/keys/request_key.c index d737cea5347c..d8c1a6a0fb08 100644 --- a/trunk/security/keys/request_key.c +++ b/trunk/security/keys/request_key.c @@ -94,7 +94,7 @@ static int call_sbin_request_key(struct key_construction *cons, } /* attach the auth key to the session keyring */ - ret = __key_link(keyring, authkey); + ret = key_link(keyring, authkey); if (ret < 0) goto error_link; diff --git a/trunk/security/keys/user_defined.c b/trunk/security/keys/user_defined.c index 7c687d568221..e9aa07929656 100644 --- a/trunk/security/keys/user_defined.c +++ b/trunk/security/keys/user_defined.c @@ -199,7 +199,8 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen) struct user_key_payload *upayload; long ret; - upayload = rcu_dereference(key->payload.data); + upayload = rcu_dereference_protected( + key->payload.data, rwsem_is_locked(&((struct key *)key)->sem)); ret = upayload->datalen; /* we can return the data as is */ diff --git a/trunk/sound/core/timer.c b/trunk/sound/core/timer.c index 73943651caed..5040c7b862fe 100644 --- a/trunk/sound/core/timer.c +++ b/trunk/sound/core/timer.c @@ -1160,6 +1160,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, { struct snd_timer_user *tu = timeri->callback_data; struct snd_timer_tread r1; + unsigned long flags; if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE) @@ -1169,9 +1170,9 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, r1.event = event; r1.tstamp = *tstamp; r1.val = resolution; - spin_lock(&tu->qlock); + spin_lock_irqsave(&tu->qlock, flags); snd_timer_user_append_to_tqueue(tu, &r1); - spin_unlock(&tu->qlock); + spin_unlock_irqrestore(&tu->qlock, flags); kill_fasync(&tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); } diff --git a/trunk/sound/isa/sb/es968.c b/trunk/sound/isa/sb/es968.c index cafc3a7316a8..ff18286fef9d 100644 --- a/trunk/sound/isa/sb/es968.c +++ b/trunk/sound/isa/sb/es968.c @@ -93,7 +93,7 @@ static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard, return err; } port[dev] = pnp_port_start(pdev, 0); - dma8[dev] = pnp_dma(pdev, 1); + dma8[dev] = pnp_dma(pdev, 0); irq[dev] = pnp_irq(pdev, 0); return 0; diff --git a/trunk/sound/pci/hda/patch_cirrus.c b/trunk/sound/pci/hda/patch_cirrus.c index 7de782a5b8f4..350ee8ac4153 100644 --- a/trunk/sound/pci/hda/patch_cirrus.c +++ b/trunk/sound/pci/hda/patch_cirrus.c @@ -766,7 +766,7 @@ static int build_input(struct hda_codec *codec) for (n = 0; n < AUTO_PIN_LAST; n++) { if (!spec->adc_nid[n]) continue; - err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[i]); + err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); if (err < 0) return err; } diff --git a/trunk/sound/pci/hda/patch_conexant.c b/trunk/sound/pci/hda/patch_conexant.c index 61682e1d09da..56e52071c769 100644 --- a/trunk/sound/pci/hda/patch_conexant.c +++ b/trunk/sound/pci/hda/patch_conexant.c @@ -1195,9 +1195,10 @@ static int patch_cxt5045(struct hda_codec *codec) switch (codec->subsystem_id >> 16) { case 0x103c: + case 0x1631: case 0x1734: - /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB - * on NID 0x17. Fix max PCM level to 0 dB + /* HP, Packard Bell, & Fujitsu-Siemens laptops have really bad + * sound over 0dB on NID 0x17. Fix max PCM level to 0 dB * (originally it has 0x2b steps with 0dB offset 0x14) */ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, @@ -2842,6 +2843,9 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { CXT5066_DELL_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), + SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), + SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), {} }; diff --git a/trunk/sound/soc/txx9/txx9aclc-ac97.c b/trunk/sound/soc/txx9/txx9aclc-ac97.c index 612e18b4bf4e..0ec20b68e8cb 100644 --- a/trunk/sound/soc/txx9/txx9aclc-ac97.c +++ b/trunk/sound/soc/txx9/txx9aclc-ac97.c @@ -254,3 +254,4 @@ module_exit(txx9aclc_ac97_exit); MODULE_AUTHOR("Atsushi Nemoto "); MODULE_DESCRIPTION("TXx9 ACLC AC97 driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:txx9aclc-ac97"); diff --git a/trunk/sound/soc/txx9/txx9aclc-generic.c b/trunk/sound/soc/txx9/txx9aclc-generic.c index 3175de9a92cb..95b17f731aec 100644 --- a/trunk/sound/soc/txx9/txx9aclc-generic.c +++ b/trunk/sound/soc/txx9/txx9aclc-generic.c @@ -96,3 +96,4 @@ module_exit(txx9aclc_generic_exit); MODULE_AUTHOR("Atsushi Nemoto "); MODULE_DESCRIPTION("Generic TXx9 ACLC ALSA SoC audio driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:txx9aclc-generic"); diff --git a/trunk/tools/perf/Documentation/perf-annotate.txt b/trunk/tools/perf/Documentation/perf-annotate.txt index 5164a655c39f..c9dcade06831 100644 --- a/trunk/tools/perf/Documentation/perf-annotate.txt +++ b/trunk/tools/perf/Documentation/perf-annotate.txt @@ -1,5 +1,5 @@ perf-annotate(1) -================ +============== NAME ---- diff --git a/trunk/tools/perf/Documentation/perf-bench.txt b/trunk/tools/perf/Documentation/perf-bench.txt index a3dbadb26ef5..ae525ac5a2ce 100644 --- a/trunk/tools/perf/Documentation/perf-bench.txt +++ b/trunk/tools/perf/Documentation/perf-bench.txt @@ -1,5 +1,5 @@ perf-bench(1) -============= +============ NAME ---- @@ -19,12 +19,12 @@ COMMON OPTIONS -f:: --format=:: Specify format style. -Current available format styles are: +Current available format styles are, 'default':: Default style. This is mainly for human reading. --------------------- -% perf bench sched pipe # with no style specified +% perf bench sched pipe # with no style specify (executing 1000000 pipe operations between two tasks) Total time:5.855 sec 5.855061 usecs/op @@ -79,7 +79,7 @@ options (20 sender and receiver processes per group) Total time:0.308 sec -% perf bench sched messaging -t -g 20 # be multi-thread, with 20 groups +% perf bench sched messaging -t -g 20 # be multi-thread,with 20 groups (20 sender and receiver threads per group) (20 groups == 800 threads run) diff --git a/trunk/tools/perf/Documentation/perf-buildid-cache.txt b/trunk/tools/perf/Documentation/perf-buildid-cache.txt index 5d1a9500277f..88bc3b519746 100644 --- a/trunk/tools/perf/Documentation/perf-buildid-cache.txt +++ b/trunk/tools/perf/Documentation/perf-buildid-cache.txt @@ -8,7 +8,7 @@ perf-buildid-cache - Manage build-id cache. SYNOPSIS -------- [verse] -'perf buildid-cache ' +'perf buildid-list ' DESCRIPTION ----------- @@ -30,4 +30,4 @@ OPTIONS SEE ALSO -------- -linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1] +linkperf:perf-record[1], linkperf:perf-report[1] diff --git a/trunk/tools/perf/Documentation/perf-diff.txt b/trunk/tools/perf/Documentation/perf-diff.txt index 20d97d84ea1c..8974e208cba6 100644 --- a/trunk/tools/perf/Documentation/perf-diff.txt +++ b/trunk/tools/perf/Documentation/perf-diff.txt @@ -1,5 +1,5 @@ perf-diff(1) -============ +============== NAME ---- diff --git a/trunk/tools/perf/Documentation/perf-inject.txt b/trunk/tools/perf/Documentation/perf-inject.txt deleted file mode 100644 index 025630d43cd2..000000000000 --- a/trunk/tools/perf/Documentation/perf-inject.txt +++ /dev/null @@ -1,35 +0,0 @@ -perf-inject(1) -============== - -NAME ----- -perf-inject - Filter to augment the events stream with additional information - -SYNOPSIS --------- -[verse] -'perf inject ' - -DESCRIPTION ------------ -perf-inject reads a perf-record event stream and repipes it to stdout. At any -point the processing code can inject other events into the event stream - in -this case build-ids (-b option) are read and injected as needed into the event -stream. - -Build-ids are just the first user of perf-inject - potentially anything that -needs userspace processing to augment the events stream with additional -information could make use of this facility. - -OPTIONS -------- --b:: ---build-ids=:: - Inject build-ids into the output stream --v:: ---verbose:: - Be more verbose. - -SEE ALSO --------- -linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] diff --git a/trunk/tools/perf/Documentation/perf-kmem.txt b/trunk/tools/perf/Documentation/perf-kmem.txt index a52fcde894c7..eac4d852e7cd 100644 --- a/trunk/tools/perf/Documentation/perf-kmem.txt +++ b/trunk/tools/perf/Documentation/perf-kmem.txt @@ -1,5 +1,5 @@ perf-kmem(1) -============ +============== NAME ---- diff --git a/trunk/tools/perf/Documentation/perf-kvm.txt b/trunk/tools/perf/Documentation/perf-kvm.txt deleted file mode 100644 index d004e19fe6d6..000000000000 --- a/trunk/tools/perf/Documentation/perf-kvm.txt +++ /dev/null @@ -1,68 +0,0 @@ -perf-kvm(1) -=========== - -NAME ----- -perf-kvm - Tool to trace/measure kvm guest os - -SYNOPSIS --------- -[verse] -'perf kvm' [--host] [--guest] [--guestmount= - [--guestkallsyms= --guestmodules= | --guestvmlinux=]] - {top|record|report|diff|buildid-list} -'perf kvm' [--host] [--guest] [--guestkallsyms= --guestmodules= - | --guestvmlinux=] {top|record|report|diff|buildid-list} - -DESCRIPTION ------------ -There are a couple of variants of perf kvm: - - 'perf kvm [options] top ' to generates and displays - a performance counter profile of guest os in realtime - of an arbitrary workload. - - 'perf kvm record ' to record the performance couinter profile - of an arbitrary workload and save it into a perf data file. If both - --host and --guest are input, the perf data file name is perf.data.kvm. - If there is no --host but --guest, the file name is perf.data.guest. - If there is no --guest but --host, the file name is perf.data.host. - - 'perf kvm report' to display the performance counter profile information - recorded via perf kvm record. - - 'perf kvm diff' to displays the performance difference amongst two perf.data - files captured via perf record. - - 'perf kvm buildid-list' to display the buildids found in a perf data file, - so that other tools can be used to fetch packages with matching symbol tables - for use by perf report. - -OPTIONS -------- ---host=:: - Collect host side performance profile. ---guest=:: - Collect guest side performance profile. ---guestmount=:: - Guest os root file system mount directory. Users mounts guest os - root directories under by a specific filesystem access method, - typically, sshfs. For example, start 2 guest os. The one's pid is 8888 - and the other's is 9999. - #mkdir ~/guestmount; cd ~/guestmount - #sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/ - #sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/ - #perf kvm --host --guest --guestmount=~/guestmount top ---guestkallsyms=:: - Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest - kernel symbols. Users copy it out from guest os. ---guestmodules=:: - Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest - kernel module information. Users copy it out from guest os. ---guestvmlinux=:: - Guest os kernel vmlinux. - -SEE ALSO --------- -linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1], -linkperf:perf-diff[1], linkperf:perf-buildid-list[1] diff --git a/trunk/tools/perf/Documentation/perf-list.txt b/trunk/tools/perf/Documentation/perf-list.txt index ad765e0b8860..8290b9422668 100644 --- a/trunk/tools/perf/Documentation/perf-list.txt +++ b/trunk/tools/perf/Documentation/perf-list.txt @@ -15,24 +15,6 @@ DESCRIPTION This command displays the symbolic event types which can be selected in the various perf commands with the -e option. -RAW HARDWARE EVENT DESCRIPTOR ------------------------------ -Even when an event is not available in a symbolic form within perf right now, -it can be encoded as , for instance, if the Intel docs -describe an event as: - - Event Umask Event Mask - Num. Value Mnemonic Description Comment - - A8H 01H LSD.UOPS Counts the number of micro-ops Use cmask=1 and - delivered by loop stream detector invert to count - cycles - -raw encoding of 0x1A8 can be used: - - perf stat -e r1a8 -a sleep 1 - perf record -e r1a8 ... - OPTIONS ------- None diff --git a/trunk/tools/perf/Documentation/perf-probe.txt b/trunk/tools/perf/Documentation/perf-probe.txt index 94a258c96a44..34202b1be0bb 100644 --- a/trunk/tools/perf/Documentation/perf-probe.txt +++ b/trunk/tools/perf/Documentation/perf-probe.txt @@ -57,14 +57,6 @@ OPTIONS --force:: Forcibly add events with existing name. --n:: ---dry-run:: - Dry run. With this option, --add and --del doesn't execute actual - adding and removal operations. - ---max-probes:: - Set the maximum number of probe points for an event. Default is 128. - PROBE SYNTAX ------------ Probe points are defined by following syntax. @@ -82,22 +74,13 @@ Probe points are defined by following syntax. 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'. 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function. It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern. -'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT). - -PROBE ARGUMENT --------------- -Each probe argument follows below syntax. - - [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] - -'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) -'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. +'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). LINE SYNTAX ----------- Line range is descripted by following syntax. - "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]" + "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]" FUNC specifies the function name of showing lines. 'RLN' is the start line number from function entry line, and 'RLN2' is the end line number. As same as diff --git a/trunk/tools/perf/Documentation/perf-record.txt b/trunk/tools/perf/Documentation/perf-record.txt index 020d871c7934..fc46c0b40f6e 100644 --- a/trunk/tools/perf/Documentation/perf-record.txt +++ b/trunk/tools/perf/Documentation/perf-record.txt @@ -58,7 +58,7 @@ OPTIONS -f:: --force:: - Overwrite existing data file. (deprecated) + Overwrite existing data file. -c:: --count=:: @@ -101,7 +101,7 @@ OPTIONS -R:: --raw-samples:: -Collect raw sample records from all opened counters (default for tracepoint counters). +Collect raw sample records from all opened counters (typically for tracepoint counters). SEE ALSO -------- diff --git a/trunk/tools/perf/Documentation/perf-sched.txt b/trunk/tools/perf/Documentation/perf-sched.txt index 8417644a6166..1ce79198997b 100644 --- a/trunk/tools/perf/Documentation/perf-sched.txt +++ b/trunk/tools/perf/Documentation/perf-sched.txt @@ -12,7 +12,7 @@ SYNOPSIS DESCRIPTION ----------- -There are four variants of perf sched: +There's four variants of perf sched: 'perf sched record ' to record the scheduling events of an arbitrary workload. @@ -27,7 +27,7 @@ There are four variants of perf sched: via perf sched record. (this is done by starting up mockup threads that mimic the workload based on the events in the trace. These threads can then replay the timings (CPU runtime and sleep patterns) - of the workload as it occurred when it was recorded - and can repeat + of the workload as it occured when it was recorded - and can repeat it a number of times, measuring its performance.) OPTIONS diff --git a/trunk/tools/perf/Documentation/perf-test.txt b/trunk/tools/perf/Documentation/perf-test.txt deleted file mode 100644 index 1c4b5f5b7f71..000000000000 --- a/trunk/tools/perf/Documentation/perf-test.txt +++ /dev/null @@ -1,22 +0,0 @@ -perf-test(1) -============ - -NAME ----- -perf-test - Runs sanity tests. - -SYNOPSIS --------- -[verse] -'perf test ' - -DESCRIPTION ------------ -This command does assorted sanity tests, initially thru linked routines but -also will look for a directory with more tests in the form of scripts. - -OPTIONS -------- --v:: ---verbose:: - Be more verbose. diff --git a/trunk/tools/perf/Documentation/perf-trace-perl.txt b/trunk/tools/perf/Documentation/perf-trace-perl.txt index ee6525ee6d69..d729cee8d987 100644 --- a/trunk/tools/perf/Documentation/perf-trace-perl.txt +++ b/trunk/tools/perf/Documentation/perf-trace-perl.txt @@ -49,10 +49,12 @@ available as calls back into the perf executable (see below). As an example, the following perf record command can be used to record all sched_wakeup events in the system: - # perf record -a -e sched:sched_wakeup + # perf record -c 1 -f -a -M -R -e sched:sched_wakeup Traces meant to be processed using a script should be recorded with -the above option: -a to enable system-wide collection. +the above options: -c 1 says to sample every event, -a to enable +system-wide collection, -M to multiplex the output, and -R to collect +raw samples. The format file for the sched_wakep event defines the following fields (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): diff --git a/trunk/tools/perf/Documentation/perf-trace-python.txt b/trunk/tools/perf/Documentation/perf-trace-python.txt index 864aac283a7b..a241aca77184 100644 --- a/trunk/tools/perf/Documentation/perf-trace-python.txt +++ b/trunk/tools/perf/Documentation/perf-trace-python.txt @@ -1,5 +1,5 @@ perf-trace-python(1) -==================== +================== NAME ---- @@ -93,7 +93,7 @@ don't care how it exited, so we'll use 'perf record' to record only the sys_enter events: ---- -# perf record -a -e raw_syscalls:sys_enter +# perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ] @@ -359,7 +359,7 @@ your script: # cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record #!/bin/bash -perf record -a -e raw_syscalls:sys_enter +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter ---- The 'report' script is also a shell script with the same base name as @@ -449,10 +449,12 @@ available as calls back into the perf executable (see below). As an example, the following perf record command can be used to record all sched_wakeup events in the system: - # perf record -a -e sched:sched_wakeup + # perf record -c 1 -f -a -M -R -e sched:sched_wakeup Traces meant to be processed using a script should be recorded with -the above option: -a to enable system-wide collection. +the above options: -c 1 says to sample every event, -a to enable +system-wide collection, -M to multiplex the output, and -R to collect +raw samples. The format file for the sched_wakep event defines the following fields (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): diff --git a/trunk/tools/perf/Documentation/perf-trace.txt b/trunk/tools/perf/Documentation/perf-trace.txt index 122ec9dc4853..8879299cd9df 100644 --- a/trunk/tools/perf/Documentation/perf-trace.txt +++ b/trunk/tools/perf/Documentation/perf-trace.txt @@ -1,5 +1,5 @@ perf-trace(1) -============= +============== NAME ---- diff --git a/trunk/tools/perf/Makefile b/trunk/tools/perf/Makefile index 0ef5cfe52f2d..bc0f670a8338 100644 --- a/trunk/tools/perf/Makefile +++ b/trunk/tools/perf/Makefile @@ -1,7 +1,3 @@ -ifeq ("$(origin O)", "command line") - OUTPUT := $(O)/ -endif - # The default target of this Makefile is... all:: @@ -154,17 +150,10 @@ all:: # Define LDFLAGS=-static to build a static binary. # # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. -# -# Define NO_DWARF if you do not want debug-info analysis feature at all. -$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null) -$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) - -$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE - @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) --include $(OUTPUT)PERF-VERSION-FILE +PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE + @$(SHELL_PATH) util/PERF-VERSION-GEN +-include PERF-VERSION-FILE uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') @@ -173,22 +162,6 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ - -e s/arm.*/arm/ -e s/sa110/arm/ \ - -e s/s390x/s390/ -e s/parisc64/parisc/ \ - -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ - -e s/sh[234].*/sh/ ) - -# Additional ARCH settings for x86 -ifeq ($(ARCH),i386) - ARCH := x86 -endif -ifeq ($(ARCH),x86_64) - ARCH := x86 -endif - -$(shell sh -c 'mkdir -p $(OUTPUT)arch/$(ARCH)/util/' 2> /dev/null) - # CFLAGS and LDFLAGS are for the users to override from the command line. # @@ -301,7 +274,7 @@ endif # Those must not be GNU-specific; they are shared with perl/ which may # be built by a different compiler. (Note that this is an artifact now # but it still might be nice to keep that distinction.) -BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include +BASIC_CFLAGS = -Iutil/include BASIC_LDFLAGS = # Guard against environment variables @@ -335,7 +308,7 @@ PROGRAMS += $(EXTRA_PROGRAMS) # # Single 'perf' binary right now: # -PROGRAMS += $(OUTPUT)perf +PROGRAMS += perf # List built-in command $C whose implementation cmd_$C() is not in # builtin-$C.o but is linked in as part of some other command. @@ -345,7 +318,7 @@ PROGRAMS += $(OUTPUT)perf ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) # what 'all' will build but not install in perfexecdir -OTHER_PROGRAMS = $(OUTPUT)perf$X +OTHER_PROGRAMS = perf$X # Set paths to tools early so that they can be used for version tests. ifndef SHELL_PATH @@ -357,7 +330,7 @@ endif export PERL_PATH -LIB_FILE=$(OUTPUT)libperf.a +LIB_FILE=libperf.a LIB_H += ../../include/linux/perf_event.h LIB_H += ../../include/linux/rbtree.h @@ -377,13 +350,12 @@ LIB_H += util/include/linux/rbtree.h LIB_H += util/include/linux/string.h LIB_H += util/include/linux/types.h LIB_H += util/include/asm/asm-offsets.h +LIB_H += util/include/asm/bitops.h LIB_H += util/include/asm/bug.h LIB_H += util/include/asm/byteorder.h -LIB_H += util/include/asm/hweight.h LIB_H += util/include/asm/swab.h LIB_H += util/include/asm/system.h LIB_H += util/include/asm/uaccess.h -LIB_H += util/include/dwarf-regs.h LIB_H += perf.h LIB_H += util/cache.h LIB_H += util/callchain.h @@ -403,6 +375,7 @@ LIB_H += util/header.h LIB_H += util/help.h LIB_H += util/session.h LIB_H += util/strbuf.h +LIB_H += util/string.h LIB_H += util/strlist.h LIB_H += util/svghelper.h LIB_H += util/run-command.h @@ -418,79 +391,77 @@ LIB_H += util/probe-finder.h LIB_H += util/probe-event.h LIB_H += util/cpumap.h -LIB_OBJS += $(OUTPUT)util/abspath.o -LIB_OBJS += $(OUTPUT)util/alias.o -LIB_OBJS += $(OUTPUT)util/build-id.o -LIB_OBJS += $(OUTPUT)util/config.o -LIB_OBJS += $(OUTPUT)util/ctype.o -LIB_OBJS += $(OUTPUT)util/debugfs.o -LIB_OBJS += $(OUTPUT)util/environment.o -LIB_OBJS += $(OUTPUT)util/event.o -LIB_OBJS += $(OUTPUT)util/exec_cmd.o -LIB_OBJS += $(OUTPUT)util/help.o -LIB_OBJS += $(OUTPUT)util/levenshtein.o -LIB_OBJS += $(OUTPUT)util/parse-options.o -LIB_OBJS += $(OUTPUT)util/parse-events.o -LIB_OBJS += $(OUTPUT)util/path.o -LIB_OBJS += $(OUTPUT)util/rbtree.o -LIB_OBJS += $(OUTPUT)util/bitmap.o -LIB_OBJS += $(OUTPUT)util/hweight.o -LIB_OBJS += $(OUTPUT)util/run-command.o -LIB_OBJS += $(OUTPUT)util/quote.o -LIB_OBJS += $(OUTPUT)util/strbuf.o -LIB_OBJS += $(OUTPUT)util/string.o -LIB_OBJS += $(OUTPUT)util/strlist.o -LIB_OBJS += $(OUTPUT)util/usage.o -LIB_OBJS += $(OUTPUT)util/wrapper.o -LIB_OBJS += $(OUTPUT)util/sigchain.o -LIB_OBJS += $(OUTPUT)util/symbol.o -LIB_OBJS += $(OUTPUT)util/color.o -LIB_OBJS += $(OUTPUT)util/pager.o -LIB_OBJS += $(OUTPUT)util/header.o -LIB_OBJS += $(OUTPUT)util/callchain.o -LIB_OBJS += $(OUTPUT)util/values.o -LIB_OBJS += $(OUTPUT)util/debug.o -LIB_OBJS += $(OUTPUT)util/map.o -LIB_OBJS += $(OUTPUT)util/session.o -LIB_OBJS += $(OUTPUT)util/thread.o -LIB_OBJS += $(OUTPUT)util/trace-event-parse.o -LIB_OBJS += $(OUTPUT)util/trace-event-read.o -LIB_OBJS += $(OUTPUT)util/trace-event-info.o -LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o -LIB_OBJS += $(OUTPUT)util/svghelper.o -LIB_OBJS += $(OUTPUT)util/sort.o -LIB_OBJS += $(OUTPUT)util/hist.o -LIB_OBJS += $(OUTPUT)util/probe-event.o -LIB_OBJS += $(OUTPUT)util/util.o -LIB_OBJS += $(OUTPUT)util/cpumap.o - -BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o - -BUILTIN_OBJS += $(OUTPUT)builtin-bench.o +LIB_OBJS += util/abspath.o +LIB_OBJS += util/alias.o +LIB_OBJS += util/build-id.o +LIB_OBJS += util/config.o +LIB_OBJS += util/ctype.o +LIB_OBJS += util/debugfs.o +LIB_OBJS += util/environment.o +LIB_OBJS += util/event.o +LIB_OBJS += util/exec_cmd.o +LIB_OBJS += util/help.o +LIB_OBJS += util/levenshtein.o +LIB_OBJS += util/parse-options.o +LIB_OBJS += util/parse-events.o +LIB_OBJS += util/path.o +LIB_OBJS += util/rbtree.o +LIB_OBJS += util/bitmap.o +LIB_OBJS += util/hweight.o +LIB_OBJS += util/find_next_bit.o +LIB_OBJS += util/run-command.o +LIB_OBJS += util/quote.o +LIB_OBJS += util/strbuf.o +LIB_OBJS += util/string.o +LIB_OBJS += util/strlist.o +LIB_OBJS += util/usage.o +LIB_OBJS += util/wrapper.o +LIB_OBJS += util/sigchain.o +LIB_OBJS += util/symbol.o +LIB_OBJS += util/color.o +LIB_OBJS += util/pager.o +LIB_OBJS += util/header.o +LIB_OBJS += util/callchain.o +LIB_OBJS += util/values.o +LIB_OBJS += util/debug.o +LIB_OBJS += util/map.o +LIB_OBJS += util/session.o +LIB_OBJS += util/thread.o +LIB_OBJS += util/trace-event-parse.o +LIB_OBJS += util/trace-event-read.o +LIB_OBJS += util/trace-event-info.o +LIB_OBJS += util/trace-event-scripting.o +LIB_OBJS += util/svghelper.o +LIB_OBJS += util/sort.o +LIB_OBJS += util/hist.o +LIB_OBJS += util/probe-event.o +LIB_OBJS += util/util.o +LIB_OBJS += util/cpumap.o + +BUILTIN_OBJS += builtin-annotate.o + +BUILTIN_OBJS += builtin-bench.o # Benchmark modules -BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o -BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o -BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o - -BUILTIN_OBJS += $(OUTPUT)builtin-diff.o -BUILTIN_OBJS += $(OUTPUT)builtin-help.o -BUILTIN_OBJS += $(OUTPUT)builtin-sched.o -BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o -BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o -BUILTIN_OBJS += $(OUTPUT)builtin-list.o -BUILTIN_OBJS += $(OUTPUT)builtin-record.o -BUILTIN_OBJS += $(OUTPUT)builtin-report.o -BUILTIN_OBJS += $(OUTPUT)builtin-stat.o -BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o -BUILTIN_OBJS += $(OUTPUT)builtin-top.o -BUILTIN_OBJS += $(OUTPUT)builtin-trace.o -BUILTIN_OBJS += $(OUTPUT)builtin-probe.o -BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o -BUILTIN_OBJS += $(OUTPUT)builtin-lock.o -BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o -BUILTIN_OBJS += $(OUTPUT)builtin-test.o -BUILTIN_OBJS += $(OUTPUT)builtin-inject.o +BUILTIN_OBJS += bench/sched-messaging.o +BUILTIN_OBJS += bench/sched-pipe.o +BUILTIN_OBJS += bench/mem-memcpy.o + +BUILTIN_OBJS += builtin-diff.o +BUILTIN_OBJS += builtin-help.o +BUILTIN_OBJS += builtin-sched.o +BUILTIN_OBJS += builtin-buildid-list.o +BUILTIN_OBJS += builtin-buildid-cache.o +BUILTIN_OBJS += builtin-list.o +BUILTIN_OBJS += builtin-record.o +BUILTIN_OBJS += builtin-report.o +BUILTIN_OBJS += builtin-stat.o +BUILTIN_OBJS += builtin-timechart.o +BUILTIN_OBJS += builtin-top.o +BUILTIN_OBJS += builtin-trace.o +BUILTIN_OBJS += builtin-probe.o +BUILTIN_OBJS += builtin-kmem.o +BUILTIN_OBJS += builtin-lock.o PERFLIBS = $(LIB_FILE) @@ -505,15 +476,6 @@ PERFLIBS = $(LIB_FILE) -include config.mak.autogen -include config.mak -ifndef NO_DWARF -ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/libdw-dev); - NO_DWARF := 1 -endif # Dwarf support -endif # NO_DWARF - --include arch/$(ARCH)/Makefile - ifeq ($(uname_S),Darwin) ifndef NO_FINK ifeq ($(shell test -d /sw/lib && echo y),y) @@ -530,10 +492,6 @@ ifeq ($(uname_S),Darwin) PTHREAD_LIBS = endif -ifneq ($(OUTPUT),) - BASIC_CFLAGS += -I$(OUTPUT) -endif - ifeq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { const char * version = gnu_get_libc_version(); return (long)version; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); @@ -546,22 +504,13 @@ else msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); endif -ifndef NO_DWARF -ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) - msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); +ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) + msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev); + BASIC_CFLAGS += -DNO_DWARF_SUPPORT else - BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT + BASIC_CFLAGS += -I/usr/include/elfutils EXTLIBS += -lelf -ldw - LIB_OBJS += $(OUTPUT)util/probe-finder.o -endif # PERF_HAVE_DWARF_REGS -endif # NO_DWARF - -ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); - BASIC_CFLAGS += -DNO_NEWT_SUPPORT -else - EXTLIBS += -lnewt - LIB_OBJS += $(OUTPUT)util/newt.o + LIB_OBJS += util/probe-finder.o endif ifndef NO_LIBPERL @@ -573,8 +522,8 @@ ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; e BASIC_CFLAGS += -DNO_LIBPERL else ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) - LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o - LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o + LIB_OBJS += util/scripting-engines/trace-event-perl.o + LIB_OBJS += scripts/perl/Perf-Trace-Util/Context.o endif ifndef NO_LIBPYTHON @@ -582,12 +531,12 @@ PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` endif -ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o $(BITBUCKET) $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) +ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o /dev/null $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) BASIC_CFLAGS += -DNO_LIBPYTHON else ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) - LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o - LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o + LIB_OBJS += util/scripting-engines/trace-event-python.o + LIB_OBJS += scripts/python/Perf-Trace-Util/Context.o endif ifdef NO_DEMANGLE @@ -658,53 +607,53 @@ ifdef NO_C99_FORMAT endif ifdef SNPRINTF_RETURNS_BOGUS COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS - COMPAT_OBJS += $(OUTPUT)compat/snprintf.o + COMPAT_OBJS += compat/snprintf.o endif ifdef FREAD_READS_DIRECTORIES COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES - COMPAT_OBJS += $(OUTPUT)compat/fopen.o + COMPAT_OBJS += compat/fopen.o endif ifdef NO_SYMLINK_HEAD BASIC_CFLAGS += -DNO_SYMLINK_HEAD endif ifdef NO_STRCASESTR COMPAT_CFLAGS += -DNO_STRCASESTR - COMPAT_OBJS += $(OUTPUT)compat/strcasestr.o + COMPAT_OBJS += compat/strcasestr.o endif ifdef NO_STRTOUMAX COMPAT_CFLAGS += -DNO_STRTOUMAX - COMPAT_OBJS += $(OUTPUT)compat/strtoumax.o + COMPAT_OBJS += compat/strtoumax.o endif ifdef NO_STRTOULL COMPAT_CFLAGS += -DNO_STRTOULL endif ifdef NO_SETENV COMPAT_CFLAGS += -DNO_SETENV - COMPAT_OBJS += $(OUTPUT)compat/setenv.o + COMPAT_OBJS += compat/setenv.o endif ifdef NO_MKDTEMP COMPAT_CFLAGS += -DNO_MKDTEMP - COMPAT_OBJS += $(OUTPUT)compat/mkdtemp.o + COMPAT_OBJS += compat/mkdtemp.o endif ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV - COMPAT_OBJS += $(OUTPUT)compat/unsetenv.o + COMPAT_OBJS += compat/unsetenv.o endif ifdef NO_SYS_SELECT_H BASIC_CFLAGS += -DNO_SYS_SELECT_H endif ifdef NO_MMAP COMPAT_CFLAGS += -DNO_MMAP - COMPAT_OBJS += $(OUTPUT)compat/mmap.o + COMPAT_OBJS += compat/mmap.o else ifdef USE_WIN32_MMAP COMPAT_CFLAGS += -DUSE_WIN32_MMAP - COMPAT_OBJS += $(OUTPUT)compat/win32mmap.o + COMPAT_OBJS += compat/win32mmap.o endif endif ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD - COMPAT_OBJS += $(OUTPUT)compat/pread.o + COMPAT_OBJS += compat/pread.o endif ifdef NO_FAST_WORKING_DIRECTORY BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY @@ -726,10 +675,10 @@ else endif endif ifdef NO_INET_NTOP - LIB_OBJS += $(OUTPUT)compat/inet_ntop.o + LIB_OBJS += compat/inet_ntop.o endif ifdef NO_INET_PTON - LIB_OBJS += $(OUTPUT)compat/inet_pton.o + LIB_OBJS += compat/inet_pton.o endif ifdef NO_ICONV @@ -746,15 +695,15 @@ endif ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" - LIB_OBJS += $(OUTPUT)ppc/sha1.o ppc/sha1ppc.o + LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o else ifdef ARM_SHA1 SHA1_HEADER = "arm/sha1.h" - LIB_OBJS += $(OUTPUT)arm/sha1.o $(OUTPUT)arm/sha1_arm.o + LIB_OBJS += arm/sha1.o arm/sha1_arm.o else ifdef MOZILLA_SHA1 SHA1_HEADER = "mozilla-sha1/sha1.h" - LIB_OBJS += $(OUTPUT)mozilla-sha1/sha1.o + LIB_OBJS += mozilla-sha1/sha1.o else SHA1_HEADER = EXTLIBS += $(LIB_4_CRYPTO) @@ -766,15 +715,15 @@ ifdef NO_PERL_MAKEMAKER endif ifdef NO_HSTRERROR COMPAT_CFLAGS += -DNO_HSTRERROR - COMPAT_OBJS += $(OUTPUT)compat/hstrerror.o + COMPAT_OBJS += compat/hstrerror.o endif ifdef NO_MEMMEM COMPAT_CFLAGS += -DNO_MEMMEM - COMPAT_OBJS += $(OUTPUT)compat/memmem.o + COMPAT_OBJS += compat/memmem.o endif ifdef INTERNAL_QSORT COMPAT_CFLAGS += -DINTERNAL_QSORT - COMPAT_OBJS += $(OUTPUT)compat/qsort.o + COMPAT_OBJS += compat/qsort.o endif ifdef RUNTIME_PREFIX COMPAT_CFLAGS += -DRUNTIME_PREFIX @@ -854,7 +803,7 @@ export TAR INSTALL DESTDIR SHELL_PATH SHELL = $(SHELL_PATH) -all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS +all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS ifneq (,$X) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) endif @@ -866,39 +815,39 @@ please_set_SHELL_PATH_to_a_more_modern_shell: shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell -strip: $(PROGRAMS) $(OUTPUT)perf$X - $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf$X +strip: $(PROGRAMS) perf$X + $(STRIP) $(STRIP_OPTS) $(PROGRAMS) perf$X -$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS +perf.o: perf.c common-cmds.h PERF-CFLAGS $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ - $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ + $(ALL_CFLAGS) -c $(filter %.c,$^) -$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) - $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(OUTPUT)perf.o \ +perf$X: perf.o $(BUILTIN_OBJS) $(PERFLIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ perf.o \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) -$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ +builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ '-DPERF_MAN_PATH="$(mandir_SQ)"' \ '-DPERF_INFO_PATH="$(infodir_SQ)"' $< -$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ +builtin-timechart.o: builtin-timechart.c common-cmds.h PERF-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ '-DPERF_MAN_PATH="$(mandir_SQ)"' \ '-DPERF_INFO_PATH="$(infodir_SQ)"' $< -$(BUILT_INS): $(OUTPUT)perf$X +$(BUILT_INS): perf$X $(QUIET_BUILT_IN)$(RM) $@ && \ ln perf$X $@ 2>/dev/null || \ ln -s perf$X $@ 2>/dev/null || \ cp perf$X $@ -$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt +common-cmds.h: util/generate-cmdlist.sh command-list.txt -$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) +common-cmds.h: $(wildcard Documentation/perf-*.txt) $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh @@ -910,7 +859,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ $@.sh >$@+ && \ chmod +x $@+ && \ - mv $@+ $(OUTPUT)$@ + mv $@+ $@ configure: configure.ac $(QUIET_GEN)$(RM) $@ $<+ && \ @@ -920,47 +869,60 @@ configure: configure.ac $(RM) $<+ # These can record PERF_VERSION -$(OUTPUT)perf.o perf.spec \ +perf.o perf.spec \ $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ - : $(OUTPUT)PERF-VERSION-FILE + : PERF-VERSION-FILE -$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< -$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS +%.o: %.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< +%.s: %.c PERF-CFLAGS $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< -$(OUTPUT)%.o: %.S - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< +%.o: %.S + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< -$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ +util/exec_cmd.o: util/exec_cmd.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ '-DBINDIR="$(bindir_relative_SQ)"' \ '-DPREFIX="$(prefix_SQ)"' \ $< -$(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $< +builtin-init-db.o: builtin-init-db.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $< + +util/config.o: util/config.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< + +util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< + +# some perf warning policies can't fit to lib/bitmap.c, eg: it warns about variable shadowing +# from that comes from kernel headers wrapping. +KBITMAP_FLAGS=`echo $(ALL_CFLAGS) | sed s/-Wshadow// | sed s/-Wswitch-default// | sed s/-Wextra//` + +util/bitmap.o: ../../lib/bitmap.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o util/bitmap.o -c $(KBITMAP_FLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +util/hweight.o: ../../lib/hweight.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o util/hweight.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +util/find_next_bit.o: ../../lib/find_next_bit.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o util/find_next_bit.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< +util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-perl.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< -$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< +scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o scripts/perl/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< -$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< +util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-python.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< -$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< +scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c PERF-CFLAGS + $(QUIET_CC)$(CC) -o scripts/python/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< -$(OUTPUT)perf-%$X: %.o $(PERFLIBS) +perf-%$X: %.o $(PERFLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) @@ -1001,17 +963,17 @@ cscope: TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) -$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS +PERF-CFLAGS: .FORCE-PERF-CFLAGS @FLAGS='$(TRACK_CFLAGS)'; \ - if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \ + if test x"$$FLAGS" != x"`cat PERF-CFLAGS 2>/dev/null`" ; then \ echo 1>&2 " * new build flags or prefix"; \ - echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ + echo "$$FLAGS" >PERF-CFLAGS; \ fi # We need to apply sq twice, once to protect from the shell -# that runs $(OUTPUT)PERF-BUILD-OPTIONS, and then again to protect it +# that runs PERF-BUILD-OPTIONS, and then again to protect it # and the first level quoting from the shell that runs "echo". -$(OUTPUT)PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS +PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ @@ -1032,7 +994,7 @@ all:: $(TEST_PROGRAMS) export NO_SVN_TESTS -check: $(OUTPUT)common-cmds.h +check: common-cmds.h if sparse; \ then \ for i in *.c */*.c; \ @@ -1066,10 +1028,10 @@ export perfexec_instdir install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) $(OUTPUT)perf$X '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' - $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' + $(INSTALL) perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' @@ -1083,7 +1045,7 @@ ifdef BUILT_INS $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' ifneq (,$X) - $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) $(OUTPUT)perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';) endif endif @@ -1167,14 +1129,14 @@ clean: $(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE) $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X $(RM) $(TEST_PROGRAMS) - $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* + $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope* $(RM) -r autom4te.cache $(RM) config.log config.mak.autogen config.mak.append config.status config.cache $(RM) -r $(PERF_TARNAME) .doc-tmp-dir $(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz $(RM) $(htmldocs).tar.gz $(manpages).tar.gz $(MAKE) -C Documentation/ clean - $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-BUILD-OPTIONS + $(RM) PERF-VERSION-FILE PERF-CFLAGS PERF-BUILD-OPTIONS .PHONY: all install clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell diff --git a/trunk/tools/perf/arch/powerpc/Makefile b/trunk/tools/perf/arch/powerpc/Makefile deleted file mode 100644 index 15130b50dfe3..000000000000 --- a/trunk/tools/perf/arch/powerpc/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o -endif diff --git a/trunk/tools/perf/arch/powerpc/util/dwarf-regs.c b/trunk/tools/perf/arch/powerpc/util/dwarf-regs.c deleted file mode 100644 index 48ae0c5e3f73..000000000000 --- a/trunk/tools/perf/arch/powerpc/util/dwarf-regs.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 Ian Munsie, IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include - - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; -}; - -#define STR(s) #s -#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} -#define GPR_DWARFNUM_NAME(num) \ - {.name = STR(%gpr##num), .dwarfnum = num} -#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} - -/* - * Reference: - * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html - */ -static const struct pt_regs_dwarfnum regdwarfnum_table[] = { - GPR_DWARFNUM_NAME(0), - GPR_DWARFNUM_NAME(1), - GPR_DWARFNUM_NAME(2), - GPR_DWARFNUM_NAME(3), - GPR_DWARFNUM_NAME(4), - GPR_DWARFNUM_NAME(5), - GPR_DWARFNUM_NAME(6), - GPR_DWARFNUM_NAME(7), - GPR_DWARFNUM_NAME(8), - GPR_DWARFNUM_NAME(9), - GPR_DWARFNUM_NAME(10), - GPR_DWARFNUM_NAME(11), - GPR_DWARFNUM_NAME(12), - GPR_DWARFNUM_NAME(13), - GPR_DWARFNUM_NAME(14), - GPR_DWARFNUM_NAME(15), - GPR_DWARFNUM_NAME(16), - GPR_DWARFNUM_NAME(17), - GPR_DWARFNUM_NAME(18), - GPR_DWARFNUM_NAME(19), - GPR_DWARFNUM_NAME(20), - GPR_DWARFNUM_NAME(21), - GPR_DWARFNUM_NAME(22), - GPR_DWARFNUM_NAME(23), - GPR_DWARFNUM_NAME(24), - GPR_DWARFNUM_NAME(25), - GPR_DWARFNUM_NAME(26), - GPR_DWARFNUM_NAME(27), - GPR_DWARFNUM_NAME(28), - GPR_DWARFNUM_NAME(29), - GPR_DWARFNUM_NAME(30), - GPR_DWARFNUM_NAME(31), - REG_DWARFNUM_NAME("%msr", 66), - REG_DWARFNUM_NAME("%ctr", 109), - REG_DWARFNUM_NAME("%link", 108), - REG_DWARFNUM_NAME("%xer", 101), - REG_DWARFNUM_NAME("%dar", 119), - REG_DWARFNUM_NAME("%dsisr", 118), - REG_DWARFNUM_END, -}; - -/** - * get_arch_regstr() - lookup register name from it's DWARF register number - * @n: the DWARF register number - * - * get_arch_regstr() returns the name of the register in struct - * regdwarfnum_table from it's DWARF register number. If the register is not - * found in the table, this returns NULL; - */ -const char *get_arch_regstr(unsigned int n) -{ - const struct pt_regs_dwarfnum *roff; - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (roff->dwarfnum == n) - return roff->name; - return NULL; -} diff --git a/trunk/tools/perf/arch/x86/Makefile b/trunk/tools/perf/arch/x86/Makefile deleted file mode 100644 index 15130b50dfe3..000000000000 --- a/trunk/tools/perf/arch/x86/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o -endif diff --git a/trunk/tools/perf/arch/x86/util/dwarf-regs.c b/trunk/tools/perf/arch/x86/util/dwarf-regs.c deleted file mode 100644 index a794d3081928..000000000000 --- a/trunk/tools/perf/arch/x86/util/dwarf-regs.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. - * Extracted from probe-finder.c - * - * Written by Masami Hiramatsu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#include -#include - -/* - * Generic dwarf analysis helpers - */ - -#define X86_32_MAX_REGS 8 -const char *x86_32_regs_table[X86_32_MAX_REGS] = { - "%ax", - "%cx", - "%dx", - "%bx", - "$stack", /* Stack address instead of %sp */ - "%bp", - "%si", - "%di", -}; - -#define X86_64_MAX_REGS 16 -const char *x86_64_regs_table[X86_64_MAX_REGS] = { - "%ax", - "%dx", - "%cx", - "%bx", - "%si", - "%di", - "%bp", - "%sp", - "%r8", - "%r9", - "%r10", - "%r11", - "%r12", - "%r13", - "%r14", - "%r15", -}; - -/* TODO: switching by dwarf address size */ -#ifdef __x86_64__ -#define ARCH_MAX_REGS X86_64_MAX_REGS -#define arch_regs_table x86_64_regs_table -#else -#define ARCH_MAX_REGS X86_32_MAX_REGS -#define arch_regs_table x86_32_regs_table -#endif - -/* Return architecture dependent register string (for kprobe-tracer) */ -const char *get_arch_regstr(unsigned int n) -{ - return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; -} diff --git a/trunk/tools/perf/bench/mem-memcpy.c b/trunk/tools/perf/bench/mem-memcpy.c index 38dae7465142..89773178e894 100644 --- a/trunk/tools/perf/bench/mem-memcpy.c +++ b/trunk/tools/perf/bench/mem-memcpy.c @@ -10,6 +10,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/parse-options.h" +#include "../util/string.h" #include "../util/header.h" #include "bench.h" @@ -23,7 +24,7 @@ static const char *length_str = "1MB"; static const char *routine = "default"; -static bool use_clock = false; +static int use_clock = 0; static int clock_fd; static const struct option options[] = { diff --git a/trunk/tools/perf/bench/sched-messaging.c b/trunk/tools/perf/bench/sched-messaging.c index da1b2e9f01ff..81cee78181fa 100644 --- a/trunk/tools/perf/bench/sched-messaging.c +++ b/trunk/tools/perf/bench/sched-messaging.c @@ -31,9 +31,9 @@ #define DATASIZE 100 -static bool use_pipes = false; +static int use_pipes = 0; static unsigned int loops = 100; -static bool thread_mode = false; +static unsigned int thread_mode = 0; static unsigned int num_groups = 10; struct sender_context { diff --git a/trunk/tools/perf/bench/sched-pipe.c b/trunk/tools/perf/bench/sched-pipe.c index d9ab3ce446ac..4f77c7c27640 100644 --- a/trunk/tools/perf/bench/sched-pipe.c +++ b/trunk/tools/perf/bench/sched-pipe.c @@ -93,7 +93,7 @@ int bench_sched_pipe(int argc, const char **argv, switch (bench_format) { case BENCH_FORMAT_DEFAULT: - printf("# Executed %d pipe operations between two tasks\n\n", + printf("# Extecuted %d pipe operations between two tasks\n\n", loops); result_usec = diff.tv_sec * 1000000; diff --git a/trunk/tools/perf/builtin-annotate.c b/trunk/tools/perf/builtin-annotate.c index ee154b58772b..6ad7148451c5 100644 --- a/trunk/tools/perf/builtin-annotate.c +++ b/trunk/tools/perf/builtin-annotate.c @@ -14,6 +14,7 @@ #include "util/cache.h" #include #include "util/symbol.h" +#include "util/string.h" #include "perf.h" #include "util/debug.h" @@ -28,11 +29,11 @@ static char const *input_name = "perf.data"; -static bool force; +static int force; -static bool full_paths; +static int full_paths; -static bool print_line; +static int print_line; struct sym_hist { u64 sum; @@ -68,13 +69,13 @@ static int sym__alloc_hist(struct symbol *self) static int annotate__hist_hit(struct hist_entry *he, u64 ip) { unsigned int sym_size, offset; - struct symbol *sym = he->ms.sym; + struct symbol *sym = he->sym; struct sym_priv *priv; struct sym_hist *h; he->count++; - if (!sym || !he->ms.map) + if (!sym || !he->map) return 0; priv = symbol__priv(sym); @@ -84,7 +85,7 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) sym_size = sym->end - sym->start; offset = ip - sym->start; - pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); + pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); if (offset >= sym_size) return 0; @@ -93,8 +94,8 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, - he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, + he->sym->name, ip, ip - he->sym->start, h->ip[offset]); return 0; } @@ -186,7 +187,7 @@ static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, static int parse_line(FILE *file, struct hist_entry *he, struct list_head *head) { - struct symbol *sym = he->ms.sym; + struct symbol *sym = he->sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2; size_t line_len; @@ -225,7 +226,7 @@ static int parse_line(FILE *file, struct hist_entry *he, } if (line_ip != -1) { - u64 start = map__rip_2objdump(he->ms.map, sym->start); + u64 start = map__rip_2objdump(he->map, sym->start); offset = line_ip - start; } @@ -243,7 +244,7 @@ static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) { - struct symbol *sym = he->ms.sym; + struct symbol *sym = he->sym; static const char *prev_line; static const char *prev_color; @@ -326,7 +327,7 @@ static void insert_source_line(struct sym_ext *sym_ext) static void free_source_line(struct hist_entry *he, int len) { - struct sym_priv *priv = symbol__priv(he->ms.sym); + struct sym_priv *priv = symbol__priv(he->sym); struct sym_ext *sym_ext = priv->ext; int i; @@ -345,7 +346,7 @@ static void free_source_line(struct hist_entry *he, int len) static void get_source_line(struct hist_entry *he, int len, const char *filename) { - struct symbol *sym = he->ms.sym; + struct symbol *sym = he->sym; u64 start; int i; char cmd[PATH_MAX * 2]; @@ -360,7 +361,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename) if (!priv->ext) return; - start = he->ms.map->unmap_ip(he->ms.map, sym->start); + start = he->map->unmap_ip(he->map, sym->start); for (i = 0; i < len; i++) { char *path = NULL; @@ -424,7 +425,7 @@ static void print_summary(const char *filename) static void hist_entry__print_hits(struct hist_entry *self) { - struct symbol *sym = self->ms.sym; + struct symbol *sym = self->sym; struct sym_priv *priv = symbol__priv(sym); struct sym_hist *h = priv->hist; u64 len = sym->end - sym->start, offset; @@ -438,9 +439,9 @@ static void hist_entry__print_hits(struct hist_entry *self) static void annotate_sym(struct hist_entry *he) { - struct map *map = he->ms.map; + struct map *map = he->map; struct dso *dso = map->dso; - struct symbol *sym = he->ms.sym; + struct symbol *sym = he->sym; const char *filename = dso->long_name, *d_filename; u64 len; char command[PATH_MAX*2]; @@ -451,16 +452,6 @@ static void annotate_sym(struct hist_entry *he) if (!filename) return; - if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) - return; - dso->annotate_warned = 1; - pr_err("Can't annotate %s: No vmlinux file was found in the " - "path:\n", sym->name); - vmlinux_path__fprintf(stderr); - return; - } - pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); @@ -525,17 +516,17 @@ static void perf_session__find_annotations(struct perf_session *self) struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); struct sym_priv *priv; - if (he->ms.sym == NULL) + if (he->sym == NULL) continue; - priv = symbol__priv(he->ms.sym); + priv = symbol__priv(he->sym); if (priv->hist == NULL) continue; annotate_sym(he); /* * Since we have a hist_entry per IP for the same symbol, free - * he->ms.sym->hist to signal we already processed this symbol. + * he->sym->hist to signal we already processed this symbol. */ free(priv->hist); priv->hist = NULL; @@ -554,7 +545,7 @@ static int __cmd_annotate(void) int ret; struct perf_session *session; - session = perf_session__new(input_name, O_RDONLY, force, false); + session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) return -ENOMEM; @@ -571,7 +562,7 @@ static int __cmd_annotate(void) perf_session__fprintf(session, stdout); if (verbose > 2) - perf_session__fprintf_dsos(session, stdout); + dsos__fprintf(stdout); perf_session__collapse_resort(&session->hists); perf_session__output_resort(&session->hists, session->event_total[0]); @@ -590,12 +581,10 @@ static const char * const annotate_usage[] = { static const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", - "only consider symbols in these dsos"), OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", "symbol to annotate"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), diff --git a/trunk/tools/perf/builtin-buildid-cache.c b/trunk/tools/perf/builtin-buildid-cache.c index f8e3d1852029..30a05f552c96 100644 --- a/trunk/tools/perf/builtin-buildid-cache.c +++ b/trunk/tools/perf/builtin-buildid-cache.c @@ -27,7 +27,7 @@ static const struct option buildid_cache_options[] = { "file list", "file(s) to add"), OPT_STRING('r', "remove", &remove_name_list_str, "file list", "file(s) to remove"), - OPT_INCR('v', "verbose", &verbose, "be more verbose"), + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose"), OPT_END() }; diff --git a/trunk/tools/perf/builtin-buildid-list.c b/trunk/tools/perf/builtin-buildid-list.c index 44a47e13bd67..d0675c02f81e 100644 --- a/trunk/tools/perf/builtin-buildid-list.c +++ b/trunk/tools/perf/builtin-buildid-list.c @@ -16,7 +16,7 @@ #include "util/symbol.h" static char const *input_name = "perf.data"; -static bool force; +static int force; static bool with_hits; static const char * const buildid_list_usage[] = { @@ -29,7 +29,7 @@ static const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose"), OPT_END() }; @@ -39,14 +39,14 @@ static int __cmd_buildid_list(void) int err = -1; struct perf_session *session; - session = perf_session__new(input_name, O_RDONLY, force, false); + session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) return -1; if (with_hits) perf_session__process_events(session, &build_id__mark_dso_hit_ops); - perf_session__fprintf_dsos_buildid(session, stdout, with_hits); + dsos__fprintf_buildid(stdout, with_hits); perf_session__delete(session); return err; diff --git a/trunk/tools/perf/builtin-diff.c b/trunk/tools/perf/builtin-diff.c index 4cce68f23686..1ea15d8aeed1 100644 --- a/trunk/tools/perf/builtin-diff.c +++ b/trunk/tools/perf/builtin-diff.c @@ -19,7 +19,7 @@ static char const *input_old = "perf.data.old", *input_new = "perf.data"; static char diff__default_sort_order[] = "dso,symbol"; -static bool force; +static int force; static bool show_displacement; static int perf_session__add_hist_entry(struct perf_session *self, @@ -33,7 +33,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return -ENOMEM; if (hit) - __perf_session__add_count(he, al, count); + he->count += count; return 0; } @@ -156,8 +156,8 @@ static int __cmd_diff(void) int ret, i; struct perf_session *session[2]; - session[0] = perf_session__new(input_old, O_RDONLY, force, false); - session[1] = perf_session__new(input_new, O_RDONLY, force, false); + session[0] = perf_session__new(input_old, O_RDONLY, force); + session[1] = perf_session__new(input_new, O_RDONLY, force); if (session[0] == NULL || session[1] == NULL) return -ENOMEM; @@ -188,7 +188,7 @@ static const char * const diff_usage[] = { }; static const struct option options[] = { - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('m', "displacement", &show_displacement, "Show position displacement relative to baseline"), @@ -225,10 +225,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used) input_new = argv[1]; } else input_new = argv[0]; - } else if (symbol_conf.default_guest_vmlinux_name || - symbol_conf.default_guest_kallsyms) { - input_old = "perf.data.host"; - input_new = "perf.data.guest"; } symbol_conf.exclude_other = false; diff --git a/trunk/tools/perf/builtin-help.c b/trunk/tools/perf/builtin-help.c index 81e3ecc40fc7..215b584007b1 100644 --- a/trunk/tools/perf/builtin-help.c +++ b/trunk/tools/perf/builtin-help.c @@ -29,7 +29,7 @@ enum help_format { HELP_FORMAT_WEB, }; -static bool show_all = false; +static int show_all = 0; static enum help_format help_format = HELP_FORMAT_MAN; static struct option builtin_help_options[] = { OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), diff --git a/trunk/tools/perf/builtin-inject.c b/trunk/tools/perf/builtin-inject.c deleted file mode 100644 index 8e3e47b064ce..000000000000 --- a/trunk/tools/perf/builtin-inject.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * builtin-inject.c - * - * Builtin inject command: Examine the live mode (stdin) event stream - * and repipe it to stdout while optionally injecting additional - * events into it. - */ -#include "builtin.h" - -#include "perf.h" -#include "util/session.h" -#include "util/debug.h" - -#include "util/parse-options.h" - -static char const *input_name = "-"; -static bool inject_build_ids; - -static int event__repipe(event_t *event __used, - struct perf_session *session __used) -{ - uint32_t size; - void *buf = event; - - size = event->header.size; - - while (size) { - int ret = write(STDOUT_FILENO, buf, size); - if (ret < 0) - return -errno; - - size -= ret; - buf += ret; - } - - return 0; -} - -static int event__repipe_mmap(event_t *self, struct perf_session *session) -{ - int err; - - err = event__process_mmap(self, session); - event__repipe(self, session); - - return err; -} - -static int event__repipe_task(event_t *self, struct perf_session *session) -{ - int err; - - err = event__process_task(self, session); - event__repipe(self, session); - - return err; -} - -static int event__repipe_tracing_data(event_t *self, - struct perf_session *session) -{ - int err; - - event__repipe(self, session); - err = event__process_tracing_data(self, session); - - return err; -} - -static int dso__read_build_id(struct dso *self) -{ - if (self->has_build_id) - return 0; - - if (filename__read_build_id(self->long_name, self->build_id, - sizeof(self->build_id)) > 0) { - self->has_build_id = true; - return 0; - } - - return -1; -} - -static int dso__inject_build_id(struct dso *self, struct perf_session *session) -{ - u16 misc = PERF_RECORD_MISC_USER; - struct machine *machine; - int err; - - if (dso__read_build_id(self) < 0) { - pr_debug("no build_id found for %s\n", self->long_name); - return -1; - } - - machine = perf_session__find_host_machine(session); - if (machine == NULL) { - pr_err("Can't find machine for session\n"); - return -1; - } - - if (self->kernel) - misc = PERF_RECORD_MISC_KERNEL; - - err = event__synthesize_build_id(self, misc, event__repipe, - machine, session); - if (err) { - pr_err("Can't synthesize build_id event for %s\n", self->long_name); - return -1; - } - - return 0; -} - -static int event__inject_buildid(event_t *event, struct perf_session *session) -{ - struct addr_location al; - struct thread *thread; - u8 cpumode; - - cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - - thread = perf_session__findnew(session, event->ip.pid); - if (thread == NULL) { - pr_err("problem processing %d event, skipping it.\n", - event->header.type); - goto repipe; - } - - thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - event->ip.pid, event->ip.ip, &al); - - if (al.map != NULL) { - if (!al.map->dso->hit) { - al.map->dso->hit = 1; - if (map__load(al.map, NULL) >= 0) { - dso__inject_build_id(al.map->dso, session); - /* - * If this fails, too bad, let the other side - * account this as unresolved. - */ - } else - pr_warning("no symbols found in %s, maybe " - "install a debug package?\n", - al.map->dso->long_name); - } - } - -repipe: - event__repipe(event, session); - return 0; -} - -struct perf_event_ops inject_ops = { - .sample = event__repipe, - .mmap = event__repipe, - .comm = event__repipe, - .fork = event__repipe, - .exit = event__repipe, - .lost = event__repipe, - .read = event__repipe, - .throttle = event__repipe, - .unthrottle = event__repipe, - .attr = event__repipe, - .event_type = event__repipe, - .tracing_data = event__repipe, - .build_id = event__repipe, -}; - -extern volatile int session_done; - -static void sig_handler(int sig __attribute__((__unused__))) -{ - session_done = 1; -} - -static int __cmd_inject(void) -{ - struct perf_session *session; - int ret = -EINVAL; - - signal(SIGINT, sig_handler); - - if (inject_build_ids) { - inject_ops.sample = event__inject_buildid; - inject_ops.mmap = event__repipe_mmap; - inject_ops.fork = event__repipe_task; - inject_ops.tracing_data = event__repipe_tracing_data; - } - - session = perf_session__new(input_name, O_RDONLY, false, true); - if (session == NULL) - return -ENOMEM; - - ret = perf_session__process_events(session, &inject_ops); - - perf_session__delete(session); - - return ret; -} - -static const char * const report_usage[] = { - "perf inject []", - NULL -}; - -static const struct option options[] = { - OPT_BOOLEAN('b', "build-ids", &inject_build_ids, - "Inject build-ids into the output stream"), - OPT_INCR('v', "verbose", &verbose, - "be more verbose (show build ids, etc)"), - OPT_END() -}; - -int cmd_inject(int argc, const char **argv, const char *prefix __used) -{ - argc = parse_options(argc, argv, options, report_usage, 0); - - /* - * Any (unrecognized) arguments left? - */ - if (argc) - usage_with_options(report_usage, options); - - if (symbol__init() < 0) - return -1; - - return __cmd_inject(); -} diff --git a/trunk/tools/perf/builtin-kmem.c b/trunk/tools/perf/builtin-kmem.c index 31f60a2535e0..924a9518931a 100644 --- a/trunk/tools/perf/builtin-kmem.c +++ b/trunk/tools/perf/builtin-kmem.c @@ -335,9 +335,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) } static struct perf_event_ops event_ops = { - .sample = process_sample_event, - .comm = event__process_comm, - .ordered_samples = true, + .sample = process_sample_event, + .comm = event__process_comm, }; static double fragmentation(unsigned long n_req, unsigned long n_alloc) @@ -352,7 +351,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session, int n_lines, int is_caller) { struct rb_node *next; - struct machine *machine; printf("%.102s\n", graph_dotted_line); printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); @@ -361,29 +359,23 @@ static void __print_result(struct rb_root *root, struct perf_session *session, next = rb_first(root); - machine = perf_session__find_host_machine(session); - if (!machine) { - pr_err("__print_result: couldn't find kernel information\n"); - return; - } while (next && n_lines--) { struct alloc_stat *data = rb_entry(next, struct alloc_stat, node); struct symbol *sym = NULL; - struct map *map; char buf[BUFSIZ]; u64 addr; if (is_caller) { addr = data->call_site; if (!raw_ip) - sym = machine__find_kernel_function(machine, addr, &map, NULL); + sym = map_groups__find_function(&session->kmaps, addr, NULL); } else addr = data->ptr; if (sym != NULL) snprintf(buf, sizeof(buf), "%s+%Lx", sym->name, - addr - map->unmap_ip(map, sym->start)); + addr - sym->start); else snprintf(buf, sizeof(buf), "%#Lx", addr); printf(" %-34s |", buf); @@ -492,13 +484,10 @@ static void sort_result(void) static int __cmd_kmem(void) { int err = -EINVAL; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); if (session == NULL) return -ENOMEM; - if (perf_session__create_kernel_maps(session) < 0) - goto out_delete; - if (!perf_session__has_traces(session, "kmem record")) goto out_delete; @@ -729,6 +718,7 @@ static const char *record_args[] = { "record", "-a", "-R", + "-M", "-f", "-c", "1", "-e", "kmem:kmalloc", diff --git a/trunk/tools/perf/builtin-kvm.c b/trunk/tools/perf/builtin-kvm.c deleted file mode 100644 index a4c7cae45024..000000000000 --- a/trunk/tools/perf/builtin-kvm.c +++ /dev/null @@ -1,144 +0,0 @@ -#include "builtin.h" -#include "perf.h" - -#include "util/util.h" -#include "util/cache.h" -#include "util/symbol.h" -#include "util/thread.h" -#include "util/header.h" -#include "util/session.h" - -#include "util/parse-options.h" -#include "util/trace-event.h" - -#include "util/debug.h" - -#include - -#include -#include -#include - -static char *file_name; -static char name_buffer[256]; - -int perf_host = 1; -int perf_guest; - -static const char * const kvm_usage[] = { - "perf kvm [] {top|record|report|diff|buildid-list}", - NULL -}; - -static const struct option kvm_options[] = { - OPT_STRING('i', "input", &file_name, "file", - "Input file name"), - OPT_STRING('o', "output", &file_name, "file", - "Output file name"), - OPT_BOOLEAN(0, "guest", &perf_guest, - "Collect guest os data"), - OPT_BOOLEAN(0, "host", &perf_host, - "Collect guest os data"), - OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", - "guest mount directory under which every guest os" - " instance has a subdir"), - OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name, - "file", "file saving guest os vmlinux"), - OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms, - "file", "file saving guest os /proc/kallsyms"), - OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, - "file", "file saving guest os /proc/modules"), - OPT_END() -}; - -static int __cmd_record(int argc, const char **argv) -{ - int rec_argc, i = 0, j; - const char **rec_argv; - - rec_argc = argc + 2; - rec_argv = calloc(rec_argc + 1, sizeof(char *)); - rec_argv[i++] = strdup("record"); - rec_argv[i++] = strdup("-o"); - rec_argv[i++] = strdup(file_name); - for (j = 1; j < argc; j++, i++) - rec_argv[i] = argv[j]; - - BUG_ON(i != rec_argc); - - return cmd_record(i, rec_argv, NULL); -} - -static int __cmd_report(int argc, const char **argv) -{ - int rec_argc, i = 0, j; - const char **rec_argv; - - rec_argc = argc + 2; - rec_argv = calloc(rec_argc + 1, sizeof(char *)); - rec_argv[i++] = strdup("report"); - rec_argv[i++] = strdup("-i"); - rec_argv[i++] = strdup(file_name); - for (j = 1; j < argc; j++, i++) - rec_argv[i] = argv[j]; - - BUG_ON(i != rec_argc); - - return cmd_report(i, rec_argv, NULL); -} - -static int __cmd_buildid_list(int argc, const char **argv) -{ - int rec_argc, i = 0, j; - const char **rec_argv; - - rec_argc = argc + 2; - rec_argv = calloc(rec_argc + 1, sizeof(char *)); - rec_argv[i++] = strdup("buildid-list"); - rec_argv[i++] = strdup("-i"); - rec_argv[i++] = strdup(file_name); - for (j = 1; j < argc; j++, i++) - rec_argv[i] = argv[j]; - - BUG_ON(i != rec_argc); - - return cmd_buildid_list(i, rec_argv, NULL); -} - -int cmd_kvm(int argc, const char **argv, const char *prefix __used) -{ - perf_host = perf_guest = 0; - - argc = parse_options(argc, argv, kvm_options, kvm_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc) - usage_with_options(kvm_usage, kvm_options); - - if (!perf_host) - perf_guest = 1; - - if (!file_name) { - if (perf_host && !perf_guest) - sprintf(name_buffer, "perf.data.host"); - else if (!perf_host && perf_guest) - sprintf(name_buffer, "perf.data.guest"); - else - sprintf(name_buffer, "perf.data.kvm"); - file_name = name_buffer; - } - - if (!strncmp(argv[0], "rec", 3)) - return __cmd_record(argc, argv); - else if (!strncmp(argv[0], "rep", 3)) - return __cmd_report(argc, argv); - else if (!strncmp(argv[0], "diff", 4)) - return cmd_diff(argc, argv, NULL); - else if (!strncmp(argv[0], "top", 3)) - return cmd_top(argc, argv, NULL); - else if (!strncmp(argv[0], "buildid-list", 12)) - return __cmd_buildid_list(argc, argv); - else - usage_with_options(kvm_usage, kvm_options); - - return 0; -} diff --git a/trunk/tools/perf/builtin-lock.c b/trunk/tools/perf/builtin-lock.c index 6605000ed73d..e12c844df1e2 100644 --- a/trunk/tools/perf/builtin-lock.c +++ b/trunk/tools/perf/builtin-lock.c @@ -23,8 +23,6 @@ #include #include -static struct perf_session *session; - /* based on kernel/lockdep.c */ #define LOCKHASH_BITS 12 #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) @@ -34,6 +32,9 @@ static struct list_head lockhash_table[LOCKHASH_SIZE]; #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) +#define LOCK_STATE_UNLOCKED 0 /* initial state */ +#define LOCK_STATE_LOCKED 1 + struct lock_stat { struct list_head hash_entry; struct rb_node rb; /* used for sorting */ @@ -46,151 +47,20 @@ struct lock_stat { void *addr; /* address of lockdep_map, used as ID */ char *name; /* for strcpy(), we cannot use const */ - unsigned int nr_acquire; + int state; + u64 prev_event_time; /* timestamp of previous event */ + unsigned int nr_acquired; + unsigned int nr_acquire; unsigned int nr_contended; unsigned int nr_release; - unsigned int nr_readlock; - unsigned int nr_trylock; /* these times are in nano sec. */ u64 wait_time_total; u64 wait_time_min; u64 wait_time_max; - - int discard; /* flag of blacklist */ -}; - -/* - * States of lock_seq_stat - * - * UNINITIALIZED is required for detecting first event of acquire. - * As the nature of lock events, there is no guarantee - * that the first event for the locks are acquire, - * it can be acquired, contended or release. - */ -#define SEQ_STATE_UNINITIALIZED 0 /* initial state */ -#define SEQ_STATE_RELEASED 1 -#define SEQ_STATE_ACQUIRING 2 -#define SEQ_STATE_ACQUIRED 3 -#define SEQ_STATE_READ_ACQUIRED 4 -#define SEQ_STATE_CONTENDED 5 - -/* - * MAX_LOCK_DEPTH - * Imported from include/linux/sched.h. - * Should this be synchronized? - */ -#define MAX_LOCK_DEPTH 48 - -/* - * struct lock_seq_stat: - * Place to put on state of one lock sequence - * 1) acquire -> acquired -> release - * 2) acquire -> contended -> acquired -> release - * 3) acquire (with read or try) -> release - * 4) Are there other patterns? - */ -struct lock_seq_stat { - struct list_head list; - int state; - u64 prev_event_time; - void *addr; - - int read_count; }; -struct thread_stat { - struct rb_node rb; - - u32 tid; - struct list_head seq_list; -}; - -static struct rb_root thread_stats; - -static struct thread_stat *thread_stat_find(u32 tid) -{ - struct rb_node *node; - struct thread_stat *st; - - node = thread_stats.rb_node; - while (node) { - st = container_of(node, struct thread_stat, rb); - if (st->tid == tid) - return st; - else if (tid < st->tid) - node = node->rb_left; - else - node = node->rb_right; - } - - return NULL; -} - -static void thread_stat_insert(struct thread_stat *new) -{ - struct rb_node **rb = &thread_stats.rb_node; - struct rb_node *parent = NULL; - struct thread_stat *p; - - while (*rb) { - p = container_of(*rb, struct thread_stat, rb); - parent = *rb; - - if (new->tid < p->tid) - rb = &(*rb)->rb_left; - else if (new->tid > p->tid) - rb = &(*rb)->rb_right; - else - BUG_ON("inserting invalid thread_stat\n"); - } - - rb_link_node(&new->rb, parent, rb); - rb_insert_color(&new->rb, &thread_stats); -} - -static struct thread_stat *thread_stat_findnew_after_first(u32 tid) -{ - struct thread_stat *st; - - st = thread_stat_find(tid); - if (st) - return st; - - st = zalloc(sizeof(struct thread_stat)); - if (!st) - die("memory allocation failed\n"); - - st->tid = tid; - INIT_LIST_HEAD(&st->seq_list); - - thread_stat_insert(st); - - return st; -} - -static struct thread_stat *thread_stat_findnew_first(u32 tid); -static struct thread_stat *(*thread_stat_findnew)(u32 tid) = - thread_stat_findnew_first; - -static struct thread_stat *thread_stat_findnew_first(u32 tid) -{ - struct thread_stat *st; - - st = zalloc(sizeof(struct thread_stat)); - if (!st) - die("memory allocation failed\n"); - st->tid = tid; - INIT_LIST_HEAD(&st->seq_list); - - rb_link_node(&st->rb, NULL, &thread_stats.rb_node); - rb_insert_color(&st->rb, &thread_stats); - - thread_stat_findnew = thread_stat_findnew_after_first; - return st; -} - /* build simple key function one is bigger than two */ #define SINGLE_KEY(member) \ static int lock_stat_key_ ## member(struct lock_stat *one, \ @@ -305,6 +175,8 @@ static struct lock_stat *lock_stat_findnew(void *addr, const char *name) goto alloc_failed; strcpy(new->name, name); + /* LOCK_STATE_UNLOCKED == 0 isn't guaranteed forever */ + new->state = LOCK_STATE_UNLOCKED; new->wait_time_min = ULLONG_MAX; list_add(&new->hash_entry, entry); @@ -316,6 +188,8 @@ static struct lock_stat *lock_stat_findnew(void *addr, const char *name) static char const *input_name = "perf.data"; +static int profile_cpu = -1; + struct raw_event_sample { u32 size; char data[0]; @@ -324,7 +198,6 @@ struct raw_event_sample { struct trace_acquire_event { void *addr; const char *name; - int flag; }; struct trace_acquired_event { @@ -368,246 +241,120 @@ struct trace_lock_handler { struct thread *thread); }; -static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr) -{ - struct lock_seq_stat *seq; - - list_for_each_entry(seq, &ts->seq_list, list) { - if (seq->addr == addr) - return seq; - } - - seq = zalloc(sizeof(struct lock_seq_stat)); - if (!seq) - die("Not enough memory\n"); - seq->state = SEQ_STATE_UNINITIALIZED; - seq->addr = addr; - - list_add(&seq->list, &ts->seq_list); - return seq; -} - -static int bad_hist[4]; - static void report_lock_acquire_event(struct trace_acquire_event *acquire_event, struct event *__event __used, int cpu __used, - u64 timestamp __used, + u64 timestamp, struct thread *thread __used) { - struct lock_stat *ls; - struct thread_stat *ts; - struct lock_seq_stat *seq; - - ls = lock_stat_findnew(acquire_event->addr, acquire_event->name); - if (ls->discard) - return; + struct lock_stat *st; - ts = thread_stat_findnew(thread->pid); - seq = get_seq(ts, acquire_event->addr); + st = lock_stat_findnew(acquire_event->addr, acquire_event->name); - switch (seq->state) { - case SEQ_STATE_UNINITIALIZED: - case SEQ_STATE_RELEASED: - if (!acquire_event->flag) { - seq->state = SEQ_STATE_ACQUIRING; - } else { - if (acquire_event->flag & 1) - ls->nr_trylock++; - if (acquire_event->flag & 2) - ls->nr_readlock++; - seq->state = SEQ_STATE_READ_ACQUIRED; - seq->read_count = 1; - ls->nr_acquired++; - } - break; - case SEQ_STATE_READ_ACQUIRED: - if (acquire_event->flag & 2) { - seq->read_count++; - ls->nr_acquired++; - goto end; - } else { - goto broken; - } + switch (st->state) { + case LOCK_STATE_UNLOCKED: break; - case SEQ_STATE_ACQUIRED: - case SEQ_STATE_ACQUIRING: - case SEQ_STATE_CONTENDED: -broken: - /* broken lock sequence, discard it */ - ls->discard = 1; - bad_hist[0]++; - list_del(&seq->list); - free(seq); - goto end; + case LOCK_STATE_LOCKED: break; default: - BUG_ON("Unknown state of lock sequence found!\n"); + BUG_ON(1); break; } - ls->nr_acquire++; - seq->prev_event_time = timestamp; -end: - return; + st->prev_event_time = timestamp; } static void report_lock_acquired_event(struct trace_acquired_event *acquired_event, struct event *__event __used, int cpu __used, - u64 timestamp __used, + u64 timestamp, struct thread *thread __used) { - struct lock_stat *ls; - struct thread_stat *ts; - struct lock_seq_stat *seq; - u64 contended_term; - - ls = lock_stat_findnew(acquired_event->addr, acquired_event->name); - if (ls->discard) - return; + struct lock_stat *st; - ts = thread_stat_findnew(thread->pid); - seq = get_seq(ts, acquired_event->addr); + st = lock_stat_findnew(acquired_event->addr, acquired_event->name); - switch (seq->state) { - case SEQ_STATE_UNINITIALIZED: - /* orphan event, do nothing */ - return; - case SEQ_STATE_ACQUIRING: - break; - case SEQ_STATE_CONTENDED: - contended_term = timestamp - seq->prev_event_time; - ls->wait_time_total += contended_term; - - if (contended_term < ls->wait_time_min) - ls->wait_time_min = contended_term; - else if (ls->wait_time_max < contended_term) - ls->wait_time_max = contended_term; + switch (st->state) { + case LOCK_STATE_UNLOCKED: + st->state = LOCK_STATE_LOCKED; + st->nr_acquired++; break; - case SEQ_STATE_RELEASED: - case SEQ_STATE_ACQUIRED: - case SEQ_STATE_READ_ACQUIRED: - /* broken lock sequence, discard it */ - ls->discard = 1; - bad_hist[1]++; - list_del(&seq->list); - free(seq); - goto end; + case LOCK_STATE_LOCKED: break; - default: - BUG_ON("Unknown state of lock sequence found!\n"); + BUG_ON(1); break; } - seq->state = SEQ_STATE_ACQUIRED; - ls->nr_acquired++; - seq->prev_event_time = timestamp; -end: - return; + st->prev_event_time = timestamp; } static void report_lock_contended_event(struct trace_contended_event *contended_event, struct event *__event __used, int cpu __used, - u64 timestamp __used, + u64 timestamp, struct thread *thread __used) { - struct lock_stat *ls; - struct thread_stat *ts; - struct lock_seq_stat *seq; - - ls = lock_stat_findnew(contended_event->addr, contended_event->name); - if (ls->discard) - return; + struct lock_stat *st; - ts = thread_stat_findnew(thread->pid); - seq = get_seq(ts, contended_event->addr); + st = lock_stat_findnew(contended_event->addr, contended_event->name); - switch (seq->state) { - case SEQ_STATE_UNINITIALIZED: - /* orphan event, do nothing */ - return; - case SEQ_STATE_ACQUIRING: + switch (st->state) { + case LOCK_STATE_UNLOCKED: break; - case SEQ_STATE_RELEASED: - case SEQ_STATE_ACQUIRED: - case SEQ_STATE_READ_ACQUIRED: - case SEQ_STATE_CONTENDED: - /* broken lock sequence, discard it */ - ls->discard = 1; - bad_hist[2]++; - list_del(&seq->list); - free(seq); - goto end; + case LOCK_STATE_LOCKED: + st->nr_contended++; break; default: - BUG_ON("Unknown state of lock sequence found!\n"); + BUG_ON(1); break; } - seq->state = SEQ_STATE_CONTENDED; - ls->nr_contended++; - seq->prev_event_time = timestamp; -end: - return; + st->prev_event_time = timestamp; } static void report_lock_release_event(struct trace_release_event *release_event, struct event *__event __used, int cpu __used, - u64 timestamp __used, + u64 timestamp, struct thread *thread __used) { - struct lock_stat *ls; - struct thread_stat *ts; - struct lock_seq_stat *seq; - - ls = lock_stat_findnew(release_event->addr, release_event->name); - if (ls->discard) - return; + struct lock_stat *st; + u64 hold_time; - ts = thread_stat_findnew(thread->pid); - seq = get_seq(ts, release_event->addr); + st = lock_stat_findnew(release_event->addr, release_event->name); - switch (seq->state) { - case SEQ_STATE_UNINITIALIZED: - goto end; - break; - case SEQ_STATE_ACQUIRED: + switch (st->state) { + case LOCK_STATE_UNLOCKED: break; - case SEQ_STATE_READ_ACQUIRED: - seq->read_count--; - BUG_ON(seq->read_count < 0); - if (!seq->read_count) { - ls->nr_release++; + case LOCK_STATE_LOCKED: + st->state = LOCK_STATE_UNLOCKED; + hold_time = timestamp - st->prev_event_time; + + if (timestamp < st->prev_event_time) { + /* terribly, this can happen... */ goto end; } - break; - case SEQ_STATE_ACQUIRING: - case SEQ_STATE_CONTENDED: - case SEQ_STATE_RELEASED: - /* broken lock sequence, discard it */ - ls->discard = 1; - bad_hist[3]++; - goto free_seq; + + if (st->wait_time_min > hold_time) + st->wait_time_min = hold_time; + if (st->wait_time_max < hold_time) + st->wait_time_max = hold_time; + st->wait_time_total += hold_time; + + st->nr_release++; break; default: - BUG_ON("Unknown state of lock sequence found!\n"); + BUG_ON(1); break; } - ls->nr_release++; -free_seq: - list_del(&seq->list); - free(seq); end: - return; + st->prev_event_time = timestamp; } /* lock oriented handlers */ @@ -634,7 +381,6 @@ process_lock_acquire_event(void *data, tmp = raw_field_value(event, "lockdep_addr", data); memcpy(&acquire_event.addr, &tmp, sizeof(void *)); acquire_event.name = (char *)raw_field_ptr(event, "name", data); - acquire_event.flag = (int)raw_field_value(event, "flag", data); if (trace_handler->acquire_event) trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread); @@ -695,7 +441,8 @@ process_lock_release_event(void *data, } static void -process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread) +process_raw_event(void *data, int cpu, + u64 timestamp, struct thread *thread) { struct event *event; int type; @@ -713,13 +460,182 @@ process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread) process_lock_release_event(data, event, cpu, timestamp, thread); } +struct raw_event_queue { + u64 timestamp; + int cpu; + void *data; + struct thread *thread; + struct list_head list; +}; + +static LIST_HEAD(raw_event_head); + +#define FLUSH_PERIOD (5 * NSEC_PER_SEC) + +static u64 flush_limit = ULLONG_MAX; +static u64 last_flush = 0; +struct raw_event_queue *last_inserted; + +static void flush_raw_event_queue(u64 limit) +{ + struct raw_event_queue *tmp, *iter; + + list_for_each_entry_safe(iter, tmp, &raw_event_head, list) { + if (iter->timestamp > limit) + return; + + if (iter == last_inserted) + last_inserted = NULL; + + process_raw_event(iter->data, iter->cpu, iter->timestamp, + iter->thread); + + last_flush = iter->timestamp; + list_del(&iter->list); + free(iter->data); + free(iter); + } +} + +static void __queue_raw_event_end(struct raw_event_queue *new) +{ + struct raw_event_queue *iter; + + list_for_each_entry_reverse(iter, &raw_event_head, list) { + if (iter->timestamp < new->timestamp) { + list_add(&new->list, &iter->list); + return; + } + } + + list_add(&new->list, &raw_event_head); +} + +static void __queue_raw_event_before(struct raw_event_queue *new, + struct raw_event_queue *iter) +{ + list_for_each_entry_continue_reverse(iter, &raw_event_head, list) { + if (iter->timestamp < new->timestamp) { + list_add(&new->list, &iter->list); + return; + } + } + + list_add(&new->list, &raw_event_head); +} + +static void __queue_raw_event_after(struct raw_event_queue *new, + struct raw_event_queue *iter) +{ + list_for_each_entry_continue(iter, &raw_event_head, list) { + if (iter->timestamp > new->timestamp) { + list_add_tail(&new->list, &iter->list); + return; + } + } + list_add_tail(&new->list, &raw_event_head); +} + +/* The queue is ordered by time */ +static void __queue_raw_event(struct raw_event_queue *new) +{ + if (!last_inserted) { + __queue_raw_event_end(new); + return; + } + + /* + * Most of the time the current event has a timestamp + * very close to the last event inserted, unless we just switched + * to another event buffer. Having a sorting based on a list and + * on the last inserted event that is close to the current one is + * probably more efficient than an rbtree based sorting. + */ + if (last_inserted->timestamp >= new->timestamp) + __queue_raw_event_before(new, last_inserted); + else + __queue_raw_event_after(new, last_inserted); +} + +static void queue_raw_event(void *data, int raw_size, int cpu, + u64 timestamp, struct thread *thread) +{ + struct raw_event_queue *new; + + if (flush_limit == ULLONG_MAX) + flush_limit = timestamp + FLUSH_PERIOD; + + if (timestamp < last_flush) { + printf("Warning: Timestamp below last timeslice flush\n"); + return; + } + + new = malloc(sizeof(*new)); + if (!new) + die("Not enough memory\n"); + + new->timestamp = timestamp; + new->cpu = cpu; + new->thread = thread; + + new->data = malloc(raw_size); + if (!new->data) + die("Not enough memory\n"); + + memcpy(new->data, data, raw_size); + + __queue_raw_event(new); + last_inserted = new; + + /* + * We want to have a slice of events covering 2 * FLUSH_PERIOD + * If FLUSH_PERIOD is big enough, it ensures every events that occured + * in the first half of the timeslice have all been buffered and there + * are none remaining (we need that because of the weakly ordered + * event recording we have). Then once we reach the 2 * FLUSH_PERIOD + * timeslice, we flush the first half to be gentle with the memory + * (the second half can still get new events in the middle, so wait + * another period to flush it) + */ + if (new->timestamp > flush_limit && + new->timestamp - flush_limit > FLUSH_PERIOD) { + flush_limit += FLUSH_PERIOD; + flush_raw_event_queue(flush_limit); + } +} + +static int process_sample_event(event_t *event, struct perf_session *session) +{ + struct thread *thread; + struct sample_data data; + + bzero(&data, sizeof(struct sample_data)); + event__parse_sample(event, session->sample_type, &data); + thread = perf_session__findnew(session, data.pid); + + if (thread == NULL) { + pr_debug("problem processing %d event, skipping it.\n", + event->header.type); + return -1; + } + + dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); + + if (profile_cpu != -1 && profile_cpu != (int) data.cpu) + return 0; + + queue_raw_event(data.raw_data, data.raw_size, data.cpu, data.time, thread); + + return 0; +} + /* TODO: various way to print, coloring, nano or milli sec */ static void print_result(void) { struct lock_stat *st; char cut_name[20]; - int bad, total; + printf("%18s ", "ID"); printf("%20s ", "Name"); printf("%10s ", "acquired"); printf("%10s ", "contended"); @@ -730,15 +646,11 @@ static void print_result(void) printf("\n\n"); - bad = total = 0; while ((st = pop_from_result())) { - total++; - if (st->discard) { - bad++; - continue; - } bzero(cut_name, 20); + printf("%p ", st->addr); + if (strlen(st->name) < 16) { /* output raw name */ printf("%20s ", st->name); @@ -761,21 +673,6 @@ static void print_result(void) 0 : st->wait_time_min); printf("\n"); } - - { - /* Output for debug, this have to be removed */ - int i; - const char *name[4] = - { "acquire", "acquired", "contended", "release" }; - - printf("\n=== output for debug===\n\n"); - printf("bad:%d, total:%d\n", bad, total); - printf("bad rate:%f\n", (double)(bad / total)); - - printf("histogram of events caused bad sequence\n"); - for (i = 0; i < 4; i++) - printf(" %10s: %d\n", name[i], bad_hist[i]); - } } static void dump_map(void) @@ -790,35 +687,16 @@ static void dump_map(void) } } -static int process_sample_event(event_t *self, struct perf_session *s) -{ - struct sample_data data; - struct thread *thread; - - bzero(&data, sizeof(data)); - event__parse_sample(self, s->sample_type, &data); - - thread = perf_session__findnew(s, data.tid); - if (thread == NULL) { - pr_debug("problem processing %d event, skipping it.\n", - self->header.type); - return -1; - } - - process_raw_event(data.raw_data, data.cpu, data.time, thread); - - return 0; -} - static struct perf_event_ops eops = { .sample = process_sample_event, .comm = event__process_comm, - .ordered_samples = true, }; +static struct perf_session *session; + static int read_events(void) { - session = perf_session__new(input_name, O_RDONLY, 0, false); + session = perf_session__new(input_name, O_RDONLY, 0); if (!session) die("Initializing perf session failed\n"); @@ -842,6 +720,7 @@ static void __cmd_report(void) setup_pager(); select_key(); read_events(); + flush_raw_event_queue(ULLONG_MAX); sort_result(); print_result(); } @@ -865,7 +744,7 @@ static const char * const lock_usage[] = { static const struct option lock_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_END() }; diff --git a/trunk/tools/perf/builtin-probe.c b/trunk/tools/perf/builtin-probe.c index 61c6d70732c9..152d6c9b1fa4 100644 --- a/trunk/tools/perf/builtin-probe.c +++ b/trunk/tools/perf/builtin-probe.c @@ -36,10 +36,13 @@ #include "builtin.h" #include "util/util.h" #include "util/strlist.h" -#include "util/symbol.h" +#include "util/event.h" #include "util/debug.h" #include "util/debugfs.h" +#include "util/symbol.h" +#include "util/thread.h" #include "util/parse-options.h" +#include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" #include "util/probe-event.h" @@ -47,84 +50,103 @@ /* Session management structure */ static struct { + bool need_dwarf; bool list_events; bool force_add; bool show_lines; - int nevents; - struct perf_probe_event events[MAX_PROBES]; + int nr_probe; + struct probe_point probes[MAX_PROBES]; struct strlist *dellist; + struct map_groups kmap_groups; + struct map *kmaps[MAP__NR_TYPES]; struct line_range line_range; - int max_probe_points; -} params; +} session; /* Parse an event definition. Note that any error must die. */ -static int parse_probe_event(const char *str) +static void parse_probe_event(const char *str) { - struct perf_probe_event *pev = ¶ms.events[params.nevents]; - int ret; + struct probe_point *pp = &session.probes[session.nr_probe]; - pr_debug("probe-definition(%d): %s\n", params.nevents, str); - if (++params.nevents == MAX_PROBES) + pr_debug("probe-definition(%d): %s\n", session.nr_probe, str); + if (++session.nr_probe == MAX_PROBES) die("Too many probes (> %d) are specified.", MAX_PROBES); - /* Parse a perf-probe command into event */ - ret = parse_perf_probe_command(str, pev); - pr_debug("%d arguments\n", pev->nargs); + /* Parse perf-probe event into probe_point */ + parse_perf_probe_event(str, pp, &session.need_dwarf); - return ret; + pr_debug("%d arguments\n", pp->nr_args); } -static int parse_probe_event_argv(int argc, const char **argv) +static void parse_probe_event_argv(int argc, const char **argv) { - int i, len, ret; + int i, len; char *buf; /* Bind up rest arguments */ len = 0; for (i = 0; i < argc; i++) len += strlen(argv[i]) + 1; - buf = xzalloc(len + 1); + buf = zalloc(len + 1); + if (!buf) + die("Failed to allocate memory for binding arguments."); len = 0; for (i = 0; i < argc; i++) len += sprintf(&buf[len], "%s ", argv[i]); - ret = parse_probe_event(buf); + parse_probe_event(buf); free(buf); - return ret; } static int opt_add_probe_event(const struct option *opt __used, const char *str, int unset __used) { if (str) - return parse_probe_event(str); - else - return 0; + parse_probe_event(str); + return 0; } static int opt_del_probe_event(const struct option *opt __used, const char *str, int unset __used) { if (str) { - if (!params.dellist) - params.dellist = strlist__new(true, NULL); - strlist__add(params.dellist, str); + if (!session.dellist) + session.dellist = strlist__new(true, NULL); + strlist__add(session.dellist, str); } return 0; } -#ifdef DWARF_SUPPORT +/* Currently just checking function name from symbol map */ +static void evaluate_probe_point(struct probe_point *pp) +{ + struct symbol *sym; + sym = map__find_symbol_by_name(session.kmaps[MAP__FUNCTION], + pp->function, NULL); + if (!sym) + die("Kernel symbol \'%s\' not found - probe not added.", + pp->function); +} + +#ifndef NO_DWARF_SUPPORT +static int open_vmlinux(void) +{ + if (map__load(session.kmaps[MAP__FUNCTION], NULL) < 0) { + pr_debug("Failed to load kernel map.\n"); + return -EINVAL; + } + pr_debug("Try to open %s\n", + session.kmaps[MAP__FUNCTION]->dso->long_name); + return open(session.kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); +} + static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) { - int ret = 0; - if (str) - ret = parse_line_range_desc(str, ¶ms.line_range); - INIT_LIST_HEAD(¶ms.line_range.line_list); - params.show_lines = true; - - return ret; + parse_line_range_desc(str, &session.line_range); + INIT_LIST_HEAD(&session.line_range.line_list); + session.show_lines = true; + return 0; } #endif @@ -133,25 +155,29 @@ static const char * const probe_usage[] = { "perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", "perf probe [] --del '[GROUP:]EVENT' ...", "perf probe --list", -#ifdef DWARF_SUPPORT +#ifndef NO_DWARF_SUPPORT "perf probe --line 'LINEDESC'", #endif NULL }; static const struct option options[] = { - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), - OPT_BOOLEAN('l', "list", ¶ms.list_events, +#ifndef NO_DWARF_SUPPORT + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), +#endif + OPT_BOOLEAN('l', "list", &session.list_events, "list up current probe events"), OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", opt_del_probe_event), OPT_CALLBACK('a', "add", NULL, -#ifdef DWARF_SUPPORT - "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" - " [[NAME=]ARG ...]", +#ifdef NO_DWARF_SUPPORT + "[EVENT=]FUNC[+OFF|%return] [ARG ...]", #else - "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]", + "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" + " [ARG ...]", #endif "probe point definition, where\n" "\t\tGROUP:\tGroup name (optional)\n" @@ -159,35 +185,51 @@ static const struct option options[] = { "\t\tFUNC:\tFunction name\n" "\t\tOFF:\tOffset from function entry (in byte)\n" "\t\t%return:\tPut the probe at function return\n" -#ifdef DWARF_SUPPORT +#ifdef NO_DWARF_SUPPORT + "\t\tARG:\tProbe argument (only \n" +#else "\t\tSRC:\tSource code path\n" "\t\tRL:\tRelative line number from function entry.\n" "\t\tAL:\tAbsolute line number in file.\n" "\t\tPT:\tLazy expression of line code.\n" "\t\tARG:\tProbe argument (local variable name or\n" - "\t\t\tkprobe-tracer argument format.)\n", -#else - "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n", #endif + "\t\t\tkprobe-tracer argument format.)\n", opt_add_probe_event), - OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events" + OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" " with existing name"), -#ifdef DWARF_SUPPORT +#ifndef NO_DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, - "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]", + "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", "Show source code lines.", opt_show_lines), - OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, - "file", "vmlinux pathname"), #endif - OPT__DRY_RUN(&probe_event_dry_run), - OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, - "Set how many probe points can be found for a probe."), OPT_END() }; +/* Initialize symbol maps for vmlinux */ +static void init_vmlinux(void) +{ + symbol_conf.sort_by_name = true; + if (symbol_conf.vmlinux_name == NULL) + symbol_conf.try_vmlinux_path = true; + else + pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); + if (symbol__init() < 0) + die("Failed to init symbol map."); + + map_groups__init(&session.kmap_groups); + if (map_groups__create_kernel_maps(&session.kmap_groups, + session.kmaps) < 0) + die("Failed to create kernel maps."); +} + int cmd_probe(int argc, const char **argv, const char *prefix __used) { - int ret; + int i, ret; +#ifndef NO_DWARF_SUPPORT + int fd; +#endif + struct probe_point *pp; argc = parse_options(argc, argv, options, probe_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -196,69 +238,123 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) pr_warning(" Error: '-' is not supported.\n"); usage_with_options(probe_usage, options); } - ret = parse_probe_event_argv(argc, argv); - if (ret < 0) { - pr_err(" Error: Parse Error. (%d)\n", ret); - return ret; - } + parse_probe_event_argv(argc, argv); } - if (params.max_probe_points == 0) - params.max_probe_points = MAX_PROBES; - - if ((!params.nevents && !params.dellist && !params.list_events && - !params.show_lines)) + if ((!session.nr_probe && !session.dellist && !session.list_events && + !session.show_lines)) usage_with_options(probe_usage, options); - if (params.list_events) { - if (params.nevents != 0 || params.dellist) { - pr_err(" Error: Don't use --list with --add/--del.\n"); + if (debugfs_valid_mountpoint(debugfs_path) < 0) + die("Failed to find debugfs path."); + + if (session.list_events) { + if (session.nr_probe != 0 || session.dellist) { + pr_warning(" Error: Don't use --list with" + " --add/--del.\n"); usage_with_options(probe_usage, options); } - if (params.show_lines) { - pr_err(" Error: Don't use --list with --line.\n"); + if (session.show_lines) { + pr_warning(" Error: Don't use --list with --line.\n"); usage_with_options(probe_usage, options); } - ret = show_perf_probe_events(); - if (ret < 0) - pr_err(" Error: Failed to show event list. (%d)\n", - ret); - return ret; + show_perf_probe_events(); + return 0; } -#ifdef DWARF_SUPPORT - if (params.show_lines) { - if (params.nevents != 0 || params.dellist) { +#ifndef NO_DWARF_SUPPORT + if (session.show_lines) { + if (session.nr_probe != 0 || session.dellist) { pr_warning(" Error: Don't use --line with" " --add/--del.\n"); usage_with_options(probe_usage, options); } - - ret = show_line_range(¶ms.line_range); - if (ret < 0) - pr_err(" Error: Failed to show lines. (%d)\n", ret); - return ret; + init_vmlinux(); + fd = open_vmlinux(); + if (fd < 0) + die("Could not open debuginfo file."); + ret = find_line_range(fd, &session.line_range); + if (ret <= 0) + die("Source line is not found.\n"); + close(fd); + show_line_range(&session.line_range); + return 0; } #endif - if (params.dellist) { - ret = del_perf_probe_events(params.dellist); - strlist__delete(params.dellist); - if (ret < 0) { - pr_err(" Error: Failed to delete events. (%d)\n", ret); - return ret; - } + if (session.dellist) { + del_trace_kprobe_events(session.dellist); + strlist__delete(session.dellist); + if (session.nr_probe == 0) + return 0; } - if (params.nevents) { - ret = add_perf_probe_events(params.events, params.nevents, - params.force_add, - params.max_probe_points); - if (ret < 0) { - pr_err(" Error: Failed to add events. (%d)\n", ret); - return ret; + /* Add probes */ + init_vmlinux(); + + if (session.need_dwarf) +#ifdef NO_DWARF_SUPPORT + die("Debuginfo-analysis is not supported"); +#else /* !NO_DWARF_SUPPORT */ + pr_debug("Some probes require debuginfo.\n"); + + fd = open_vmlinux(); + if (fd < 0) { + if (session.need_dwarf) + die("Could not open debuginfo file."); + + pr_debug("Could not open vmlinux/module file." + " Try to use symbols.\n"); + goto end_dwarf; + } + + /* Searching probe points */ + for (i = 0; i < session.nr_probe; i++) { + pp = &session.probes[i]; + if (pp->found) + continue; + + lseek(fd, SEEK_SET, 0); + ret = find_probe_point(fd, pp); + if (ret > 0) + continue; + if (ret == 0) { /* No error but failed to find probe point. */ + synthesize_perf_probe_point(pp); + die("Probe point '%s' not found. - probe not added.", + pp->probes[0]); } + /* Error path */ + if (session.need_dwarf) { + if (ret == -ENOENT) + pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); + die("Could not analyze debuginfo."); + } + pr_debug("An error occurred in debuginfo analysis." + " Try to use symbols.\n"); + break; + } + close(fd); + +end_dwarf: +#endif /* !NO_DWARF_SUPPORT */ + + /* Synthesize probes without dwarf */ + for (i = 0; i < session.nr_probe; i++) { + pp = &session.probes[i]; + if (pp->found) /* This probe is already found. */ + continue; + + evaluate_probe_point(pp); + ret = synthesize_trace_kprobe_event(pp); + if (ret == -E2BIG) + die("probe point definition becomes too long."); + else if (ret < 0) + die("Failed to synthesize a probe point."); } + + /* Settng up probe points */ + add_trace_kprobe_events(session.probes, session.nr_probe, + session.force_add); return 0; } diff --git a/trunk/tools/perf/builtin-record.c b/trunk/tools/perf/builtin-record.c index d3981ac50e1d..3b8b6387c47c 100644 --- a/trunk/tools/perf/builtin-record.c +++ b/trunk/tools/perf/builtin-record.c @@ -15,6 +15,7 @@ #include "util/util.h" #include "util/parse-options.h" #include "util/parse-events.h" +#include "util/string.h" #include "util/header.h" #include "util/event.h" @@ -26,41 +27,31 @@ #include #include -enum write_mode_t { - WRITE_FORCE, - WRITE_APPEND -}; - -static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; +static int fd[MAX_NR_CPUS][MAX_COUNTERS]; -static unsigned int user_interval = UINT_MAX; static long default_interval = 0; static int nr_cpus = 0; static unsigned int page_size; static unsigned int mmap_pages = 128; -static unsigned int user_freq = UINT_MAX; static int freq = 1000; static int output; -static int pipe_output = 0; static const char *output_name = "perf.data"; static int group = 0; static unsigned int realtime_prio = 0; -static bool raw_samples = false; -static bool system_wide = false; +static int raw_samples = 0; +static int system_wide = 0; static int profile_cpu = -1; static pid_t target_pid = -1; -static pid_t target_tid = -1; -static pid_t *all_tids = NULL; -static int thread_num = 0; static pid_t child_pid = -1; -static bool inherit = true; -static enum write_mode_t write_mode = WRITE_FORCE; -static bool call_graph = false; -static bool inherit_stat = false; -static bool no_samples = false; -static bool sample_address = false; -static bool multiplex = false; +static int inherit = 1; +static int force = 0; +static int append_file = 0; +static int call_graph = 0; +static int inherit_stat = 0; +static int no_samples = 0; +static int sample_address = 0; +static int multiplex = 0; static int multiplex_fd = -1; static long samples = 0; @@ -69,7 +60,7 @@ static struct timeval this_read; static u64 bytes_written = 0; -static struct pollfd *event_array; +static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; static int nr_poll = 0; static int nr_cpu = 0; @@ -86,7 +77,7 @@ struct mmap_data { unsigned int prev; }; -static struct mmap_data *mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; +static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; static unsigned long mmap_read_head(struct mmap_data *md) { @@ -110,11 +101,6 @@ static void mmap_write_tail(struct mmap_data *md, unsigned long tail) pc->data_tail = tail; } -static void advance_output(size_t size) -{ - bytes_written += size; -} - static void write_output(void *buf, size_t size) { while (size) { @@ -239,13 +225,12 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n return h_attr; } -static void create_counter(int counter, int cpu) +static void create_counter(int counter, int cpu, pid_t pid) { char *filter = filters[counter]; struct perf_event_attr *attr = attrs + counter; struct perf_header_attr *h_attr; int track = !counter; /* only the first counter needs these */ - int thread_index; int ret; struct { u64 count; @@ -263,19 +248,10 @@ static void create_counter(int counter, int cpu) if (nr_counters > 1) attr->sample_type |= PERF_SAMPLE_ID; - /* - * We default some events to a 1 default interval. But keep - * it a weak assumption overridable by the user. - */ - if (!attr->sample_period || (user_freq != UINT_MAX && - user_interval != UINT_MAX)) { - if (freq) { - attr->sample_type |= PERF_SAMPLE_PERIOD; - attr->freq = 1; - attr->sample_freq = freq; - } else { - attr->sample_period = default_interval; - } + if (freq) { + attr->sample_type |= PERF_SAMPLE_PERIOD; + attr->freq = 1; + attr->sample_freq = freq; } if (no_samples) @@ -299,129 +275,118 @@ static void create_counter(int counter, int cpu) attr->mmap = track; attr->comm = track; attr->inherit = inherit; - if (target_pid == -1 && !system_wide) { - attr->disabled = 1; - attr->enable_on_exec = 1; - } + attr->disabled = 1; - for (thread_index = 0; thread_index < thread_num; thread_index++) { try_again: - fd[nr_cpu][counter][thread_index] = sys_perf_event_open(attr, - all_tids[thread_index], cpu, group_fd, 0); - - if (fd[nr_cpu][counter][thread_index] < 0) { - int err = errno; - - if (err == EPERM || err == EACCES) - die("Permission error - are you root?\n" - "\t Consider tweaking" - " /proc/sys/kernel/perf_event_paranoid.\n"); - else if (err == ENODEV && profile_cpu != -1) { - die("No such device - did you specify" - " an out-of-range profile CPU?\n"); - } + fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0); - /* - * If it's cycles then fall back to hrtimer - * based cpu-clock-tick sw counter, which - * is always available even if no PMU support: - */ - if (attr->type == PERF_TYPE_HARDWARE - && attr->config == PERF_COUNT_HW_CPU_CYCLES) { - - if (verbose) - warning(" ... trying to fall back to cpu-clock-ticks\n"); - attr->type = PERF_TYPE_SOFTWARE; - attr->config = PERF_COUNT_SW_CPU_CLOCK; - goto try_again; - } - printf("\n"); - error("perfcounter syscall returned with %d (%s)\n", - fd[nr_cpu][counter][thread_index], strerror(err)); + if (fd[nr_cpu][counter] < 0) { + int err = errno; -#if defined(__i386__) || defined(__x86_64__) - if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) - die("No hardware sampling interrupt available." - " No APIC? If so then you can boot the kernel" - " with the \"lapic\" boot parameter to" - " force-enable it.\n"); -#endif + if (err == EPERM || err == EACCES) + die("Permission error - are you root?\n"); + else if (err == ENODEV && profile_cpu != -1) + die("No such device - did you specify an out-of-range profile CPU?\n"); - die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); - exit(-1); + /* + * If it's cycles then fall back to hrtimer + * based cpu-clock-tick sw counter, which + * is always available even if no PMU support: + */ + if (attr->type == PERF_TYPE_HARDWARE + && attr->config == PERF_COUNT_HW_CPU_CYCLES) { + + if (verbose) + warning(" ... trying to fall back to cpu-clock-ticks\n"); + attr->type = PERF_TYPE_SOFTWARE; + attr->config = PERF_COUNT_SW_CPU_CLOCK; + goto try_again; } + printf("\n"); + error("perfcounter syscall returned with %d (%s)\n", + fd[nr_cpu][counter], strerror(err)); - h_attr = get_header_attr(attr, counter); - if (h_attr == NULL) - die("nomem\n"); +#if defined(__i386__) || defined(__x86_64__) + if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) + die("No hardware sampling interrupt available. No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.\n"); +#endif - if (!file_new) { - if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { - fprintf(stderr, "incompatible append\n"); - exit(-1); - } - } + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); + exit(-1); + } - if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) { - perror("Unable to read perf file descriptor\n"); - exit(-1); - } + h_attr = get_header_attr(attr, counter); + if (h_attr == NULL) + die("nomem\n"); - if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { - pr_warning("Not enough memory to add id\n"); + if (!file_new) { + if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { + fprintf(stderr, "incompatible append\n"); exit(-1); } + } - assert(fd[nr_cpu][counter][thread_index] >= 0); - fcntl(fd[nr_cpu][counter][thread_index], F_SETFL, O_NONBLOCK); + if (read(fd[nr_cpu][counter], &read_data, sizeof(read_data)) == -1) { + perror("Unable to read perf file descriptor\n"); + exit(-1); + } - /* - * First counter acts as the group leader: - */ - if (group && group_fd == -1) - group_fd = fd[nr_cpu][counter][thread_index]; - if (multiplex && multiplex_fd == -1) - multiplex_fd = fd[nr_cpu][counter][thread_index]; + if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { + pr_warning("Not enough memory to add id\n"); + exit(-1); + } - if (multiplex && fd[nr_cpu][counter][thread_index] != multiplex_fd) { + assert(fd[nr_cpu][counter] >= 0); + fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); - ret = ioctl(fd[nr_cpu][counter][thread_index], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); - assert(ret != -1); - } else { - event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index]; - event_array[nr_poll].events = POLLIN; - nr_poll++; - - mmap_array[nr_cpu][counter][thread_index].counter = counter; - mmap_array[nr_cpu][counter][thread_index].prev = 0; - mmap_array[nr_cpu][counter][thread_index].mask = mmap_pages*page_size - 1; - mmap_array[nr_cpu][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, - PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter][thread_index], 0); - if (mmap_array[nr_cpu][counter][thread_index].base == MAP_FAILED) { - error("failed to mmap with %d (%s)\n", errno, strerror(errno)); - exit(-1); - } + /* + * First counter acts as the group leader: + */ + if (group && group_fd == -1) + group_fd = fd[nr_cpu][counter]; + if (multiplex && multiplex_fd == -1) + multiplex_fd = fd[nr_cpu][counter]; + + if (multiplex && fd[nr_cpu][counter] != multiplex_fd) { + + ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); + assert(ret != -1); + } else { + event_array[nr_poll].fd = fd[nr_cpu][counter]; + event_array[nr_poll].events = POLLIN; + nr_poll++; + + mmap_array[nr_cpu][counter].counter = counter; + mmap_array[nr_cpu][counter].prev = 0; + mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; + mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0); + if (mmap_array[nr_cpu][counter].base == MAP_FAILED) { + error("failed to mmap with %d (%s)\n", errno, strerror(errno)); + exit(-1); } + } - if (filter != NULL) { - ret = ioctl(fd[nr_cpu][counter][thread_index], - PERF_EVENT_IOC_SET_FILTER, filter); - if (ret) { - error("failed to set filter with %d (%s)\n", errno, - strerror(errno)); - exit(-1); - } + if (filter != NULL) { + ret = ioctl(fd[nr_cpu][counter], + PERF_EVENT_IOC_SET_FILTER, filter); + if (ret) { + error("failed to set filter with %d (%s)\n", errno, + strerror(errno)); + exit(-1); } } + + ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE); } -static void open_counters(int cpu) +static void open_counters(int cpu, pid_t pid) { int counter; group_fd = -1; for (counter = 0; counter < nr_counters; counter++) - create_counter(counter, cpu); + create_counter(counter, cpu, pid); nr_cpu++; } @@ -441,57 +406,10 @@ static int process_buildids(void) static void atexit_header(void) { - if (!pipe_output) { - session->header.data_size += bytes_written; - - process_buildids(); - perf_header__write(&session->header, output, true); - } -} - -static void event__synthesize_guest_os(struct machine *machine, void *data) -{ - int err; - char *guest_kallsyms; - char path[PATH_MAX]; - struct perf_session *psession = data; - - if (machine__is_host(machine)) - return; - - /* - *As for guest kernel when processing subcommand record&report, - *we arrange module mmap prior to guest kernel mmap and trigger - *a preload dso because default guest module symbols are loaded - *from guest kallsyms instead of /lib/modules/XXX/XXX. This - *method is used to avoid symbol missing when the first addr is - *in module instead of in guest kernel. - */ - err = event__synthesize_modules(process_synthesized_event, - psession, machine); - if (err < 0) - pr_err("Couldn't record guest kernel [%d]'s reference" - " relocation symbol.\n", machine->pid); - - if (machine__is_default_guest(machine)) - guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms; - else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); - guest_kallsyms = path; - } + session->header.data_size += bytes_written; - /* - * We use _stext for guest kernel because guest kernel's /proc/kallsyms - * have no _text sometimes. - */ - err = event__synthesize_kernel_mmap(process_synthesized_event, - psession, machine, "_text"); - if (err < 0) - err = event__synthesize_kernel_mmap(process_synthesized_event, - psession, machine, "_stext"); - if (err < 0) - pr_err("Couldn't record guest kernel [%d]'s reference" - " relocation symbol.\n", machine->pid); + process_buildids(); + perf_header__write(&session->header, output, true); } static int __cmd_record(int argc, const char **argv) @@ -503,9 +421,8 @@ static int __cmd_record(int argc, const char **argv) int err; unsigned long waking = 0; int child_ready_pipe[2], go_pipe[2]; - const bool forks = argc > 0; + const bool forks = target_pid == -1 && argc > 0; char buf; - struct machine *machine; page_size = sysconf(_SC_PAGE_SIZE); @@ -518,63 +435,70 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } - if (!strcmp(output_name, "-")) - pipe_output = 1; - else if (!stat(output_name, &st) && st.st_size) { - if (write_mode == WRITE_FORCE) { + if (!stat(output_name, &st) && st.st_size) { + if (!force) { + if (!append_file) { + pr_err("Error, output file %s exists, use -A " + "to append or -f to overwrite.\n", + output_name); + exit(-1); + } + } else { char oldname[PATH_MAX]; snprintf(oldname, sizeof(oldname), "%s.old", output_name); unlink(oldname); rename(output_name, oldname); } - } else if (write_mode == WRITE_APPEND) { - write_mode = WRITE_FORCE; + } else { + append_file = 0; } flags = O_CREAT|O_RDWR; - if (write_mode == WRITE_APPEND) + if (append_file) file_new = 0; else flags |= O_TRUNC; - if (pipe_output) - output = STDOUT_FILENO; - else - output = open(output_name, flags, S_IRUSR | S_IWUSR); + output = open(output_name, flags, S_IRUSR|S_IWUSR); if (output < 0) { perror("failed to create output file"); exit(-1); } - session = perf_session__new(output_name, O_WRONLY, - write_mode == WRITE_FORCE, false); + session = perf_session__new(output_name, O_WRONLY, force); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; } if (!file_new) { - err = perf_header__read(session, output); + err = perf_header__read(&session->header, output); if (err < 0) return err; } - if (have_tracepoints(attrs, nr_counters)) + if (raw_samples) { perf_header__set_feat(&session->header, HEADER_TRACE_INFO); + } else { + for (i = 0; i < nr_counters; i++) { + if (attrs[i].sample_type & PERF_SAMPLE_RAW) { + perf_header__set_feat(&session->header, HEADER_TRACE_INFO); + break; + } + } + } atexit(atexit_header); if (forks) { - child_pid = fork(); + pid = fork(); if (pid < 0) { perror("failed to fork"); exit(-1); } - if (!child_pid) { - if (pipe_output) - dup2(2, 1); + if (!pid) { close(child_ready_pipe[0]); close(go_pipe[1]); fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); @@ -603,8 +527,10 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } - if (!system_wide && target_tid == -1 && target_pid == -1) - all_tids[0] = child_pid; + child_pid = pid; + + if (!system_wide) + target_pid = pid; close(child_ready_pipe[1]); close(go_pipe[0]); @@ -618,19 +544,16 @@ static int __cmd_record(int argc, const char **argv) close(child_ready_pipe[0]); } + if ((!system_wide && !inherit) || profile_cpu != -1) { - open_counters(profile_cpu); + open_counters(profile_cpu, target_pid); } else { nr_cpus = read_cpu_map(); for (i = 0; i < nr_cpus; i++) - open_counters(cpumap[i]); + open_counters(cpumap[i], target_pid); } - if (pipe_output) { - err = perf_header__write_pipe(output); - if (err < 0) - return err; - } else if (file_new) { + if (file_new) { err = perf_header__write(&session->header, output, false); if (err < 0) return err; @@ -638,70 +561,21 @@ static int __cmd_record(int argc, const char **argv) post_processing_offset = lseek(output, 0, SEEK_CUR); - if (pipe_output) { - err = event__synthesize_attrs(&session->header, - process_synthesized_event, - session); - if (err < 0) { - pr_err("Couldn't synthesize attrs.\n"); - return err; - } - - err = event__synthesize_event_types(process_synthesized_event, - session); - if (err < 0) { - pr_err("Couldn't synthesize event_types.\n"); - return err; - } - - if (have_tracepoints(attrs, nr_counters)) { - /* - * FIXME err <= 0 here actually means that - * there were no tracepoints so its not really - * an error, just that we don't need to - * synthesize anything. We really have to - * return this more properly and also - * propagate errors that now are calling die() - */ - err = event__synthesize_tracing_data(output, attrs, - nr_counters, - process_synthesized_event, - session); - if (err <= 0) { - pr_err("Couldn't record tracing data.\n"); - return err; - } - advance_output(err); - } - } - - machine = perf_session__find_host_machine(session); - if (!machine) { - pr_err("Couldn't find native kernel information.\n"); - return -1; - } - err = event__synthesize_kernel_mmap(process_synthesized_event, - session, machine, "_text"); - if (err < 0) - err = event__synthesize_kernel_mmap(process_synthesized_event, - session, machine, "_stext"); + session, "_text"); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } - err = event__synthesize_modules(process_synthesized_event, - session, machine); + err = event__synthesize_modules(process_synthesized_event, session); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } - if (perf_guest) - perf_session__process_machines(session, event__synthesize_guest_os); if (!system_wide && profile_cpu == -1) - event__synthesize_thread(target_tid, process_synthesized_event, + event__synthesize_thread(target_pid, process_synthesized_event, session); else event__synthesize_threads(process_synthesized_event, session); @@ -724,16 +598,11 @@ static int __cmd_record(int argc, const char **argv) for (;;) { int hits = samples; - int thread; for (i = 0; i < nr_cpu; i++) { for (counter = 0; counter < nr_counters; counter++) { - for (thread = 0; - thread < thread_num; thread++) { - if (mmap_array[i][counter][thread].base) - mmap_read(&mmap_array[i][counter][thread]); - } - + if (mmap_array[i][counter].base) + mmap_read(&mmap_array[i][counter]); } } @@ -746,15 +615,8 @@ static int __cmd_record(int argc, const char **argv) if (done) { for (i = 0; i < nr_cpu; i++) { - for (counter = 0; - counter < nr_counters; - counter++) { - for (thread = 0; - thread < thread_num; - thread++) - ioctl(fd[i][counter][thread], - PERF_EVENT_IOC_DISABLE); - } + for (counter = 0; counter < nr_counters; counter++) + ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE); } } } @@ -779,8 +641,6 @@ static const char * const record_usage[] = { NULL }; -static bool force, append_file; - static const struct option options[] = { OPT_CALLBACK('e', "event", NULL, "event", "event selector. use 'perf list' to list available events", @@ -788,9 +648,7 @@ static const struct option options[] = { OPT_CALLBACK(0, "filter", NULL, "filter", "event filter", parse_filter), OPT_INTEGER('p', "pid", &target_pid, - "record events on existing process id"), - OPT_INTEGER('t', "tid", &target_tid, - "record events on existing thread id"), + "record events on existing pid"), OPT_INTEGER('r', "realtime", &realtime_prio, "collect data with this RT SCHED_FIFO priority"), OPT_BOOLEAN('R', "raw-samples", &raw_samples, @@ -802,20 +660,20 @@ static const struct option options[] = { OPT_INTEGER('C', "profile_cpu", &profile_cpu, "CPU to profile on"), OPT_BOOLEAN('f', "force", &force, - "overwrite existing data file (deprecated)"), - OPT_LONG('c', "count", &user_interval, + "overwrite existing data file"), + OPT_LONG('c', "count", &default_interval, "event period to sample"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_BOOLEAN('i', "inherit", &inherit, "child tasks inherit counters"), - OPT_INTEGER('F', "freq", &user_freq, + OPT_INTEGER('F', "freq", &freq, "profile at this frequency"), OPT_INTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), OPT_BOOLEAN('g', "call-graph", &call_graph, "do call-graph (stack chain/backtrace) recording"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_BOOLEAN('s', "stat", &inherit_stat, "per thread counts"), @@ -830,23 +688,12 @@ static const struct option options[] = { int cmd_record(int argc, const char **argv, const char *prefix __used) { - int i,j; + int counter; argc = parse_options(argc, argv, options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && target_pid == -1 && target_tid == -1 && - !system_wide && profile_cpu == -1) - usage_with_options(record_usage, options); - - if (force && append_file) { - fprintf(stderr, "Can't overwrite and append at the same time." - " You need to choose between -f and -A"); + if (!argc && target_pid == -1 && !system_wide && profile_cpu == -1) usage_with_options(record_usage, options); - } else if (append_file) { - write_mode = WRITE_APPEND; - } else { - write_mode = WRITE_FORCE; - } symbol__init(); @@ -856,42 +703,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; } - if (target_pid != -1) { - target_tid = target_pid; - thread_num = find_all_tid(target_pid, &all_tids); - if (thread_num <= 0) { - fprintf(stderr, "Can't find all threads of pid %d\n", - target_pid); - usage_with_options(record_usage, options); - } - } else { - all_tids=malloc(sizeof(pid_t)); - if (!all_tids) - return -ENOMEM; - - all_tids[0] = target_tid; - thread_num = 1; - } - - for (i = 0; i < MAX_NR_CPUS; i++) { - for (j = 0; j < MAX_COUNTERS; j++) { - fd[i][j] = malloc(sizeof(int)*thread_num); - mmap_array[i][j] = zalloc( - sizeof(struct mmap_data)*thread_num); - if (!fd[i][j] || !mmap_array[i][j]) - return -ENOMEM; - } - } - event_array = malloc( - sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); - if (!event_array) - return -ENOMEM; - - if (user_interval != UINT_MAX) - default_interval = user_interval; - if (user_freq != UINT_MAX) - freq = user_freq; - /* * User specified count overrides default frequency. */ @@ -904,5 +715,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) exit(EXIT_FAILURE); } + for (counter = 0; counter < nr_counters; counter++) { + if (attrs[counter].sample_period) + continue; + + attrs[counter].sample_period = default_interval; + } + return __cmd_record(argc, argv); } diff --git a/trunk/tools/perf/builtin-report.c b/trunk/tools/perf/builtin-report.c index 0152b5412cc3..f815de25d0fc 100644 --- a/trunk/tools/perf/builtin-report.c +++ b/trunk/tools/perf/builtin-report.c @@ -14,6 +14,7 @@ #include "util/cache.h" #include #include "util/symbol.h" +#include "util/string.h" #include "util/callchain.h" #include "util/strlist.h" #include "util/values.h" @@ -32,11 +33,11 @@ static char const *input_name = "perf.data"; -static bool force; +static int force; static bool hide_unresolved; static bool dont_use_callchains; -static bool show_threads; +static int show_threads; static struct perf_read_values show_threads_values; static char default_pretty_printing_style[] = "normal"; @@ -80,20 +81,15 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, struct sample_data *data) { - struct map_symbol *syms = NULL; - struct symbol *parent = NULL; + struct symbol **syms = NULL, *parent = NULL; bool hit; - int err; struct hist_entry *he; struct event_stat_id *stats; struct perf_event_attr *attr; - if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { + if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) syms = perf_session__resolve_callchain(self, al->thread, data->callchain, &parent); - if (syms == NULL) - return -ENOMEM; - } attr = perf_header__find_attr(data->id, &self->header); if (attr) @@ -108,16 +104,13 @@ static int perf_session__add_hist_entry(struct perf_session *self, return -ENOMEM; if (hit) - __perf_session__add_count(he, al, data->period); + he->count += data->period; if (symbol_conf.use_callchain) { if (!hit) - callchain_init(he->callchain); - err = append_chain(he->callchain, data->callchain, syms); + callchain_init(&he->callchain); + append_chain(&he->callchain, data->callchain, syms); free(syms); - - if (err) - return err; } return 0; @@ -267,29 +260,15 @@ static struct perf_event_ops event_ops = { .fork = event__process_task, .lost = event__process_lost, .read = process_read_event, - .attr = event__process_attr, - .event_type = event__process_event_type, - .tracing_data = event__process_tracing_data, - .build_id = event__process_build_id, }; -extern volatile int session_done; - -static void sig_handler(int sig __attribute__((__unused__))) -{ - session_done = 1; -} - static int __cmd_report(void) { int ret = -EINVAL; struct perf_session *session; struct rb_node *next; - const char *help = "For a higher level overview, try: perf report --sort comm,dso"; - - signal(SIGINT, sig_handler); - session = perf_session__new(input_name, O_RDONLY, force, false); + session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) return -ENOMEM; @@ -313,49 +292,39 @@ static int __cmd_report(void) perf_session__fprintf(session, stdout); if (verbose > 2) - perf_session__fprintf_dsos(session, stdout); + dsos__fprintf(stdout); next = rb_first(&session->stats_by_id); while (next) { struct event_stat_id *stats; - u64 nr_hists; stats = rb_entry(next, struct event_stat_id, rb_node); perf_session__collapse_resort(&stats->hists); - nr_hists = perf_session__output_resort(&stats->hists, - stats->stats.total); - if (use_browser) - perf_session__browse_hists(&stats->hists, nr_hists, - stats->stats.total, help, - input_name); - else { - if (rb_first(&session->stats_by_id) == - rb_last(&session->stats_by_id)) - fprintf(stdout, "# Samples: %Ld\n#\n", - stats->stats.total); - else - fprintf(stdout, "# Samples: %Ld %s\n#\n", - stats->stats.total, - __event_name(stats->type, stats->config)); - - perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, - stats->stats.total); - fprintf(stdout, "\n\n"); - } + perf_session__output_resort(&stats->hists, stats->stats.total); + if (rb_first(&session->stats_by_id) == + rb_last(&session->stats_by_id)) + fprintf(stdout, "# Samples: %Ld\n#\n", + stats->stats.total); + else + fprintf(stdout, "# Samples: %Ld %s\n#\n", + stats->stats.total, + __event_name(stats->type, stats->config)); + perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, + stats->stats.total); + fprintf(stdout, "\n\n"); next = rb_next(&stats->rb_node); } - if (!use_browser && sort_order == default_sort_order && - parent_pattern == default_parent_pattern) { - fprintf(stdout, "#\n# (%s)\n#\n", help); + if (sort_order == default_sort_order && + parent_pattern == default_parent_pattern) + fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); - if (show_threads) { - bool style = !strcmp(pretty_printing_style, "raw"); - perf_read_values_display(stdout, &show_threads_values, - style); - perf_read_values_destroy(&show_threads_values); - } + if (show_threads) { + bool raw_printing_style = !strcmp(pretty_printing_style, "raw"); + perf_read_values_display(stdout, &show_threads_values, + raw_printing_style); + perf_read_values_destroy(&show_threads_values); } out_delete: perf_session__delete(session); @@ -431,7 +400,7 @@ static const char * const report_usage[] = { static const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), @@ -450,8 +419,6 @@ static const struct option options[] = { "sort by key(s): pid, comm, dso, symbol, parent"), OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, "Don't shorten the pathnames taking into account the cwd"), - OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, - "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", "regex filter to identify parent, see: '--sort parent'"), OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, @@ -480,8 +447,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) { argc = parse_options(argc, argv, options, report_usage, 0); - if (strcmp(input_name, "-") != 0) - setup_browser(); + setup_pager(); if (symbol__init() < 0) return -1; @@ -489,8 +455,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) setup_sorting(report_usage, options); if (parent_pattern != default_parent_pattern) { - if (sort_dimension__add("parent") < 0) - return -1; + sort_dimension__add("parent"); sort_parent.elide = 1; } else symbol_conf.exclude_other = false; diff --git a/trunk/tools/perf/builtin-sched.c b/trunk/tools/perf/builtin-sched.c index aef6ed0e119c..4f5a03e43444 100644 --- a/trunk/tools/perf/builtin-sched.c +++ b/trunk/tools/perf/builtin-sched.c @@ -68,10 +68,10 @@ enum sched_event_type { struct sched_atom { enum sched_event_type type; - int specific_wait; u64 timestamp; u64 duration; unsigned long nr; + int specific_wait; sem_t *wait_sem; struct task_desc *wakee; }; @@ -1651,16 +1651,15 @@ static int process_lost_event(event_t *event __used, } static struct perf_event_ops event_ops = { - .sample = process_sample_event, - .comm = event__process_comm, - .lost = process_lost_event, - .ordered_samples = true, + .sample = process_sample_event, + .comm = event__process_comm, + .lost = process_lost_event, }; static int read_events(void) { int err = -EINVAL; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); if (session == NULL) return -ENOMEM; @@ -1791,7 +1790,7 @@ static const char * const sched_usage[] = { static const struct option sched_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), @@ -1806,7 +1805,7 @@ static const char * const latency_usage[] = { static const struct option latency_options[] = { OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): runtime, switch, avg, max"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_INTEGER('C', "CPU", &profile_cpu, "CPU to profile on"), @@ -1823,7 +1822,7 @@ static const char * const replay_usage[] = { static const struct option replay_options[] = { OPT_INTEGER('r', "repeat", &replay_repeat, "repeat the workload replay N times (-1: infinite)"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), @@ -1851,6 +1850,7 @@ static const char *record_args[] = { "record", "-a", "-R", + "-M", "-f", "-m", "1024", "-c", "1", diff --git a/trunk/tools/perf/builtin-stat.c b/trunk/tools/perf/builtin-stat.c index e619ac89dff5..95db31cff6fd 100644 --- a/trunk/tools/perf/builtin-stat.c +++ b/trunk/tools/perf/builtin-stat.c @@ -46,7 +46,6 @@ #include "util/debug.h" #include "util/header.h" #include "util/cpumap.h" -#include "util/thread.h" #include #include @@ -67,21 +66,18 @@ static struct perf_event_attr default_attrs[] = { }; -static bool system_wide = false; +static int system_wide = 0; static unsigned int nr_cpus = 0; static int run_idx = 0; static int run_count = 1; -static bool inherit = true; -static bool scale = true; +static int inherit = 1; +static int scale = 1; static pid_t target_pid = -1; -static pid_t target_tid = -1; -static pid_t *all_tids = NULL; -static int thread_num = 0; static pid_t child_pid = -1; -static bool null_run = false; +static int null_run = 0; -static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; +static int fd[MAX_NR_CPUS][MAX_COUNTERS]; static int event_scaled[MAX_COUNTERS]; @@ -144,11 +140,9 @@ struct stats runtime_branches_stats; #define ERR_PERF_OPEN \ "Error: counter %d, sys_perf_event_open() syscall returned with %d (%s)\n" -static int create_perf_stat_counter(int counter) +static void create_perf_stat_counter(int counter, int pid) { struct perf_event_attr *attr = attrs + counter; - int thread; - int ncreated = 0; if (scale) attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | @@ -158,33 +152,21 @@ static int create_perf_stat_counter(int counter) unsigned int cpu; for (cpu = 0; cpu < nr_cpus; cpu++) { - fd[cpu][counter][0] = sys_perf_event_open(attr, - -1, cpumap[cpu], -1, 0); - if (fd[cpu][counter][0] < 0) - pr_debug(ERR_PERF_OPEN, counter, - fd[cpu][counter][0], strerror(errno)); - else - ++ncreated; + fd[cpu][counter] = sys_perf_event_open(attr, -1, cpumap[cpu], -1, 0); + if (fd[cpu][counter] < 0 && verbose) + fprintf(stderr, ERR_PERF_OPEN, counter, + fd[cpu][counter], strerror(errno)); } } else { attr->inherit = inherit; - if (target_pid == -1) { - attr->disabled = 1; - attr->enable_on_exec = 1; - } - for (thread = 0; thread < thread_num; thread++) { - fd[0][counter][thread] = sys_perf_event_open(attr, - all_tids[thread], -1, -1, 0); - if (fd[0][counter][thread] < 0) - pr_debug(ERR_PERF_OPEN, counter, - fd[0][counter][thread], - strerror(errno)); - else - ++ncreated; - } - } + attr->disabled = 1; + attr->enable_on_exec = 1; - return ncreated; + fd[0][counter] = sys_perf_event_open(attr, pid, -1, -1, 0); + if (fd[0][counter] < 0 && verbose) + fprintf(stderr, ERR_PERF_OPEN, counter, + fd[0][counter], strerror(errno)); + } } /* @@ -208,28 +190,25 @@ static void read_counter(int counter) unsigned int cpu; size_t res, nv; int scaled; - int i, thread; + int i; count[0] = count[1] = count[2] = 0; nv = scale ? 3 : 1; for (cpu = 0; cpu < nr_cpus; cpu++) { - for (thread = 0; thread < thread_num; thread++) { - if (fd[cpu][counter][thread] < 0) - continue; - - res = read(fd[cpu][counter][thread], - single_count, nv * sizeof(u64)); - assert(res == nv * sizeof(u64)); - - close(fd[cpu][counter][thread]); - fd[cpu][counter][thread] = -1; - - count[0] += single_count[0]; - if (scale) { - count[1] += single_count[1]; - count[2] += single_count[2]; - } + if (fd[cpu][counter] < 0) + continue; + + res = read(fd[cpu][counter], single_count, nv * sizeof(u64)); + assert(res == nv * sizeof(u64)); + + close(fd[cpu][counter]); + fd[cpu][counter] = -1; + + count[0] += single_count[0]; + if (scale) { + count[1] += single_count[1]; + count[2] += single_count[2]; } } @@ -271,9 +250,10 @@ static int run_perf_stat(int argc __used, const char **argv) { unsigned long long t0, t1; int status = 0; - int counter, ncreated = 0; + int counter; + int pid = target_pid; int child_ready_pipe[2], go_pipe[2]; - const bool forks = (argc > 0); + const bool forks = (target_pid == -1 && argc > 0); char buf; if (!system_wide) @@ -285,10 +265,10 @@ static int run_perf_stat(int argc __used, const char **argv) } if (forks) { - if ((child_pid = fork()) < 0) + if ((pid = fork()) < 0) perror("failed to fork"); - if (!child_pid) { + if (!pid) { close(child_ready_pipe[0]); close(go_pipe[1]); fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); @@ -317,8 +297,7 @@ static int run_perf_stat(int argc __used, const char **argv) exit(-1); } - if (target_tid == -1 && target_pid == -1 && !system_wide) - all_tids[0] = child_pid; + child_pid = pid; /* * Wait for the child to be ready to exec. @@ -331,16 +310,7 @@ static int run_perf_stat(int argc __used, const char **argv) } for (counter = 0; counter < nr_counters; counter++) - ncreated += create_perf_stat_counter(counter); - - if (ncreated == 0) { - pr_err("No permission to collect %sstats.\n" - "Consider tweaking /proc/sys/kernel/perf_event_paranoid.\n", - system_wide ? "system-wide " : ""); - if (child_pid != -1) - kill(child_pid, SIGTERM); - return -1; - } + create_perf_stat_counter(counter, pid); /* * Enable counters and exec the command: @@ -351,7 +321,7 @@ static int run_perf_stat(int argc __used, const char **argv) close(go_pipe[1]); wait(&status); } else { - while(!done) sleep(1); + while(!done); } t1 = rdclock(); @@ -459,14 +429,12 @@ static void print_stat(int argc, const char **argv) fprintf(stderr, "\n"); fprintf(stderr, " Performance counter stats for "); - if(target_pid == -1 && target_tid == -1) { + if(target_pid == -1) { fprintf(stderr, "\'%s", argv[0]); for (i = 1; i < argc; i++) fprintf(stderr, " %s", argv[i]); - } else if (target_pid != -1) - fprintf(stderr, "process id \'%d", target_pid); - else - fprintf(stderr, "thread id \'%d", target_tid); + }else + fprintf(stderr, "task pid \'%d", target_pid); fprintf(stderr, "\'"); if (run_count > 1) @@ -491,7 +459,7 @@ static volatile int signr = -1; static void skip_signal(int signo) { - if(child_pid == -1) + if(target_pid != -1) done = 1; signr = signo; @@ -521,14 +489,12 @@ static const struct option options[] = { OPT_BOOLEAN('i', "inherit", &inherit, "child tasks inherit counters"), OPT_INTEGER('p', "pid", &target_pid, - "stat events on existing process id"), - OPT_INTEGER('t', "tid", &target_tid, - "stat events on existing thread id"), + "stat events on existing pid"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('c', "scale", &scale, "scale/normalize counters"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_INTEGER('r', "repeat", &run_count, "repeat command and print average + stddev (max: 100)"), @@ -540,11 +506,10 @@ static const struct option options[] = { int cmd_stat(int argc, const char **argv, const char *prefix __used) { int status; - int i,j; argc = parse_options(argc, argv, options, stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && target_pid == -1 && target_tid == -1) + if (!argc && target_pid == -1) usage_with_options(stat_usage, options); if (run_count <= 0) usage_with_options(stat_usage, options); @@ -560,31 +525,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) else nr_cpus = 1; - if (target_pid != -1) { - target_tid = target_pid; - thread_num = find_all_tid(target_pid, &all_tids); - if (thread_num <= 0) { - fprintf(stderr, "Can't find all threads of pid %d\n", - target_pid); - usage_with_options(stat_usage, options); - } - } else { - all_tids=malloc(sizeof(pid_t)); - if (!all_tids) - return -ENOMEM; - - all_tids[0] = target_tid; - thread_num = 1; - } - - for (i = 0; i < MAX_NR_CPUS; i++) { - for (j = 0; j < MAX_COUNTERS; j++) { - fd[i][j] = malloc(sizeof(int)*thread_num); - if (!fd[i][j]) - return -ENOMEM; - } - } - /* * We dont want to block the signals - that would cause * child tasks to inherit that and Ctrl-C would not work. @@ -603,8 +543,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) status = run_perf_stat(argc, argv); } - if (status != -1) - print_stat(argc, argv); + print_stat(argc, argv); return status; } diff --git a/trunk/tools/perf/builtin-test.c b/trunk/tools/perf/builtin-test.c deleted file mode 100644 index 0339612e7385..000000000000 --- a/trunk/tools/perf/builtin-test.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * builtin-test.c - * - * Builtin regression testing command: ever growing number of sanity tests - */ -#include "builtin.h" - -#include "util/cache.h" -#include "util/debug.h" -#include "util/parse-options.h" -#include "util/session.h" -#include "util/symbol.h" -#include "util/thread.h" - -static long page_size; - -static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) -{ - bool *visited = symbol__priv(sym); - *visited = true; - return 0; -} - -static int test__vmlinux_matches_kallsyms(void) -{ - int err = -1; - struct rb_node *nd; - struct symbol *sym; - struct map *kallsyms_map, *vmlinux_map; - struct machine kallsyms, vmlinux; - enum map_type type = MAP__FUNCTION; - struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; - - /* - * Step 1: - * - * Init the machines that will hold kernel, modules obtained from - * both vmlinux + .ko files and from /proc/kallsyms split by modules. - */ - machine__init(&kallsyms, "", HOST_KERNEL_ID); - machine__init(&vmlinux, "", HOST_KERNEL_ID); - - /* - * Step 2: - * - * Create the kernel maps for kallsyms and the DSO where we will then - * load /proc/kallsyms. Also create the modules maps from /proc/modules - * and find the .ko files that match them in /lib/modules/`uname -r`/. - */ - if (machine__create_kernel_maps(&kallsyms) < 0) { - pr_debug("machine__create_kernel_maps "); - return -1; - } - - /* - * Step 3: - * - * Load and split /proc/kallsyms into multiple maps, one per module. - */ - if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) { - pr_debug("dso__load_kallsyms "); - goto out; - } - - /* - * Step 4: - * - * kallsyms will be internally on demand sorted by name so that we can - * find the reference relocation * symbol, i.e. the symbol we will use - * to see if the running kernel was relocated by checking if it has the - * same value in the vmlinux file we load. - */ - kallsyms_map = machine__kernel_map(&kallsyms, type); - - sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); - if (sym == NULL) { - pr_debug("dso__find_symbol_by_name "); - goto out; - } - - ref_reloc_sym.addr = sym->start; - - /* - * Step 5: - * - * Now repeat step 2, this time for the vmlinux file we'll auto-locate. - */ - if (machine__create_kernel_maps(&vmlinux) < 0) { - pr_debug("machine__create_kernel_maps "); - goto out; - } - - vmlinux_map = machine__kernel_map(&vmlinux, type); - map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym; - - /* - * Step 6: - * - * Locate a vmlinux file in the vmlinux path that has a buildid that - * matches the one of the running kernel. - * - * While doing that look if we find the ref reloc symbol, if we find it - * we'll have its ref_reloc_symbol.unrelocated_addr and then - * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines - * to fixup the symbols. - */ - if (machine__load_vmlinux_path(&vmlinux, type, - vmlinux_matches_kallsyms_filter) <= 0) { - pr_debug("machine__load_vmlinux_path "); - goto out; - } - - err = 0; - /* - * Step 7: - * - * Now look at the symbols in the vmlinux DSO and check if we find all of them - * in the kallsyms dso. For the ones that are in both, check its names and - * end addresses too. - */ - for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { - struct symbol *pair; - - sym = rb_entry(nd, struct symbol, rb_node); - pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); - - if (pair && pair->start == sym->start) { -next_pair: - if (strcmp(sym->name, pair->name) == 0) { - /* - * kallsyms don't have the symbol end, so we - * set that by using the next symbol start - 1, - * in some cases we get this up to a page - * wrong, trace_kmalloc when I was developing - * this code was one such example, 2106 bytes - * off the real size. More than that and we - * _really_ have a problem. - */ - s64 skew = sym->end - pair->end; - if (llabs(skew) < page_size) - continue; - - pr_debug("%#Lx: diff end addr for %s v: %#Lx k: %#Lx\n", - sym->start, sym->name, sym->end, pair->end); - } else { - struct rb_node *nnd = rb_prev(&pair->rb_node); - - if (nnd) { - struct symbol *next = rb_entry(nnd, struct symbol, rb_node); - - if (next->start == sym->start) { - pair = next; - goto next_pair; - } - } - pr_debug("%#Lx: diff name v: %s k: %s\n", - sym->start, sym->name, pair->name); - } - } else - pr_debug("%#Lx: %s not on kallsyms\n", sym->start, sym->name); - - err = -1; - } - - if (!verbose) - goto out; - - pr_info("Maps only in vmlinux:\n"); - - for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node), *pair; - /* - * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while - * the kernel will have the path for the vmlinux file being used, - * so use the short name, less descriptive but the same ("[kernel]" in - * both cases. - */ - pair = map_groups__find_by_name(&kallsyms.kmaps, type, - (pos->dso->kernel ? - pos->dso->short_name : - pos->dso->name)); - if (pair) - pair->priv = 1; - else - map__fprintf(pos, stderr); - } - - pr_info("Maps in vmlinux with a different name in kallsyms:\n"); - - for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node), *pair; - - pair = map_groups__find(&kallsyms.kmaps, type, pos->start); - if (pair == NULL || pair->priv) - continue; - - if (pair->start == pos->start) { - pair->priv = 1; - pr_info(" %Lx-%Lx %Lx %s in kallsyms as", - pos->start, pos->end, pos->pgoff, pos->dso->name); - if (pos->pgoff != pair->pgoff || pos->end != pair->end) - pr_info(": \n*%Lx-%Lx %Lx", - pair->start, pair->end, pair->pgoff); - pr_info(" %s\n", pair->dso->name); - pair->priv = 1; - } - } - - pr_info("Maps only in kallsyms:\n"); - - for (nd = rb_first(&kallsyms.kmaps.maps[type]); - nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node); - - if (!pos->priv) - map__fprintf(pos, stderr); - } -out: - return err; -} - -static struct test { - const char *desc; - int (*func)(void); -} tests[] = { - { - .desc = "vmlinux symtab matches kallsyms", - .func = test__vmlinux_matches_kallsyms, - }, - { - .func = NULL, - }, -}; - -static int __cmd_test(void) -{ - int i = 0; - - page_size = sysconf(_SC_PAGE_SIZE); - - while (tests[i].func) { - int err; - pr_info("%2d: %s:", i + 1, tests[i].desc); - pr_debug("\n--- start ---\n"); - err = tests[i].func(); - pr_debug("---- end ----\n%s:", tests[i].desc); - pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); - ++i; - } - - return 0; -} - -static const char * const test_usage[] = { - "perf test []", - NULL, -}; - -static const struct option test_options[] = { - OPT_BOOLEAN('v', "verbose", &verbose, - "be more verbose (show symbol address, etc)"), - OPT_END() -}; - -int cmd_test(int argc, const char **argv, const char *prefix __used) -{ - argc = parse_options(argc, argv, test_options, test_usage, 0); - if (argc) - usage_with_options(test_usage, test_options); - - symbol_conf.priv_size = sizeof(int); - symbol_conf.sort_by_name = true; - symbol_conf.try_vmlinux_path = true; - - if (symbol__init() < 0) - return -1; - - setup_pager(); - - return __cmd_test(); -} diff --git a/trunk/tools/perf/builtin-timechart.c b/trunk/tools/perf/builtin-timechart.c index 5a52ed9fc10b..0d4d8ff7914b 100644 --- a/trunk/tools/perf/builtin-timechart.c +++ b/trunk/tools/perf/builtin-timechart.c @@ -21,6 +21,7 @@ #include "util/cache.h" #include #include "util/symbol.h" +#include "util/string.h" #include "util/callchain.h" #include "util/strlist.h" @@ -42,7 +43,7 @@ static u64 turbo_frequency; static u64 first_time, last_time; -static bool power_only; +static int power_only; struct per_pid; @@ -77,6 +78,8 @@ struct per_pid { struct per_pidcomm *all; struct per_pidcomm *current; + + int painted; }; @@ -143,6 +146,9 @@ struct wake_event { static struct power_event *power_events; static struct wake_event *wake_events; +struct sample_wrapper *all_samples; + + struct process_filter; struct process_filter { char *name; @@ -563,6 +569,88 @@ static void end_sample_processing(void) } } +static u64 sample_time(event_t *event, const struct perf_session *session) +{ + int cursor; + + cursor = 0; + if (session->sample_type & PERF_SAMPLE_IP) + cursor++; + if (session->sample_type & PERF_SAMPLE_TID) + cursor++; + if (session->sample_type & PERF_SAMPLE_TIME) + return event->sample.array[cursor]; + return 0; +} + + +/* + * We first queue all events, sorted backwards by insertion. + * The order will get flipped later. + */ +static int queue_sample_event(event_t *event, struct perf_session *session) +{ + struct sample_wrapper *copy, *prev; + int size; + + size = event->sample.header.size + sizeof(struct sample_wrapper) + 8; + + copy = malloc(size); + if (!copy) + return 1; + + memset(copy, 0, size); + + copy->next = NULL; + copy->timestamp = sample_time(event, session); + + memcpy(©->data, event, event->sample.header.size); + + /* insert in the right place in the list */ + + if (!all_samples) { + /* first sample ever */ + all_samples = copy; + return 0; + } + + if (all_samples->timestamp < copy->timestamp) { + /* insert at the head of the list */ + copy->next = all_samples; + all_samples = copy; + return 0; + } + + prev = all_samples; + while (prev->next) { + if (prev->next->timestamp < copy->timestamp) { + copy->next = prev->next; + prev->next = copy; + return 0; + } + prev = prev->next; + } + /* insert at the end of the list */ + prev->next = copy; + + return 0; +} + +static void sort_queued_samples(void) +{ + struct sample_wrapper *cursor, *next; + + cursor = all_samples; + all_samples = NULL; + + while (cursor) { + next = cursor->next; + cursor->next = all_samples; + all_samples = cursor; + cursor = next; + } +} + /* * Sort the pid datastructure */ @@ -926,17 +1014,31 @@ static void write_svg_file(const char *filename) svg_close(); } +static void process_samples(struct perf_session *session) +{ + struct sample_wrapper *cursor; + event_t *event; + + sort_queued_samples(); + + cursor = all_samples; + while (cursor) { + event = (void *)&cursor->data; + cursor = cursor->next; + process_sample_event(event, session); + } +} + static struct perf_event_ops event_ops = { - .comm = process_comm_event, - .fork = process_fork_event, - .exit = process_exit_event, - .sample = process_sample_event, - .ordered_samples = true, + .comm = process_comm_event, + .fork = process_fork_event, + .exit = process_exit_event, + .sample = queue_sample_event, }; static int __cmd_timechart(void) { - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); int ret = -EINVAL; if (session == NULL) @@ -949,6 +1051,8 @@ static int __cmd_timechart(void) if (ret) goto out_delete; + process_samples(session); + end_sample_processing(); sort_pids(); @@ -971,6 +1075,7 @@ static const char *record_args[] = { "record", "-a", "-R", + "-M", "-f", "-c", "1", "-e", "power:power_start", diff --git a/trunk/tools/perf/builtin-top.c b/trunk/tools/perf/builtin-top.c index 3de397764cb3..1f529321607e 100644 --- a/trunk/tools/perf/builtin-top.c +++ b/trunk/tools/perf/builtin-top.c @@ -55,9 +55,9 @@ #include #include -static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; +static int fd[MAX_NR_CPUS][MAX_COUNTERS]; -static bool system_wide = false; +static int system_wide = 0; static int default_interval = 0; @@ -65,21 +65,18 @@ static int count_filter = 5; static int print_entries; static int target_pid = -1; -static int target_tid = -1; -static pid_t *all_tids = NULL; -static int thread_num = 0; -static bool inherit = false; +static int inherit = 0; static int profile_cpu = -1; static int nr_cpus = 0; static unsigned int realtime_prio = 0; -static bool group = false; +static int group = 0; static unsigned int page_size; static unsigned int mmap_pages = 16; static int freq = 1000; /* 1 KHz */ static int delay_secs = 2; -static bool zero = false; -static bool dump_symtab = false; +static int zero = 0; +static int dump_symtab = 0; static bool hide_kernel_symbols = false; static bool hide_user_symbols = false; @@ -136,7 +133,7 @@ static inline struct symbol *sym_entry__symbol(struct sym_entry *self) return ((void *)self) + symbol_conf.priv_size; } -void get_term_dimensions(struct winsize *ws) +static void get_term_dimensions(struct winsize *ws) { char *s = getenv("LINES"); @@ -172,7 +169,7 @@ static void sig_winch_handler(int sig __used) update_print_entries(&winsize); } -static int parse_source(struct sym_entry *syme) +static void parse_source(struct sym_entry *syme) { struct symbol *sym; struct sym_entry_source *source; @@ -183,21 +180,12 @@ static int parse_source(struct sym_entry *syme) u64 len; if (!syme) - return -1; - - sym = sym_entry__symbol(syme); - map = syme->map; - - /* - * We can't annotate with just /proc/kallsyms - */ - if (map->dso->origin == DSO__ORIG_KERNEL) - return -1; + return; if (syme->src == NULL) { syme->src = zalloc(sizeof(*source)); if (syme->src == NULL) - return -1; + return; pthread_mutex_init(&syme->src->lock, NULL); } @@ -207,6 +195,9 @@ static int parse_source(struct sym_entry *syme) pthread_mutex_lock(&source->lock); goto out_assign; } + + sym = sym_entry__symbol(syme); + map = syme->map; path = map->dso->long_name; len = sym->end - sym->start; @@ -218,7 +209,7 @@ static int parse_source(struct sym_entry *syme) file = popen(command, "r"); if (!file) - return -1; + return; pthread_mutex_lock(&source->lock); source->lines_tail = &source->lines; @@ -254,7 +245,6 @@ static int parse_source(struct sym_entry *syme) out_assign: sym_filter_entry = syme; pthread_mutex_unlock(&source->lock); - return 0; } static void __zero_source_counters(struct sym_entry *syme) @@ -420,9 +410,7 @@ static double sym_weight(const struct sym_entry *sym) } static long samples; -static long kernel_samples, us_samples; -static long exact_samples; -static long guest_us_samples, guest_kernel_samples; +static long userspace_samples; static const char CONSOLE_CLEAR[] = ""; static void __list_insert_active_sym(struct sym_entry *syme) @@ -462,11 +450,7 @@ static void print_sym_table(void) int printed = 0, j; int counter, snap = !display_weighted ? sym_counter : 0; float samples_per_sec = samples/delay_secs; - float ksamples_per_sec = kernel_samples/delay_secs; - float us_samples_per_sec = (us_samples)/delay_secs; - float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs; - float guest_us_samples_per_sec = (guest_us_samples)/delay_secs; - float esamples_percent = (100.0*exact_samples)/samples; + float ksamples_per_sec = (samples-userspace_samples)/delay_secs; float sum_ksamples = 0.0; struct sym_entry *syme, *n; struct rb_root tmp = RB_ROOT; @@ -474,8 +458,7 @@ static void print_sym_table(void) int sym_width = 0, dso_width = 0, dso_short_width = 0; const int win_width = winsize.ws_col - 1; - samples = us_samples = kernel_samples = exact_samples = 0; - guest_kernel_samples = guest_us_samples = 0; + samples = userspace_samples = 0; /* Sort the active symbols */ pthread_mutex_lock(&active_symbols_lock); @@ -506,30 +489,9 @@ static void print_sym_table(void) puts(CONSOLE_CLEAR); printf("%-*.*s\n", win_width, win_width, graph_dotted_line); - if (!perf_guest) { - printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%" - " exact: %4.1f%% [", - samples_per_sec, - 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / - samples_per_sec)), - esamples_percent); - } else { - printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" - " guest kernel:%4.1f%% guest us:%4.1f%%" - " exact: %4.1f%% [", - samples_per_sec, - 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) / - samples_per_sec)), - 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) / - samples_per_sec)), - 100.0 - (100.0 * ((samples_per_sec - - guest_kernel_samples_per_sec) / - samples_per_sec)), - 100.0 - (100.0 * ((samples_per_sec - - guest_us_samples_per_sec) / - samples_per_sec)), - esamples_percent); - } + printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", + samples_per_sec, + 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); if (nr_counters == 1 || !display_weighted) { printf("%Ld", (u64)attrs[0].sample_period); @@ -552,15 +514,13 @@ static void print_sym_table(void) if (target_pid != -1) printf(" (target_pid: %d", target_pid); - else if (target_tid != -1) - printf(" (target_tid: %d", target_tid); else printf(" (all"); if (profile_cpu != -1) printf(", cpu: %d)\n", profile_cpu); else { - if (target_tid != -1) + if (target_pid != -1) printf(")\n"); else printf(", %d CPUs)\n", nr_cpus); @@ -622,6 +582,7 @@ static void print_sym_table(void) syme = rb_entry(nd, struct sym_entry, rb_node); sym = sym_entry__symbol(syme); + if (++printed > print_entries || (int)syme->snap_count < count_filter) continue; @@ -785,7 +746,7 @@ static int key_mapped(int c) return 0; } -static void handle_keypress(struct perf_session *session, int c) +static void handle_keypress(int c) { if (!key_mapped(c)) { struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; @@ -854,7 +815,7 @@ static void handle_keypress(struct perf_session *session, int c) case 'Q': printf("exiting.\n"); if (dump_symtab) - perf_session__fprintf_dsos(session, stderr); + dsos__fprintf(stderr); exit(0); case 's': prompt_symbol(&sym_filter_entry, "Enter details symbol"); @@ -878,7 +839,7 @@ static void handle_keypress(struct perf_session *session, int c) display_weighted = ~display_weighted; break; case 'z': - zero = !zero; + zero = ~zero; break; default: break; @@ -890,7 +851,6 @@ static void *display_thread(void *arg __used) struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; struct termios tc, save; int delay_msecs, c; - struct perf_session *session = (struct perf_session *) arg; tcgetattr(0, &save); tc = save; @@ -911,7 +871,7 @@ static void *display_thread(void *arg __used) c = getc(stdin); tcsetattr(0, TCSAFLUSH, &save); - handle_keypress(session, c); + handle_keypress(c); goto repeat; return NULL; @@ -982,48 +942,24 @@ static void event__process_sample(const event_t *self, u64 ip = self->ip.ip; struct sym_entry *syme; struct addr_location al; - struct machine *machine; u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; ++samples; switch (origin) { case PERF_RECORD_MISC_USER: - ++us_samples; + ++userspace_samples; if (hide_user_symbols) return; - machine = perf_session__find_host_machine(session); break; case PERF_RECORD_MISC_KERNEL: - ++kernel_samples; if (hide_kernel_symbols) return; - machine = perf_session__find_host_machine(session); - break; - case PERF_RECORD_MISC_GUEST_KERNEL: - ++guest_kernel_samples; - machine = perf_session__find_machine(session, self->ip.pid); break; - case PERF_RECORD_MISC_GUEST_USER: - ++guest_us_samples; - /* - * TODO: we don't process guest user from host side - * except simple counting. - */ - return; default: return; } - if (!machine && perf_guest) { - pr_err("Can't find guest [%d]'s kernel information\n", - self->ip.pid); - return; - } - - if (self->header.misc & PERF_RECORD_MISC_EXACT) - exact_samples++; - if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || al.filtered) return; @@ -1040,7 +976,7 @@ static void event__process_sample(const event_t *self, * --hide-kernel-symbols, even if the user specifies an * invalid --vmlinux ;-) */ - if (al.map == machine->vmlinux_maps[MAP__FUNCTION] && + if (al.map == session->vmlinux_maps[MAP__FUNCTION] && RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { pr_err("The %s file can't be used\n", symbol_conf.vmlinux_name); @@ -1054,17 +990,7 @@ static void event__process_sample(const event_t *self, if (sym_filter_entry_sched) { sym_filter_entry = sym_filter_entry_sched; sym_filter_entry_sched = NULL; - if (parse_source(sym_filter_entry) < 0) { - struct symbol *sym = sym_entry__symbol(sym_filter_entry); - - pr_err("Can't annotate %s", sym->name); - if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) { - pr_err(": No vmlinux file was found in the path:\n"); - vmlinux_path__fprintf(stderr); - } else - pr_err(".\n"); - exit(1); - } + parse_source(sym_filter_entry); } syme = symbol__priv(al.sym); @@ -1180,21 +1106,16 @@ static void perf_session__mmap_read_counter(struct perf_session *self, md->prev = old; } -static struct pollfd *event_array; -static struct mmap_data *mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; +static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; +static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; static void perf_session__mmap_read(struct perf_session *self) { - int i, counter, thread_index; + int i, counter; for (i = 0; i < nr_cpus; i++) { for (counter = 0; counter < nr_counters; counter++) - for (thread_index = 0; - thread_index < thread_num; - thread_index++) { - perf_session__mmap_read_counter(self, - &mmap_array[i][counter][thread_index]); - } + perf_session__mmap_read_counter(self, &mmap_array[i][counter]); } } @@ -1205,10 +1126,9 @@ static void start_counter(int i, int counter) { struct perf_event_attr *attr; int cpu; - int thread_index; cpu = profile_cpu; - if (target_tid == -1 && profile_cpu == -1) + if (target_pid == -1 && profile_cpu == -1) cpu = cpumap[i]; attr = attrs + counter; @@ -1224,58 +1144,55 @@ static void start_counter(int i, int counter) attr->inherit = (cpu < 0) && inherit; attr->mmap = 1; - for (thread_index = 0; thread_index < thread_num; thread_index++) { try_again: - fd[i][counter][thread_index] = sys_perf_event_open(attr, - all_tids[thread_index], cpu, group_fd, 0); - - if (fd[i][counter][thread_index] < 0) { - int err = errno; - - if (err == EPERM || err == EACCES) - die("No permission - are you root?\n"); - /* - * If it's cycles then fall back to hrtimer - * based cpu-clock-tick sw counter, which - * is always available even if no PMU support: - */ - if (attr->type == PERF_TYPE_HARDWARE - && attr->config == PERF_COUNT_HW_CPU_CYCLES) { - - if (verbose) - warning(" ... trying to fall back to cpu-clock-ticks\n"); - - attr->type = PERF_TYPE_SOFTWARE; - attr->config = PERF_COUNT_SW_CPU_CLOCK; - goto try_again; - } - printf("\n"); - error("perfcounter syscall returned with %d (%s)\n", - fd[i][counter][thread_index], strerror(err)); - die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); - exit(-1); - } - assert(fd[i][counter][thread_index] >= 0); - fcntl(fd[i][counter][thread_index], F_SETFL, O_NONBLOCK); + fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); + if (fd[i][counter] < 0) { + int err = errno; + + if (err == EPERM || err == EACCES) + die("No permission - are you root?\n"); /* - * First counter acts as the group leader: + * If it's cycles then fall back to hrtimer + * based cpu-clock-tick sw counter, which + * is always available even if no PMU support: */ - if (group && group_fd == -1) - group_fd = fd[i][counter][thread_index]; - - event_array[nr_poll].fd = fd[i][counter][thread_index]; - event_array[nr_poll].events = POLLIN; - nr_poll++; - - mmap_array[i][counter][thread_index].counter = counter; - mmap_array[i][counter][thread_index].prev = 0; - mmap_array[i][counter][thread_index].mask = mmap_pages*page_size - 1; - mmap_array[i][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, - PROT_READ, MAP_SHARED, fd[i][counter][thread_index], 0); - if (mmap_array[i][counter][thread_index].base == MAP_FAILED) - die("failed to mmap with %d (%s)\n", errno, strerror(errno)); + if (attr->type == PERF_TYPE_HARDWARE + && attr->config == PERF_COUNT_HW_CPU_CYCLES) { + + if (verbose) + warning(" ... trying to fall back to cpu-clock-ticks\n"); + + attr->type = PERF_TYPE_SOFTWARE; + attr->config = PERF_COUNT_SW_CPU_CLOCK; + goto try_again; + } + printf("\n"); + error("perfcounter syscall returned with %d (%s)\n", + fd[i][counter], strerror(err)); + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); + exit(-1); } + assert(fd[i][counter] >= 0); + fcntl(fd[i][counter], F_SETFL, O_NONBLOCK); + + /* + * First counter acts as the group leader: + */ + if (group && group_fd == -1) + group_fd = fd[i][counter]; + + event_array[nr_poll].fd = fd[i][counter]; + event_array[nr_poll].events = POLLIN; + nr_poll++; + + mmap_array[i][counter].counter = counter; + mmap_array[i][counter].prev = 0; + mmap_array[i][counter].mask = mmap_pages*page_size - 1; + mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size, + PROT_READ, MAP_SHARED, fd[i][counter], 0); + if (mmap_array[i][counter].base == MAP_FAILED) + die("failed to mmap with %d (%s)\n", errno, strerror(errno)); } static int __cmd_top(void) @@ -1287,12 +1204,12 @@ static int __cmd_top(void) * FIXME: perf_session__new should allow passing a O_MMAP, so that all this * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. */ - struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false); + struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); if (session == NULL) return -ENOMEM; - if (target_tid != -1) - event__synthesize_thread(target_tid, event__process, session); + if (target_pid != -1) + event__synthesize_thread(target_pid, event__process, session); else event__synthesize_threads(event__process, session); @@ -1303,11 +1220,11 @@ static int __cmd_top(void) } /* Wait for a minimal set of events before starting the snapshot */ - poll(&event_array[0], nr_poll, 100); + poll(event_array, nr_poll, 100); perf_session__mmap_read(session); - if (pthread_create(&thread, NULL, display_thread, session)) { + if (pthread_create(&thread, NULL, display_thread, NULL)) { printf("Could not create display thread.\n"); exit(-1); } @@ -1346,9 +1263,7 @@ static const struct option options[] = { OPT_INTEGER('c', "count", &default_interval, "event period to sample"), OPT_INTEGER('p', "pid", &target_pid, - "profile events on existing process id"), - OPT_INTEGER('t', "tid", &target_tid, - "profile events on existing thread id"), + "profile events on existing pid"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), OPT_INTEGER('C', "CPU", &profile_cpu, @@ -1381,7 +1296,7 @@ static const struct option options[] = { "display this many functions"), OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, "hide user symbols"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_END() }; @@ -1389,7 +1304,6 @@ static const struct option options[] = { int cmd_top(int argc, const char **argv, const char *prefix __used) { int counter; - int i,j; page_size = sysconf(_SC_PAGE_SIZE); @@ -1397,39 +1311,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) if (argc) usage_with_options(top_usage, options); - if (target_pid != -1) { - target_tid = target_pid; - thread_num = find_all_tid(target_pid, &all_tids); - if (thread_num <= 0) { - fprintf(stderr, "Can't find all threads of pid %d\n", - target_pid); - usage_with_options(top_usage, options); - } - } else { - all_tids=malloc(sizeof(pid_t)); - if (!all_tids) - return -ENOMEM; - - all_tids[0] = target_tid; - thread_num = 1; - } - - for (i = 0; i < MAX_NR_CPUS; i++) { - for (j = 0; j < MAX_COUNTERS; j++) { - fd[i][j] = malloc(sizeof(int)*thread_num); - mmap_array[i][j] = zalloc( - sizeof(struct mmap_data)*thread_num); - if (!fd[i][j] || !mmap_array[i][j]) - return -ENOMEM; - } - } - event_array = malloc( - sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); - if (!event_array) - return -ENOMEM; - /* CPU and PID are mutually exclusive */ - if (target_tid > 0 && profile_cpu != -1) { + if (target_pid != -1 && profile_cpu != -1) { printf("WARNING: PID switch overriding CPU\n"); sleep(1); profile_cpu = -1; @@ -1470,7 +1353,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) attrs[counter].sample_period = default_interval; } - if (target_tid != -1 || profile_cpu != -1) + if (target_pid != -1 || profile_cpu != -1) nr_cpus = 1; else nr_cpus = read_cpu_map(); diff --git a/trunk/tools/perf/builtin-trace.c b/trunk/tools/perf/builtin-trace.c index 9c483e92e8db..407041d20de0 100644 --- a/trunk/tools/perf/builtin-trace.c +++ b/trunk/tools/perf/builtin-trace.c @@ -11,8 +11,6 @@ static char const *script_name; static char const *generate_script_lang; -static bool debug_ordering; -static u64 last_timestamp; static int default_start_script(const char *script __unused, int argc __unused, @@ -89,14 +87,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) } if (session->sample_type & PERF_SAMPLE_RAW) { - if (debug_ordering) { - if (data.time < last_timestamp) { - pr_err("Samples misordered, previous: %llu " - "this: %llu\n", last_timestamp, - data.time); - } - last_timestamp = data.time; - } /* * FIXME: better resolve from pid from the struct trace_entry * field, although it should be the same than this perf @@ -114,24 +104,10 @@ static int process_sample_event(event_t *event, struct perf_session *session) static struct perf_event_ops event_ops = { .sample = process_sample_event, .comm = event__process_comm, - .attr = event__process_attr, - .event_type = event__process_event_type, - .tracing_data = event__process_tracing_data, - .build_id = event__process_build_id, - .ordered_samples = true, }; -extern volatile int session_done; - -static void sig_handler(int sig __unused) -{ - session_done = 1; -} - static int __cmd_trace(struct perf_session *session) { - signal(SIGINT, sig_handler); - return perf_session__process_events(session, &event_ops); } @@ -529,7 +505,7 @@ static const char * const trace_usage[] = { static const struct option options[] = { OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), - OPT_INCR('v', "verbose", &verbose, + OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('L', "Latency", &latency_format, "show latency attributes (irqs/preemption disabled, etc)"), @@ -542,8 +518,6 @@ static const struct option options[] = { "generate perf-trace.xx script in specified language"), OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_BOOLEAN('d', "debug-ordering", &debug_ordering, - "check that samples time ordering is monotonic"), OPT_END() }; @@ -574,65 +548,6 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) suffix = REPORT_SUFFIX; } - if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) { - char *record_script_path, *report_script_path; - int live_pipe[2]; - pid_t pid; - - record_script_path = get_script_path(argv[1], RECORD_SUFFIX); - if (!record_script_path) { - fprintf(stderr, "record script not found\n"); - return -1; - } - - report_script_path = get_script_path(argv[1], REPORT_SUFFIX); - if (!report_script_path) { - fprintf(stderr, "report script not found\n"); - return -1; - } - - if (pipe(live_pipe) < 0) { - perror("failed to create pipe"); - exit(-1); - } - - pid = fork(); - if (pid < 0) { - perror("failed to fork"); - exit(-1); - } - - if (!pid) { - dup2(live_pipe[1], 1); - close(live_pipe[0]); - - __argv = malloc(5 * sizeof(const char *)); - __argv[0] = "/bin/sh"; - __argv[1] = record_script_path; - __argv[2] = "-o"; - __argv[3] = "-"; - __argv[4] = NULL; - - execvp("/bin/sh", (char **)__argv); - exit(-1); - } - - dup2(live_pipe[0], 0); - close(live_pipe[1]); - - __argv = malloc((argc + 3) * sizeof(const char *)); - __argv[0] = "/bin/sh"; - __argv[1] = report_script_path; - for (i = 2; i < argc; i++) - __argv[i] = argv[i]; - __argv[i++] = "-i"; - __argv[i++] = "-"; - __argv[i++] = NULL; - - execvp("/bin/sh", (char **)__argv); - exit(-1); - } - if (suffix) { script_path = get_script_path(argv[2], suffix); if (!script_path) { @@ -661,12 +576,11 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) if (!script_name) setup_pager(); - session = perf_session__new(input_name, O_RDONLY, 0, false); + session = perf_session__new(input_name, O_RDONLY, 0); if (session == NULL) return -ENOMEM; - if (strcmp(input_name, "-") && - !perf_session__has_traces(session, "record -R")) + if (!perf_session__has_traces(session, "record -R")) return -EINVAL; if (generate_script_lang) { diff --git a/trunk/tools/perf/builtin.h b/trunk/tools/perf/builtin.h index 921245b28583..10fe49e7048a 100644 --- a/trunk/tools/perf/builtin.h +++ b/trunk/tools/perf/builtin.h @@ -32,8 +32,5 @@ extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_probe(int argc, const char **argv, const char *prefix); extern int cmd_kmem(int argc, const char **argv, const char *prefix); extern int cmd_lock(int argc, const char **argv, const char *prefix); -extern int cmd_kvm(int argc, const char **argv, const char *prefix); -extern int cmd_test(int argc, const char **argv, const char *prefix); -extern int cmd_inject(int argc, const char **argv, const char *prefix); #endif diff --git a/trunk/tools/perf/command-list.txt b/trunk/tools/perf/command-list.txt index 949d77fc0b97..db6ee94d4a8e 100644 --- a/trunk/tools/perf/command-list.txt +++ b/trunk/tools/perf/command-list.txt @@ -8,7 +8,6 @@ perf-bench mainporcelain common perf-buildid-cache mainporcelain common perf-buildid-list mainporcelain common perf-diff mainporcelain common -perf-inject mainporcelain common perf-list mainporcelain common perf-sched mainporcelain common perf-record mainporcelain common @@ -20,5 +19,3 @@ perf-trace mainporcelain common perf-probe mainporcelain common perf-kmem mainporcelain common perf-lock mainporcelain common -perf-kvm mainporcelain common -perf-test mainporcelain common diff --git a/trunk/tools/perf/perf-archive.sh b/trunk/tools/perf/perf-archive.sh index 2e7a4f417e20..910468e6e01c 100644 --- a/trunk/tools/perf/perf-archive.sh +++ b/trunk/tools/perf/perf-archive.sh @@ -30,7 +30,4 @@ done tar cfj $PERF_DATA.tar.bz2 -C $DEBUGDIR -T $MANIFEST rm -f $MANIFEST $BUILDIDS -echo -e "Now please run:\n" -echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n" -echo "wherever you need to run 'perf report' on." exit 0 diff --git a/trunk/tools/perf/perf.c b/trunk/tools/perf/perf.c index 08e0e5d2b50e..cd32c200cdb3 100644 --- a/trunk/tools/perf/perf.c +++ b/trunk/tools/perf/perf.c @@ -13,10 +13,9 @@ #include "util/quote.h" #include "util/run-command.h" #include "util/parse-events.h" +#include "util/string.h" #include "util/debugfs.h" -bool use_browser; - const char perf_usage_string[] = "perf [--version] [--help] COMMAND [ARGS]"; @@ -263,8 +262,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) set_debugfs_path(); status = p->fn(argc, argv, prefix); - exit_browser(status); - if (status) return status & 0xff; @@ -307,9 +304,6 @@ static void handle_internal_command(int argc, const char **argv) { "probe", cmd_probe, 0 }, { "kmem", cmd_kmem, 0 }, { "lock", cmd_lock, 0 }, - { "kvm", cmd_kvm, 0 }, - { "test", cmd_test, 0 }, - { "inject", cmd_inject, 0 }, }; unsigned int i; static const char ext[] = STRIP_EXTENSION; diff --git a/trunk/tools/perf/perf.h b/trunk/tools/perf/perf.h index 02821febb704..6fb379bc1d1f 100644 --- a/trunk/tools/perf/perf.h +++ b/trunk/tools/perf/perf.h @@ -1,10 +1,6 @@ #ifndef _PERF_PERF_H #define _PERF_PERF_H -struct winsize; - -void get_term_dimensions(struct winsize *ws); - #if defined(__i386__) #include "../../arch/x86/include/asm/unistd.h" #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") @@ -106,6 +102,8 @@ static inline unsigned long long rdclock(void) #define __user #define asmlinkage +#define __used __attribute__((__unused__)) + #define unlikely(x) __builtin_expect(!!(x), 0) #define min(x, y) ({ \ typeof(x) _min1 = (x); \ @@ -131,6 +129,4 @@ struct ip_callchain { u64 ips[0]; }; -extern int perf_host, perf_guest; - #endif diff --git a/trunk/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm b/trunk/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm index d94b40c8ac85..f869c48dc9b0 100644 --- a/trunk/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm +++ b/trunk/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm @@ -15,7 +15,6 @@ our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs -clear_term ); our $VERSION = '0.01'; @@ -56,11 +55,6 @@ sub nsecs_str { return $str; } -sub clear_term -{ - print "\x1b[H\x1b[2J"; -} - 1; __END__ =head1 NAME diff --git a/trunk/tools/perf/scripts/perl/bin/check-perf-trace-record b/trunk/tools/perf/scripts/perl/bin/check-perf-trace-record index 423ad6aed056..e6cb1474f8e8 100644 --- a/trunk/tools/perf/scripts/perl/bin/check-perf-trace-record +++ b/trunk/tools/perf/scripts/perl/bin/check-perf-trace-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree +perf record -c 1 -f -a -M -R -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree diff --git a/trunk/tools/perf/scripts/perl/bin/failed-syscalls-record b/trunk/tools/perf/scripts/perl/bin/failed-syscalls-record index eb5846bcb565..f8885d389e6f 100644 --- a/trunk/tools/perf/scripts/perl/bin/failed-syscalls-record +++ b/trunk/tools/perf/scripts/perl/bin/failed-syscalls-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_exit $@ +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit diff --git a/trunk/tools/perf/scripts/perl/bin/failed-syscalls-report b/trunk/tools/perf/scripts/perl/bin/failed-syscalls-report index f6346082a8fc..8bfc660e5056 100644 --- a/trunk/tools/perf/scripts/perl/bin/failed-syscalls-report +++ b/trunk/tools/perf/scripts/perl/bin/failed-syscalls-report @@ -1,10 +1,4 @@ #!/bin/bash # description: system-wide failed syscalls # args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then - comm=$1 - shift - fi -fi -perf trace $@ -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $comm +perf trace -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $1 diff --git a/trunk/tools/perf/scripts/perl/bin/rw-by-file-record b/trunk/tools/perf/scripts/perl/bin/rw-by-file-record index 5bfaae5a6cba..b25056ebf963 100644 --- a/trunk/tools/perf/scripts/perl/bin/rw-by-file-record +++ b/trunk/tools/perf/scripts/perl/bin/rw-by-file-record @@ -1,3 +1,2 @@ #!/bin/bash -perf record -a -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ - +perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_enter_write diff --git a/trunk/tools/perf/scripts/perl/bin/rw-by-file-report b/trunk/tools/perf/scripts/perl/bin/rw-by-file-report index d83070b7eeb5..eddb9ccce6a5 100644 --- a/trunk/tools/perf/scripts/perl/bin/rw-by-file-report +++ b/trunk/tools/perf/scripts/perl/bin/rw-by-file-report @@ -1,13 +1,7 @@ #!/bin/bash # description: r/w activity for a program, by file # args: -if [ $# -lt 1 ] ; then - echo "usage: rw-by-file " - exit -fi -comm=$1 -shift -perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $comm +perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1 diff --git a/trunk/tools/perf/scripts/perl/bin/rw-by-pid-record b/trunk/tools/perf/scripts/perl/bin/rw-by-pid-record index 6e0b2f7755ac..8903979c5b6c 100644 --- a/trunk/tools/perf/scripts/perl/bin/rw-by-pid-record +++ b/trunk/tools/perf/scripts/perl/bin/rw-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ +perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write diff --git a/trunk/tools/perf/scripts/perl/bin/rw-by-pid-report b/trunk/tools/perf/scripts/perl/bin/rw-by-pid-report index 7ef46983f62f..7f44c25cc857 100644 --- a/trunk/tools/perf/scripts/perl/bin/rw-by-pid-report +++ b/trunk/tools/perf/scripts/perl/bin/rw-by-pid-report @@ -1,6 +1,6 @@ #!/bin/bash # description: system-wide r/w activity -perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl +perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl diff --git a/trunk/tools/perf/scripts/perl/bin/rwtop-record b/trunk/tools/perf/scripts/perl/bin/rwtop-record deleted file mode 100644 index 6e0b2f7755ac..000000000000 --- a/trunk/tools/perf/scripts/perl/bin/rwtop-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/trunk/tools/perf/scripts/perl/bin/rwtop-report b/trunk/tools/perf/scripts/perl/bin/rwtop-report deleted file mode 100644 index 93e698cd3f38..000000000000 --- a/trunk/tools/perf/scripts/perl/bin/rwtop-report +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# description: system-wide r/w top -# args: [interval] -n_args=0 -for i in "$@" -do - if expr match "$i" "-" > /dev/null ; then - break - fi - n_args=$(( $n_args + 1 )) -done -if [ "$n_args" -gt 1 ] ; then - echo "usage: rwtop-report [interval]" - exit -fi -if [ "$n_args" -gt 0 ] ; then - interval=$1 - shift -fi -perf trace $@ -s ~/libexec/perf-core/scripts/perl/rwtop.pl $interval - - - diff --git a/trunk/tools/perf/scripts/perl/bin/wakeup-latency-record b/trunk/tools/perf/scripts/perl/bin/wakeup-latency-record index 9f2acaaae9f0..6abedda911a4 100644 --- a/trunk/tools/perf/scripts/perl/bin/wakeup-latency-record +++ b/trunk/tools/perf/scripts/perl/bin/wakeup-latency-record @@ -1,5 +1,5 @@ #!/bin/bash -perf record -a -e sched:sched_switch -e sched:sched_wakeup $@ +perf record -c 1 -f -a -M -R -e sched:sched_switch -e sched:sched_wakeup diff --git a/trunk/tools/perf/scripts/perl/bin/wakeup-latency-report b/trunk/tools/perf/scripts/perl/bin/wakeup-latency-report index a0d898f9ca1d..fce3adcb3249 100644 --- a/trunk/tools/perf/scripts/perl/bin/wakeup-latency-report +++ b/trunk/tools/perf/scripts/perl/bin/wakeup-latency-report @@ -1,6 +1,6 @@ #!/bin/bash # description: system-wide min/max/avg wakeup latency -perf trace $@ -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl +perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl diff --git a/trunk/tools/perf/scripts/perl/bin/workqueue-stats-record b/trunk/tools/perf/scripts/perl/bin/workqueue-stats-record index 85301f2471ff..fce6637b19ba 100644 --- a/trunk/tools/perf/scripts/perl/bin/workqueue-stats-record +++ b/trunk/tools/perf/scripts/perl/bin/workqueue-stats-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ +perf record -c 1 -f -a -M -R -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion diff --git a/trunk/tools/perf/scripts/perl/bin/workqueue-stats-report b/trunk/tools/perf/scripts/perl/bin/workqueue-stats-report index 35081132ef97..71cfbd182fb9 100644 --- a/trunk/tools/perf/scripts/perl/bin/workqueue-stats-report +++ b/trunk/tools/perf/scripts/perl/bin/workqueue-stats-report @@ -1,6 +1,6 @@ #!/bin/bash # description: workqueue stats (ins/exe/create/destroy) -perf trace $@ -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl +perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl diff --git a/trunk/tools/perf/scripts/perl/rwtop.pl b/trunk/tools/perf/scripts/perl/rwtop.pl deleted file mode 100644 index ec2ab49a6f25..000000000000 --- a/trunk/tools/perf/scripts/perl/rwtop.pl +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/perl -w -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 - -# read/write top -# -# Periodically displays system-wide r/w call activity, broken down by -# pid. If an [interval] arg is specified, the display will be -# refreshed every [interval] seconds. The default interval is 3 -# seconds. - -use 5.010000; -use strict; -use warnings; - -use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; -use lib "./Perf-Trace-Util/lib"; -use Perf::Trace::Core; -use Perf::Trace::Util; - -my $default_interval = 3; -my $nlines = 20; -my $print_thread; - -my %reads; -my %writes; - -my $interval = shift; -if (!$interval) { - $interval = $default_interval; -} - -sub syscalls::sys_exit_read -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, - $nr, $ret) = @_; - - if ($ret > 0) { - $reads{$common_pid}{bytes_read} += $ret; - } else { - if (!defined ($reads{$common_pid}{bytes_read})) { - $reads{$common_pid}{bytes_read} = 0; - } - $reads{$common_pid}{errors}{$ret}++; - } -} - -sub syscalls::sys_enter_read -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, - $nr, $fd, $buf, $count) = @_; - - $reads{$common_pid}{bytes_requested} += $count; - $reads{$common_pid}{total_reads}++; - $reads{$common_pid}{comm} = $common_comm; -} - -sub syscalls::sys_exit_write -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, - $nr, $ret) = @_; - - if ($ret <= 0) { - $writes{$common_pid}{errors}{$ret}++; - } -} - -sub syscalls::sys_enter_write -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm, - $nr, $fd, $buf, $count) = @_; - - $writes{$common_pid}{bytes_written} += $count; - $writes{$common_pid}{total_writes}++; - $writes{$common_pid}{comm} = $common_comm; -} - -sub trace_begin -{ - $SIG{ALRM} = \&print_totals; - alarm 1; -} - -sub trace_end -{ - print_unhandled(); - print_totals(); -} - -sub print_totals -{ - my $count; - - $count = 0; - - clear_term(); - - printf("\nread counts by pid:\n\n"); - - printf("%6s %20s %10s %10s %10s\n", "pid", "comm", - "# reads", "bytes_req", "bytes_read"); - printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------", - "----------", "----------", "----------"); - - foreach my $pid (sort {$reads{$b}{bytes_read} <=> - $reads{$a}{bytes_read}} keys %reads) { - my $comm = $reads{$pid}{comm}; - my $total_reads = $reads{$pid}{total_reads}; - my $bytes_requested = $reads{$pid}{bytes_requested}; - my $bytes_read = $reads{$pid}{bytes_read}; - - printf("%6s %-20s %10s %10s %10s\n", $pid, $comm, - $total_reads, $bytes_requested, $bytes_read); - - if (++$count == $nlines) { - last; - } - } - - $count = 0; - - printf("\nwrite counts by pid:\n\n"); - - printf("%6s %20s %10s %13s\n", "pid", "comm", - "# writes", "bytes_written"); - printf("%6s %-20s %10s %13s\n", "------", "--------------------", - "----------", "-------------"); - - foreach my $pid (sort {$writes{$b}{bytes_written} <=> - $writes{$a}{bytes_written}} keys %writes) { - my $comm = $writes{$pid}{comm}; - my $total_writes = $writes{$pid}{total_writes}; - my $bytes_written = $writes{$pid}{bytes_written}; - - printf("%6s %-20s %10s %13s\n", $pid, $comm, - $total_writes, $bytes_written); - - if (++$count == $nlines) { - last; - } - } - - %reads = (); - %writes = (); - alarm $interval; -} - -my %unhandled; - -sub print_unhandled -{ - if ((scalar keys %unhandled) == 0) { - return; - } - - print "\nunhandled events:\n\n"; - - printf("%-40s %10s\n", "event", "count"); - printf("%-40s %10s\n", "----------------------------------------", - "-----------"); - - foreach my $event_name (keys %unhandled) { - printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); - } -} - -sub trace_unhandled -{ - my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, - $common_pid, $common_comm) = @_; - - $unhandled{$event_name}++; -} diff --git a/trunk/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/trunk/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 9689bc0acd9f..83e91435ed09 100644 --- a/trunk/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/trunk/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py @@ -23,6 +23,3 @@ def nsecs_nsecs(nsecs): def nsecs_str(nsecs): str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)), return str - -def clear_term(): - print("\x1b[H\x1b[2J") diff --git a/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record b/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record index eb5846bcb565..f8885d389e6f 100644 --- a/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record +++ b/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_exit $@ +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit diff --git a/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report index 8c128eff9c0a..1e0c0a860c87 100644 --- a/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report +++ b/trunk/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report @@ -1,10 +1,4 @@ #!/bin/bash # description: system-wide failed syscalls, by pid # args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then - comm=$1 - shift - fi -fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $comm +perf trace -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $1 diff --git a/trunk/tools/perf/scripts/python/bin/sctop-record b/trunk/tools/perf/scripts/python/bin/sctop-record deleted file mode 100644 index 1fc5998b721d..000000000000 --- a/trunk/tools/perf/scripts/python/bin/sctop-record +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -perf record -a -e raw_syscalls:sys_enter $@ diff --git a/trunk/tools/perf/scripts/python/bin/sctop-report b/trunk/tools/perf/scripts/python/bin/sctop-report deleted file mode 100644 index b01c842ae7b4..000000000000 --- a/trunk/tools/perf/scripts/python/bin/sctop-report +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# description: syscall top -# args: [comm] [interval] -n_args=0 -for i in "$@" -do - if expr match "$i" "-" > /dev/null ; then - break - fi - n_args=$(( $n_args + 1 )) -done -if [ "$n_args" -gt 2 ] ; then - echo "usage: sctop-report [comm] [interval]" - exit -fi -if [ "$n_args" -gt 1 ] ; then - comm=$1 - interval=$2 - shift 2 -elif [ "$n_args" -gt 0 ] ; then - interval=$1 - shift -fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/sctop.py $comm $interval diff --git a/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-record b/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-record index 1fc5998b721d..45a8c50359da 100644 --- a/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-record +++ b/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_enter $@ +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter diff --git a/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-report index c53362e48602..f8044d192271 100644 --- a/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-report +++ b/trunk/tools/perf/scripts/python/bin/syscall-counts-by-pid-report @@ -1,10 +1,4 @@ #!/bin/bash # description: system-wide syscall counts, by pid # args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then - comm=$1 - shift - fi -fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $comm +perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $1 diff --git a/trunk/tools/perf/scripts/python/bin/syscall-counts-record b/trunk/tools/perf/scripts/python/bin/syscall-counts-record index 1fc5998b721d..45a8c50359da 100644 --- a/trunk/tools/perf/scripts/python/bin/syscall-counts-record +++ b/trunk/tools/perf/scripts/python/bin/syscall-counts-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_enter $@ +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter diff --git a/trunk/tools/perf/scripts/python/bin/syscall-counts-report b/trunk/tools/perf/scripts/python/bin/syscall-counts-report index 8c21552b3cdc..a366aa61612f 100644 --- a/trunk/tools/perf/scripts/python/bin/syscall-counts-report +++ b/trunk/tools/perf/scripts/python/bin/syscall-counts-report @@ -1,10 +1,4 @@ #!/bin/bash # description: system-wide syscall counts # args: [comm] -if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then - comm=$1 - shift - fi -fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts.py $comm +perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts.py $1 diff --git a/trunk/tools/perf/scripts/python/sctop.py b/trunk/tools/perf/scripts/python/sctop.py deleted file mode 100644 index 6cafad40c296..000000000000 --- a/trunk/tools/perf/scripts/python/sctop.py +++ /dev/null @@ -1,78 +0,0 @@ -# system call top -# (c) 2010, Tom Zanussi -# Licensed under the terms of the GNU GPL License version 2 -# -# Periodically displays system-wide system call totals, broken down by -# syscall. If a [comm] arg is specified, only syscalls called by -# [comm] are displayed. If an [interval] arg is specified, the display -# will be refreshed every [interval] seconds. The default interval is -# 3 seconds. - -import thread -import time -import os -import sys - -sys.path.append(os.environ['PERF_EXEC_PATH'] + \ - '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') - -from perf_trace_context import * -from Core import * -from Util import * - -usage = "perf trace -s syscall-counts.py [comm] [interval]\n"; - -for_comm = None -default_interval = 3 -interval = default_interval - -if len(sys.argv) > 3: - sys.exit(usage) - -if len(sys.argv) > 2: - for_comm = sys.argv[1] - interval = int(sys.argv[2]) -elif len(sys.argv) > 1: - try: - interval = int(sys.argv[1]) - except ValueError: - for_comm = sys.argv[1] - interval = default_interval - -syscalls = autodict() - -def trace_begin(): - thread.start_new_thread(print_syscall_totals, (interval,)) - pass - -def raw_syscalls__sys_enter(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, - id, args): - if for_comm is not None: - if common_comm != for_comm: - return - try: - syscalls[id] += 1 - except TypeError: - syscalls[id] = 1 - -def print_syscall_totals(interval): - while 1: - clear_term() - if for_comm is not None: - print "\nsyscall events for %s:\n\n" % (for_comm), - else: - print "\nsyscall events:\n\n", - - print "%-40s %10s\n" % ("event", "count"), - print "%-40s %10s\n" % ("----------------------------------------", \ - "----------"), - - for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ - reverse = True): - try: - print "%-40d %10d\n" % (id, val), - except TypeError: - pass - syscalls.clear() - time.sleep(interval) diff --git a/trunk/tools/perf/util/PERF-VERSION-GEN b/trunk/tools/perf/util/PERF-VERSION-GEN index 49ece7921914..54552a00a117 100755 --- a/trunk/tools/perf/util/PERF-VERSION-GEN +++ b/trunk/tools/perf/util/PERF-VERSION-GEN @@ -1,10 +1,6 @@ #!/bin/sh -if [ $# -eq 1 ] ; then - OUTPUT=$1 -fi - -GVF=${OUTPUT}PERF-VERSION-FILE +GVF=PERF-VERSION-FILE DEF_VER=v0.0.2.PERF LF=' diff --git a/trunk/tools/perf/util/bitmap.c b/trunk/tools/perf/util/bitmap.c deleted file mode 100644 index 5e230acae1e9..000000000000 --- a/trunk/tools/perf/util/bitmap.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * From lib/bitmap.c - * Helper functions for bitmap.h. - * - * This source code is licensed under the GNU General Public License, - * Version 2. See the file COPYING for more details. - */ -#include - -int __bitmap_weight(const unsigned long *bitmap, int bits) -{ - int k, w = 0, lim = bits/BITS_PER_LONG; - - for (k = 0; k < lim; k++) - w += hweight_long(bitmap[k]); - - if (bits % BITS_PER_LONG) - w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits)); - - return w; -} diff --git a/trunk/tools/perf/util/build-id.c b/trunk/tools/perf/util/build-id.c index 0f60a3906808..04904b35ba81 100644 --- a/trunk/tools/perf/util/build-id.c +++ b/trunk/tools/perf/util/build-id.c @@ -24,7 +24,7 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) } thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - event->ip.pid, event->ip.ip, &al); + event->ip.ip, &al); if (al.map != NULL) al.map->dso->hit = 1; diff --git a/trunk/tools/perf/util/cache.h b/trunk/tools/perf/util/cache.h index 4b9aab7f0405..918eb376abe3 100644 --- a/trunk/tools/perf/util/cache.h +++ b/trunk/tools/perf/util/cache.h @@ -1,7 +1,6 @@ #ifndef __PERF_CACHE_H #define __PERF_CACHE_H -#include #include "util.h" #include "strbuf.h" #include "../perf.h" @@ -70,19 +69,6 @@ extern const char *pager_program; extern int pager_in_use(void); extern int pager_use_color; -extern bool use_browser; - -#ifdef NO_NEWT_SUPPORT -static inline void setup_browser(void) -{ - setup_pager(); -} -static inline void exit_browser(bool wait_for_ok __used) {} -#else -void setup_browser(void); -void exit_browser(bool wait_for_ok); -#endif - extern const char *editor_program; extern const char *excludes_file; diff --git a/trunk/tools/perf/util/callchain.c b/trunk/tools/perf/util/callchain.c index db628af6d20d..b3b71258272a 100644 --- a/trunk/tools/perf/util/callchain.c +++ b/trunk/tools/perf/util/callchain.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010, Frederic Weisbecker + * Copyright (C) 2009, Frederic Weisbecker * * Handle the callchains from the stream in an ad-hoc radix tree and then * sort them in an rbtree. @@ -183,23 +183,12 @@ create_child(struct callchain_node *parent, bool inherit_children) return new; } - -struct resolved_ip { - u64 ip; - struct map_symbol ms; -}; - -struct resolved_chain { - u64 nr; - struct resolved_ip ips[0]; -}; - - /* * Fill the node with callchain values */ static void -fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) +fill_node(struct callchain_node *node, struct ip_callchain *chain, + int start, struct symbol **syms) { unsigned int i; @@ -211,8 +200,8 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) perror("not enough memory for the code path tree"); return; } - call->ip = chain->ips[i].ip; - call->ms = chain->ips[i].ms; + call->ip = chain->ips[i]; + call->sym = syms[i]; list_add_tail(&call->list, &node->val); } node->val_nr = chain->nr - start; @@ -221,13 +210,13 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) } static void -add_child(struct callchain_node *parent, struct resolved_chain *chain, - int start) +add_child(struct callchain_node *parent, struct ip_callchain *chain, + int start, struct symbol **syms) { struct callchain_node *new; new = create_child(parent, false); - fill_node(new, chain, start); + fill_node(new, chain, start, syms); new->children_hit = 0; new->hit = 1; @@ -239,8 +228,9 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain, * Then create another child to host the given callchain of new branch */ static void -split_add_child(struct callchain_node *parent, struct resolved_chain *chain, - struct callchain_list *to_split, int idx_parents, int idx_local) +split_add_child(struct callchain_node *parent, struct ip_callchain *chain, + struct callchain_list *to_split, int idx_parents, int idx_local, + struct symbol **syms) { struct callchain_node *new; struct list_head *old_tail; @@ -267,7 +257,7 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, /* create a new child for the new branch if any */ if (idx_total < chain->nr) { parent->hit = 0; - add_child(parent, chain, idx_total); + add_child(parent, chain, idx_total, syms); parent->children_hit++; } else { parent->hit = 1; @@ -275,33 +265,32 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, } static int -__append_chain(struct callchain_node *root, struct resolved_chain *chain, - unsigned int start); +__append_chain(struct callchain_node *root, struct ip_callchain *chain, + unsigned int start, struct symbol **syms); static void -__append_chain_children(struct callchain_node *root, - struct resolved_chain *chain, - unsigned int start) +__append_chain_children(struct callchain_node *root, struct ip_callchain *chain, + struct symbol **syms, unsigned int start) { struct callchain_node *rnode; /* lookup in childrens */ chain_for_each_child(rnode, root) { - unsigned int ret = __append_chain(rnode, chain, start); + unsigned int ret = __append_chain(rnode, chain, start, syms); if (!ret) goto inc_children_hit; } /* nothing in children, add to the current node */ - add_child(root, chain, start); + add_child(root, chain, start, syms); inc_children_hit: root->children_hit++; } static int -__append_chain(struct callchain_node *root, struct resolved_chain *chain, - unsigned int start) +__append_chain(struct callchain_node *root, struct ip_callchain *chain, + unsigned int start, struct symbol **syms) { struct callchain_list *cnode; unsigned int i = start; @@ -313,19 +302,13 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, * anywhere inside a function. */ list_for_each_entry(cnode, &root->val, list) { - struct symbol *sym; - if (i == chain->nr) break; - - sym = chain->ips[i].ms.sym; - - if (cnode->ms.sym && sym) { - if (cnode->ms.sym->start != sym->start) + if (cnode->sym && syms[i]) { + if (cnode->sym->start != syms[i]->start) break; - } else if (cnode->ip != chain->ips[i].ip) + } else if (cnode->ip != chain->ips[i]) break; - if (!found) found = true; i++; @@ -337,7 +320,7 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, /* we match only a part of the node. Split it and add the new chain */ if (i - start < root->val_nr) { - split_add_child(root, chain, cnode, start, i - start); + split_add_child(root, chain, cnode, start, i - start, syms); return 0; } @@ -348,50 +331,15 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, } /* We match the node and still have a part remaining */ - __append_chain_children(root, chain, i); + __append_chain_children(root, chain, syms, i); return 0; } -static void filter_context(struct ip_callchain *old, struct resolved_chain *new, - struct map_symbol *syms) -{ - int i, j = 0; - - for (i = 0; i < (int)old->nr; i++) { - if (old->ips[i] >= PERF_CONTEXT_MAX) - continue; - - new->ips[j].ip = old->ips[i]; - new->ips[j].ms = syms[i]; - j++; - } - - new->nr = j; -} - - -int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct map_symbol *syms) +void append_chain(struct callchain_node *root, struct ip_callchain *chain, + struct symbol **syms) { - struct resolved_chain *filtered; - if (!chain->nr) - return 0; - - filtered = malloc(sizeof(*filtered) + - chain->nr * sizeof(struct resolved_ip)); - if (!filtered) - return -ENOMEM; - - filter_context(chain, filtered, syms); - - if (!filtered->nr) - goto end; - - __append_chain_children(root, filtered, 0); -end: - free(filtered); - - return 0; + return; + __append_chain_children(root, chain, syms, 0); } diff --git a/trunk/tools/perf/util/callchain.h b/trunk/tools/perf/util/callchain.h index 8a7e8bbd0fda..ad4626de4c2b 100644 --- a/trunk/tools/perf/util/callchain.h +++ b/trunk/tools/perf/util/callchain.h @@ -39,7 +39,7 @@ struct callchain_param { struct callchain_list { u64 ip; - struct map_symbol ms; + struct symbol *sym; struct list_head list; }; @@ -56,6 +56,6 @@ static inline u64 cumul_hits(struct callchain_node *node) } int register_callchain_param(struct callchain_param *param); -int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct map_symbol *syms); +void append_chain(struct callchain_node *root, struct ip_callchain *chain, + struct symbol **syms); #endif /* __PERF_CALLCHAIN_H */ diff --git a/trunk/tools/perf/util/color.c b/trunk/tools/perf/util/color.c index e191eb9a667f..e88bca55a599 100644 --- a/trunk/tools/perf/util/color.c +++ b/trunk/tools/perf/util/color.c @@ -166,31 +166,6 @@ int perf_color_default_config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } -static int __color_vsnprintf(char *bf, size_t size, const char *color, - const char *fmt, va_list args, const char *trail) -{ - int r = 0; - - /* - * Auto-detect: - */ - if (perf_use_color_default < 0) { - if (isatty(1) || pager_in_use()) - perf_use_color_default = 1; - else - perf_use_color_default = 0; - } - - if (perf_use_color_default && *color) - r += snprintf(bf, size, "%s", color); - r += vsnprintf(bf + r, size - r, fmt, args); - if (perf_use_color_default && *color) - r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); - if (trail) - r += snprintf(bf + r, size - r, "%s", trail); - return r; -} - static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args, const char *trail) { @@ -216,28 +191,11 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, return r; } -int color_vsnprintf(char *bf, size_t size, const char *color, - const char *fmt, va_list args) -{ - return __color_vsnprintf(bf, size, color, fmt, args, NULL); -} - int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args) { return __color_vfprintf(fp, color, fmt, args, NULL); } -int color_snprintf(char *bf, size_t size, const char *color, - const char *fmt, ...) -{ - va_list args; - int r; - - va_start(args, fmt); - r = color_vsnprintf(bf, size, color, fmt, args); - va_end(args); - return r; -} int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) { @@ -316,9 +274,3 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } - -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) -{ - const char *color = get_percent_color(percent); - return color_snprintf(bf, size, color, fmt, percent); -} diff --git a/trunk/tools/perf/util/color.h b/trunk/tools/perf/util/color.h index dea082b79602..24e8809210bb 100644 --- a/trunk/tools/perf/util/color.h +++ b/trunk/tools/perf/util/color.h @@ -32,14 +32,10 @@ int perf_color_default_config(const char *var, const char *value, void *cb); int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *value, const char *var, char *dst); void color_parse_mem(const char *value, int len, const char *var, char *dst); -int color_vsnprintf(char *bf, size_t size, const char *color, - const char *fmt, va_list args); int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); -int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); diff --git a/trunk/tools/perf/util/debug.c b/trunk/tools/perf/util/debug.c index dd824cf3b628..0905600c3851 100644 --- a/trunk/tools/perf/util/debug.c +++ b/trunk/tools/perf/util/debug.c @@ -6,14 +6,13 @@ #include #include -#include "cache.h" #include "color.h" #include "event.h" #include "debug.h" #include "util.h" int verbose = 0; -bool dump_trace = false; +int dump_trace = 0; int eprintf(int level, const char *fmt, ...) { @@ -22,10 +21,7 @@ int eprintf(int level, const char *fmt, ...) if (verbose >= level) { va_start(args, fmt); - if (use_browser) - ret = browser__show_help(fmt, args); - else - ret = vfprintf(stderr, fmt, args); + ret = vfprintf(stderr, fmt, args); va_end(args); } diff --git a/trunk/tools/perf/util/debug.h b/trunk/tools/perf/util/debug.h index 047ac3324ebe..c6c24c522dea 100644 --- a/trunk/tools/perf/util/debug.h +++ b/trunk/tools/perf/util/debug.h @@ -2,38 +2,14 @@ #ifndef __PERF_DEBUG_H #define __PERF_DEBUG_H -#include #include "event.h" extern int verbose; -extern bool dump_trace; +extern int dump_trace; +int eprintf(int level, + const char *fmt, ...) __attribute__((format(printf, 2, 3))); int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); -struct ui_progress; - -#ifdef NO_NEWT_SUPPORT -static inline int browser__show_help(const char *format __used, va_list ap __used) -{ - return 0; -} - -static inline struct ui_progress *ui_progress__new(const char *title __used, - u64 total __used) -{ - return (struct ui_progress *)1; -} - -static inline void ui_progress__update(struct ui_progress *self __used, - u64 curr __used) {} - -static inline void ui_progress__delete(struct ui_progress *self __used) {} -#else -int browser__show_help(const char *format, va_list ap); -struct ui_progress *ui_progress__new(const char *title, u64 total); -void ui_progress__update(struct ui_progress *self, u64 curr); -void ui_progress__delete(struct ui_progress *self); -#endif - #endif /* __PERF_DEBUG_H */ diff --git a/trunk/tools/perf/util/event.c b/trunk/tools/perf/util/event.c index 23d5dfd4ed73..705ec63548b4 100644 --- a/trunk/tools/perf/util/event.c +++ b/trunk/tools/perf/util/event.c @@ -112,11 +112,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, event_t ev = { .header = { .type = PERF_RECORD_MMAP, - /* - * Just like the kernel, see __perf_event_mmap - * in kernel/perf_event.c - */ - .misc = PERF_RECORD_MISC_USER, + .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ }, }; int n; @@ -134,7 +130,6 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, continue; pbf += n + 3; if (*pbf == 'x') { /* vm_exec */ - u64 vm_pgoff; char *execname = strchr(bf, '/'); /* Catch VDSO */ @@ -144,14 +139,6 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, if (execname == NULL) continue; - pbf += 3; - n = hex2u64(pbf, &vm_pgoff); - /* pgoff is in bytes, not pages */ - if (n >= 0) - ev.mmap.pgoff = vm_pgoff << getpagesize(); - else - ev.mmap.pgoff = 0; - size = strlen(execname); execname[size - 1] = '\0'; /* Remove \n */ memcpy(ev.mmap.filename, execname, size); @@ -171,23 +158,11 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, } int event__synthesize_modules(event__handler_t process, - struct perf_session *session, - struct machine *machine) + struct perf_session *session) { struct rb_node *nd; - struct map_groups *kmaps = &machine->kmaps; - u16 misc; - - /* - * kernel uses 0 for user space maps, see kernel/perf_event.c - * __perf_event_mmap - */ - if (machine__is_host(machine)) - misc = PERF_RECORD_MISC_KERNEL; - else - misc = PERF_RECORD_MISC_GUEST_KERNEL; - for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); + for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { event_t ev; size_t size; @@ -198,13 +173,12 @@ int event__synthesize_modules(event__handler_t process, size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); memset(&ev, 0, sizeof(ev)); - ev.mmap.header.misc = misc; + ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ ev.mmap.header.type = PERF_RECORD_MMAP; ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); ev.mmap.start = pos->start; ev.mmap.len = pos->end - pos->start; - ev.mmap.pid = machine->pid; memcpy(ev.mmap.filename, pos->dso->long_name, pos->dso->long_name_len + 1); @@ -267,18 +241,13 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start) int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, - struct machine *machine, const char *symbol_name) { size_t size; - const char *filename, *mmap_name; - char path[PATH_MAX]; - char name_buff[PATH_MAX]; - struct map *map; - event_t ev = { .header = { .type = PERF_RECORD_MMAP, + .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ }, }; /* @@ -288,37 +257,16 @@ int event__synthesize_kernel_mmap(event__handler_t process, */ struct process_symbol_args args = { .name = symbol_name, }; - mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); - if (machine__is_host(machine)) { - /* - * kernel uses PERF_RECORD_MISC_USER for user space maps, - * see kernel/perf_event.c __perf_event_mmap - */ - ev.header.misc = PERF_RECORD_MISC_KERNEL; - filename = "/proc/kallsyms"; - } else { - ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; - if (machine__is_default_guest(machine)) - filename = (char *) symbol_conf.default_guest_kallsyms; - else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); - filename = path; - } - } - - if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) + if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) return -ENOENT; - map = machine->vmlinux_maps[MAP__FUNCTION]; size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), - "%s%s", mmap_name, symbol_name) + 1; + "[kernel.kallsyms.%s]", symbol_name) + 1; size = ALIGN(size, sizeof(u64)); - ev.mmap.header.size = (sizeof(ev.mmap) - - (sizeof(ev.mmap.filename) - size)); + ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); ev.mmap.pgoff = args.start; - ev.mmap.start = map->start; - ev.mmap.len = map->end - ev.mmap.start; - ev.mmap.pid = machine->pid; + ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; + ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; return process(&ev, session); } @@ -372,50 +320,22 @@ int event__process_lost(event_t *self, struct perf_session *session) return 0; } -static void event_set_kernel_mmap_len(struct map **maps, event_t *self) -{ - maps[MAP__FUNCTION]->start = self->mmap.start; - maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; - /* - * Be a bit paranoid here, some perf.data file came with - * a zero sized synthesized MMAP event for the kernel. - */ - if (maps[MAP__FUNCTION]->end == 0) - maps[MAP__FUNCTION]->end = ~0UL; -} - -static int event__process_kernel_mmap(event_t *self, - struct perf_session *session) +int event__process_mmap(event_t *self, struct perf_session *session) { + struct thread *thread; struct map *map; - char kmmap_prefix[PATH_MAX]; - struct machine *machine; - enum dso_kernel_type kernel_type; - bool is_kernel_mmap; - - machine = perf_session__findnew_machine(session, self->mmap.pid); - if (!machine) { - pr_err("Can't find id %d's machine\n", self->mmap.pid); - goto out_problem; - } - machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); - if (machine__is_host(machine)) - kernel_type = DSO_TYPE_KERNEL; - else - kernel_type = DSO_TYPE_GUEST_KERNEL; - - is_kernel_mmap = memcmp(self->mmap.filename, - kmmap_prefix, - strlen(kmmap_prefix)) == 0; - if (self->mmap.filename[0] == '/' || - (!is_kernel_mmap && self->mmap.filename[0] == '[')) { + dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", + self->mmap.pid, self->mmap.tid, self->mmap.start, + self->mmap.len, self->mmap.pgoff, self->mmap.filename); - char short_module_name[1024]; - char *name, *dot; + if (self->mmap.pid == 0) { + static const char kmmap_prefix[] = "[kernel.kallsyms."; if (self->mmap.filename[0] == '/') { - name = strrchr(self->mmap.filename, '/'); + char short_module_name[1024]; + char *name = strrchr(self->mmap.filename, '/'), *dot; + if (name == NULL) goto out_problem; @@ -423,82 +343,58 @@ static int event__process_kernel_mmap(event_t *self, dot = strrchr(name, '.'); if (dot == NULL) goto out_problem; + snprintf(short_module_name, sizeof(short_module_name), - "[%.*s]", (int)(dot - name), name); + "[%.*s]", (int)(dot - name), name); strxfrchar(short_module_name, '-', '_'); - } else - strcpy(short_module_name, self->mmap.filename); - - map = machine__new_module(machine, self->mmap.start, - self->mmap.filename); - if (map == NULL) - goto out_problem; - - name = strdup(short_module_name); - if (name == NULL) - goto out_problem; - - map->dso->short_name = name; - map->end = map->start + self->mmap.len; - } else if (is_kernel_mmap) { - const char *symbol_name = (self->mmap.filename + - strlen(kmmap_prefix)); - /* - * Should be there already, from the build-id table in - * the header. - */ - struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, - kmmap_prefix); - if (kernel == NULL) - goto out_problem; - - kernel->kernel = kernel_type; - if (__machine__create_kernel_maps(machine, kernel) < 0) - goto out_problem; - - event_set_kernel_mmap_len(machine->vmlinux_maps, self); - perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, - symbol_name, - self->mmap.pgoff); - if (machine__is_default_guest(machine)) { + + map = perf_session__new_module_map(session, + self->mmap.start, + self->mmap.filename); + if (map == NULL) + goto out_problem; + + name = strdup(short_module_name); + if (name == NULL) + goto out_problem; + + map->dso->short_name = name; + map->end = map->start + self->mmap.len; + } else if (memcmp(self->mmap.filename, kmmap_prefix, + sizeof(kmmap_prefix) - 1) == 0) { + const char *symbol_name = (self->mmap.filename + + sizeof(kmmap_prefix) - 1); /* - * preload dso of guest kernel and modules + * Should be there already, from the build-id table in + * the header. */ - dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], - NULL); - } - } - return 0; -out_problem: - return -1; -} + struct dso *kernel = __dsos__findnew(&dsos__kernel, + "[kernel.kallsyms]"); + if (kernel == NULL) + goto out_problem; -int event__process_mmap(event_t *self, struct perf_session *session) -{ - struct machine *machine; - struct thread *thread; - struct map *map; - u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - int ret = 0; + kernel->kernel = 1; + if (__perf_session__create_kernel_maps(session, kernel) < 0) + goto out_problem; - dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", - self->mmap.pid, self->mmap.tid, self->mmap.start, - self->mmap.len, self->mmap.pgoff, self->mmap.filename); - - if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || - cpumode == PERF_RECORD_MISC_KERNEL) { - ret = event__process_kernel_mmap(self, session); - if (ret < 0) - goto out_problem; + session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; + session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; + /* + * Be a bit paranoid here, some perf.data file came with + * a zero sized synthesized MMAP event for the kernel. + */ + if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) + session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; + + perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, + self->mmap.pgoff); + } return 0; } thread = perf_session__findnew(session, self->mmap.pid); - machine = perf_session__find_host_machine(session); - map = map__new(&machine->user_dsos, self->mmap.start, - self->mmap.len, self->mmap.pgoff, - self->mmap.pid, self->mmap.filename, - MAP__FUNCTION, session->cwd, session->cwdlen); + map = map__new(&self->mmap, MAP__FUNCTION, + session->cwd, session->cwdlen); if (thread == NULL || map == NULL) goto out_problem; @@ -538,52 +434,22 @@ int event__process_task(event_t *self, struct perf_session *session) void thread__find_addr_map(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, pid_t pid, u64 addr, + enum map_type type, u64 addr, struct addr_location *al) { struct map_groups *mg = &self->mg; - struct machine *machine = NULL; al->thread = self; al->addr = addr; - al->cpumode = cpumode; - al->filtered = false; - if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { + if (cpumode == PERF_RECORD_MISC_KERNEL) { al->level = 'k'; - machine = perf_session__find_host_machine(session); - mg = &machine->kmaps; - } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { + mg = &session->kmaps; + } else if (cpumode == PERF_RECORD_MISC_USER) al->level = '.'; - machine = perf_session__find_host_machine(session); - } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { - al->level = 'g'; - machine = perf_session__find_machine(session, pid); - if (!machine) { - al->map = NULL; - return; - } - mg = &machine->kmaps; - } else { - /* - * 'u' means guest os user space. - * TODO: We don't support guest user space. Might support late. - */ - if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) - al->level = 'u'; - else - al->level = 'H'; + else { + al->level = 'H'; al->map = NULL; - - if ((cpumode == PERF_RECORD_MISC_GUEST_USER || - cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && - !perf_guest) - al->filtered = true; - if ((cpumode == PERF_RECORD_MISC_USER || - cpumode == PERF_RECORD_MISC_KERNEL) && - !perf_host) - al->filtered = true; - return; } try_again: @@ -598,10 +464,8 @@ void thread__find_addr_map(struct thread *self, * "[vdso]" dso, but for now lets use the old trick of looking * in the whole kernel symbol list. */ - if ((long long)al->addr < 0 && - cpumode == PERF_RECORD_MISC_KERNEL && - machine && mg != &machine->kmaps) { - mg = &machine->kmaps; + if ((long long)al->addr < 0 && mg != &session->kmaps) { + mg = &session->kmaps; goto try_again; } } else @@ -610,11 +474,11 @@ void thread__find_addr_map(struct thread *self, void thread__find_addr_location(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, pid_t pid, u64 addr, + enum map_type type, u64 addr, struct addr_location *al, symbol_filter_t filter) { - thread__find_addr_map(self, session, cpumode, type, pid, addr, al); + thread__find_addr_map(self, session, cpumode, type, addr, al); if (al->map != NULL) al->sym = map__find_symbol(al->map, al->addr, filter); else @@ -626,10 +490,8 @@ static void dso__calc_col_width(struct dso *self) if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.dso_list || strlist__has_entry(symbol_conf.dso_list, self->name))) { - u16 slen = self->short_name_len; - if (verbose) - slen = self->long_name_len; - if (dsos__col_width < slen) + unsigned int slen = strlen(self->name); + if (slen > dsos__col_width) dsos__col_width = slen; } @@ -651,37 +513,30 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - self->ip.pid, self->ip.ip, al); + thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, + self->ip.ip, al, filter); dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : ""); - al->sym = NULL; - - if (al->map) { - if (symbol_conf.dso_list && - (!al->map || !al->map->dso || - !(strlist__has_entry(symbol_conf.dso_list, - al->map->dso->short_name) || - (al->map->dso->short_name != al->map->dso->long_name && - strlist__has_entry(symbol_conf.dso_list, - al->map->dso->long_name))))) - goto out_filtered; - /* - * We have to do this here as we may have a dso with no symbol - * hit that has a name longer than the ones with symbols - * sampled. - */ - if (!sort_dso.elide && !al->map->dso->slen_calculated) - dso__calc_col_width(al->map->dso); - - al->sym = map__find_symbol(al->map, al->addr, filter); - } + /* + * We have to do this here as we may have a dso with no symbol hit that + * has a name longer than the ones with symbols sampled. + */ + if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) + dso__calc_col_width(al->map->dso); + + if (symbol_conf.dso_list && + (!al->map || !al->map->dso || + !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || + (al->map->dso->short_name != al->map->dso->long_name && + strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) + goto out_filtered; if (symbol_conf.sym_list && al->sym && !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) goto out_filtered; + al->filtered = false; return 0; out_filtered: @@ -715,7 +570,6 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data) array++; } - data->id = -1ULL; if (type & PERF_SAMPLE_ID) { data->id = *array; array++; diff --git a/trunk/tools/perf/util/event.h b/trunk/tools/perf/util/event.h index b364da5b0cbf..a33b94952e34 100644 --- a/trunk/tools/perf/util/event.h +++ b/trunk/tools/perf/util/event.h @@ -68,53 +68,21 @@ struct sample_data { u64 addr; u64 id; u64 stream_id; - u64 period; u32 cpu; + u64 period; + struct ip_callchain *callchain; u32 raw_size; void *raw_data; - struct ip_callchain *callchain; }; #define BUILD_ID_SIZE 20 struct build_id_event { struct perf_event_header header; - pid_t pid; u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; char filename[]; }; -enum perf_header_event_type { /* above any possible kernel type */ - PERF_RECORD_HEADER_ATTR = 64, - PERF_RECORD_HEADER_EVENT_TYPE = 65, - PERF_RECORD_HEADER_TRACING_DATA = 66, - PERF_RECORD_HEADER_BUILD_ID = 67, - PERF_RECORD_HEADER_MAX -}; - -struct attr_event { - struct perf_event_header header; - struct perf_event_attr attr; - u64 id[]; -}; - -#define MAX_EVENT_NAME 64 - -struct perf_trace_event_type { - u64 event_id; - char name[MAX_EVENT_NAME]; -}; - -struct event_type_event { - struct perf_event_header header; - struct perf_trace_event_type event_type; -}; - -struct tracing_data_event { - struct perf_event_header header; - u32 size; -}; - typedef union event_union { struct perf_event_header header; struct ip_event ip; @@ -124,10 +92,6 @@ typedef union event_union { struct lost_event lost; struct read_event read; struct sample_event sample; - struct attr_event attr; - struct event_type_event event_type; - struct tracing_data_event tracing_data; - struct build_id_event build_id; } event_t; struct events_stats { @@ -155,13 +119,10 @@ int event__synthesize_thread(pid_t pid, event__handler_t process, void event__synthesize_threads(event__handler_t process, struct perf_session *session); int event__synthesize_kernel_mmap(event__handler_t process, - struct perf_session *session, - struct machine *machine, - const char *symbol_name); - + struct perf_session *session, + const char *symbol_name); int event__synthesize_modules(event__handler_t process, - struct perf_session *session, - struct machine *machine); + struct perf_session *session); int event__process_comm(event_t *self, struct perf_session *session); int event__process_lost(event_t *self, struct perf_session *session); diff --git a/trunk/tools/perf/util/header.c b/trunk/tools/perf/util/header.c index 8847bec64c54..6c9aa16ee51f 100644 --- a/trunk/tools/perf/util/header.c +++ b/trunk/tools/perf/util/header.c @@ -99,6 +99,13 @@ int perf_header__add_attr(struct perf_header *self, return 0; } +#define MAX_EVENT_NAME 64 + +struct perf_trace_event_type { + u64 event_id; + char name[MAX_EVENT_NAME]; +}; + static int event_count; static struct perf_trace_event_type *events; @@ -190,8 +197,7 @@ static int write_padded(int fd, const void *bf, size_t count, continue; \ else -static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, - u16 misc, int fd) +static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) { struct dso *pos; @@ -206,7 +212,6 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, len = ALIGN(len, NAME_ALIGN); memset(&b, 0, sizeof(b)); memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); - b.pid = pid; b.header.misc = misc; b.header.size = sizeof(b) + len; err = do_write(fd, &b, sizeof(b)); @@ -221,32 +226,13 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, return 0; } -static int dsos__write_buildid_table(struct perf_header *header, int fd) +static int dsos__write_buildid_table(int fd) { - struct perf_session *session = container_of(header, - struct perf_session, header); - struct rb_node *nd; - int err = 0; - u16 kmisc, umisc; - - for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { - struct machine *pos = rb_entry(nd, struct machine, rb_node); - if (machine__is_host(pos)) { - kmisc = PERF_RECORD_MISC_KERNEL; - umisc = PERF_RECORD_MISC_USER; - } else { - kmisc = PERF_RECORD_MISC_GUEST_KERNEL; - umisc = PERF_RECORD_MISC_GUEST_USER; - } - - err = __dsos__write_buildid_table(&pos->kernel_dsos, pos->pid, - kmisc, fd); - if (err == 0) - err = __dsos__write_buildid_table(&pos->user_dsos, - pos->pid, umisc, fd); - if (err) - break; - } + int err = __dsos__write_buildid_table(&dsos__kernel, + PERF_RECORD_MISC_KERNEL, fd); + if (err == 0) + err = __dsos__write_buildid_table(&dsos__user, + PERF_RECORD_MISC_USER, fd); return err; } @@ -363,12 +349,9 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) return err; } -static int dsos__cache_build_ids(struct perf_header *self) +static int dsos__cache_build_ids(void) { - struct perf_session *session = container_of(self, - struct perf_session, header); - struct rb_node *nd; - int ret = 0; + int err_kernel, err_user; char debugdir[PATH_MAX]; snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), @@ -377,28 +360,9 @@ static int dsos__cache_build_ids(struct perf_header *self) if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) return -1; - for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { - struct machine *pos = rb_entry(nd, struct machine, rb_node); - ret |= __dsos__cache_build_ids(&pos->kernel_dsos, debugdir); - ret |= __dsos__cache_build_ids(&pos->user_dsos, debugdir); - } - return ret ? -1 : 0; -} - -static bool dsos__read_build_ids(struct perf_header *self, bool with_hits) -{ - bool ret = false; - struct perf_session *session = container_of(self, - struct perf_session, header); - struct rb_node *nd; - - for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { - struct machine *pos = rb_entry(nd, struct machine, rb_node); - ret |= __dsos__read_build_ids(&pos->kernel_dsos, with_hits); - ret |= __dsos__read_build_ids(&pos->user_dsos, with_hits); - } - - return ret; + err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); + err_user = __dsos__cache_build_ids(&dsos__user, debugdir); + return err_kernel || err_user ? -1 : 0; } static int perf_header__adds_write(struct perf_header *self, int fd) @@ -409,7 +373,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) u64 sec_start; int idx = 0, err; - if (dsos__read_build_ids(self, true)) + if (dsos__read_build_ids(true)) perf_header__set_feat(self, HEADER_BUILD_ID); nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); @@ -436,6 +400,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; } + if (perf_header__has_feat(self, HEADER_BUILD_ID)) { struct perf_file_section *buildid_sec; @@ -443,14 +408,14 @@ static int perf_header__adds_write(struct perf_header *self, int fd) /* Write build-ids */ buildid_sec->offset = lseek(fd, 0, SEEK_CUR); - err = dsos__write_buildid_table(self, fd); + err = dsos__write_buildid_table(fd); if (err < 0) { pr_debug("failed to write buildid table\n"); goto out_free; } buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; - dsos__cache_build_ids(self); + dsos__cache_build_ids(); } lseek(fd, sec_start, SEEK_SET); @@ -462,25 +427,6 @@ static int perf_header__adds_write(struct perf_header *self, int fd) return err; } -int perf_header__write_pipe(int fd) -{ - struct perf_pipe_file_header f_header; - int err; - - f_header = (struct perf_pipe_file_header){ - .magic = PERF_MAGIC, - .size = sizeof(f_header), - }; - - err = do_write(fd, &f_header, sizeof(f_header)); - if (err < 0) { - pr_debug("failed to write perf pipe header\n"); - return err; - } - - return 0; -} - int perf_header__write(struct perf_header *self, int fd, bool at_exit) { struct perf_file_header f_header; @@ -572,10 +518,25 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) return 0; } +static int do_read(int fd, void *buf, size_t size) +{ + while (size) { + int ret = read(fd, buf, size); + + if (ret <= 0) + return -1; + + size -= ret; + buf += ret; + } + + return 0; +} + static int perf_header__getbuffer64(struct perf_header *self, int fd, void *buf, size_t size) { - if (do_read(fd, buf, size) <= 0) + if (do_read(fd, buf, size)) return -1; if (self->needs_swap) @@ -631,7 +592,7 @@ int perf_file_header__read(struct perf_file_header *self, { lseek(fd, 0, SEEK_SET); - if (do_read(fd, self, sizeof(*self)) <= 0 || + if (do_read(fd, self, sizeof(*self)) || memcmp(&self->magic, __perf_magic, sizeof(self->magic))) return -1; @@ -675,93 +636,6 @@ int perf_file_header__read(struct perf_file_header *self, return 0; } -static int __event_process_build_id(struct build_id_event *bev, - char *filename, - struct perf_session *session) -{ - int err = -1; - struct list_head *head; - struct machine *machine; - u16 misc; - struct dso *dso; - enum dso_kernel_type dso_type; - - machine = perf_session__findnew_machine(session, bev->pid); - if (!machine) - goto out; - - misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - - switch (misc) { - case PERF_RECORD_MISC_KERNEL: - dso_type = DSO_TYPE_KERNEL; - head = &machine->kernel_dsos; - break; - case PERF_RECORD_MISC_GUEST_KERNEL: - dso_type = DSO_TYPE_GUEST_KERNEL; - head = &machine->kernel_dsos; - break; - case PERF_RECORD_MISC_USER: - case PERF_RECORD_MISC_GUEST_USER: - dso_type = DSO_TYPE_USER; - head = &machine->user_dsos; - break; - default: - goto out; - } - - dso = __dsos__findnew(head, filename); - if (dso != NULL) { - char sbuild_id[BUILD_ID_SIZE * 2 + 1]; - - dso__set_build_id(dso, &bev->build_id); - - if (filename[0] == '[') - dso->kernel = dso_type; - - build_id__sprintf(dso->build_id, sizeof(dso->build_id), - sbuild_id); - pr_debug("build id event received for %s: %s\n", - dso->long_name, sbuild_id); - } - - err = 0; -out: - return err; -} - -static int perf_header__read_build_ids(struct perf_header *self, - int input, u64 offset, u64 size) -{ - struct perf_session *session = container_of(self, - struct perf_session, header); - struct build_id_event bev; - char filename[PATH_MAX]; - u64 limit = offset + size; - int err = -1; - - while (offset < limit) { - ssize_t len; - - if (read(input, &bev, sizeof(bev)) != sizeof(bev)) - goto out; - - if (self->needs_swap) - perf_event_header__bswap(&bev.header); - - len = bev.header.size - sizeof(bev); - if (read(input, filename, len) != len) - goto out; - - __event_process_build_id(&bev, filename, session); - - offset += bev.header.size; - } - err = 0; -out: - return err; -} - static int perf_file_section__process(struct perf_file_section *self, struct perf_header *ph, int feat, int fd) @@ -774,7 +648,7 @@ static int perf_file_section__process(struct perf_file_section *self, switch (feat) { case HEADER_TRACE_INFO: - trace_report(fd, false); + trace_report(fd); break; case HEADER_BUILD_ID: @@ -788,56 +662,13 @@ static int perf_file_section__process(struct perf_file_section *self, return 0; } -static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, - struct perf_header *ph, int fd, - bool repipe) -{ - if (do_read(fd, self, sizeof(*self)) <= 0 || - memcmp(&self->magic, __perf_magic, sizeof(self->magic))) - return -1; - - if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0) - return -1; - - if (self->size != sizeof(*self)) { - u64 size = bswap_64(self->size); - - if (size != sizeof(*self)) - return -1; - - ph->needs_swap = true; - } - - return 0; -} - -static int perf_header__read_pipe(struct perf_session *session, int fd) +int perf_header__read(struct perf_header *self, int fd) { - struct perf_header *self = &session->header; - struct perf_pipe_file_header f_header; - - if (perf_file_header__read_pipe(&f_header, self, fd, - session->repipe) < 0) { - pr_debug("incompatible file format\n"); - return -EINVAL; - } - - session->fd = fd; - - return 0; -} - -int perf_header__read(struct perf_session *session, int fd) -{ - struct perf_header *self = &session->header; struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; int nr_attrs, nr_ids, i, j; - if (session->fd_pipe) - return perf_header__read_pipe(session, fd); - if (perf_file_header__read(&f_header, self, fd) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; @@ -922,14 +753,6 @@ perf_header__find_attr(u64 id, struct perf_header *header) { int i; - /* - * We set id to -1 if the data file doesn't contain sample - * ids. Check for this and avoid walking through the entire - * list of ids which may be large. - */ - if (id == -1ULL) - return NULL; - for (i = 0; i < header->attrs; i++) { struct perf_header_attr *attr = header->attr[i]; int j; @@ -942,231 +765,3 @@ perf_header__find_attr(u64 id, struct perf_header *header) return NULL; } - -int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, - event__handler_t process, - struct perf_session *session) -{ - event_t *ev; - size_t size; - int err; - - size = sizeof(struct perf_event_attr); - size = ALIGN(size, sizeof(u64)); - size += sizeof(struct perf_event_header); - size += ids * sizeof(u64); - - ev = malloc(size); - - ev->attr.attr = *attr; - memcpy(ev->attr.id, id, ids * sizeof(u64)); - - ev->attr.header.type = PERF_RECORD_HEADER_ATTR; - ev->attr.header.size = size; - - err = process(ev, session); - - free(ev); - - return err; -} - -int event__synthesize_attrs(struct perf_header *self, - event__handler_t process, - struct perf_session *session) -{ - struct perf_header_attr *attr; - int i, err = 0; - - for (i = 0; i < self->attrs; i++) { - attr = self->attr[i]; - - err = event__synthesize_attr(&attr->attr, attr->ids, attr->id, - process, session); - if (err) { - pr_debug("failed to create perf header attribute\n"); - return err; - } - } - - return err; -} - -int event__process_attr(event_t *self, struct perf_session *session) -{ - struct perf_header_attr *attr; - unsigned int i, ids, n_ids; - - attr = perf_header_attr__new(&self->attr.attr); - if (attr == NULL) - return -ENOMEM; - - ids = self->header.size; - ids -= (void *)&self->attr.id - (void *)self; - n_ids = ids / sizeof(u64); - - for (i = 0; i < n_ids; i++) { - if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) { - perf_header_attr__delete(attr); - return -ENOMEM; - } - } - - if (perf_header__add_attr(&session->header, attr) < 0) { - perf_header_attr__delete(attr); - return -ENOMEM; - } - - perf_session__update_sample_type(session); - - return 0; -} - -int event__synthesize_event_type(u64 event_id, char *name, - event__handler_t process, - struct perf_session *session) -{ - event_t ev; - size_t size = 0; - int err = 0; - - memset(&ev, 0, sizeof(ev)); - - ev.event_type.event_type.event_id = event_id; - memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME); - strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); - - ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; - size = strlen(name); - size = ALIGN(size, sizeof(u64)); - ev.event_type.header.size = sizeof(ev.event_type) - - (sizeof(ev.event_type.event_type.name) - size); - - err = process(&ev, session); - - return err; -} - -int event__synthesize_event_types(event__handler_t process, - struct perf_session *session) -{ - struct perf_trace_event_type *type; - int i, err = 0; - - for (i = 0; i < event_count; i++) { - type = &events[i]; - - err = event__synthesize_event_type(type->event_id, type->name, - process, session); - if (err) { - pr_debug("failed to create perf header event type\n"); - return err; - } - } - - return err; -} - -int event__process_event_type(event_t *self, - struct perf_session *session __unused) -{ - if (perf_header__push_event(self->event_type.event_type.event_id, - self->event_type.event_type.name) < 0) - return -ENOMEM; - - return 0; -} - -int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, - int nb_events, - event__handler_t process, - struct perf_session *session __unused) -{ - event_t ev; - ssize_t size = 0, aligned_size = 0, padding; - int err = 0; - - memset(&ev, 0, sizeof(ev)); - - ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; - size = read_tracing_data_size(fd, pattrs, nb_events); - if (size <= 0) - return size; - aligned_size = ALIGN(size, sizeof(u64)); - padding = aligned_size - size; - ev.tracing_data.header.size = sizeof(ev.tracing_data); - ev.tracing_data.size = aligned_size; - - process(&ev, session); - - err = read_tracing_data(fd, pattrs, nb_events); - write_padded(fd, NULL, 0, padding); - - return aligned_size; -} - -int event__process_tracing_data(event_t *self, - struct perf_session *session) -{ - ssize_t size_read, padding, size = self->tracing_data.size; - off_t offset = lseek(session->fd, 0, SEEK_CUR); - char buf[BUFSIZ]; - - /* setup for reading amidst mmap */ - lseek(session->fd, offset + sizeof(struct tracing_data_event), - SEEK_SET); - - size_read = trace_report(session->fd, session->repipe); - - padding = ALIGN(size_read, sizeof(u64)) - size_read; - - if (read(session->fd, buf, padding) < 0) - die("reading input file"); - if (session->repipe) { - int retw = write(STDOUT_FILENO, buf, padding); - if (retw <= 0 || retw != padding) - die("repiping tracing data padding"); - } - - if (size_read + padding != size) - die("tracing data size mismatch"); - - return size_read + padding; -} - -int event__synthesize_build_id(struct dso *pos, u16 misc, - event__handler_t process, - struct machine *machine, - struct perf_session *session) -{ - event_t ev; - size_t len; - int err = 0; - - if (!pos->hit) - return err; - - memset(&ev, 0, sizeof(ev)); - - len = pos->long_name_len + 1; - len = ALIGN(len, NAME_ALIGN); - memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); - ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; - ev.build_id.header.misc = misc; - ev.build_id.pid = machine->pid; - ev.build_id.header.size = sizeof(ev.build_id) + len; - memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); - - err = process(&ev, session); - - return err; -} - -int event__process_build_id(event_t *self, - struct perf_session *session) -{ - __event_process_build_id(&self->build_id, - self->build_id.filename, - session); - return 0; -} diff --git a/trunk/tools/perf/util/header.h b/trunk/tools/perf/util/header.h index 402ac2454cf8..82a6af72d4cc 100644 --- a/trunk/tools/perf/util/header.h +++ b/trunk/tools/perf/util/header.h @@ -39,11 +39,6 @@ struct perf_file_header { DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); }; -struct perf_pipe_file_header { - u64 magic; - u64 size; -}; - struct perf_header; int perf_file_header__read(struct perf_file_header *self, @@ -52,22 +47,21 @@ int perf_file_header__read(struct perf_file_header *self, struct perf_header { int frozen; int attrs, size; - bool needs_swap; struct perf_header_attr **attr; s64 attr_offset; u64 data_offset; u64 data_size; u64 event_offset; u64 event_size; + bool needs_swap; DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); }; int perf_header__init(struct perf_header *self); void perf_header__exit(struct perf_header *self); -int perf_header__read(struct perf_session *session, int fd); +int perf_header__read(struct perf_header *self, int fd); int perf_header__write(struct perf_header *self, int fd, bool at_exit); -int perf_header__write_pipe(int fd); int perf_header__add_attr(struct perf_header *self, struct perf_header_attr *attr); @@ -95,33 +89,4 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, const char *name, bool is_kallsyms); int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); -int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, - event__handler_t process, - struct perf_session *session); -int event__synthesize_attrs(struct perf_header *self, - event__handler_t process, - struct perf_session *session); -int event__process_attr(event_t *self, struct perf_session *session); - -int event__synthesize_event_type(u64 event_id, char *name, - event__handler_t process, - struct perf_session *session); -int event__synthesize_event_types(event__handler_t process, - struct perf_session *session); -int event__process_event_type(event_t *self, - struct perf_session *session); - -int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, - int nb_events, - event__handler_t process, - struct perf_session *session); -int event__process_tracing_data(event_t *self, - struct perf_session *session); - -int event__synthesize_build_id(struct dso *pos, u16 misc, - event__handler_t process, - struct machine *machine, - struct perf_session *session); -int event__process_build_id(event_t *self, struct perf_session *session); - #endif /* __PERF_HEADER_H */ diff --git a/trunk/tools/perf/util/hist.c b/trunk/tools/perf/util/hist.c index ad6b22dde27f..2be33c7dbf03 100644 --- a/trunk/tools/perf/util/hist.c +++ b/trunk/tools/perf/util/hist.c @@ -8,30 +8,6 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count) -{ - he->count += count; - - switch (al->cpumode) { - case PERF_RECORD_MISC_KERNEL: - he->count_sys += count; - break; - case PERF_RECORD_MISC_USER: - he->count_us += count; - break; - case PERF_RECORD_MISC_GUEST_KERNEL: - he->count_guest_sys += count; - break; - case PERF_RECORD_MISC_GUEST_USER: - he->count_guest_us += count; - break; - default: - break; - } -} - /* * histogram, sorted on item, collects counts */ @@ -46,10 +22,8 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct hist_entry *he; struct hist_entry entry = { .thread = al->thread, - .ms = { - .map = al->map, - .sym = al->sym, - }, + .map = al->map, + .sym = al->sym, .ip = al->addr, .level = al->level, .count = count, @@ -74,8 +48,7 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, p = &(*p)->rb_right; } - he = malloc(sizeof(*he) + (symbol_conf.use_callchain ? - sizeof(struct callchain_node) : 0)); + he = malloc(sizeof(*he)); if (!he) return NULL; *he = entry; @@ -92,7 +65,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; list_for_each_entry(se, &hist_entry__sort_list, list) { - cmp = se->se_cmp(left, right); + cmp = se->cmp(left, right); if (cmp) break; } @@ -109,7 +82,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) list_for_each_entry(se, &hist_entry__sort_list, list) { int64_t (*f)(struct hist_entry *, struct hist_entry *); - f = se->se_collapse ?: se->se_cmp; + f = se->collapse ?: se->cmp; cmp = f(left, right); if (cmp) @@ -193,7 +166,7 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, struct hist_entry *iter; if (symbol_conf.use_callchain) - callchain_param.sort(&he->sorted_chain, he->callchain, + callchain_param.sort(&he->sorted_chain, &he->callchain, min_callchain_hits, &callchain_param); while (*p != NULL) { @@ -210,13 +183,12 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, rb_insert_color(&he->rb_node, root); } -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) +void perf_session__output_resort(struct rb_root *hists, u64 total_samples) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; - u64 nr_hists = 0; min_callchain_hits = total_samples * (callchain_param.min_percent / 100); @@ -231,11 +203,9 @@ u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) rb_erase(&n->rb_node, hists); perf_session__insert_output_hist_entry(&tmp, n, min_callchain_hits); - ++nr_hists; } *hists = tmp; - return nr_hists; } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) @@ -288,8 +258,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, } else ret += fprintf(fp, "%s", " "); } - if (chain->ms.sym) - ret += fprintf(fp, "%s\n", chain->ms.sym->name); + if (chain->sym) + ret += fprintf(fp, "%s\n", chain->sym->name); else ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); @@ -308,7 +278,7 @@ static void init_rem_hits(void) } strcpy(rem_sq_bracket->name, "[...]"); - rem_hits.ms.sym = rem_sq_bracket; + rem_hits.sym = rem_sq_bracket; } static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, @@ -358,6 +328,8 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, left_margin); i = 0; list_for_each_entry(chain, &child->val, list) { + if (chain->ip >= PERF_CONTEXT_MAX) + continue; ret += ipchain__fprintf_graph(fp, chain, depth, new_depth_mask, i++, new_total, @@ -396,6 +368,9 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, int ret = 0; list_for_each_entry(chain, &self->val, list) { + if (chain->ip >= PERF_CONTEXT_MAX) + continue; + if (!i++ && sort__first_dimension == SORT_SYM) continue; @@ -410,8 +385,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, } else ret += callchain__fprintf_left_margin(fp, left_margin); - if (chain->ms.sym) - ret += fprintf(fp, " %s\n", chain->ms.sym->name); + if (chain->sym) + ret += fprintf(fp, " %s\n", chain->sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); } @@ -436,8 +411,8 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, list_for_each_entry(chain, &self->val, list) { if (chain->ip >= PERF_CONTEXT_MAX) continue; - if (chain->ms.sym) - ret += fprintf(fp, " %s\n", chain->ms.sym->name); + if (chain->sym) + ret += fprintf(fp, " %s\n", chain->sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); @@ -480,17 +455,16 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -int hist_entry__snprintf(struct hist_entry *self, - char *s, size_t size, - struct perf_session *pair_session, - bool show_displacement, - long displacement, bool color, - u64 session_total) +static size_t hist_entry__fprintf(struct hist_entry *self, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp, + u64 session_total) { struct sort_entry *se; - u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; + u64 count, total; const char *sep = symbol_conf.field_sep; - int ret; + size_t ret; if (symbol_conf.exclude_other && !self->parent) return 0; @@ -498,55 +472,22 @@ int hist_entry__snprintf(struct hist_entry *self, if (pair_session) { count = self->pair ? self->pair->count : 0; total = pair_session->events_stats.total; - count_sys = self->pair ? self->pair->count_sys : 0; - count_us = self->pair ? self->pair->count_us : 0; - count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; - count_guest_us = self->pair ? self->pair->count_guest_us : 0; } else { count = self->count; total = session_total; - count_sys = self->count_sys; - count_us = self->count_us; - count_guest_sys = self->count_guest_sys; - count_guest_us = self->count_guest_us; } - if (total) { - if (color) - ret = percent_color_snprintf(s, size, - sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); - else - ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); - if (symbol_conf.show_cpu_utilization) { - ret += percent_color_snprintf(s + ret, size - ret, - sep ? "%.2f" : " %6.2f%%", - (count_sys * 100.0) / total); - ret += percent_color_snprintf(s + ret, size - ret, - sep ? "%.2f" : " %6.2f%%", - (count_us * 100.0) / total); - if (perf_guest) { - ret += percent_color_snprintf(s + ret, - size - ret, - sep ? "%.2f" : " %6.2f%%", - (count_guest_sys * 100.0) / - total); - ret += percent_color_snprintf(s + ret, - size - ret, - sep ? "%.2f" : " %6.2f%%", - (count_guest_us * 100.0) / - total); - } - } - } else - ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); + if (total) + ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + else + ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); + fprintf(fp, "%c%lld", *sep, count); else - ret += snprintf(s + ret, size - ret, "%11lld", count); + fprintf(fp, "%11lld", count); } if (pair_session) { @@ -566,9 +507,9 @@ int hist_entry__snprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); + ret += fprintf(fp, "%c%s", *sep, bf); else - ret += snprintf(s + ret, size - ret, "%11.11s", bf); + ret += fprintf(fp, "%11.11s", bf); if (show_displacement) { if (displacement) @@ -577,9 +518,9 @@ int hist_entry__snprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); + fprintf(fp, "%c%s", *sep, bf); else - ret += snprintf(s + ret, size - ret, "%6.6s", bf); + fprintf(fp, "%6.6s", bf); } } @@ -587,41 +528,27 @@ int hist_entry__snprintf(struct hist_entry *self, if (se->elide) continue; - ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); - ret += se->se_snprintf(self, s + ret, size - ret, - se->se_width ? *se->se_width : 0); + fprintf(fp, "%s", sep ?: " "); + ret += se->print(fp, self, se->width ? *se->width : 0); } - return ret; -} + ret += fprintf(fp, "\n"); -int hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, - u64 session_total) -{ - char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), pair_session, - show_displacement, displacement, - true, session_total); - return fprintf(fp, "%s\n", bf); -} + if (symbol_conf.use_callchain) { + int left_margin = 0; -static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, - u64 session_total) -{ - int left_margin = 0; + if (sort__first_dimension == SORT_COMM) { + se = list_first_entry(&hist_entry__sort_list, typeof(*se), + list); + left_margin = se->width ? *se->width : 0; + left_margin -= thread__comm_len(self->thread); + } - if (sort__first_dimension == SORT_COMM) { - struct sort_entry *se = list_first_entry(&hist_entry__sort_list, - typeof(*se), list); - left_margin = se->se_width ? *se->se_width : 0; - left_margin -= thread__comm_len(self->thread); + hist_entry_callchain__fprintf(fp, self, session_total, + left_margin); } - return hist_entry_callchain__fprintf(fp, self, session_total, - left_margin); + return ret; } size_t perf_session__fprintf_hists(struct rb_root *hists, @@ -649,24 +576,6 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, fputs(" Samples ", fp); } - if (symbol_conf.show_cpu_utilization) { - if (sep) { - ret += fprintf(fp, "%csys", *sep); - ret += fprintf(fp, "%cus", *sep); - if (perf_guest) { - ret += fprintf(fp, "%cguest sys", *sep); - ret += fprintf(fp, "%cguest us", *sep); - } - } else { - ret += fprintf(fp, " sys "); - ret += fprintf(fp, " us "); - if (perf_guest) { - ret += fprintf(fp, " guest sys "); - ret += fprintf(fp, " guest us "); - } - } - } - if (pair) { if (sep) ret += fprintf(fp, "%cDelta", *sep); @@ -685,22 +594,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, if (se->elide) continue; if (sep) { - fprintf(fp, "%c%s", *sep, se->se_header); + fprintf(fp, "%c%s", *sep, se->header); continue; } - width = strlen(se->se_header); - if (se->se_width) { + width = strlen(se->header); + if (se->width) { if (symbol_conf.col_width_list_str) { if (col_width) { - *se->se_width = atoi(col_width); + *se->width = atoi(col_width); col_width = strchr(col_width, ','); if (col_width) ++col_width; } } - width = *se->se_width = max(*se->se_width, width); + width = *se->width = max(*se->width, width); } - fprintf(fp, " %*s", width, se->se_header); + fprintf(fp, " %*s", width, se->header); } fprintf(fp, "\n"); @@ -722,10 +631,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, continue; fprintf(fp, " "); - if (se->se_width) - width = *se->se_width; + if (se->width) + width = *se->width; else - width = strlen(se->se_header); + width = strlen(se->header); for (i = 0; i < width; i++) fprintf(fp, "."); } @@ -746,13 +655,9 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, } ret += hist_entry__fprintf(h, pair, show_displacement, displacement, fp, session_total); - - if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, session_total); - - if (h->ms.map == NULL && verbose > 1) { + if (h->map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, - MAP__FUNCTION, verbose, fp); + MAP__FUNCTION, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } } diff --git a/trunk/tools/perf/util/hist.h b/trunk/tools/perf/util/hist.h index 9df1c340ec92..16f360cce5bf 100644 --- a/trunk/tools/perf/util/hist.h +++ b/trunk/tools/perf/util/hist.h @@ -12,28 +12,15 @@ struct addr_location; struct symbol; struct rb_root; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count); struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct addr_location *al, struct symbol *parent, u64 count, bool *hit); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -int hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, - u64 session_total); -int hist_entry__snprintf(struct hist_entry *self, - char *bf, size_t size, - struct perf_session *pair_session, - bool show_displacement, long displacement, - bool color, u64 session_total); void hist_entry__free(struct hist_entry *); -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); +void perf_session__output_resort(struct rb_root *hists, u64 total_samples); void perf_session__collapse_resort(struct rb_root *hists); size_t perf_session__fprintf_hists(struct rb_root *hists, struct perf_session *pair, diff --git a/trunk/tools/perf/util/hweight.c b/trunk/tools/perf/util/hweight.c deleted file mode 100644 index 5c1d0d099f0d..000000000000 --- a/trunk/tools/perf/util/hweight.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -/** - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. - */ - -unsigned int hweight32(unsigned int w) -{ - unsigned int res = w - ((w >> 1) & 0x55555555); - res = (res & 0x33333333) + ((res >> 2) & 0x33333333); - res = (res + (res >> 4)) & 0x0F0F0F0F; - res = res + (res >> 8); - return (res + (res >> 16)) & 0x000000FF; -} - -unsigned long hweight64(__u64 w) -{ -#if BITS_PER_LONG == 32 - return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w); -#elif BITS_PER_LONG == 64 - __u64 res = w - ((w >> 1) & 0x5555555555555555ul); - res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); - res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful; - res = res + (res >> 8); - res = res + (res >> 16); - return (res + (res >> 32)) & 0x00000000000000FFul; -#endif -} diff --git a/trunk/tools/perf/util/include/asm/bitops.h b/trunk/tools/perf/util/include/asm/bitops.h new file mode 100644 index 000000000000..58e9817ffae0 --- /dev/null +++ b/trunk/tools/perf/util/include/asm/bitops.h @@ -0,0 +1,18 @@ +#ifndef _PERF_ASM_BITOPS_H_ +#define _PERF_ASM_BITOPS_H_ + +#include +#include "../../types.h" +#include + +/* CHECKME: Not sure both always match */ +#define BITS_PER_LONG __WORDSIZE + +#include "../../../../include/asm-generic/bitops/__fls.h" +#include "../../../../include/asm-generic/bitops/fls.h" +#include "../../../../include/asm-generic/bitops/fls64.h" +#include "../../../../include/asm-generic/bitops/__ffs.h" +#include "../../../../include/asm-generic/bitops/ffz.h" +#include "../../../../include/asm-generic/bitops/hweight.h" + +#endif diff --git a/trunk/tools/perf/util/include/asm/hweight.h b/trunk/tools/perf/util/include/asm/hweight.h deleted file mode 100644 index 36cf26d434a5..000000000000 --- a/trunk/tools/perf/util/include/asm/hweight.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef PERF_HWEIGHT_H -#define PERF_HWEIGHT_H - -#include -unsigned int hweight32(unsigned int w); -unsigned long hweight64(__u64 w); - -#endif /* PERF_HWEIGHT_H */ diff --git a/trunk/tools/perf/util/include/dwarf-regs.h b/trunk/tools/perf/util/include/dwarf-regs.h deleted file mode 100644 index cf6727e99c44..000000000000 --- a/trunk/tools/perf/util/include/dwarf-regs.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _PERF_DWARF_REGS_H_ -#define _PERF_DWARF_REGS_H_ - -#ifdef DWARF_SUPPORT -const char *get_arch_regstr(unsigned int n); -#endif - -#endif diff --git a/trunk/tools/perf/util/include/linux/bitmap.h b/trunk/tools/perf/util/include/linux/bitmap.h index eda4416efa0a..94507639a8c4 100644 --- a/trunk/tools/perf/util/include/linux/bitmap.h +++ b/trunk/tools/perf/util/include/linux/bitmap.h @@ -1,35 +1,3 @@ -#ifndef _PERF_BITOPS_H -#define _PERF_BITOPS_H - -#include -#include - -int __bitmap_weight(const unsigned long *bitmap, int bits); - -#define BITMAP_LAST_WORD_MASK(nbits) \ -( \ - ((nbits) % BITS_PER_LONG) ? \ - (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ -) - -#define small_const_nbits(nbits) \ - (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) - -static inline void bitmap_zero(unsigned long *dst, int nbits) -{ - if (small_const_nbits(nbits)) - *dst = 0UL; - else { - int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); - memset(dst, 0, len); - } -} - -static inline int bitmap_weight(const unsigned long *src, int nbits) -{ - if (small_const_nbits(nbits)) - return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits)); - return __bitmap_weight(src, nbits); -} - -#endif /* _PERF_BITOPS_H */ +#include "../../../../include/linux/bitmap.h" +#include "../../../../include/asm-generic/bitops/find.h" +#include diff --git a/trunk/tools/perf/util/include/linux/bitops.h b/trunk/tools/perf/util/include/linux/bitops.h index bb4ac2e05385..8d63116e9435 100644 --- a/trunk/tools/perf/util/include/linux/bitops.h +++ b/trunk/tools/perf/util/include/linux/bitops.h @@ -1,12 +1,13 @@ #ifndef _PERF_LINUX_BITOPS_H_ #define _PERF_LINUX_BITOPS_H_ -#include -#include +#define __KERNEL__ -#define BITS_PER_LONG __WORDSIZE -#define BITS_PER_BYTE 8 -#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define CONFIG_GENERIC_FIND_NEXT_BIT +#define CONFIG_GENERIC_FIND_FIRST_BIT +#include "../../../../include/linux/bitops.h" + +#undef __KERNEL__ static inline void set_bit(int nr, unsigned long *addr) { @@ -19,9 +20,10 @@ static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; } -static inline unsigned long hweight_long(unsigned long w) -{ - return sizeof(w) == 4 ? hweight32(w) : hweight64(w); -} +unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned + long size, unsigned long offset); + +unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned + long size, unsigned long offset); #endif diff --git a/trunk/tools/perf/util/include/linux/compiler.h b/trunk/tools/perf/util/include/linux/compiler.h index 791f9dd27ebf..dfb0713ed47f 100644 --- a/trunk/tools/perf/util/include/linux/compiler.h +++ b/trunk/tools/perf/util/include/linux/compiler.h @@ -7,6 +7,4 @@ #define __user #define __attribute_const__ -#define __used __attribute__((__unused__)) - #endif diff --git a/trunk/tools/perf/util/include/linux/kernel.h b/trunk/tools/perf/util/include/linux/kernel.h index 388ab1bfd114..f2611655ab51 100644 --- a/trunk/tools/perf/util/include/linux/kernel.h +++ b/trunk/tools/perf/util/include/linux/kernel.h @@ -85,19 +85,16 @@ simple_strtoul(const char *nptr, char **endptr, int base) return strtoul(nptr, endptr, base); } -int eprintf(int level, - const char *fmt, ...) __attribute__((format(printf, 2, 3))); - #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif #define pr_err(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) + do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) #define pr_warning(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) + do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) #define pr_info(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) + do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) #define pr_debug(fmt, ...) \ eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debugN(n, fmt, ...) \ diff --git a/trunk/tools/perf/util/map.c b/trunk/tools/perf/util/map.c index 44a4df68b3cf..e509cd59c67d 100644 --- a/trunk/tools/perf/util/map.c +++ b/trunk/tools/perf/util/map.c @@ -1,11 +1,9 @@ +#include "event.h" #include "symbol.h" -#include -#include #include #include #include -#include -#include "map.h" +#include "debug.h" const char *map_type__name[MAP__NR_TYPES] = { [MAP__FUNCTION] = "Functions", @@ -38,16 +36,15 @@ void map__init(struct map *self, enum map_type type, self->map_ip = map__map_ip; self->unmap_ip = map__unmap_ip; RB_CLEAR_NODE(&self->rb_node); - self->groups = NULL; } -struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, - u64 pgoff, u32 pid, char *filename, - enum map_type type, char *cwd, int cwdlen) +struct map *map__new(struct mmap_event *event, enum map_type type, + char *cwd, int cwdlen) { struct map *self = malloc(sizeof(*self)); if (self != NULL) { + const char *filename = event->filename; char newfilename[PATH_MAX]; struct dso *dso; int anon; @@ -65,15 +62,16 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, anon = is_anon_memory(filename); if (anon) { - snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); + snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid); filename = newfilename; } - dso = __dsos__findnew(dsos__list, filename); + dso = dsos__findnew(filename); if (dso == NULL) goto out_delete; - map__init(self, type, start, start + len, pgoff, dso); + map__init(self, type, event->start, event->start + event->len, + event->pgoff, dso); if (anon) { set_identity: @@ -237,416 +235,3 @@ u64 map__objdump_2ip(struct map *map, u64 addr) map->unmap_ip(map, addr); /* RIP -> IP */ return ip; } - -void map_groups__init(struct map_groups *self) -{ - int i; - for (i = 0; i < MAP__NR_TYPES; ++i) { - self->maps[i] = RB_ROOT; - INIT_LIST_HEAD(&self->removed_maps[i]); - } - self->machine = NULL; -} - -void map_groups__flush(struct map_groups *self) -{ - int type; - - for (type = 0; type < MAP__NR_TYPES; type++) { - struct rb_root *root = &self->maps[type]; - struct rb_node *next = rb_first(root); - - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node); - next = rb_next(&pos->rb_node); - rb_erase(&pos->rb_node, root); - /* - * We may have references to this map, for - * instance in some hist_entry instances, so - * just move them to a separate list. - */ - list_add_tail(&pos->node, &self->removed_maps[pos->type]); - } - } -} - -struct symbol *map_groups__find_symbol(struct map_groups *self, - enum map_type type, u64 addr, - struct map **mapp, - symbol_filter_t filter) -{ - struct map *map = map_groups__find(self, type, addr); - - if (map != NULL) { - if (mapp != NULL) - *mapp = map; - return map__find_symbol(map, map->map_ip(map, addr), filter); - } - - return NULL; -} - -struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, - enum map_type type, - const char *name, - struct map **mapp, - symbol_filter_t filter) -{ - struct rb_node *nd; - - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node); - struct symbol *sym = map__find_symbol_by_name(pos, name, filter); - - if (sym == NULL) - continue; - if (mapp != NULL) - *mapp = pos; - return sym; - } - - return NULL; -} - -size_t __map_groups__fprintf_maps(struct map_groups *self, - enum map_type type, int verbose, FILE *fp) -{ - size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); - struct rb_node *nd; - - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node); - printed += fprintf(fp, "Map:"); - printed += map__fprintf(pos, fp); - if (verbose > 2) { - printed += dso__fprintf(pos->dso, type, fp); - printed += fprintf(fp, "--\n"); - } - } - - return printed; -} - -size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) -{ - size_t printed = 0, i; - for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_maps(self, i, verbose, fp); - return printed; -} - -static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, - enum map_type type, - int verbose, FILE *fp) -{ - struct map *pos; - size_t printed = 0; - - list_for_each_entry(pos, &self->removed_maps[type], node) { - printed += fprintf(fp, "Map:"); - printed += map__fprintf(pos, fp); - if (verbose > 1) { - printed += dso__fprintf(pos->dso, type, fp); - printed += fprintf(fp, "--\n"); - } - } - return printed; -} - -static size_t map_groups__fprintf_removed_maps(struct map_groups *self, - int verbose, FILE *fp) -{ - size_t printed = 0, i; - for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); - return printed; -} - -size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) -{ - size_t printed = map_groups__fprintf_maps(self, verbose, fp); - printed += fprintf(fp, "Removed maps:\n"); - return printed + map_groups__fprintf_removed_maps(self, verbose, fp); -} - -int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, - int verbose, FILE *fp) -{ - struct rb_root *root = &self->maps[map->type]; - struct rb_node *next = rb_first(root); - - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node); - next = rb_next(&pos->rb_node); - - if (!map__overlap(pos, map)) - continue; - - if (verbose >= 2) { - fputs("overlapping maps:\n", fp); - map__fprintf(map, fp); - map__fprintf(pos, fp); - } - - rb_erase(&pos->rb_node, root); - /* - * We may have references to this map, for instance in some - * hist_entry instances, so just move them to a separate - * list. - */ - list_add_tail(&pos->node, &self->removed_maps[map->type]); - /* - * Now check if we need to create new maps for areas not - * overlapped by the new map: - */ - if (map->start > pos->start) { - struct map *before = map__clone(pos); - - if (before == NULL) - return -ENOMEM; - - before->end = map->start - 1; - map_groups__insert(self, before); - if (verbose >= 2) - map__fprintf(before, fp); - } - - if (map->end < pos->end) { - struct map *after = map__clone(pos); - - if (after == NULL) - return -ENOMEM; - - after->start = map->end + 1; - map_groups__insert(self, after); - if (verbose >= 2) - map__fprintf(after, fp); - } - } - - return 0; -} - -/* - * XXX This should not really _copy_ te maps, but refcount them. - */ -int map_groups__clone(struct map_groups *self, - struct map_groups *parent, enum map_type type) -{ - struct rb_node *nd; - for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { - struct map *map = rb_entry(nd, struct map, rb_node); - struct map *new = map__clone(map); - if (new == NULL) - return -ENOMEM; - map_groups__insert(self, new); - } - return 0; -} - -static u64 map__reloc_map_ip(struct map *map, u64 ip) -{ - return ip + (s64)map->pgoff; -} - -static u64 map__reloc_unmap_ip(struct map *map, u64 ip) -{ - return ip - (s64)map->pgoff; -} - -void map__reloc_vmlinux(struct map *self) -{ - struct kmap *kmap = map__kmap(self); - s64 reloc; - - if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) - return; - - reloc = (kmap->ref_reloc_sym->unrelocated_addr - - kmap->ref_reloc_sym->addr); - - if (!reloc) - return; - - self->map_ip = map__reloc_map_ip; - self->unmap_ip = map__reloc_unmap_ip; - self->pgoff = reloc; -} - -void maps__insert(struct rb_root *maps, struct map *map) -{ - struct rb_node **p = &maps->rb_node; - struct rb_node *parent = NULL; - const u64 ip = map->start; - struct map *m; - - while (*p != NULL) { - parent = *p; - m = rb_entry(parent, struct map, rb_node); - if (ip < m->start) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&map->rb_node, parent, p); - rb_insert_color(&map->rb_node, maps); -} - -struct map *maps__find(struct rb_root *maps, u64 ip) -{ - struct rb_node **p = &maps->rb_node; - struct rb_node *parent = NULL; - struct map *m; - - while (*p != NULL) { - parent = *p; - m = rb_entry(parent, struct map, rb_node); - if (ip < m->start) - p = &(*p)->rb_left; - else if (ip > m->end) - p = &(*p)->rb_right; - else - return m; - } - - return NULL; -} - -int machine__init(struct machine *self, const char *root_dir, pid_t pid) -{ - map_groups__init(&self->kmaps); - RB_CLEAR_NODE(&self->rb_node); - INIT_LIST_HEAD(&self->user_dsos); - INIT_LIST_HEAD(&self->kernel_dsos); - - self->kmaps.machine = self; - self->pid = pid; - self->root_dir = strdup(root_dir); - return self->root_dir == NULL ? -ENOMEM : 0; -} - -struct machine *machines__add(struct rb_root *self, pid_t pid, - const char *root_dir) -{ - struct rb_node **p = &self->rb_node; - struct rb_node *parent = NULL; - struct machine *pos, *machine = malloc(sizeof(*machine)); - - if (!machine) - return NULL; - - if (machine__init(machine, root_dir, pid) != 0) { - free(machine); - return NULL; - } - - while (*p != NULL) { - parent = *p; - pos = rb_entry(parent, struct machine, rb_node); - if (pid < pos->pid) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&machine->rb_node, parent, p); - rb_insert_color(&machine->rb_node, self); - - return machine; -} - -struct machine *machines__find(struct rb_root *self, pid_t pid) -{ - struct rb_node **p = &self->rb_node; - struct rb_node *parent = NULL; - struct machine *machine; - struct machine *default_machine = NULL; - - while (*p != NULL) { - parent = *p; - machine = rb_entry(parent, struct machine, rb_node); - if (pid < machine->pid) - p = &(*p)->rb_left; - else if (pid > machine->pid) - p = &(*p)->rb_right; - else - return machine; - if (!machine->pid) - default_machine = machine; - } - - return default_machine; -} - -/* - * FIXME: Why repeatedly search for this? - */ -struct machine *machines__find_host(struct rb_root *self) -{ - struct rb_node **p = &self->rb_node; - struct rb_node *parent = NULL; - struct machine *machine; - pid_t pid = HOST_KERNEL_ID; - - while (*p != NULL) { - parent = *p; - machine = rb_entry(parent, struct machine, rb_node); - if (pid < machine->pid) - p = &(*p)->rb_left; - else if (pid > machine->pid) - p = &(*p)->rb_right; - else - return machine; - } - - return NULL; -} - -struct machine *machines__findnew(struct rb_root *self, pid_t pid) -{ - char path[PATH_MAX]; - const char *root_dir; - struct machine *machine = machines__find(self, pid); - - if (!machine || machine->pid != pid) { - if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) - root_dir = ""; - else { - if (!symbol_conf.guestmount) - goto out; - sprintf(path, "%s/%d", symbol_conf.guestmount, pid); - if (access(path, R_OK)) { - pr_err("Can't access file %s\n", path); - goto out; - } - root_dir = path; - } - machine = machines__add(self, pid, root_dir); - } - -out: - return machine; -} - -void machines__process(struct rb_root *self, machine__process_t process, void *data) -{ - struct rb_node *nd; - - for (nd = rb_first(self); nd; nd = rb_next(nd)) { - struct machine *pos = rb_entry(nd, struct machine, rb_node); - process(pos, data); - } -} - -char *machine__mmap_name(struct machine *self, char *bf, size_t size) -{ - if (machine__is_host(self)) - snprintf(bf, size, "[%s]", "kernel.kallsyms"); - else if (machine__is_default_guest(self)) - snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); - else - snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid); - - return bf; -} diff --git a/trunk/tools/perf/util/map.h b/trunk/tools/perf/util/map.h index f39134512829..b756368076c6 100644 --- a/trunk/tools/perf/util/map.h +++ b/trunk/tools/perf/util/map.h @@ -4,9 +4,7 @@ #include #include #include -#include -#include -#include "types.h" +#include enum map_type { MAP__FUNCTION = 0, @@ -20,7 +18,6 @@ extern const char *map_type__name[MAP__NR_TYPES]; struct dso; struct ref_reloc_sym; struct map_groups; -struct machine; struct map { union { @@ -30,7 +27,6 @@ struct map { u64 start; u64 end; enum map_type type; - u32 priv; u64 pgoff; /* ip -> dso rip */ @@ -39,7 +35,6 @@ struct map { u64 (*unmap_ip)(struct map *, u64); struct dso *dso; - struct map_groups *groups; }; struct kmap { @@ -47,32 +42,6 @@ struct kmap { struct map_groups *kmaps; }; -struct map_groups { - struct rb_root maps[MAP__NR_TYPES]; - struct list_head removed_maps[MAP__NR_TYPES]; - struct machine *machine; -}; - -/* Native host kernel uses -1 as pid index in machine */ -#define HOST_KERNEL_ID (-1) -#define DEFAULT_GUEST_KERNEL_ID (0) - -struct machine { - struct rb_node rb_node; - pid_t pid; - char *root_dir; - struct list_head user_dsos; - struct list_head kernel_dsos; - struct map_groups kmaps; - struct map *vmlinux_maps[MAP__NR_TYPES]; -}; - -static inline -struct map *machine__kernel_map(struct machine *self, enum map_type type) -{ - return self->vmlinux_maps[type]; -} - static inline struct kmap *map__kmap(struct map *self) { return (struct kmap *)(self + 1); @@ -99,14 +68,14 @@ u64 map__rip_2objdump(struct map *map, u64 rip); u64 map__objdump_2ip(struct map *map, u64 addr); struct symbol; +struct mmap_event; typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso); -struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, - u64 pgoff, u32 pid, char *filename, - enum map_type type, char *cwd, int cwdlen); +struct map *map__new(struct mmap_event *event, enum map_type, + char *cwd, int cwdlen); void map__delete(struct map *self); struct map *map__clone(struct map *self); int map__overlap(struct map *l, struct map *r); @@ -122,96 +91,4 @@ void map__fixup_end(struct map *self); void map__reloc_vmlinux(struct map *self); -size_t __map_groups__fprintf_maps(struct map_groups *self, - enum map_type type, int verbose, FILE *fp); -void maps__insert(struct rb_root *maps, struct map *map); -struct map *maps__find(struct rb_root *maps, u64 addr); -void map_groups__init(struct map_groups *self); -int map_groups__clone(struct map_groups *self, - struct map_groups *parent, enum map_type type); -size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); -size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); - -typedef void (*machine__process_t)(struct machine *self, void *data); - -void machines__process(struct rb_root *self, machine__process_t process, void *data); -struct machine *machines__add(struct rb_root *self, pid_t pid, - const char *root_dir); -struct machine *machines__find_host(struct rb_root *self); -struct machine *machines__find(struct rb_root *self, pid_t pid); -struct machine *machines__findnew(struct rb_root *self, pid_t pid); -char *machine__mmap_name(struct machine *self, char *bf, size_t size); -int machine__init(struct machine *self, const char *root_dir, pid_t pid); - -/* - * Default guest kernel is defined by parameter --guestkallsyms - * and --guestmodules - */ -static inline bool machine__is_default_guest(struct machine *self) -{ - return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false; -} - -static inline bool machine__is_host(struct machine *self) -{ - return self ? self->pid == HOST_KERNEL_ID : false; -} - -static inline void map_groups__insert(struct map_groups *self, struct map *map) -{ - maps__insert(&self->maps[map->type], map); - map->groups = self; -} - -static inline struct map *map_groups__find(struct map_groups *self, - enum map_type type, u64 addr) -{ - return maps__find(&self->maps[type], addr); -} - -struct symbol *map_groups__find_symbol(struct map_groups *self, - enum map_type type, u64 addr, - struct map **mapp, - symbol_filter_t filter); - -struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, - enum map_type type, - const char *name, - struct map **mapp, - symbol_filter_t filter); - -static inline -struct symbol *machine__find_kernel_symbol(struct machine *self, - enum map_type type, u64 addr, - struct map **mapp, - symbol_filter_t filter) -{ - return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter); -} - -static inline -struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, - struct map **mapp, - symbol_filter_t filter) -{ - return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter); -} - -static inline -struct symbol *map_groups__find_function_by_name(struct map_groups *self, - const char *name, struct map **mapp, - symbol_filter_t filter) -{ - return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); -} - -int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, - int verbose, FILE *fp); - -struct map *map_groups__find_by_name(struct map_groups *self, - enum map_type type, const char *name); -struct map *machine__new_module(struct machine *self, u64 start, const char *filename); - -void map_groups__flush(struct map_groups *self); - #endif /* __PERF_MAP_H */ diff --git a/trunk/tools/perf/util/newt.c b/trunk/tools/perf/util/newt.c deleted file mode 100644 index 7a123a94e3fc..000000000000 --- a/trunk/tools/perf/util/newt.c +++ /dev/null @@ -1,725 +0,0 @@ -#define _GNU_SOURCE -#include -#undef _GNU_SOURCE - -#include -#include -#include - -#include "cache.h" -#include "hist.h" -#include "session.h" -#include "sort.h" -#include "symbol.h" - -struct ui_progress { - newtComponent form, scale; -}; - -struct ui_progress *ui_progress__new(const char *title, u64 total) -{ - struct ui_progress *self = malloc(sizeof(*self)); - - if (self != NULL) { - int cols; - newtGetScreenSize(&cols, NULL); - cols -= 4; - newtCenteredWindow(cols, 1, title); - self->form = newtForm(NULL, NULL, 0); - if (self->form == NULL) - goto out_free_self; - self->scale = newtScale(0, 0, cols, total); - if (self->scale == NULL) - goto out_free_form; - newtFormAddComponents(self->form, self->scale, NULL); - newtRefresh(); - } - - return self; - -out_free_form: - newtFormDestroy(self->form); -out_free_self: - free(self); - return NULL; -} - -void ui_progress__update(struct ui_progress *self, u64 curr) -{ - newtScaleSet(self->scale, curr); - newtRefresh(); -} - -void ui_progress__delete(struct ui_progress *self) -{ - newtFormDestroy(self->form); - newtPopWindow(); - free(self); -} - -static char browser__last_msg[1024]; - -int browser__show_help(const char *format, va_list ap) -{ - int ret; - static int backlog; - - ret = vsnprintf(browser__last_msg + backlog, - sizeof(browser__last_msg) - backlog, format, ap); - backlog += ret; - - if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); - newtRefresh(); - backlog = 0; - } - - return ret; -} - -static void newt_form__set_exit_keys(newtComponent self) -{ - newtFormAddHotKey(self, NEWT_KEY_ESCAPE); - newtFormAddHotKey(self, 'Q'); - newtFormAddHotKey(self, 'q'); - newtFormAddHotKey(self, CTRL('c')); -} - -static newtComponent newt_form__new(void) -{ - newtComponent self = newtForm(NULL, NULL, 0); - if (self) - newt_form__set_exit_keys(self); - return self; -} - -static int popup_menu(int argc, char * const argv[]) -{ - struct newtExitStruct es; - int i, rc = -1, max_len = 5; - newtComponent listbox, form = newt_form__new(); - - if (form == NULL) - return -1; - - listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); - if (listbox == NULL) - goto out_destroy_form; - - newtFormAddComponents(form, listbox, NULL); - - for (i = 0; i < argc; ++i) { - int len = strlen(argv[i]); - if (len > max_len) - max_len = len; - if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) - goto out_destroy_form; - } - - newtCenteredWindow(max_len, argc, NULL); - newtFormRun(form, &es); - rc = newtListboxGetCurrent(listbox) - NULL; - if (es.reason == NEWT_EXIT_HOTKEY) - rc = -1; - newtPopWindow(); -out_destroy_form: - newtFormDestroy(form); - return rc; -} - -static bool dialog_yesno(const char *msg) -{ - /* newtWinChoice should really be accepting const char pointers... */ - char yes[] = "Yes", no[] = "No"; - return newtWinChoice(NULL, yes, no, (char *)msg) == 1; -} - -/* - * When debugging newt problems it was useful to be able to "unroll" - * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate - * a source file with the sequence of calls to these methods, to then - * tweak the arrays to get the intended results, so I'm keeping this code - * here, may be useful again in the future. - */ -#undef NEWT_DEBUG - -static void newt_checkbox_tree__add(newtComponent tree, const char *str, - void *priv, int *indexes) -{ -#ifdef NEWT_DEBUG - /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ - int i = 0, len = 40 - strlen(str); - - fprintf(stderr, - "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", - len, len, " ", str, priv); - while (indexes[i] != NEWT_ARG_LAST) { - if (indexes[i] != NEWT_ARG_APPEND) - fprintf(stderr, " %d,", indexes[i]); - else - fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); - ++i; - } - fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); - fflush(stderr); -#endif - newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); -} - -static char *callchain_list__sym_name(struct callchain_list *self, - char *bf, size_t bfsize) -{ - if (self->ms.sym) - return self->ms.sym->name; - - snprintf(bf, bfsize, "%#Lx", self->ip); - return bf; -} - -static void __callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int depth) -{ - struct rb_node *node; - u64 new_total, remaining; - int idx = 0; - - if (callchain_param.mode == CHAIN_GRAPH_REL) - new_total = self->children_hit; - else - new_total = total; - - remaining = new_total; - node = rb_first(&self->rb_root); - while (node) { - struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); - struct rb_node *next = rb_next(node); - u64 cumul = cumul_hits(child); - struct callchain_list *chain; - int first = true, printed = 0; - int chain_idx = -1; - remaining -= cumul; - - indexes[depth] = NEWT_ARG_APPEND; - indexes[depth + 1] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &child->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], - *alloc_str = NULL; - const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - - if (first) { - double percent = cumul * 100.0 / new_total; - - first = false; - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } else { - indexes[depth] = idx; - indexes[depth + 1] = NEWT_ARG_APPEND; - indexes[depth + 2] = NEWT_ARG_LAST; - ++chain_idx; - } - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - free(alloc_str); - ++printed; - } - - indexes[depth] = idx; - if (chain_idx != -1) - indexes[depth + 1] = chain_idx; - if (printed != 0) - ++idx; - __callchain__append_graph_browser(child, tree, new_total, indexes, - depth + (chain_idx != -1 ? 2 : 1)); - node = next; - } -} - -static void callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int parent_idx) -{ - struct callchain_list *chain; - int i = 0; - - indexes[1] = NEWT_ARG_APPEND; - indexes[2] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &self->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *str; - - if (chain->ip >= PERF_CONTEXT_MAX) - continue; - - if (!i++ && sort__first_dimension == SORT_SYM) - continue; - - str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - } - - indexes[1] = parent_idx; - indexes[2] = NEWT_ARG_APPEND; - indexes[3] = NEWT_ARG_LAST; - __callchain__append_graph_browser(self, tree, total, indexes, 2); -} - -static void hist_entry__append_callchain_browser(struct hist_entry *self, - newtComponent tree, u64 total, int parent_idx) -{ - struct rb_node *rb_node; - int indexes[1024] = { [0] = parent_idx, }; - int idx = 0; - struct callchain_node *chain; - - rb_node = rb_first(&self->sorted_chain); - while (rb_node) { - chain = rb_entry(rb_node, struct callchain_node, rb_node); - switch (callchain_param.mode) { - case CHAIN_FLAT: - break; - case CHAIN_GRAPH_ABS: /* falldown */ - case CHAIN_GRAPH_REL: - callchain__append_graph_browser(chain, tree, total, indexes, idx++); - break; - case CHAIN_NONE: - default: - break; - } - rb_node = rb_next(rb_node); - } -} - -static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent tree, u64 total) -{ - char s[256]; - size_t ret; - - if (symbol_conf.exclude_other && !self->parent) - return 0; - - ret = hist_entry__snprintf(self, s, sizeof(s), NULL, - false, 0, false, total); - if (symbol_conf.use_callchain) { - int indexes[2]; - - indexes[0] = NEWT_ARG_APPEND; - indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, &self->ms, indexes); - } else - newtListboxAppendEntry(tree, s, &self->ms); - - return ret; -} - -static void map_symbol__annotate_browser(const struct map_symbol *self, - const char *input_name) -{ - FILE *fp; - int cols, rows; - newtComponent form, tree; - struct newtExitStruct es; - char *str; - size_t line_len, max_line_len = 0; - size_t max_usable_width; - char *line = NULL; - - if (self->sym == NULL) - return; - - if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", - input_name, self->map->dso->name, self->sym->name) < 0) - return; - - fp = popen(str, "r"); - if (fp == NULL) - goto out_free_str; - - newtPushHelpLine("Press ESC to exit"); - newtGetScreenSize(&cols, &rows); - tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); - - while (!feof(fp)) { - if (getline(&line, &line_len, fp) < 0 || !line_len) - break; - while (line_len != 0 && isspace(line[line_len - 1])) - line[--line_len] = '\0'; - - if (line_len > max_line_len) - max_line_len = line_len; - newtListboxAppendEntry(tree, line, NULL); - } - fclose(fp); - free(line); - - max_usable_width = cols - 22; - if (max_line_len > max_usable_width) - max_line_len = max_usable_width; - - newtListboxSetWidth(tree, max_line_len); - - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); - form = newt_form__new(); - newtFormAddComponents(form, tree, NULL); - - newtFormRun(form, &es); - newtFormDestroy(form); - newtPopWindow(); - newtPopHelpLine(); -out_free_str: - free(str); -} - -static const void *newt__symbol_tree_get_current(newtComponent self) -{ - if (symbol_conf.use_callchain) - return newtCheckboxTreeGetCurrent(self); - return newtListboxGetCurrent(self); -} - -static void hist_browser__selection(newtComponent self, void *data) -{ - const struct map_symbol **symbol_ptr = data; - *symbol_ptr = newt__symbol_tree_get_current(self); -} - -struct hist_browser { - newtComponent form, tree; - const struct map_symbol *selection; -}; - -static struct hist_browser *hist_browser__new(void) -{ - struct hist_browser *self = malloc(sizeof(*self)); - - if (self != NULL) - self->form = NULL; - - return self; -} - -static void hist_browser__delete(struct hist_browser *self) -{ - newtFormDestroy(self->form); - newtPopWindow(); - free(self); -} - -static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total, const char *title) -{ - int max_len = 0, idx, cols, rows; - struct ui_progress *progress; - struct rb_node *nd; - u64 curr_hist = 0; - char seq[] = "."; - char str[256]; - - if (self->form) { - newtFormDestroy(self->form); - newtPopWindow(); - } - - snprintf(str, sizeof(str), "Samples: %Ld ", - session_total); - newtDrawRootText(0, 0, str); - - newtGetScreenSize(NULL, &rows); - - if (symbol_conf.use_callchain) - self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - self->tree = newtListbox(0, 0, rows - 5, - (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - - newtComponentAddCallback(self->tree, hist_browser__selection, - &self->selection); - - progress = ui_progress__new("Adding entries to the browser...", nr_hists); - if (progress == NULL) - return -1; - - idx = 0; - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len; - - if (h->filtered) - continue; - - len = hist_entry__append_browser(h, self->tree, session_total); - if (len > max_len) - max_len = len; - if (symbol_conf.use_callchain) - hist_entry__append_callchain_browser(h, self->tree, - session_total, idx++); - ++curr_hist; - if (curr_hist % 5) - ui_progress__update(progress, curr_hist); - } - - ui_progress__delete(progress); - - newtGetScreenSize(&cols, &rows); - - if (max_len > cols) - max_len = cols - 3; - - if (!symbol_conf.use_callchain) - newtListboxSetWidth(self->tree, max_len); - - newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), - rows - 5, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - newtFormAddHotKey(self->form, 'A'); - newtFormAddHotKey(self->form, 'a'); - newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - newtFormAddComponents(self->form, self->tree, NULL); - self->selection = newt__symbol_tree_get_current(self->tree); - - return 0; -} - -enum hist_filter { - HIST_FILTER__DSO, - HIST_FILTER__THREAD, -}; - -static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered |= (1 << HIST_FILTER__DSO); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__DSO); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (thread != NULL && h->thread != thread) { - h->filtered |= (1 << HIST_FILTER__THREAD); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__THREAD); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static struct thread *hist_browser__selected_thread(struct hist_browser *self) -{ - int *indexes; - - if (!symbol_conf.use_callchain) - goto out; - - indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); - if (indexes) { - bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; - free(indexes); - if (is_hist_entry) - goto out; - } - return NULL; -out: - return *(struct thread **)(self->selection + 1); -} - -static int hist_browser__title(char *bf, size_t size, const char *input_name, - const struct dso *dso, const struct thread *thread) -{ - int printed = 0; - - if (thread) - printed += snprintf(bf + printed, size - printed, - "Thread: %s(%d)", - (thread->comm_set ? thread->comm : ""), - thread->pid); - if (dso) - printed += snprintf(bf + printed, size - printed, - "%sDSO: %s", thread ? " " : "", - dso->short_name); - return printed ?: snprintf(bf, size, "Report: %s", input_name); -} - -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name) -{ - struct hist_browser *browser = hist_browser__new(); - const struct thread *thread_filter = NULL; - const struct dso *dso_filter = NULL; - struct newtExitStruct es; - char msg[160]; - int err = -1; - - if (browser == NULL) - return -1; - - newtPushHelpLine(helpline); - - hist_browser__title(msg, sizeof(msg), input_name, - dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) - goto out; - - while (1) { - const struct thread *thread; - const struct dso *dso; - char *options[16]; - int nr_options = 0, choice = 0, i, - annotate = -2, zoom_dso = -2, zoom_thread = -2; - - newtFormRun(browser->form, &es); - if (es.reason == NEWT_EXIT_HOTKEY) { - if (toupper(es.u.key) == 'A') - goto do_annotate; - if (es.u.key == NEWT_KEY_ESCAPE || - toupper(es.u.key) == 'Q' || - es.u.key == CTRL('c')) { - if (dialog_yesno("Do you really want to exit?")) - break; - else - continue; - } - } - - if (browser->selection->sym != NULL && - asprintf(&options[nr_options], "Annotate %s", - browser->selection->sym->name) > 0) - annotate = nr_options++; - - thread = hist_browser__selected_thread(browser); - if (thread != NULL && - asprintf(&options[nr_options], "Zoom %s %s(%d) thread", - (thread_filter ? "out of" : "into"), - (thread->comm_set ? thread->comm : ""), - thread->pid) > 0) - zoom_thread = nr_options++; - - dso = browser->selection->map ? browser->selection->map->dso : NULL; - if (dso != NULL && - asprintf(&options[nr_options], "Zoom %s %s DSO", - (dso_filter ? "out of" : "into"), - (dso->kernel ? "the Kernel" : dso->short_name)) > 0) - zoom_dso = nr_options++; - - options[nr_options++] = (char *)"Exit"; - - choice = popup_menu(nr_options, options); - - for (i = 0; i < nr_options - 1; ++i) - free(options[i]); - - if (choice == nr_options - 1) - break; - - if (choice == -1) - continue; -do_annotate: - if (choice == annotate) { - if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { - newtPopHelpLine(); - newtPushHelpLine("No vmlinux file found, can't " - "annotate with just a " - "kallsyms file"); - continue; - } - map_symbol__annotate_browser(browser->selection, input_name); - } else if (choice == zoom_dso) { - if (dso_filter) { - newtPopHelpLine(); - dso_filter = NULL; - } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - newtPushHelpLine(msg); - dso_filter = dso; - } - nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); - hist_browser__title(msg, sizeof(msg), input_name, - dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) - goto out; - } else if (choice == zoom_thread) { - if (thread_filter) { - newtPopHelpLine(); - thread_filter = NULL; - } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s(%d) thread\"", - (thread->comm_set ? thread->comm : ""), - thread->pid); - newtPushHelpLine(msg); - thread_filter = thread; - } - nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); - hist_browser__title(msg, sizeof(msg), input_name, - dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) - goto out; - } - } - err = 0; -out: - hist_browser__delete(browser); - return err; -} - -void setup_browser(void) -{ - if (!isatty(1)) - return; - - use_browser = true; - newtInit(); - newtCls(); - newtPushHelpLine(" "); -} - -void exit_browser(bool wait_for_ok) -{ - if (use_browser) { - if (wait_for_ok) { - char title[] = "Fatal Error", ok[] = "Ok"; - newtWinMessage(title, ok, browser__last_msg); - } - newtFinished(); - } -} diff --git a/trunk/tools/perf/util/parse-events.c b/trunk/tools/perf/util/parse-events.c index bc8b7e614207..05d0c5c2030c 100644 --- a/trunk/tools/perf/util/parse-events.c +++ b/trunk/tools/perf/util/parse-events.c @@ -5,7 +5,6 @@ #include "parse-events.h" #include "exec_cmd.h" #include "string.h" -#include "symbol.h" #include "cache.h" #include "header.h" #include "debugfs.h" @@ -410,6 +409,7 @@ static enum event_result parse_single_tracepoint_event(char *sys_name, const char *evt_name, unsigned int evt_length, + char *flags, struct perf_event_attr *attr, const char **strp) { @@ -418,6 +418,14 @@ parse_single_tracepoint_event(char *sys_name, u64 id; int fd; + if (flags) { + if (!strncmp(flags, "record", strlen(flags))) { + attr->sample_type |= PERF_SAMPLE_RAW; + attr->sample_type |= PERF_SAMPLE_TIME; + attr->sample_type |= PERF_SAMPLE_CPU; + } + } + snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_name, evt_name); @@ -436,13 +444,6 @@ parse_single_tracepoint_event(char *sys_name, attr->type = PERF_TYPE_TRACEPOINT; *strp = evt_name + evt_length; - attr->sample_type |= PERF_SAMPLE_RAW; - attr->sample_type |= PERF_SAMPLE_TIME; - attr->sample_type |= PERF_SAMPLE_CPU; - - attr->sample_period = 1; - - return EVT_HANDLED; } @@ -531,7 +532,8 @@ static enum event_result parse_tracepoint_event(const char **strp, flags); } else return parse_single_tracepoint_event(sys_name, evt_name, - evt_length, attr, strp); + evt_length, flags, + attr, strp); } static enum event_result @@ -654,10 +656,6 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) return EVT_FAILED; n = hex2u64(str + 1, &config); if (n > 0) { - if (str[n+1] == 'p') { - attr->precise = 1; - n++; - } *strp = str + n + 1; attr->type = PERF_TYPE_RAW; attr->config = config; @@ -936,7 +934,7 @@ void print_events(void) printf("\n"); printf(" %-42s [%s]\n", - "rNNN (NNN=)", event_type_descriptors[PERF_TYPE_RAW]); + "rNNN", event_type_descriptors[PERF_TYPE_RAW]); printf("\n"); printf(" %-42s [%s]\n", diff --git a/trunk/tools/perf/util/parse-events.h b/trunk/tools/perf/util/parse-events.h index fc4ab3fe877a..b8c1f64bc935 100644 --- a/trunk/tools/perf/util/parse-events.h +++ b/trunk/tools/perf/util/parse-events.h @@ -13,7 +13,6 @@ struct tracepoint_path { }; extern struct tracepoint_path *tracepoint_id_to_path(u64 config); -extern bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events); extern int nr_counters; diff --git a/trunk/tools/perf/util/parse-options.c b/trunk/tools/perf/util/parse-options.c index ed887642460c..efebd5b476b3 100644 --- a/trunk/tools/perf/util/parse-options.c +++ b/trunk/tools/perf/util/parse-options.c @@ -49,7 +49,6 @@ static int get_value(struct parse_opt_ctx_t *p, break; /* FALLTHROUGH */ case OPTION_BOOLEAN: - case OPTION_INCR: case OPTION_BIT: case OPTION_SET_INT: case OPTION_SET_PTR: @@ -74,10 +73,6 @@ static int get_value(struct parse_opt_ctx_t *p, return 0; case OPTION_BOOLEAN: - *(bool *)opt->value = unset ? false : true; - return 0; - - case OPTION_INCR: *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; return 0; @@ -483,7 +478,6 @@ int usage_with_options_internal(const char * const *usagestr, case OPTION_GROUP: case OPTION_BIT: case OPTION_BOOLEAN: - case OPTION_INCR: case OPTION_SET_INT: case OPTION_SET_PTR: case OPTION_LONG: @@ -506,7 +500,6 @@ int usage_with_options_internal(const char * const *usagestr, void usage_with_options(const char * const *usagestr, const struct option *opts) { - exit_browser(false); usage_with_options_internal(usagestr, opts, 0); exit(129); } diff --git a/trunk/tools/perf/util/parse-options.h b/trunk/tools/perf/util/parse-options.h index b2da725f102a..948805af43c2 100644 --- a/trunk/tools/perf/util/parse-options.h +++ b/trunk/tools/perf/util/parse-options.h @@ -8,8 +8,7 @@ enum parse_opt_type { OPTION_GROUP, /* options with no arguments */ OPTION_BIT, - OPTION_BOOLEAN, - OPTION_INCR, + OPTION_BOOLEAN, /* _INCR would have been a better name */ OPTION_SET_INT, OPTION_SET_PTR, /* options with arguments (usually) */ @@ -96,7 +95,6 @@ struct option { #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } diff --git a/trunk/tools/perf/util/probe-event.c b/trunk/tools/perf/util/probe-event.c index 914c67095d96..7c004b6ef24f 100644 --- a/trunk/tools/perf/util/probe-event.c +++ b/trunk/tools/perf/util/probe-event.c @@ -33,27 +33,20 @@ #include #undef _GNU_SOURCE -#include "util.h" #include "event.h" #include "string.h" #include "strlist.h" #include "debug.h" #include "cache.h" #include "color.h" -#include "symbol.h" -#include "thread.h" -#include "debugfs.h" -#include "trace-event.h" /* For __unused */ +#include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" -#include "probe-finder.h" #define MAX_CMDLEN 256 #define MAX_PROBE_ARGS 128 #define PERFPROBE_GROUP "probe" -bool probe_event_dry_run; /* Dry run flag */ - -#define semantic_error(msg ...) pr_err("Semantic error :" msg) +#define semantic_error(msg ...) die("Semantic error :" msg) /* If there is no space to write, returns -E2BIG. */ static int e_snprintf(char *str, size_t size, const char *format, ...) @@ -71,275 +64,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } -static char *synthesize_perf_probe_point(struct perf_probe_point *pp); -static struct machine machine; - -/* Initialize symbol maps and path of vmlinux */ -static int init_vmlinux(void) -{ - struct dso *kernel; - int ret; - - symbol_conf.sort_by_name = true; - if (symbol_conf.vmlinux_name == NULL) - symbol_conf.try_vmlinux_path = true; - else - pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); - ret = symbol__init(); - if (ret < 0) { - pr_debug("Failed to init symbol map.\n"); - goto out; - } - - ret = machine__init(&machine, "/", 0); - if (ret < 0) - goto out; - - kernel = dso__new_kernel(symbol_conf.vmlinux_name); - if (kernel == NULL) - die("Failed to create kernel dso."); - - ret = __machine__create_kernel_maps(&machine, kernel); - if (ret < 0) - pr_debug("Failed to create kernel maps.\n"); - -out: - if (ret < 0) - pr_warning("Failed to init vmlinux path.\n"); - return ret; -} - -#ifdef DWARF_SUPPORT -static int open_vmlinux(void) -{ - if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { - pr_debug("Failed to load kernel map.\n"); - return -EINVAL; - } - pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); - return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); -} - -/* Convert trace point to probe point with debuginfo */ -static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, - struct perf_probe_point *pp) -{ - struct symbol *sym; - int fd, ret = -ENOENT; - - sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], - tp->symbol, NULL); - if (sym) { - fd = open_vmlinux(); - if (fd >= 0) { - ret = find_perf_probe_point(fd, - sym->start + tp->offset, pp); - close(fd); - } - } - if (ret <= 0) { - pr_debug("Failed to find corresponding probes from " - "debuginfo. Use kprobe event information.\n"); - pp->function = strdup(tp->symbol); - if (pp->function == NULL) - return -ENOMEM; - pp->offset = tp->offset; - } - pp->retprobe = tp->retprobe; - - return 0; -} - -/* Try to find perf_probe_event with debuginfo */ -static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, - int max_tevs) -{ - bool need_dwarf = perf_probe_event_need_dwarf(pev); - int fd, ntevs; - - fd = open_vmlinux(); - if (fd < 0) { - if (need_dwarf) { - pr_warning("Failed to open debuginfo file.\n"); - return fd; - } - pr_debug("Could not open vmlinux. Try to use symbols.\n"); - return 0; - } - - /* Searching trace events corresponding to probe event */ - ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs); - close(fd); - - if (ntevs > 0) { /* Succeeded to find trace events */ - pr_debug("find %d kprobe_trace_events.\n", ntevs); - return ntevs; - } - - if (ntevs == 0) { /* No error but failed to find probe point. */ - pr_warning("Probe point '%s' not found.\n", - synthesize_perf_probe_point(&pev->point)); - return -ENOENT; - } - /* Error path : ntevs < 0 */ - pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); - if (ntevs == -EBADF) { - pr_warning("Warning: No dwarf info found in the vmlinux - " - "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); - if (!need_dwarf) { - pr_debug("Trying to use symbols.\nn"); - return 0; - } - } - return ntevs; -} - -#define LINEBUF_SIZE 256 -#define NR_ADDITIONAL_LINES 2 - -static int show_one_line(FILE *fp, int l, bool skip, bool show_num) -{ - char buf[LINEBUF_SIZE]; - const char *color = PERF_COLOR_BLUE; - - if (fgets(buf, LINEBUF_SIZE, fp) == NULL) - goto error; - if (!skip) { - if (show_num) - fprintf(stdout, "%7d %s", l, buf); - else - color_fprintf(stdout, color, " %s", buf); - } - - while (strlen(buf) == LINEBUF_SIZE - 1 && - buf[LINEBUF_SIZE - 2] != '\n') { - if (fgets(buf, LINEBUF_SIZE, fp) == NULL) - goto error; - if (!skip) { - if (show_num) - fprintf(stdout, "%s", buf); - else - color_fprintf(stdout, color, "%s", buf); - } - } - - return 0; -error: - if (feof(fp)) - pr_warning("Source file is shorter than expected.\n"); - else - pr_warning("File read error: %s\n", strerror(errno)); - - return -1; -} - -/* - * Show line-range always requires debuginfo to find source file and - * line number. - */ -int show_line_range(struct line_range *lr) -{ - int l = 1; - struct line_node *ln; - FILE *fp; - int fd, ret; - - /* Search a line range */ - ret = init_vmlinux(); - if (ret < 0) - return ret; - - fd = open_vmlinux(); - if (fd < 0) { - pr_warning("Failed to open debuginfo file.\n"); - return fd; - } - - ret = find_line_range(fd, lr); - close(fd); - if (ret == 0) { - pr_warning("Specified source line is not found.\n"); - return -ENOENT; - } else if (ret < 0) { - pr_warning("Debuginfo analysis failed. (%d)\n", ret); - return ret; - } - - setup_pager(); - - if (lr->function) - fprintf(stdout, "<%s:%d>\n", lr->function, - lr->start - lr->offset); - else - fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); - - fp = fopen(lr->path, "r"); - if (fp == NULL) { - pr_warning("Failed to open %s: %s\n", lr->path, - strerror(errno)); - return -errno; - } - /* Skip to starting line number */ - while (l < lr->start && ret >= 0) - ret = show_one_line(fp, l++, true, false); - if (ret < 0) - goto end; - - list_for_each_entry(ln, &lr->line_list, list) { - while (ln->line > l && ret >= 0) - ret = show_one_line(fp, (l++) - lr->offset, - false, false); - if (ret >= 0) - ret = show_one_line(fp, (l++) - lr->offset, - false, true); - if (ret < 0) - goto end; - } - - if (lr->end == INT_MAX) - lr->end = l + NR_ADDITIONAL_LINES; - while (l <= lr->end && !feof(fp) && ret >= 0) - ret = show_one_line(fp, (l++) - lr->offset, false, false); -end: - fclose(fp); - return ret; -} - -#else /* !DWARF_SUPPORT */ - -static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, - struct perf_probe_point *pp) -{ - pp->function = strdup(tp->symbol); - if (pp->function == NULL) - return -ENOMEM; - pp->offset = tp->offset; - pp->retprobe = tp->retprobe; - - return 0; -} - -static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs __unused, - int max_tevs __unused) -{ - if (perf_probe_event_need_dwarf(pev)) { - pr_warning("Debuginfo-analysis is not supported.\n"); - return -ENOSYS; - } - return 0; -} - -int show_line_range(struct line_range *lr __unused) -{ - pr_warning("Debuginfo-analysis is not supported.\n"); - return -ENOSYS; -} - -#endif - -int parse_line_range_desc(const char *arg, struct line_range *lr) +void parse_line_range_desc(const char *arg, struct line_range *lr) { const char *ptr; char *tmp; @@ -350,45 +75,29 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) */ ptr = strchr(arg, ':'); if (ptr) { - lr->start = (int)strtoul(ptr + 1, &tmp, 0); - if (*tmp == '+') { - lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); - lr->end--; /* - * Adjust the number of lines here. - * If the number of lines == 1, the - * the end of line should be equal to - * the start of line. - */ - } else if (*tmp == '-') - lr->end = (int)strtoul(tmp + 1, &tmp, 0); + lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); + if (*tmp == '+') + lr->end = lr->start + (unsigned int)strtoul(tmp + 1, + &tmp, 0); + else if (*tmp == '-') + lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); else - lr->end = INT_MAX; - pr_debug("Line range is %d to %d\n", lr->start, lr->end); - if (lr->start > lr->end) { + lr->end = 0; + pr_debug("Line range is %u to %u\n", lr->start, lr->end); + if (lr->end && lr->start > lr->end) semantic_error("Start line must be smaller" - " than end line.\n"); - return -EINVAL; - } - if (*tmp != '\0') { - semantic_error("Tailing with invalid character '%d'.\n", + " than end line."); + if (*tmp != '\0') + semantic_error("Tailing with invalid character '%d'.", *tmp); - return -EINVAL; - } tmp = strndup(arg, (ptr - arg)); - } else { + } else tmp = strdup(arg); - lr->end = INT_MAX; - } - - if (tmp == NULL) - return -ENOMEM; if (strchr(tmp, '.')) lr->file = tmp; else lr->function = tmp; - - return 0; } /* Check the name is good for event/group */ @@ -404,9 +113,8 @@ static bool check_event_name(const char *name) } /* Parse probepoint definition. */ -static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) +static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) { - struct perf_probe_point *pp = &pev->point; char *ptr, *tmp; char c, nc = 0; /* @@ -421,19 +129,13 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) if (ptr && *ptr == '=') { /* Event name */ *ptr = '\0'; tmp = ptr + 1; - if (strchr(arg, ':')) { - semantic_error("Group name is not supported yet.\n"); - return -ENOTSUP; - } - if (!check_event_name(arg)) { + ptr = strchr(arg, ':'); + if (ptr) /* Group name is not supported yet. */ + semantic_error("Group name is not supported yet."); + if (!check_event_name(arg)) semantic_error("%s is bad for event name -it must " - "follow C symbol-naming rule.\n", arg); - return -EINVAL; - } - pev->event = strdup(arg); - if (pev->event == NULL) - return -ENOMEM; - pev->group = NULL; + "follow C symbol-naming rule.", arg); + pp->event = strdup(arg); arg = tmp; } @@ -443,15 +145,12 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) *ptr++ = '\0'; } - tmp = strdup(arg); - if (tmp == NULL) - return -ENOMEM; - /* Check arg is function or file and copy it */ - if (strchr(tmp, '.')) /* File */ - pp->file = tmp; + if (strchr(arg, '.')) /* File */ + pp->file = strdup(arg); else /* Function */ - pp->function = tmp; + pp->function = strdup(arg); + DIE_IF(pp->file == NULL && pp->function == NULL); /* Parse other options */ while (ptr) { @@ -459,8 +158,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) c = nc; if (c == ';') { /* Lazy pattern must be the last part */ pp->lazy_line = strdup(arg); - if (pp->lazy_line == NULL) - return -ENOMEM; break; } ptr = strpbrk(arg, ";:+@%"); @@ -471,658 +168,266 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) switch (c) { case ':': /* Line number */ pp->line = strtoul(arg, &tmp, 0); - if (*tmp != '\0') { + if (*tmp != '\0') semantic_error("There is non-digit char" - " in line number.\n"); - return -EINVAL; - } + " in line number."); break; case '+': /* Byte offset from a symbol */ pp->offset = strtoul(arg, &tmp, 0); - if (*tmp != '\0') { + if (*tmp != '\0') semantic_error("There is non-digit character" - " in offset.\n"); - return -EINVAL; - } + " in offset."); break; case '@': /* File name */ - if (pp->file) { - semantic_error("SRC@SRC is not allowed.\n"); - return -EINVAL; - } + if (pp->file) + semantic_error("SRC@SRC is not allowed."); pp->file = strdup(arg); - if (pp->file == NULL) - return -ENOMEM; + DIE_IF(pp->file == NULL); break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { pp->retprobe = 1; - } else { /* Others not supported yet */ - semantic_error("%%%s is not supported.\n", arg); - return -ENOTSUP; - } + } else /* Others not supported yet */ + semantic_error("%%%s is not supported.", arg); break; - default: /* Buggy case */ - pr_err("This program has a bug at %s:%d.\n", - __FILE__, __LINE__); - return -ENOTSUP; + default: + DIE_IF("Program has a bug."); break; } } /* Exclusion check */ - if (pp->lazy_line && pp->line) { + if (pp->lazy_line && pp->line) semantic_error("Lazy pattern can't be used with line number."); - return -EINVAL; - } - if (pp->lazy_line && pp->offset) { + if (pp->lazy_line && pp->offset) semantic_error("Lazy pattern can't be used with offset."); - return -EINVAL; - } - if (pp->line && pp->offset) { + if (pp->line && pp->offset) semantic_error("Offset can't be used with line number."); - return -EINVAL; - } - if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { + if (!pp->line && !pp->lazy_line && pp->file && !pp->function) semantic_error("File always requires line number or " "lazy pattern."); - return -EINVAL; - } - if (pp->offset && !pp->function) { + if (pp->offset && !pp->function) semantic_error("Offset requires an entry function."); - return -EINVAL; - } - if (pp->retprobe && !pp->function) { + if (pp->retprobe && !pp->function) semantic_error("Return probe requires an entry function."); - return -EINVAL; - } - if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { + if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) semantic_error("Offset/Line/Lazy pattern can't be used with " "return probe."); - return -EINVAL; - } - pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", + pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", pp->function, pp->file, pp->line, pp->offset, pp->retprobe, pp->lazy_line); - return 0; } -/* Parse perf-probe event argument */ -static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) +/* Parse perf-probe event definition */ +void parse_perf_probe_event(const char *str, struct probe_point *pp, + bool *need_dwarf) { - char *tmp; - struct perf_probe_arg_field **fieldp; - - pr_debug("parsing arg: %s into ", str); + char **argv; + int argc, i; - tmp = strchr(str, '='); - if (tmp) { - arg->name = strndup(str, tmp - str); - if (arg->name == NULL) - return -ENOMEM; - pr_debug("name:%s ", arg->name); - str = tmp + 1; - } + *need_dwarf = false; - tmp = strchr(str, ':'); - if (tmp) { /* Type setting */ - *tmp = '\0'; - arg->type = strdup(tmp + 1); - if (arg->type == NULL) - return -ENOMEM; - pr_debug("type:%s ", arg->type); - } + argv = argv_split(str, &argc); + if (!argv) + die("argv_split failed."); + if (argc > MAX_PROBE_ARGS + 1) + semantic_error("Too many arguments"); - tmp = strpbrk(str, "-."); - if (!is_c_varname(str) || !tmp) { - /* A variable, register, symbol or special value */ - arg->var = strdup(str); - if (arg->var == NULL) - return -ENOMEM; - pr_debug("%s\n", arg->var); - return 0; - } - - /* Structure fields */ - arg->var = strndup(str, tmp - str); - if (arg->var == NULL) - return -ENOMEM; - pr_debug("%s, ", arg->var); - fieldp = &arg->field; - - do { - *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); - if (*fieldp == NULL) - return -ENOMEM; - if (*tmp == '.') { - str = tmp + 1; - (*fieldp)->ref = false; - } else if (tmp[1] == '>') { - str = tmp + 2; - (*fieldp)->ref = true; - } else { - semantic_error("Argument parse error: %s\n", str); - return -EINVAL; - } - - tmp = strpbrk(str, "-."); - if (tmp) { - (*fieldp)->name = strndup(str, tmp - str); - if ((*fieldp)->name == NULL) - return -ENOMEM; - pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); - fieldp = &(*fieldp)->next; - } - } while (tmp); - (*fieldp)->name = strdup(str); - if ((*fieldp)->name == NULL) - return -ENOMEM; - pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); - - /* If no name is specified, set the last field name */ - if (!arg->name) { - arg->name = strdup((*fieldp)->name); - if (arg->name == NULL) - return -ENOMEM; - } - return 0; -} - -/* Parse perf-probe event command */ -int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) -{ - char **argv; - int argc, i, ret = 0; - - argv = argv_split(cmd, &argc); - if (!argv) { - pr_debug("Failed to split arguments.\n"); - return -ENOMEM; - } - if (argc - 1 > MAX_PROBE_ARGS) { - semantic_error("Too many probe arguments (%d).\n", argc - 1); - ret = -ERANGE; - goto out; - } /* Parse probe point */ - ret = parse_perf_probe_point(argv[0], pev); - if (ret < 0) - goto out; + parse_perf_probe_probepoint(argv[0], pp); + if (pp->file || pp->line || pp->lazy_line) + *need_dwarf = true; /* Copy arguments and ensure return probe has no C argument */ - pev->nargs = argc - 1; - pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); - if (pev->args == NULL) { - ret = -ENOMEM; - goto out; - } - for (i = 0; i < pev->nargs && ret >= 0; i++) { - ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); - if (ret >= 0 && - is_c_varname(pev->args[i].var) && pev->point.retprobe) { - semantic_error("You can't specify local variable for" - " kretprobe.\n"); - ret = -EINVAL; + pp->nr_args = argc - 1; + pp->args = zalloc(sizeof(char *) * pp->nr_args); + for (i = 0; i < pp->nr_args; i++) { + pp->args[i] = strdup(argv[i + 1]); + if (!pp->args[i]) + die("Failed to copy argument."); + if (is_c_varname(pp->args[i])) { + if (pp->retprobe) + semantic_error("You can't specify local" + " variable for kretprobe"); + *need_dwarf = true; } } -out: - argv_free(argv); - return ret; -} - -/* Return true if this perf_probe_event requires debuginfo */ -bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) -{ - int i; - - if (pev->point.file || pev->point.line || pev->point.lazy_line) - return true; - - for (i = 0; i < pev->nargs; i++) - if (is_c_varname(pev->args[i].var)) - return true; - - return false; + argv_free(argv); } /* Parse kprobe_events event into struct probe_point */ -int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) +void parse_trace_kprobe_event(const char *str, struct probe_point *pp) { - struct kprobe_trace_point *tp = &tev->point; char pr; char *p; int ret, i, argc; char **argv; - pr_debug("Parsing kprobe_events: %s\n", cmd); - argv = argv_split(cmd, &argc); - if (!argv) { - pr_debug("Failed to split arguments.\n"); - return -ENOMEM; - } - if (argc < 2) { - semantic_error("Too few probe arguments.\n"); - ret = -ERANGE; - goto out; - } + pr_debug("Parsing kprobe_events: %s\n", str); + argv = argv_split(str, &argc); + if (!argv) + die("argv_split failed."); + if (argc < 2) + semantic_error("Too less arguments."); /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", - &pr, (float *)(void *)&tev->group, - (float *)(void *)&tev->event); - if (ret != 3) { - semantic_error("Failed to parse event name: %s\n", argv[0]); - ret = -EINVAL; - goto out; - } - pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); + &pr, (float *)(void *)&pp->group, + (float *)(void *)&pp->event); + if (ret != 3) + semantic_error("Failed to parse event name: %s", argv[0]); + pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); - tp->retprobe = (pr == 'r'); + pp->retprobe = (pr == 'r'); /* Scan function name and offset */ - ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, - &tp->offset); + ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, + &pp->offset); if (ret == 1) - tp->offset = 0; + pp->offset = 0; - tev->nargs = argc - 2; - tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); - if (tev->args == NULL) { - ret = -ENOMEM; - goto out; - } - for (i = 0; i < tev->nargs; i++) { + /* kprobe_events doesn't have this information */ + pp->line = 0; + pp->file = NULL; + + pp->nr_args = argc - 2; + pp->args = zalloc(sizeof(char *) * pp->nr_args); + for (i = 0; i < pp->nr_args; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ - *p++ = '\0'; - else - p = argv[i + 2]; - tev->args[i].name = strdup(argv[i + 2]); - /* TODO: parse regs and offset */ - tev->args[i].value = strdup(p); - if (tev->args[i].name == NULL || tev->args[i].value == NULL) { - ret = -ENOMEM; - goto out; - } + *p = '\0'; + pp->args[i] = strdup(argv[i + 2]); + if (!pp->args[i]) + die("Failed to copy argument."); } - ret = 0; -out: + argv_free(argv); - return ret; } -/* Compose only probe arg */ -int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) +/* Synthesize only probe point (not argument) */ +int synthesize_perf_probe_point(struct probe_point *pp) { - struct perf_probe_arg_field *field = pa->field; + char *buf; + char offs[64] = "", line[64] = ""; int ret; - char *tmp = buf; - if (pa->name && pa->var) - ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); - else - ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); - if (ret <= 0) - goto error; - tmp += ret; - len -= ret; - - while (field) { - ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", - field->name); - if (ret <= 0) - goto error; - tmp += ret; - len -= ret; - field = field->next; - } - - if (pa->type) { - ret = e_snprintf(tmp, len, ":%s", pa->type); - if (ret <= 0) - goto error; - tmp += ret; - len -= ret; - } - - return tmp - buf; -error: - pr_debug("Failed to synthesize perf probe argument: %s", - strerror(-ret)); - return ret; -} - -/* Compose only probe point (not argument) */ -static char *synthesize_perf_probe_point(struct perf_probe_point *pp) -{ - char *buf, *tmp; - char offs[32] = "", line[32] = "", file[32] = ""; - int ret, len; - - buf = zalloc(MAX_CMDLEN); - if (buf == NULL) { - ret = -ENOMEM; - goto error; - } + pp->probes[0] = buf = zalloc(MAX_CMDLEN); + pp->found = 1; + if (!buf) + die("Failed to allocate memory by zalloc."); if (pp->offset) { - ret = e_snprintf(offs, 32, "+%lu", pp->offset); + ret = e_snprintf(offs, 64, "+%d", pp->offset); if (ret <= 0) goto error; } if (pp->line) { - ret = e_snprintf(line, 32, ":%d", pp->line); - if (ret <= 0) - goto error; - } - if (pp->file) { - len = strlen(pp->file) - 31; - if (len < 0) - len = 0; - tmp = strchr(pp->file + len, '/'); - if (!tmp) - tmp = pp->file + len; - ret = e_snprintf(file, 32, "@%s", tmp + 1); + ret = e_snprintf(line, 64, ":%d", pp->line); if (ret <= 0) goto error; } if (pp->function) - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, - offs, pp->retprobe ? "%return" : "", line, - file); + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, + offs, pp->retprobe ? "%return" : "", line); else - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); - if (ret <= 0) - goto error; - - return buf; + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); + if (ret <= 0) { error: - pr_debug("Failed to synthesize perf probe point: %s", - strerror(-ret)); - if (buf) - free(buf); - return NULL; + free(pp->probes[0]); + pp->probes[0] = NULL; + pp->found = 0; + } + return ret; } -#if 0 -char *synthesize_perf_probe_command(struct perf_probe_event *pev) +int synthesize_perf_probe_event(struct probe_point *pp) { char *buf; int i, len, ret; - buf = synthesize_perf_probe_point(&pev->point); - if (!buf) - return NULL; + len = synthesize_perf_probe_point(pp); + if (len < 0) + return 0; - len = strlen(buf); - for (i = 0; i < pev->nargs; i++) { + buf = pp->probes[0]; + for (i = 0; i < pp->nr_args; i++) { ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pev->args[i].name); - if (ret <= 0) { - free(buf); - return NULL; - } + pp->args[i]); + if (ret <= 0) + goto error; len += ret; } + pp->found = 1; - return buf; -} -#endif - -static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, - char **buf, size_t *buflen, - int depth) -{ - int ret; - if (ref->next) { - depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, - buflen, depth + 1); - if (depth < 0) - goto out; - } - - ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); - if (ret < 0) - depth = ret; - else { - *buf += ret; - *buflen -= ret; - } -out: - return depth; - -} - -static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, - char *buf, size_t buflen) -{ - int ret, depth = 0; - char *tmp = buf; - - /* Argument name or separator */ - if (arg->name) - ret = e_snprintf(buf, buflen, " %s=", arg->name); - else - ret = e_snprintf(buf, buflen, " "); - if (ret < 0) - return ret; - buf += ret; - buflen -= ret; - - /* Dereferencing arguments */ - if (arg->ref) { - depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, - &buflen, 1); - if (depth < 0) - return depth; - } - - /* Print argument value */ - ret = e_snprintf(buf, buflen, "%s", arg->value); - if (ret < 0) - return ret; - buf += ret; - buflen -= ret; - - /* Closing */ - while (depth--) { - ret = e_snprintf(buf, buflen, ")"); - if (ret < 0) - return ret; - buf += ret; - buflen -= ret; - } - /* Print argument type */ - if (arg->type) { - ret = e_snprintf(buf, buflen, ":%s", arg->type); - if (ret <= 0) - return ret; - buf += ret; - } + return pp->found; +error: + free(pp->probes[0]); + pp->probes[0] = NULL; - return buf - tmp; + return ret; } -char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) +int synthesize_trace_kprobe_event(struct probe_point *pp) { - struct kprobe_trace_point *tp = &tev->point; char *buf; int i, len, ret; - buf = zalloc(MAX_CMDLEN); - if (buf == NULL) - return NULL; - - len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", - tp->retprobe ? 'r' : 'p', - tev->group, tev->event, - tp->symbol, tp->offset); - if (len <= 0) + pp->probes[0] = buf = zalloc(MAX_CMDLEN); + if (!buf) + die("Failed to allocate memory by zalloc."); + ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); + if (ret <= 0) goto error; + len = ret; - for (i = 0; i < tev->nargs; i++) { - ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, - MAX_CMDLEN - len); + for (i = 0; i < pp->nr_args; i++) { + ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", + pp->args[i]); if (ret <= 0) goto error; len += ret; } + pp->found = 1; - return buf; + return pp->found; error: - free(buf); - return NULL; -} - -int convert_to_perf_probe_event(struct kprobe_trace_event *tev, - struct perf_probe_event *pev) -{ - char buf[64] = ""; - int i, ret; - - /* Convert event/group name */ - pev->event = strdup(tev->event); - pev->group = strdup(tev->group); - if (pev->event == NULL || pev->group == NULL) - return -ENOMEM; - - /* Convert trace_point to probe_point */ - ret = convert_to_perf_probe_point(&tev->point, &pev->point); - if (ret < 0) - return ret; - - /* Convert trace_arg to probe_arg */ - pev->nargs = tev->nargs; - pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); - if (pev->args == NULL) - return -ENOMEM; - for (i = 0; i < tev->nargs && ret >= 0; i++) { - if (tev->args[i].name) - pev->args[i].name = strdup(tev->args[i].name); - else { - ret = synthesize_kprobe_trace_arg(&tev->args[i], - buf, 64); - pev->args[i].name = strdup(buf); - } - if (pev->args[i].name == NULL && ret >= 0) - ret = -ENOMEM; - } - - if (ret < 0) - clear_perf_probe_event(pev); + free(pp->probes[0]); + pp->probes[0] = NULL; return ret; } -void clear_perf_probe_event(struct perf_probe_event *pev) -{ - struct perf_probe_point *pp = &pev->point; - struct perf_probe_arg_field *field, *next; - int i; - - if (pev->event) - free(pev->event); - if (pev->group) - free(pev->group); - if (pp->file) - free(pp->file); - if (pp->function) - free(pp->function); - if (pp->lazy_line) - free(pp->lazy_line); - for (i = 0; i < pev->nargs; i++) { - if (pev->args[i].name) - free(pev->args[i].name); - if (pev->args[i].var) - free(pev->args[i].var); - if (pev->args[i].type) - free(pev->args[i].type); - field = pev->args[i].field; - while (field) { - next = field->next; - if (field->name) - free(field->name); - free(field); - field = next; - } - } - if (pev->args) - free(pev->args); - memset(pev, 0, sizeof(*pev)); -} - -void clear_kprobe_trace_event(struct kprobe_trace_event *tev) -{ - struct kprobe_trace_arg_ref *ref, *next; - int i; - - if (tev->event) - free(tev->event); - if (tev->group) - free(tev->group); - if (tev->point.symbol) - free(tev->point.symbol); - for (i = 0; i < tev->nargs; i++) { - if (tev->args[i].name) - free(tev->args[i].name); - if (tev->args[i].value) - free(tev->args[i].value); - if (tev->args[i].type) - free(tev->args[i].type); - ref = tev->args[i].ref; - while (ref) { - next = ref->next; - free(ref); - ref = next; - } - } - if (tev->args) - free(tev->args); - memset(tev, 0, sizeof(*tev)); -} - -static int open_kprobe_events(bool readwrite) +static int open_kprobe_events(int flags, int mode) { char buf[PATH_MAX]; - const char *__debugfs; int ret; - __debugfs = debugfs_find_mountpoint(); - if (__debugfs == NULL) { - pr_warning("Debugfs is not mounted.\n"); - return -ENOENT; - } - - ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); - if (ret >= 0) { - pr_debug("Opening %s write=%d\n", buf, readwrite); - if (readwrite && !probe_event_dry_run) - ret = open(buf, O_RDWR, O_APPEND); - else - ret = open(buf, O_RDONLY, 0); - } + ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); + if (ret < 0) + die("Failed to make kprobe_events path."); + ret = open(buf, flags, mode); if (ret < 0) { if (errno == ENOENT) - pr_warning("kprobe_events file does not exist - please" - " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); + die("kprobe_events file does not exist -" + " please rebuild with CONFIG_KPROBE_EVENT."); else - pr_warning("Failed to open kprobe_events file: %s\n", - strerror(errno)); + die("Could not open kprobe_events file: %s", + strerror(errno)); } return ret; } /* Get raw string list of current kprobe_events */ -static struct strlist *get_kprobe_trace_command_rawlist(int fd) +static struct strlist *get_trace_kprobe_event_rawlist(int fd) { int ret, idx; FILE *fp; @@ -1142,486 +447,271 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd) if (p[idx] == '\n') p[idx] = '\0'; ret = strlist__add(sl, buf); - if (ret < 0) { - pr_debug("strlist__add failed: %s\n", strerror(-ret)); - strlist__delete(sl); - return NULL; - } + if (ret < 0) + die("strlist__add failed: %s", strerror(-ret)); } fclose(fp); return sl; } +/* Free and zero clear probe_point */ +static void clear_probe_point(struct probe_point *pp) +{ + int i; + + if (pp->event) + free(pp->event); + if (pp->group) + free(pp->group); + if (pp->function) + free(pp->function); + if (pp->file) + free(pp->file); + if (pp->lazy_line) + free(pp->lazy_line); + for (i = 0; i < pp->nr_args; i++) + free(pp->args[i]); + if (pp->args) + free(pp->args); + for (i = 0; i < pp->found; i++) + free(pp->probes[i]); + memset(pp, 0, sizeof(*pp)); +} + /* Show an event */ -static int show_perf_probe_event(struct perf_probe_event *pev) +static void show_perf_probe_event(const char *event, const char *place, + struct probe_point *pp) { int i, ret; char buf[128]; - char *place; - - /* Synthesize only event probe point */ - place = synthesize_perf_probe_point(&pev->point); - if (!place) - return -EINVAL; - ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); + ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); if (ret < 0) - return ret; - - printf(" %-20s (on %s", buf, place); + die("Failed to copy event: %s", strerror(-ret)); + printf(" %-40s (on %s", buf, place); - if (pev->nargs > 0) { + if (pp->nr_args > 0) { printf(" with"); - for (i = 0; i < pev->nargs; i++) { - ret = synthesize_perf_probe_arg(&pev->args[i], - buf, 128); - if (ret < 0) - break; - printf(" %s", buf); - } + for (i = 0; i < pp->nr_args; i++) + printf(" %s", pp->args[i]); } printf(")\n"); - free(place); - return ret; } /* List up current perf-probe events */ -int show_perf_probe_events(void) +void show_perf_probe_events(void) { - int fd, ret; - struct kprobe_trace_event tev; - struct perf_probe_event pev; + int fd; + struct probe_point pp; struct strlist *rawlist; struct str_node *ent; setup_pager(); - ret = init_vmlinux(); - if (ret < 0) - return ret; - - memset(&tev, 0, sizeof(tev)); - memset(&pev, 0, sizeof(pev)); + memset(&pp, 0, sizeof(pp)); - fd = open_kprobe_events(false); - if (fd < 0) - return fd; - - rawlist = get_kprobe_trace_command_rawlist(fd); + fd = open_kprobe_events(O_RDONLY, 0); + rawlist = get_trace_kprobe_event_rawlist(fd); close(fd); - if (!rawlist) - return -ENOENT; strlist__for_each(ent, rawlist) { - ret = parse_kprobe_trace_command(ent->s, &tev); - if (ret >= 0) { - ret = convert_to_perf_probe_event(&tev, &pev); - if (ret >= 0) - ret = show_perf_probe_event(&pev); - } - clear_perf_probe_event(&pev); - clear_kprobe_trace_event(&tev); - if (ret < 0) - break; + parse_trace_kprobe_event(ent->s, &pp); + /* Synthesize only event probe point */ + synthesize_perf_probe_point(&pp); + /* Show an event */ + show_perf_probe_event(pp.event, pp.probes[0], &pp); + clear_probe_point(&pp); } - strlist__delete(rawlist); - return ret; + strlist__delete(rawlist); } /* Get current perf-probe event names */ -static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) +static struct strlist *get_perf_event_names(int fd, bool include_group) { char buf[128]; struct strlist *sl, *rawlist; struct str_node *ent; - struct kprobe_trace_event tev; - int ret = 0; + struct probe_point pp; - memset(&tev, 0, sizeof(tev)); + memset(&pp, 0, sizeof(pp)); + rawlist = get_trace_kprobe_event_rawlist(fd); - rawlist = get_kprobe_trace_command_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - ret = parse_kprobe_trace_command(ent->s, &tev); - if (ret < 0) - break; + parse_trace_kprobe_event(ent->s, &pp); if (include_group) { - ret = e_snprintf(buf, 128, "%s:%s", tev.group, - tev.event); - if (ret >= 0) - ret = strlist__add(sl, buf); + if (e_snprintf(buf, 128, "%s:%s", pp.group, + pp.event) < 0) + die("Failed to copy group:event name."); + strlist__add(sl, buf); } else - ret = strlist__add(sl, tev.event); - clear_kprobe_trace_event(&tev); - if (ret < 0) - break; + strlist__add(sl, pp.event); + clear_probe_point(&pp); } + strlist__delete(rawlist); - if (ret < 0) { - strlist__delete(sl); - return NULL; - } return sl; } -static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) +static void write_trace_kprobe_event(int fd, const char *buf) { - int ret = 0; - char *buf = synthesize_kprobe_trace_command(tev); - - if (!buf) { - pr_debug("Failed to synthesize kprobe trace event.\n"); - return -EINVAL; - } + int ret; pr_debug("Writing event: %s\n", buf); - if (!probe_event_dry_run) { - ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - pr_warning("Failed to write event: %s\n", - strerror(errno)); - } - free(buf); - return ret; + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to write event: %s", strerror(errno)); } -static int get_new_event_name(char *buf, size_t len, const char *base, - struct strlist *namelist, bool allow_suffix) +static void get_new_event_name(char *buf, size_t len, const char *base, + struct strlist *namelist, bool allow_suffix) { int i, ret; /* Try no suffix */ ret = e_snprintf(buf, len, "%s", base); - if (ret < 0) { - pr_debug("snprintf() failed: %s\n", strerror(-ret)); - return ret; - } + if (ret < 0) + die("snprintf() failed: %s", strerror(-ret)); if (!strlist__has_entry(namelist, buf)) - return 0; + return; if (!allow_suffix) { pr_warning("Error: event \"%s\" already exists. " "(Use -f to force duplicates.)\n", base); - return -EEXIST; + die("Can't add new event."); } /* Try to add suffix */ for (i = 1; i < MAX_EVENT_INDEX; i++) { ret = e_snprintf(buf, len, "%s_%d", base, i); - if (ret < 0) { - pr_debug("snprintf() failed: %s\n", strerror(-ret)); - return ret; - } + if (ret < 0) + die("snprintf() failed: %s", strerror(-ret)); if (!strlist__has_entry(namelist, buf)) break; } - if (i == MAX_EVENT_INDEX) { - pr_warning("Too many events are on the same function.\n"); - ret = -ERANGE; - } - - return ret; + if (i == MAX_EVENT_INDEX) + die("Too many events are on the same function."); } -static int __add_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event *tevs, - int ntevs, bool allow_suffix) +void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add) { - int i, fd, ret; - struct kprobe_trace_event *tev = NULL; - char buf[64]; - const char *event, *group; + int i, j, fd; + struct probe_point *pp; + char buf[MAX_CMDLEN]; + char event[64]; struct strlist *namelist; + bool allow_suffix; - fd = open_kprobe_events(true); - if (fd < 0) - return fd; + fd = open_kprobe_events(O_RDWR, O_APPEND); /* Get current event names */ - namelist = get_kprobe_trace_event_names(fd, false); - if (!namelist) { - pr_debug("Failed to get current event list.\n"); - return -EIO; - } - - ret = 0; - printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); - for (i = 0; i < ntevs; i++) { - tev = &tevs[i]; - if (pev->event) - event = pev->event; - else - if (pev->point.function) - event = pev->point.function; - else - event = tev->point.symbol; - if (pev->group) - group = pev->group; - else - group = PERFPROBE_GROUP; - - /* Get an unused new event name */ - ret = get_new_event_name(buf, 64, event, - namelist, allow_suffix); - if (ret < 0) - break; - event = buf; - - tev->event = strdup(event); - tev->group = strdup(group); - if (tev->event == NULL || tev->group == NULL) { - ret = -ENOMEM; - break; + namelist = get_perf_event_names(fd, false); + + for (j = 0; j < nr_probes; j++) { + pp = probes + j; + if (!pp->event) + pp->event = strdup(pp->function); + if (!pp->group) + pp->group = strdup(PERFPROBE_GROUP); + DIE_IF(!pp->event || !pp->group); + /* If force_add is true, suffix search is allowed */ + allow_suffix = force_add; + for (i = 0; i < pp->found; i++) { + /* Get an unused new event name */ + get_new_event_name(event, 64, pp->event, namelist, + allow_suffix); + snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", + pp->retprobe ? 'r' : 'p', + pp->group, event, + pp->probes[i]); + write_trace_kprobe_event(fd, buf); + printf("Added new event:\n"); + /* Get the first parameter (probe-point) */ + sscanf(pp->probes[i], "%s", buf); + show_perf_probe_event(event, buf, pp); + /* Add added event name to namelist */ + strlist__add(namelist, event); + /* + * Probes after the first probe which comes from same + * user input are always allowed to add suffix, because + * there might be several addresses corresponding to + * one code line. + */ + allow_suffix = true; } - ret = write_kprobe_trace_event(fd, tev); - if (ret < 0) - break; - /* Add added event name to namelist */ - strlist__add(namelist, event); - - /* Trick here - save current event/group */ - event = pev->event; - group = pev->group; - pev->event = tev->event; - pev->group = tev->group; - show_perf_probe_event(pev); - /* Trick here - restore current event/group */ - pev->event = (char *)event; - pev->group = (char *)group; - - /* - * Probes after the first probe which comes from same - * user input are always allowed to add suffix, because - * there might be several addresses corresponding to - * one code line. - */ - allow_suffix = true; - } - - if (ret >= 0) { - /* Show how to use the event. */ - printf("\nYou can now use it on all perf tools, such as:\n\n"); - printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, - tev->event); } + /* Show how to use the event. */ + printf("\nYou can now use it on all perf tools, such as:\n\n"); + printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); strlist__delete(namelist); close(fd); - return ret; -} - -static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, - int max_tevs) -{ - struct symbol *sym; - int ret = 0, i; - struct kprobe_trace_event *tev; - - /* Convert perf_probe_event with debuginfo */ - ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); - if (ret != 0) - return ret; - - /* Allocate trace event buffer */ - tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); - if (tev == NULL) - return -ENOMEM; - - /* Copy parameters */ - tev->point.symbol = strdup(pev->point.function); - if (tev->point.symbol == NULL) { - ret = -ENOMEM; - goto error; - } - tev->point.offset = pev->point.offset; - tev->nargs = pev->nargs; - if (tev->nargs) { - tev->args = zalloc(sizeof(struct kprobe_trace_arg) - * tev->nargs); - if (tev->args == NULL) { - ret = -ENOMEM; - goto error; - } - for (i = 0; i < tev->nargs; i++) { - if (pev->args[i].name) { - tev->args[i].name = strdup(pev->args[i].name); - if (tev->args[i].name == NULL) { - ret = -ENOMEM; - goto error; - } - } - tev->args[i].value = strdup(pev->args[i].var); - if (tev->args[i].value == NULL) { - ret = -ENOMEM; - goto error; - } - if (pev->args[i].type) { - tev->args[i].type = strdup(pev->args[i].type); - if (tev->args[i].type == NULL) { - ret = -ENOMEM; - goto error; - } - } - } - } - - /* Currently just checking function name from symbol map */ - sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], - tev->point.symbol, NULL); - if (!sym) { - pr_warning("Kernel symbol \'%s\' not found.\n", - tev->point.symbol); - ret = -ENOENT; - goto error; - } - - return 1; -error: - clear_kprobe_trace_event(tev); - free(tev); - *tevs = NULL; - return ret; -} - -struct __event_package { - struct perf_probe_event *pev; - struct kprobe_trace_event *tevs; - int ntevs; -}; - -int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - bool force_add, int max_tevs) -{ - int i, j, ret; - struct __event_package *pkgs; - - pkgs = zalloc(sizeof(struct __event_package) * npevs); - if (pkgs == NULL) - return -ENOMEM; - - /* Init vmlinux path */ - ret = init_vmlinux(); - if (ret < 0) - return ret; - - /* Loop 1: convert all events */ - for (i = 0; i < npevs; i++) { - pkgs[i].pev = &pevs[i]; - /* Convert with or without debuginfo */ - ret = convert_to_kprobe_trace_events(pkgs[i].pev, - &pkgs[i].tevs, max_tevs); - if (ret < 0) - goto end; - pkgs[i].ntevs = ret; - } - - /* Loop 2: add all events */ - for (i = 0; i < npevs && ret >= 0; i++) - ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, - pkgs[i].ntevs, force_add); -end: - /* Loop 3: cleanup trace events */ - for (i = 0; i < npevs; i++) - for (j = 0; j < pkgs[i].ntevs; j++) - clear_kprobe_trace_event(&pkgs[i].tevs[j]); - - return ret; } -static int __del_trace_kprobe_event(int fd, struct str_node *ent) +static void __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; char buf[128]; - int ret; /* Convert from perf-probe event to trace-kprobe event */ - ret = e_snprintf(buf, 128, "-:%s", ent->s); - if (ret < 0) - goto error; - + if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) + die("Failed to copy event."); p = strchr(buf + 2, ':'); - if (!p) { - pr_debug("Internal error: %s should have ':' but not.\n", - ent->s); - ret = -ENOTSUP; - goto error; - } + if (!p) + die("Internal error: %s should have ':' but not.", ent->s); *p = '/'; - pr_debug("Writing event: %s\n", buf); - ret = write(fd, buf, strlen(buf)); - if (ret < 0) - goto error; - + write_trace_kprobe_event(fd, buf); printf("Remove event: %s\n", ent->s); - return 0; -error: - pr_warning("Failed to delete event: %s\n", strerror(-ret)); - return ret; } -static int del_trace_kprobe_event(int fd, const char *group, - const char *event, struct strlist *namelist) +static void del_trace_kprobe_event(int fd, const char *group, + const char *event, struct strlist *namelist) { char buf[128]; struct str_node *ent, *n; - int found = 0, ret = 0; + int found = 0; - ret = e_snprintf(buf, 128, "%s:%s", group, event); - if (ret < 0) { - pr_err("Failed to copy event."); - return ret; - } + if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) + die("Failed to copy event."); if (strpbrk(buf, "*?")) { /* Glob-exp */ strlist__for_each_safe(ent, n, namelist) if (strglobmatch(ent->s, buf)) { found++; - ret = __del_trace_kprobe_event(fd, ent); - if (ret < 0) - break; + __del_trace_kprobe_event(fd, ent); strlist__remove(namelist, ent); } } else { ent = strlist__find(namelist, buf); if (ent) { found++; - ret = __del_trace_kprobe_event(fd, ent); - if (ret >= 0) - strlist__remove(namelist, ent); + __del_trace_kprobe_event(fd, ent); + strlist__remove(namelist, ent); } } - if (found == 0 && ret >= 0) - pr_info("Info: Event \"%s\" does not exist.\n", buf); - - return ret; + if (found == 0) + pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); } -int del_perf_probe_events(struct strlist *dellist) +void del_trace_kprobe_events(struct strlist *dellist) { - int fd, ret = 0; + int fd; const char *group, *event; char *p, *str; struct str_node *ent; struct strlist *namelist; - fd = open_kprobe_events(true); - if (fd < 0) - return fd; - + fd = open_kprobe_events(O_RDWR, O_APPEND); /* Get current event names */ - namelist = get_kprobe_trace_event_names(fd, true); - if (namelist == NULL) - return -EINVAL; + namelist = get_perf_event_names(fd, true); strlist__for_each(ent, dellist) { str = strdup(ent->s); - if (str == NULL) { - ret = -ENOMEM; - break; - } + if (!str) + die("Failed to copy event."); pr_debug("Parsing: %s\n", str); p = strchr(str, ':'); if (p) { @@ -1633,14 +723,80 @@ int del_perf_probe_events(struct strlist *dellist) event = str; } pr_debug("Group: %s, Event: %s\n", group, event); - ret = del_trace_kprobe_event(fd, group, event, namelist); + del_trace_kprobe_event(fd, group, event, namelist); free(str); - if (ret < 0) - break; } strlist__delete(namelist); close(fd); +} - return ret; +#define LINEBUF_SIZE 256 +#define NR_ADDITIONAL_LINES 2 + +static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +{ + char buf[LINEBUF_SIZE]; + const char *color = PERF_COLOR_BLUE; + + if (fgets(buf, LINEBUF_SIZE, fp) == NULL) + goto error; + if (!skip) { + if (show_num) + fprintf(stdout, "%7u %s", l, buf); + else + color_fprintf(stdout, color, " %s", buf); + } + + while (strlen(buf) == LINEBUF_SIZE - 1 && + buf[LINEBUF_SIZE - 2] != '\n') { + if (fgets(buf, LINEBUF_SIZE, fp) == NULL) + goto error; + if (!skip) { + if (show_num) + fprintf(stdout, "%s", buf); + else + color_fprintf(stdout, color, "%s", buf); + } + } + return; +error: + if (feof(fp)) + die("Source file is shorter than expected."); + else + die("File read error: %s", strerror(errno)); } +void show_line_range(struct line_range *lr) +{ + unsigned int l = 1; + struct line_node *ln; + FILE *fp; + + setup_pager(); + + if (lr->function) + fprintf(stdout, "<%s:%d>\n", lr->function, + lr->start - lr->offset); + else + fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); + + fp = fopen(lr->path, "r"); + if (fp == NULL) + die("Failed to open %s: %s", lr->path, strerror(errno)); + /* Skip to starting line number */ + while (l < lr->start) + show_one_line(fp, l++, true, false); + + list_for_each_entry(ln, &lr->line_list, list) { + while (ln->line > l) + show_one_line(fp, (l++) - lr->offset, false, false); + show_one_line(fp, (l++) - lr->offset, false, true); + } + + if (lr->end == INT_MAX) + lr->end = l + NR_ADDITIONAL_LINES; + while (l < lr->end && !feof(fp)) + show_one_line(fp, (l++) - lr->offset, false, false); + + fclose(fp); +} diff --git a/trunk/tools/perf/util/probe-event.h b/trunk/tools/perf/util/probe-event.h index e9db1a214ca4..711287d4baea 100644 --- a/trunk/tools/perf/util/probe-event.h +++ b/trunk/tools/perf/util/probe-event.h @@ -2,125 +2,21 @@ #define _PROBE_EVENT_H #include +#include "probe-finder.h" #include "strlist.h" -extern bool probe_event_dry_run; - -/* kprobe-tracer tracing point */ -struct kprobe_trace_point { - char *symbol; /* Base symbol */ - unsigned long offset; /* Offset from symbol */ - bool retprobe; /* Return probe flag */ -}; - -/* kprobe-tracer tracing argument referencing offset */ -struct kprobe_trace_arg_ref { - struct kprobe_trace_arg_ref *next; /* Next reference */ - long offset; /* Offset value */ -}; - -/* kprobe-tracer tracing argument */ -struct kprobe_trace_arg { - char *name; /* Argument name */ - char *value; /* Base value */ - char *type; /* Type name */ - struct kprobe_trace_arg_ref *ref; /* Referencing offset */ -}; - -/* kprobe-tracer tracing event (point + arg) */ -struct kprobe_trace_event { - char *event; /* Event name */ - char *group; /* Group name */ - struct kprobe_trace_point point; /* Trace point */ - int nargs; /* Number of args */ - struct kprobe_trace_arg *args; /* Arguments */ -}; - -/* Perf probe probing point */ -struct perf_probe_point { - char *file; /* File path */ - char *function; /* Function name */ - int line; /* Line number */ - bool retprobe; /* Return probe flag */ - char *lazy_line; /* Lazy matching pattern */ - unsigned long offset; /* Offset from function entry */ -}; - -/* Perf probe probing argument field chain */ -struct perf_probe_arg_field { - struct perf_probe_arg_field *next; /* Next field */ - char *name; /* Name of the field */ - bool ref; /* Referencing flag */ -}; - -/* Perf probe probing argument */ -struct perf_probe_arg { - char *name; /* Argument name */ - char *var; /* Variable name */ - char *type; /* Type name */ - struct perf_probe_arg_field *field; /* Structure fields */ -}; - -/* Perf probe probing event (point + arg) */ -struct perf_probe_event { - char *event; /* Event name */ - char *group; /* Group name */ - struct perf_probe_point point; /* Probe point */ - int nargs; /* Number of arguments */ - struct perf_probe_arg *args; /* Arguments */ -}; - - -/* Line number container */ -struct line_node { - struct list_head list; - int line; -}; - -/* Line range */ -struct line_range { - char *file; /* File name */ - char *function; /* Function name */ - int start; /* Start line number */ - int end; /* End line number */ - int offset; /* Start line offset */ - char *path; /* Real path name */ - struct list_head line_list; /* Visible lines */ -}; - -/* Command string to events */ -extern int parse_perf_probe_command(const char *cmd, - struct perf_probe_event *pev); -extern int parse_kprobe_trace_command(const char *cmd, - struct kprobe_trace_event *tev); - -/* Events to command string */ -extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); -extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); -extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, - size_t len); - -/* Check the perf_probe_event needs debuginfo */ -extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); - -/* Convert from kprobe_trace_event to perf_probe_event */ -extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev, - struct perf_probe_event *pev); - -/* Release event contents */ -extern void clear_perf_probe_event(struct perf_probe_event *pev); -extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); - -/* Command string to line-range */ -extern int parse_line_range_desc(const char *cmd, struct line_range *lr); - - -extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - bool force_add, int max_probe_points); -extern int del_perf_probe_events(struct strlist *dellist); -extern int show_perf_probe_events(void); -extern int show_line_range(struct line_range *lr); - +extern void parse_line_range_desc(const char *arg, struct line_range *lr); +extern void parse_perf_probe_event(const char *str, struct probe_point *pp, + bool *need_dwarf); +extern int synthesize_perf_probe_point(struct probe_point *pp); +extern int synthesize_perf_probe_event(struct probe_point *pp); +extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); +extern int synthesize_trace_kprobe_event(struct probe_point *pp); +extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add); +extern void del_trace_kprobe_events(struct strlist *dellist); +extern void show_perf_probe_events(void); +extern void show_line_range(struct line_range *lr); /* Maximum index number of event-name postfix */ #define MAX_EVENT_INDEX 1024 diff --git a/trunk/tools/perf/util/probe-finder.c b/trunk/tools/perf/util/probe-finder.c index 562b1443e785..c171a243d05b 100644 --- a/trunk/tools/perf/util/probe-finder.c +++ b/trunk/tools/perf/util/probe-finder.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "string.h" #include "event.h" @@ -39,8 +38,57 @@ #include "util.h" #include "probe-finder.h" -/* Kprobe tracer basic type is up to u64 */ -#define MAX_BASIC_TYPE_BITS 64 + +/* + * Generic dwarf analysis helpers + */ + +#define X86_32_MAX_REGS 8 +const char *x86_32_regs_table[X86_32_MAX_REGS] = { + "%ax", + "%cx", + "%dx", + "%bx", + "$stack", /* Stack address instead of %sp */ + "%bp", + "%si", + "%di", +}; + +#define X86_64_MAX_REGS 16 +const char *x86_64_regs_table[X86_64_MAX_REGS] = { + "%ax", + "%dx", + "%cx", + "%bx", + "%si", + "%di", + "%bp", + "%sp", + "%r8", + "%r9", + "%r10", + "%r11", + "%r12", + "%r13", + "%r14", + "%r15", +}; + +/* TODO: switching by dwarf address size */ +#ifdef __x86_64__ +#define ARCH_MAX_REGS X86_64_MAX_REGS +#define arch_regs_table x86_64_regs_table +#else +#define ARCH_MAX_REGS X86_32_MAX_REGS +#define arch_regs_table x86_32_regs_table +#endif + +/* Return architecture dependent register string (for kprobe-tracer) */ +static const char *get_arch_regstr(unsigned int n) +{ + return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; +} /* * Compare the tail of two strings. @@ -60,7 +108,7 @@ static int strtailcmp(const char *s1, const char *s2) /* Line number list operations */ /* Add a line to line number list */ -static int line_list__add_line(struct list_head *head, int line) +static void line_list__add_line(struct list_head *head, unsigned int line) { struct line_node *ln; struct list_head *p; @@ -71,23 +119,21 @@ static int line_list__add_line(struct list_head *head, int line) p = &ln->list; goto found; } else if (ln->line == line) /* Already exist */ - return 1; + return ; } /* List is empty, or the smallest entry */ p = head; found: pr_debug("line list: add a line %u\n", line); ln = zalloc(sizeof(struct line_node)); - if (ln == NULL) - return -ENOMEM; + DIE_IF(ln == NULL); ln->line = line; INIT_LIST_HEAD(&ln->list); list_add(&ln->list, p); - return 0; } /* Check if the line in line number list */ -static int line_list__has_line(struct list_head *head, int line) +static int line_list__has_line(struct list_head *head, unsigned int line) { struct line_node *ln; @@ -138,129 +184,9 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) if (strtailcmp(src, fname) == 0) break; } - if (i == nfiles) - return NULL; return src; } -/* Compare diename and tname */ -static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) -{ - const char *name; - name = dwarf_diename(dw_die); - return name ? strcmp(tname, name) : -1; -} - -/* Get type die, but skip qualifiers and typedef */ -static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) -{ - Dwarf_Attribute attr; - int tag; - - do { - if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || - dwarf_formref_die(&attr, die_mem) == NULL) - return NULL; - - tag = dwarf_tag(die_mem); - vr_die = die_mem; - } while (tag == DW_TAG_const_type || - tag == DW_TAG_restrict_type || - tag == DW_TAG_volatile_type || - tag == DW_TAG_shared_type || - tag == DW_TAG_typedef); - - return die_mem; -} - -static bool die_is_signed_type(Dwarf_Die *tp_die) -{ - Dwarf_Attribute attr; - Dwarf_Word ret; - - if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || - dwarf_formudata(&attr, &ret) != 0) - return false; - - return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || - ret == DW_ATE_signed_fixed); -} - -static int die_get_byte_size(Dwarf_Die *tp_die) -{ - Dwarf_Attribute attr; - Dwarf_Word ret; - - if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || - dwarf_formudata(&attr, &ret) != 0) - return 0; - - return (int)ret; -} - -/* Get data_member_location offset */ -static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) -{ - Dwarf_Attribute attr; - Dwarf_Op *expr; - size_t nexpr; - int ret; - - if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) - return -ENOENT; - - if (dwarf_formudata(&attr, offs) != 0) { - /* DW_AT_data_member_location should be DW_OP_plus_uconst */ - ret = dwarf_getlocation(&attr, &expr, &nexpr); - if (ret < 0 || nexpr == 0) - return -ENOENT; - - if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { - pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", - expr[0].atom, nexpr); - return -ENOTSUP; - } - *offs = (Dwarf_Word)expr[0].number; - } - return 0; -} - -/* Return values for die_find callbacks */ -enum { - DIE_FIND_CB_FOUND = 0, /* End of Search */ - DIE_FIND_CB_CHILD = 1, /* Search only children */ - DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ - DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ -}; - -/* Search a child die */ -static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, - int (*callback)(Dwarf_Die *, void *), - void *data, Dwarf_Die *die_mem) -{ - Dwarf_Die child_die; - int ret; - - ret = dwarf_child(rt_die, die_mem); - if (ret != 0) - return NULL; - - do { - ret = callback(die_mem, data); - if (ret == DIE_FIND_CB_FOUND) - return die_mem; - - if ((ret & DIE_FIND_CB_CHILD) && - die_find_child(die_mem, callback, data, &child_die)) { - memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); - return die_mem; - } - } while ((ret & DIE_FIND_CB_SIBLING) && - dwarf_siblingof(die_mem, die_mem) == 0); - - return NULL; -} - struct __addr_die_search_param { Dwarf_Addr addr; Dwarf_Die *die_mem; @@ -279,8 +205,8 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) } /* Search a real subprogram including this line, */ -static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { struct __addr_die_search_param ad; ad.addr = addr; @@ -292,64 +218,77 @@ static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, return die_mem; } -/* die_find callback for inline function search */ -static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) +/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ +static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { - Dwarf_Addr *addr = data; + Dwarf_Die child_die; + int ret; - if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && - dwarf_haspc(die_mem, *addr)) - return DIE_FIND_CB_FOUND; + ret = dwarf_child(sp_die, die_mem); + if (ret != 0) + return NULL; - return DIE_FIND_CB_CONTINUE; + do { + if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && + dwarf_haspc(die_mem, addr)) + return die_mem; + + if (die_get_inlinefunc(die_mem, addr, &child_die)) { + memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); + return die_mem; + } + } while (dwarf_siblingof(die_mem, die_mem) == 0); + + return NULL; } -/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ -static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +/* Compare diename and tname */ +static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) { - return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); + const char *name; + name = dwarf_diename(dw_die); + DIE_IF(name == NULL); + return strcmp(tname, name); } -static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) +/* Get entry pc(or low pc, 1st entry of ranges) of the die */ +static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) { - const char *name = data; - int tag; - - tag = dwarf_tag(die_mem); - if ((tag == DW_TAG_formal_parameter || - tag == DW_TAG_variable) && - (die_compare_name(die_mem, name) == 0)) - return DIE_FIND_CB_FOUND; + Dwarf_Addr epc; + int ret; - return DIE_FIND_CB_CONTINUE; + ret = dwarf_entrypc(dw_die, &epc); + DIE_IF(ret == -1); + return epc; } -/* Find a variable called 'name' */ +/* Get a variable die */ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, Dwarf_Die *die_mem) { - return die_find_child(sp_die, __die_find_variable_cb, (void *)name, - die_mem); -} + Dwarf_Die child_die; + int tag; + int ret; -static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) -{ - const char *name = data; + ret = dwarf_child(sp_die, die_mem); + if (ret != 0) + return NULL; - if ((dwarf_tag(die_mem) == DW_TAG_member) && - (die_compare_name(die_mem, name) == 0)) - return DIE_FIND_CB_FOUND; + do { + tag = dwarf_tag(die_mem); + if ((tag == DW_TAG_formal_parameter || + tag == DW_TAG_variable) && + (die_compare_name(die_mem, name) == 0)) + return die_mem; - return DIE_FIND_CB_SIBLING; -} + if (die_find_variable(die_mem, name, &child_die)) { + memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); + return die_mem; + } + } while (dwarf_siblingof(die_mem, die_mem) == 0); -/* Find a member called 'name' */ -static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, - Dwarf_Die *die_mem) -{ - return die_find_child(st_die, __die_find_member_cb, (void *)name, - die_mem); + return NULL; } /* @@ -357,22 +296,19 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, */ /* Show a location */ -static int convert_location(Dwarf_Op *op, struct probe_finder *pf) +static void show_location(Dwarf_Op *op, struct probe_finder *pf) { unsigned int regn; Dwarf_Word offs = 0; - bool ref = false; + int deref = 0, ret; const char *regs; - struct kprobe_trace_arg *tvar = pf->tvar; + /* TODO: support CFA */ /* If this is based on frame buffer, set the offset */ if (op->atom == DW_OP_fbreg) { - if (pf->fb_ops == NULL) { - pr_warning("The attribute of frame base is not " - "supported.\n"); - return -ENOTSUP; - } - ref = true; + if (pf->fb_ops == NULL) + die("The attribute of frame base is not supported.\n"); + deref = 1; offs = op->number; op = &pf->fb_ops[0]; } @@ -380,164 +316,35 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { regn = op->atom - DW_OP_breg0; offs += op->number; - ref = true; + deref = 1; } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { regn = op->atom - DW_OP_reg0; } else if (op->atom == DW_OP_bregx) { regn = op->number; offs += op->number2; - ref = true; + deref = 1; } else if (op->atom == DW_OP_regx) { regn = op->number; - } else { - pr_warning("DW_OP %x is not supported.\n", op->atom); - return -ENOTSUP; - } + } else + die("DW_OP %d is not supported.", op->atom); regs = get_arch_regstr(regn); - if (!regs) { - pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); - return -ERANGE; - } - - tvar->value = strdup(regs); - if (tvar->value == NULL) - return -ENOMEM; - - if (ref) { - tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); - if (tvar->ref == NULL) - return -ENOMEM; - tvar->ref->offset = (long)offs; - } - return 0; -} - -static int convert_variable_type(Dwarf_Die *vr_die, - struct kprobe_trace_arg *targ) -{ - Dwarf_Die type; - char buf[16]; - int ret; - - if (die_get_real_type(vr_die, &type) == NULL) { - pr_warning("Failed to get a type information of %s.\n", - dwarf_diename(vr_die)); - return -ENOENT; - } - - ret = die_get_byte_size(&type) * 8; - if (ret) { - /* Check the bitwidth */ - if (ret > MAX_BASIC_TYPE_BITS) { - pr_info("%s exceeds max-bitwidth." - " Cut down to %d bits.\n", - dwarf_diename(&type), MAX_BASIC_TYPE_BITS); - ret = MAX_BASIC_TYPE_BITS; - } - - ret = snprintf(buf, 16, "%c%d", - die_is_signed_type(&type) ? 's' : 'u', ret); - if (ret < 0 || ret >= 16) { - if (ret >= 16) - ret = -E2BIG; - pr_warning("Failed to convert variable type: %s\n", - strerror(-ret)); - return ret; - } - targ->type = strdup(buf); - if (targ->type == NULL) - return -ENOMEM; - } - return 0; -} - -static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, - struct perf_probe_arg_field *field, - struct kprobe_trace_arg_ref **ref_ptr, - Dwarf_Die *die_mem) -{ - struct kprobe_trace_arg_ref *ref = *ref_ptr; - Dwarf_Die type; - Dwarf_Word offs; - int ret; - - pr_debug("converting %s in %s\n", field->name, varname); - if (die_get_real_type(vr_die, &type) == NULL) { - pr_warning("Failed to get the type of %s.\n", varname); - return -ENOENT; - } - - /* Check the pointer and dereference */ - if (dwarf_tag(&type) == DW_TAG_pointer_type) { - if (!field->ref) { - pr_err("Semantic error: %s must be referred by '->'\n", - field->name); - return -EINVAL; - } - /* Get the type pointed by this pointer */ - if (die_get_real_type(&type, &type) == NULL) { - pr_warning("Failed to get the type of %s.\n", varname); - return -ENOENT; - } - /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) { - pr_warning("%s is not a data structure.\n", varname); - return -EINVAL; - } - - ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); - if (ref == NULL) - return -ENOMEM; - if (*ref_ptr) - (*ref_ptr)->next = ref; - else - *ref_ptr = ref; - } else { - /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) { - pr_warning("%s is not a data structure.\n", varname); - return -EINVAL; - } - if (field->ref) { - pr_err("Semantic error: %s must be referred by '.'\n", - field->name); - return -EINVAL; - } - if (!ref) { - pr_warning("Structure on a register is not " - "supported yet.\n"); - return -ENOTSUP; - } - } - - if (die_find_member(&type, field->name, die_mem) == NULL) { - pr_warning("%s(tyep:%s) has no member %s.\n", varname, - dwarf_diename(&type), field->name); - return -EINVAL; - } + if (!regs) + die("%u exceeds max register number.", regn); - /* Get the offset of the field */ - ret = die_get_data_member_location(die_mem, &offs); - if (ret < 0) { - pr_warning("Failed to get the offset of %s.\n", field->name); - return ret; - } - ref->offset += (long)offs; - - /* Converting next field */ - if (field->next) - return convert_variable_fields(die_mem, field->name, - field->next, &ref, die_mem); + if (deref) + ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", + pf->var, (intmax_t)offs, regs); else - return 0; + ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); + DIE_IF(ret < 0); + DIE_IF(ret >= pf->len); } /* Show a variables in kprobe event format */ -static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) +static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Attribute attr; - Dwarf_Die die_mem; Dwarf_Op *expr; size_t nexpr; int ret; @@ -549,191 +356,142 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) if (ret <= 0 || nexpr == 0) goto error; - ret = convert_location(expr, pf); - if (ret == 0 && pf->pvar->field) { - ret = convert_variable_fields(vr_die, pf->pvar->var, - pf->pvar->field, &pf->tvar->ref, - &die_mem); - vr_die = &die_mem; - } - if (ret == 0) { - if (pf->pvar->type) { - pf->tvar->type = strdup(pf->pvar->type); - if (pf->tvar->type == NULL) - ret = -ENOMEM; - } else - ret = convert_variable_type(vr_die, pf->tvar); - } + show_location(expr, pf); /* *expr will be cached in libdw. Don't free it. */ - return ret; + return ; error: /* TODO: Support const_value */ - pr_err("Failed to find the location of %s at this address.\n" - " Perhaps, it has been optimized out.\n", pf->pvar->var); - return -ENOENT; + die("Failed to find the location of %s at this address.\n" + " Perhaps, it has been optimized out.", pf->var); } /* Find a variable in a subprogram die */ -static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) +static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { - Dwarf_Die vr_die; - char buf[32], *ptr; int ret; + Dwarf_Die vr_die; - /* TODO: Support arrays */ - if (pf->pvar->name) - pf->tvar->name = strdup(pf->pvar->name); - else { - ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); - if (ret < 0) - return ret; - ptr = strchr(buf, ':'); /* Change type separator to _ */ - if (ptr) - *ptr = '_'; - pf->tvar->name = strdup(buf); - } - if (pf->tvar->name == NULL) - return -ENOMEM; - - if (!is_c_varname(pf->pvar->var)) { - /* Copy raw parameters */ - pf->tvar->value = strdup(pf->pvar->var); - if (pf->tvar->value == NULL) - return -ENOMEM; - else - return 0; + /* TODO: Support struct members and arrays */ + if (!is_c_varname(pf->var)) { + /* Output raw parameters */ + ret = snprintf(pf->buf, pf->len, " %s", pf->var); + DIE_IF(ret < 0); + DIE_IF(ret >= pf->len); + return ; } - pr_debug("Searching '%s' variable in context.\n", - pf->pvar->var); + pr_debug("Searching '%s' variable in context.\n", pf->var); /* Search child die for local variables and parameters. */ - if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { - pr_warning("Failed to find '%s' in this function.\n", - pf->pvar->var); - return -ENOENT; - } - return convert_variable(&vr_die, pf); + if (!die_find_variable(sp_die, pf->var, &vr_die)) + die("Failed to find '%s' in this function.", pf->var); + + show_variable(&vr_die, pf); } /* Show a probe point to output buffer */ -static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) +static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) { - struct kprobe_trace_event *tev; + struct probe_point *pp = pf->pp; Dwarf_Addr eaddr; Dwarf_Die die_mem; const char *name; - int ret, i; + char tmp[MAX_PROBE_BUFFER]; + int ret, i, len; Dwarf_Attribute fb_attr; size_t nops; - if (pf->ntevs == pf->max_tevs) { - pr_warning("Too many( > %d) probe point found.\n", - pf->max_tevs); - return -ERANGE; - } - tev = &pf->tevs[pf->ntevs++]; - /* If no real subprogram, find a real one */ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { - sp_die = die_find_real_subprogram(&pf->cu_die, + sp_die = die_get_real_subprogram(&pf->cu_die, pf->addr, &die_mem); - if (!sp_die) { - pr_warning("Failed to find probe point in any " - "functions.\n"); - return -ENOENT; - } + if (!sp_die) + die("Probe point is not found in subprograms."); } - /* Copy the name of probe point */ + /* Output name of probe point */ name = dwarf_diename(sp_die); if (name) { - if (dwarf_entrypc(sp_die, &eaddr) != 0) { - pr_warning("Failed to get entry pc of %s\n", - dwarf_diename(sp_die)); - return -ENOENT; + dwarf_entrypc(sp_die, &eaddr); + ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, + (unsigned long)(pf->addr - eaddr)); + /* Copy the function name if possible */ + if (!pp->function) { + pp->function = strdup(name); + pp->offset = (size_t)(pf->addr - eaddr); } - tev->point.symbol = strdup(name); - if (tev->point.symbol == NULL) - return -ENOMEM; - tev->point.offset = (unsigned long)(pf->addr - eaddr); - } else + } else { /* This function has no name. */ - tev->point.offset = (unsigned long)pf->addr; - - pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, - tev->point.offset); + ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", + (uintmax_t)pf->addr); + if (!pp->function) { + /* TODO: Use _stext */ + pp->function = strdup(""); + pp->offset = (size_t)pf->addr; + } + } + DIE_IF(ret < 0); + DIE_IF(ret >= MAX_PROBE_BUFFER); + len = ret; + pr_debug("Probe point found: %s\n", tmp); /* Get the frame base attribute/ops */ dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) { + if (ret <= 0 || nops == 0) pf->fb_ops = NULL; - } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && - pf->cfi != NULL) { - Dwarf_Frame *frame; - if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || - dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { - pr_warning("Failed to get CFA on 0x%jx\n", - (uintmax_t)pf->addr); - return -ENOENT; - } - } /* Find each argument */ - tev->nargs = pf->pev->nargs; - tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); - if (tev->args == NULL) - return -ENOMEM; - for (i = 0; i < pf->pev->nargs; i++) { - pf->pvar = &pf->pev->args[i]; - pf->tvar = &tev->args[i]; - ret = find_variable(sp_die, pf); - if (ret != 0) - return ret; + /* TODO: use dwarf_cfi_addrframe */ + for (i = 0; i < pp->nr_args; i++) { + pf->var = pp->args[i]; + pf->buf = &tmp[len]; + pf->len = MAX_PROBE_BUFFER - len; + find_variable(sp_die, pf); + len += strlen(pf->buf); } /* *pf->fb_ops will be cached in libdw. Don't free it. */ pf->fb_ops = NULL; - return 0; + + if (pp->found == MAX_PROBES) + die("Too many( > %d) probe point found.\n", MAX_PROBES); + + pp->probes[pp->found] = strdup(tmp); + pp->found++; } /* Find probe point from its line number */ -static int find_probe_point_by_line(struct probe_finder *pf) +static void find_probe_point_by_line(struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; int lineno; - int ret = 0; + int ret; - if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { - pr_warning("No source lines found in this CU.\n"); - return -ENOENT; - } + ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); + DIE_IF(ret != 0); - for (i = 0; i < nlines && ret == 0; i++) { + for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); - if (dwarf_lineno(line, &lineno) != 0 || - lineno != pf->lno) + dwarf_lineno(line, &lineno); + if (lineno != pf->lno) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; - if (dwarf_lineaddr(line, &addr) != 0) { - pr_warning("Failed to get the address of the line.\n"); - return -ENOENT; - } + ret = dwarf_lineaddr(line, &addr); + DIE_IF(ret != 0); pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", (int)i, lineno, (uintmax_t)addr); pf->addr = addr; - ret = convert_probe_point(NULL, pf); + show_probe_point(NULL, pf); /* Continuing, because target line might be inlined. */ } - return ret; } /* Find lines which match lazy pattern */ @@ -741,27 +499,16 @@ static int find_lazy_match_lines(struct list_head *head, const char *fname, const char *pat) { char *fbuf, *p1, *p2; - int fd, ret, line, nlines = 0; + int fd, line, nlines = 0; struct stat st; fd = open(fname, O_RDONLY); - if (fd < 0) { - pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); - return fd; - } - - ret = fstat(fd, &st); - if (ret < 0) { - pr_warning("Failed to get the size of %s: %s\n", - fname, strerror(errno)); - return ret; - } - fbuf = xmalloc(st.st_size + 2); - ret = read(fd, fbuf, st.st_size); - if (ret < 0) { - pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); - return ret; - } + if (fd < 0) + die("failed to open %s", fname); + DIE_IF(fstat(fd, &st) < 0); + fbuf = malloc(st.st_size + 2); + DIE_IF(fbuf == NULL); + DIE_IF(read(fd, fbuf, st.st_size) < 0); close(fd); fbuf[st.st_size] = '\n'; /* Dummy line */ fbuf[st.st_size + 1] = '\0'; @@ -781,7 +528,7 @@ static int find_lazy_match_lines(struct list_head *head, } /* Find probe points from lazy pattern */ -static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) +static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; @@ -789,46 +536,37 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) Dwarf_Addr addr; Dwarf_Die die_mem; int lineno; - int ret = 0; + int ret; if (list_empty(&pf->lcache)) { /* Matching lazy line pattern */ ret = find_lazy_match_lines(&pf->lcache, pf->fname, - pf->pev->point.lazy_line); - if (ret == 0) { - pr_debug("No matched lines found in %s.\n", pf->fname); - return 0; - } else if (ret < 0) - return ret; + pf->pp->lazy_line); + if (ret <= 0) + die("No matched lines found in %s.", pf->fname); } - if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { - pr_warning("No source lines found in this CU.\n"); - return -ENOENT; - } - - for (i = 0; i < nlines && ret >= 0; i++) { + ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); + DIE_IF(ret != 0); + for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); - if (dwarf_lineno(line, &lineno) != 0 || - !line_list__has_line(&pf->lcache, lineno)) + dwarf_lineno(line, &lineno); + if (!line_list__has_line(&pf->lcache, lineno)) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; - if (dwarf_lineaddr(line, &addr) != 0) { - pr_debug("Failed to get the address of line %d.\n", - lineno); - continue; - } + ret = dwarf_lineaddr(line, &addr); + DIE_IF(ret != 0); if (sp_die) { /* Address filtering 1: does sp_die include addr? */ if (!dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ - if (die_find_inlinefunc(sp_die, addr, &die_mem)) + if (die_get_inlinefunc(sp_die, addr, &die_mem)) continue; } @@ -836,44 +574,27 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) (int)i, lineno, (unsigned long long)addr); pf->addr = addr; - ret = convert_probe_point(sp_die, pf); + show_probe_point(sp_die, pf); /* Continuing, because target line might be inlined. */ } /* TODO: deallocate lines, but how? */ - return ret; } -/* Callback parameter with return value */ -struct dwarf_callback_param { - void *data; - int retval; -}; - static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) { - struct dwarf_callback_param *param = data; - struct probe_finder *pf = param->data; - struct perf_probe_point *pp = &pf->pev->point; - Dwarf_Addr addr; + struct probe_finder *pf = (struct probe_finder *)data; + struct probe_point *pp = pf->pp; if (pp->lazy_line) - param->retval = find_probe_point_lazy(in_die, pf); + find_probe_point_lazy(in_die, pf); else { /* Get probe address */ - if (dwarf_entrypc(in_die, &addr) != 0) { - pr_warning("Failed to get entry pc of %s.\n", - dwarf_diename(in_die)); - param->retval = -ENOENT; - return DWARF_CB_ABORT; - } - pf->addr = addr; + pf->addr = die_get_entrypc(in_die); pf->addr += pp->offset; pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr); - param->retval = convert_probe_point(in_die, pf); - if (param->retval < 0) - return DWARF_CB_ABORT; + show_probe_point(in_die, pf); } return DWARF_CB_OK; @@ -882,88 +603,59 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) /* Search function from function name */ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { - struct dwarf_callback_param *param = data; - struct probe_finder *pf = param->data; - struct perf_probe_point *pp = &pf->pev->point; + struct probe_finder *pf = (struct probe_finder *)data; + struct probe_point *pp = pf->pp; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || die_compare_name(sp_die, pp->function) != 0) - return DWARF_CB_OK; + return 0; pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); pf->lno += pp->line; - param->retval = find_probe_point_by_line(pf); + find_probe_point_by_line(pf); } else if (!dwarf_func_inline(sp_die)) { /* Real function */ if (pp->lazy_line) - param->retval = find_probe_point_lazy(sp_die, pf); + find_probe_point_lazy(sp_die, pf); else { - if (dwarf_entrypc(sp_die, &pf->addr) != 0) { - pr_warning("Failed to get entry pc of %s.\n", - dwarf_diename(sp_die)); - param->retval = -ENOENT; - return DWARF_CB_ABORT; - } + pf->addr = die_get_entrypc(sp_die); pf->addr += pp->offset; /* TODO: Check the address in this function */ - param->retval = convert_probe_point(sp_die, pf); + show_probe_point(sp_die, pf); } - } else { - struct dwarf_callback_param _param = {.data = (void *)pf, - .retval = 0}; + } else /* Inlined function: search instances */ - dwarf_func_inline_instances(sp_die, probe_point_inline_cb, - &_param); - param->retval = _param.retval; - } + dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); - return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ + return 1; /* Exit; no same symbol in this CU. */ } -static int find_probe_point_by_func(struct probe_finder *pf) +static void find_probe_point_by_func(struct probe_finder *pf) { - struct dwarf_callback_param _param = {.data = (void *)pf, - .retval = 0}; - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); - return _param.retval; + dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); } -/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ -int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, int max_tevs) +/* Find a probe point */ +int find_probe_point(int fd, struct probe_point *pp) { - struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; - struct perf_probe_point *pp = &pev->point; + struct probe_finder pf = {.pp = pp}; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; - int ret = 0; - - pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); - if (pf.tevs == NULL) - return -ENOMEM; - *tevs = pf.tevs; - pf.ntevs = 0; dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) { - pr_warning("No dwarf info found in the vmlinux - " - "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - return -EBADF; - } - - /* Get the call frame information from this dwarf */ - pf.cfi = dwarf_getcfi(dbg); + if (!dbg) + return -ENOENT; + pp->found = 0; off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ - while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && - ret >= 0) { + while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); if (!diep) @@ -977,12 +669,12 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, if (!pp->file || pf.fname) { if (pp->function) - ret = find_probe_point_by_func(&pf); + find_probe_point_by_func(&pf); else if (pp->lazy_line) - ret = find_probe_point_lazy(NULL, &pf); + find_probe_point_lazy(NULL, &pf); else { pf.lno = pp->line; - ret = find_probe_point_by_line(&pf); + find_probe_point_by_line(&pf); } } off = noff; @@ -990,169 +682,41 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, line_list__free(&pf.lcache); dwarf_end(dbg); - return (ret < 0) ? ret : pf.ntevs; -} - -/* Reverse search */ -int find_perf_probe_point(int fd, unsigned long addr, - struct perf_probe_point *ppt) -{ - Dwarf_Die cudie, spdie, indie; - Dwarf *dbg; - Dwarf_Line *line; - Dwarf_Addr laddr, eaddr; - const char *tmp; - int lineno, ret = 0; - bool found = false; - - dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) - return -EBADF; - - /* Find cu die */ - if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { - ret = -EINVAL; - goto end; - } - - /* Find a corresponding line */ - line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); - if (line) { - if (dwarf_lineaddr(line, &laddr) == 0 && - (Dwarf_Addr)addr == laddr && - dwarf_lineno(line, &lineno) == 0) { - tmp = dwarf_linesrc(line, NULL, NULL); - if (tmp) { - ppt->line = lineno; - ppt->file = strdup(tmp); - if (ppt->file == NULL) { - ret = -ENOMEM; - goto end; - } - found = true; - } - } - } - - /* Find a corresponding function */ - if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { - tmp = dwarf_diename(&spdie); - if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) - goto end; - - if (ppt->line) { - if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, - &indie)) { - /* addr in an inline function */ - tmp = dwarf_diename(&indie); - if (!tmp) - goto end; - ret = dwarf_decl_line(&indie, &lineno); - } else { - if (eaddr == addr) { /* Function entry */ - lineno = ppt->line; - ret = 0; - } else - ret = dwarf_decl_line(&spdie, &lineno); - } - if (ret == 0) { - /* Make a relative line number */ - ppt->line -= lineno; - goto found; - } - } - /* We don't have a line number, let's use offset */ - ppt->offset = addr - (unsigned long)eaddr; -found: - ppt->function = strdup(tmp); - if (ppt->function == NULL) { - ret = -ENOMEM; - goto end; - } - found = true; - } - -end: - dwarf_end(dbg); - if (ret >= 0) - ret = found ? 1 : 0; - return ret; -} - -/* Add a line and store the src path */ -static int line_range_add_line(const char *src, unsigned int lineno, - struct line_range *lr) -{ - /* Copy real path */ - if (!lr->path) { - lr->path = strdup(src); - if (lr->path == NULL) - return -ENOMEM; - } - return line_list__add_line(&lr->line_list, lineno); -} - -/* Search function declaration lines */ -static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) -{ - struct dwarf_callback_param *param = data; - struct line_finder *lf = param->data; - const char *src; - int lineno; - - src = dwarf_decl_file(sp_die); - if (src && strtailcmp(src, lf->fname) != 0) - return DWARF_CB_OK; - - if (dwarf_decl_line(sp_die, &lineno) != 0 || - (lf->lno_s > lineno || lf->lno_e < lineno)) - return DWARF_CB_OK; - - param->retval = line_range_add_line(src, lineno, lf->lr); - if (param->retval < 0) - return DWARF_CB_ABORT; - return DWARF_CB_OK; -} - -static int find_line_range_func_decl_lines(struct line_finder *lf) -{ - struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; - dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); - return param.retval; + return pp->found; } /* Find line range from its line number */ -static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) +static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; - int lineno, ret = 0; + int lineno; + int ret; const char *src; Dwarf_Die die_mem; line_list__init(&lf->lr->line_list); - if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { - pr_warning("No source lines found in this CU.\n"); - return -ENOENT; - } + ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); + DIE_IF(ret != 0); - /* Search probable lines on lines list */ for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); - if (dwarf_lineno(line, &lineno) != 0 || - (lf->lno_s > lineno || lf->lno_e < lineno)) + ret = dwarf_lineno(line, &lineno); + DIE_IF(ret != 0); + if (lf->lno_s > lineno || lf->lno_e < lineno) continue; if (sp_die) { /* Address filtering 1: does sp_die include addr? */ - if (dwarf_lineaddr(line, &addr) != 0 || - !dwarf_haspc(sp_die, addr)) + ret = dwarf_lineaddr(line, &addr); + DIE_IF(ret != 0); + if (!dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ - if (die_find_inlinefunc(sp_die, addr, &die_mem)) + if (die_get_inlinefunc(sp_die, addr, &die_mem)) continue; } @@ -1161,49 +725,30 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) if (strtailcmp(src, lf->fname) != 0) continue; - ret = line_range_add_line(src, lineno, lf->lr); - if (ret < 0) - return ret; + /* Copy real path */ + if (!lf->lr->path) + lf->lr->path = strdup(src); + line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); } - - /* - * Dwarf lines doesn't include function declarations. We have to - * check functions list or given function. - */ - if (sp_die) { - src = dwarf_decl_file(sp_die); - if (src && dwarf_decl_line(sp_die, &lineno) == 0 && - (lf->lno_s <= lineno && lf->lno_e >= lineno)) - ret = line_range_add_line(src, lineno, lf->lr); - } else - ret = find_line_range_func_decl_lines(lf); - /* Update status */ - if (ret >= 0) - if (!list_empty(&lf->lr->line_list)) - ret = lf->found = 1; - else - ret = 0; /* Lines are not found */ + if (!list_empty(&lf->lr->line_list)) + lf->found = 1; else { free(lf->lr->path); lf->lr->path = NULL; } - return ret; } static int line_range_inline_cb(Dwarf_Die *in_die, void *data) { - struct dwarf_callback_param *param = data; - - param->retval = find_line_range_by_line(in_die, param->data); + find_line_range_by_line(in_die, (struct line_finder *)data); return DWARF_CB_ABORT; /* No need to find other instances */ } /* Search function from function name */ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) { - struct dwarf_callback_param *param = data; - struct line_finder *lf = param->data; + struct line_finder *lf = (struct line_finder *)data; struct line_range *lr = lf->lr; if (dwarf_tag(sp_die) == DW_TAG_subprogram && @@ -1212,55 +757,44 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; - if (lf->lno_s < 0) /* Overflow */ - lf->lno_s = INT_MAX; - lf->lno_e = lr->offset + lr->end; - if (lf->lno_e < 0) /* Overflow */ + if (!lr->end) lf->lno_e = INT_MAX; - pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); + else + lf->lno_e = lr->offset + lr->end; lr->start = lf->lno_s; lr->end = lf->lno_e; - if (dwarf_func_inline(sp_die)) { - struct dwarf_callback_param _param; - _param.data = (void *)lf; - _param.retval = 0; + if (dwarf_func_inline(sp_die)) dwarf_func_inline_instances(sp_die, - line_range_inline_cb, - &_param); - param->retval = _param.retval; - } else - param->retval = find_line_range_by_line(sp_die, lf); - return DWARF_CB_ABORT; + line_range_inline_cb, lf); + else + find_line_range_by_line(sp_die, lf); + return 1; } - return DWARF_CB_OK; + return 0; } -static int find_line_range_by_func(struct line_finder *lf) +static void find_line_range_by_func(struct line_finder *lf) { - struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); - return param.retval; + dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); } int find_line_range(int fd, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; - int ret = 0; + int ret; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) { - pr_warning("No dwarf info found in the vmlinux - " - "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - return -EBADF; - } + if (!dbg) + return -ENOENT; /* Loop on CUs (Compilation Unit) */ - while (!lf.found && ret >= 0) { - if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) + while (!lf.found) { + ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); + if (ret != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ @@ -1276,18 +810,20 @@ int find_line_range(int fd, struct line_range *lr) if (!lr->file || lf.fname) { if (lr->function) - ret = find_line_range_by_func(&lf); + find_line_range_by_func(&lf); else { lf.lno_s = lr->start; - lf.lno_e = lr->end; - ret = find_line_range_by_line(NULL, &lf); + if (!lr->end) + lf.lno_e = INT_MAX; + else + lf.lno_e = lr->end; + find_line_range_by_line(NULL, &lf); } } off = noff; } pr_debug("path: %lx\n", (unsigned long)lr->path); dwarf_end(dbg); - - return (ret < 0) ? ret : lf.found; + return lf.found; } diff --git a/trunk/tools/perf/util/probe-finder.h b/trunk/tools/perf/util/probe-finder.h index 66f1980e3855..21f7354397b4 100644 --- a/trunk/tools/perf/util/probe-finder.h +++ b/trunk/tools/perf/util/probe-finder.h @@ -3,7 +3,6 @@ #include #include "util.h" -#include "probe-event.h" #define MAX_PATH_LEN 256 #define MAX_PROBE_BUFFER 1024 @@ -15,39 +14,67 @@ static inline int is_c_varname(const char *name) return isalpha(name[0]) || name[0] == '_'; } -#ifdef DWARF_SUPPORT -/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ -extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs, - int max_tevs); +struct probe_point { + char *event; /* Event name */ + char *group; /* Event group */ -/* Find a perf_probe_point from debuginfo */ -extern int find_perf_probe_point(int fd, unsigned long addr, - struct perf_probe_point *ppt); + /* Inputs */ + char *file; /* File name */ + int line; /* Line number */ + char *lazy_line; /* Lazy line pattern */ + char *function; /* Function name */ + int offset; /* Offset bytes */ + + int nr_args; /* Number of arguments */ + char **args; /* Arguments */ + + int retprobe; /* Return probe */ + + /* Output */ + int found; /* Number of found probe points */ + char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ +}; + +/* Line number container */ +struct line_node { + struct list_head list; + unsigned int line; +}; + +/* Line range */ +struct line_range { + char *file; /* File name */ + char *function; /* Function name */ + unsigned int start; /* Start line number */ + unsigned int end; /* End line number */ + int offset; /* Start line offset */ + char *path; /* Real path name */ + struct list_head line_list; /* Visible lines */ +}; + +#ifndef NO_DWARF_SUPPORT +extern int find_probe_point(int fd, struct probe_point *pp); extern int find_line_range(int fd, struct line_range *lr); #include #include struct probe_finder { - struct perf_probe_event *pev; /* Target probe event */ - struct kprobe_trace_event *tevs; /* Result trace events */ - int ntevs; /* Number of trace events */ - int max_tevs; /* Max number of trace events */ + struct probe_point *pp; /* Target probe point */ /* For function searching */ - int lno; /* Line number */ Dwarf_Addr addr; /* Address */ - const char *fname; /* Real file name */ + const char *fname; /* File name */ + int lno; /* Line number */ Dwarf_Die cu_die; /* Current CU */ - struct list_head lcache; /* Line cache for lazy match */ /* For variable searching */ - Dwarf_CFI *cfi; /* Call Frame Information */ Dwarf_Op *fb_ops; /* Frame base attribute */ - struct perf_probe_arg *pvar; /* Current target variable */ - struct kprobe_trace_arg *tvar; /* Current result variable */ + const char *var; /* Current variable name */ + char *buf; /* Current output buffer */ + int len; /* Length of output buffer */ + struct list_head lcache; /* Line cache for lazy match */ }; struct line_finder { @@ -60,6 +87,6 @@ struct line_finder { int found; }; -#endif /* DWARF_SUPPORT */ +#endif /* NO_DWARF_SUPPORT */ #endif /*_PROBE_FINDER_H */ diff --git a/trunk/tools/perf/util/session.c b/trunk/tools/perf/util/session.c index 5d353e70fe26..eed1cb889008 100644 --- a/trunk/tools/perf/util/session.c +++ b/trunk/tools/perf/util/session.c @@ -14,16 +14,6 @@ static int perf_session__open(struct perf_session *self, bool force) { struct stat input_stat; - if (!strcmp(self->filename, "-")) { - self->fd_pipe = true; - self->fd = STDIN_FILENO; - - if (perf_header__read(self, self->fd) < 0) - pr_err("incompatible file format"); - - return 0; - } - self->fd = open(self->filename, O_RDONLY); if (self->fd < 0) { pr_err("failed to open file: %s", self->filename); @@ -48,7 +38,7 @@ static int perf_session__open(struct perf_session *self, bool force) goto out_close; } - if (perf_header__read(self, self->fd) < 0) { + if (perf_header__read(&self->header, self->fd) < 0) { pr_err("incompatible file format"); goto out_close; } @@ -62,22 +52,12 @@ static int perf_session__open(struct perf_session *self, bool force) return -1; } -void perf_session__update_sample_type(struct perf_session *self) +static inline int perf_session__create_kernel_maps(struct perf_session *self) { - self->sample_type = perf_header__sample_type(&self->header); + return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); } -int perf_session__create_kernel_maps(struct perf_session *self) -{ - struct rb_root *machines = &self->machines; - int ret = machines__create_kernel_maps(machines, HOST_KERNEL_ID); - - if (ret >= 0) - ret = machines__create_guest_kernel_maps(machines); - return ret; -} - -struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) +struct perf_session *perf_session__new(const char *filename, int mode, bool force) { size_t len = filename ? strlen(filename) + 1 : 0; struct perf_session *self = zalloc(sizeof(*self) + len); @@ -96,10 +76,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->cwd = NULL; self->cwdlen = 0; self->unknown_events = 0; - self->machines = RB_ROOT; - self->repipe = repipe; - self->ordered_samples.flush_limit = ULLONG_MAX; - INIT_LIST_HEAD(&self->ordered_samples.samples_head); + map_groups__init(&self->kmaps); if (mode == O_RDONLY) { if (perf_session__open(self, force) < 0) @@ -113,7 +90,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc goto out_delete; } - perf_session__update_sample_type(self); + self->sample_type = perf_header__sample_type(&self->header); out: return self; out_free: @@ -140,17 +117,22 @@ static bool symbol__match_parent_regex(struct symbol *sym) return 0; } -struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent) +struct symbol **perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent) { u8 cpumode = PERF_RECORD_MISC_USER; + struct symbol **syms = NULL; unsigned int i; - struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); - if (!syms) - return NULL; + if (symbol_conf.use_callchain) { + syms = calloc(chain->nr, sizeof(*syms)); + if (!syms) { + fprintf(stderr, "Can't allocate memory for symbols\n"); + exit(-1); + } + } for (i = 0; i < chain->nr; i++) { u64 ip = chain->ips[i]; @@ -170,17 +152,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, continue; } - al.filtered = false; thread__find_addr_location(thread, self, cpumode, - MAP__FUNCTION, thread->pid, ip, &al, NULL); + MAP__FUNCTION, ip, &al, NULL); if (al.sym != NULL) { if (sort__has_parent && !*parent && symbol__match_parent_regex(al.sym)) *parent = al.sym; if (!symbol_conf.use_callchain) break; - syms[i].map = al.map; - syms[i].sym = al.sym; + syms[i] = al.sym; } } @@ -214,14 +194,6 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) handler->throttle = process_event_stub; if (handler->unthrottle == NULL) handler->unthrottle = process_event_stub; - if (handler->attr == NULL) - handler->attr = process_event_stub; - if (handler->event_type == NULL) - handler->event_type = process_event_stub; - if (handler->tracing_data == NULL) - handler->tracing_data = process_event_stub; - if (handler->build_id == NULL) - handler->build_id = process_event_stub; } static const char *event__name[] = { @@ -235,23 +207,16 @@ static const char *event__name[] = { [PERF_RECORD_FORK] = "FORK", [PERF_RECORD_READ] = "READ", [PERF_RECORD_SAMPLE] = "SAMPLE", - [PERF_RECORD_HEADER_ATTR] = "ATTR", - [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", - [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", - [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", }; -unsigned long event__total[PERF_RECORD_HEADER_MAX]; +unsigned long event__total[PERF_RECORD_MAX]; void event__print_totals(void) { int i; - for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { - if (!event__name[i]) - continue; + for (i = 0; i < PERF_RECORD_MAX; ++i) pr_info("%10s events: %10ld\n", event__name[i], event__total[i]); - } } void mem_bswap_64(void *src, int byte_size) @@ -305,37 +270,6 @@ static void event__read_swap(event_t *self) self->read.id = bswap_64(self->read.id); } -static void event__attr_swap(event_t *self) -{ - size_t size; - - self->attr.attr.type = bswap_32(self->attr.attr.type); - self->attr.attr.size = bswap_32(self->attr.attr.size); - self->attr.attr.config = bswap_64(self->attr.attr.config); - self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period); - self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type); - self->attr.attr.read_format = bswap_64(self->attr.attr.read_format); - self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events); - self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type); - self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr); - self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len); - - size = self->header.size; - size -= (void *)&self->attr.id - (void *)self; - mem_bswap_64(self->attr.id, size); -} - -static void event__event_type_swap(event_t *self) -{ - self->event_type.event_type.event_id = - bswap_64(self->event_type.event_type.event_id); -} - -static void event__tracing_data_swap(event_t *self) -{ - self->tracing_data.size = bswap_32(self->tracing_data.size); -} - typedef void (*event__swap_op)(event_t *self); static event__swap_op event__swap_ops[] = { @@ -346,185 +280,9 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_LOST] = event__all64_swap, [PERF_RECORD_READ] = event__read_swap, [PERF_RECORD_SAMPLE] = event__all64_swap, - [PERF_RECORD_HEADER_ATTR] = event__attr_swap, - [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, - [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap, - [PERF_RECORD_HEADER_BUILD_ID] = NULL, - [PERF_RECORD_HEADER_MAX] = NULL, -}; - -struct sample_queue { - u64 timestamp; - struct sample_event *event; - struct list_head list; + [PERF_RECORD_MAX] = NULL, }; -#define FLUSH_PERIOD (2 * NSEC_PER_SEC) - -static void flush_sample_queue(struct perf_session *s, - struct perf_event_ops *ops) -{ - struct list_head *head = &s->ordered_samples.samples_head; - u64 limit = s->ordered_samples.flush_limit; - struct sample_queue *tmp, *iter; - - if (!ops->ordered_samples) - return; - - list_for_each_entry_safe(iter, tmp, head, list) { - if (iter->timestamp > limit) - return; - - if (iter == s->ordered_samples.last_inserted) - s->ordered_samples.last_inserted = NULL; - - ops->sample((event_t *)iter->event, s); - - s->ordered_samples.last_flush = iter->timestamp; - list_del(&iter->list); - free(iter->event); - free(iter); - } -} - -static void __queue_sample_end(struct sample_queue *new, struct list_head *head) -{ - struct sample_queue *iter; - - list_for_each_entry_reverse(iter, head, list) { - if (iter->timestamp < new->timestamp) { - list_add(&new->list, &iter->list); - return; - } - } - - list_add(&new->list, head); -} - -static void __queue_sample_before(struct sample_queue *new, - struct sample_queue *iter, - struct list_head *head) -{ - list_for_each_entry_continue_reverse(iter, head, list) { - if (iter->timestamp < new->timestamp) { - list_add(&new->list, &iter->list); - return; - } - } - - list_add(&new->list, head); -} - -static void __queue_sample_after(struct sample_queue *new, - struct sample_queue *iter, - struct list_head *head) -{ - list_for_each_entry_continue(iter, head, list) { - if (iter->timestamp > new->timestamp) { - list_add_tail(&new->list, &iter->list); - return; - } - } - list_add_tail(&new->list, head); -} - -/* The queue is ordered by time */ -static void __queue_sample_event(struct sample_queue *new, - struct perf_session *s) -{ - struct sample_queue *last_inserted = s->ordered_samples.last_inserted; - struct list_head *head = &s->ordered_samples.samples_head; - - - if (!last_inserted) { - __queue_sample_end(new, head); - return; - } - - /* - * Most of the time the current event has a timestamp - * very close to the last event inserted, unless we just switched - * to another event buffer. Having a sorting based on a list and - * on the last inserted event that is close to the current one is - * probably more efficient than an rbtree based sorting. - */ - if (last_inserted->timestamp >= new->timestamp) - __queue_sample_before(new, last_inserted, head); - else - __queue_sample_after(new, last_inserted, head); -} - -static int queue_sample_event(event_t *event, struct sample_data *data, - struct perf_session *s, - struct perf_event_ops *ops) -{ - u64 timestamp = data->time; - struct sample_queue *new; - u64 flush_limit; - - - if (s->ordered_samples.flush_limit == ULLONG_MAX) - s->ordered_samples.flush_limit = timestamp + FLUSH_PERIOD; - - if (timestamp < s->ordered_samples.last_flush) { - printf("Warning: Timestamp below last timeslice flush\n"); - return -EINVAL; - } - - new = malloc(sizeof(*new)); - if (!new) - return -ENOMEM; - - new->timestamp = timestamp; - - new->event = malloc(event->header.size); - if (!new->event) { - free(new); - return -ENOMEM; - } - - memcpy(new->event, event, event->header.size); - - __queue_sample_event(new, s); - s->ordered_samples.last_inserted = new; - - /* - * We want to have a slice of events covering 2 * FLUSH_PERIOD - * If FLUSH_PERIOD is big enough, it ensures every events that occured - * in the first half of the timeslice have all been buffered and there - * are none remaining (we need that because of the weakly ordered - * event recording we have). Then once we reach the 2 * FLUSH_PERIOD - * timeslice, we flush the first half to be gentle with the memory - * (the second half can still get new events in the middle, so wait - * another period to flush it) - */ - flush_limit = s->ordered_samples.flush_limit; - - if (new->timestamp > flush_limit && - new->timestamp - flush_limit > FLUSH_PERIOD) { - s->ordered_samples.flush_limit += FLUSH_PERIOD; - flush_sample_queue(s, ops); - } - - return 0; -} - -static int perf_session__process_sample(event_t *event, struct perf_session *s, - struct perf_event_ops *ops) -{ - struct sample_data data; - - if (!ops->ordered_samples) - return ops->sample(event, s); - - bzero(&data, sizeof(struct sample_data)); - event__parse_sample(event, s->sample_type, &data); - - queue_sample_event(event, &data, s, ops); - - return 0; -} - static int perf_session__process_event(struct perf_session *self, event_t *event, struct perf_event_ops *ops, @@ -532,7 +290,7 @@ static int perf_session__process_event(struct perf_session *self, { trace_event(event); - if (event->header.type < PERF_RECORD_HEADER_MAX) { + if (event->header.type < PERF_RECORD_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); @@ -545,7 +303,7 @@ static int perf_session__process_event(struct perf_session *self, switch (event->header.type) { case PERF_RECORD_SAMPLE: - return perf_session__process_sample(event, self, ops); + return ops->sample(event, self); case PERF_RECORD_MMAP: return ops->mmap(event, self); case PERF_RECORD_COMM: @@ -562,16 +320,6 @@ static int perf_session__process_event(struct perf_session *self, return ops->throttle(event, self); case PERF_RECORD_UNTHROTTLE: return ops->unthrottle(event, self); - case PERF_RECORD_HEADER_ATTR: - return ops->attr(event, self); - case PERF_RECORD_HEADER_EVENT_TYPE: - return ops->event_type(event, self); - case PERF_RECORD_HEADER_TRACING_DATA: - /* setup for reading amidst mmap */ - lseek(self->fd, offset + head, SEEK_SET); - return ops->tracing_data(event, self); - case PERF_RECORD_HEADER_BUILD_ID: - return ops->build_id(event, self); default: self->unknown_events++; return -1; @@ -585,111 +333,56 @@ void perf_event_header__bswap(struct perf_event_header *self) self->size = bswap_16(self->size); } -static struct thread *perf_session__register_idle_thread(struct perf_session *self) -{ - struct thread *thread = perf_session__findnew(self, 0); - - if (thread == NULL || thread__set_comm(thread, "swapper")) { - pr_err("problem inserting idle task.\n"); - thread = NULL; - } - - return thread; -} - -int do_read(int fd, void *buf, size_t size) -{ - void *buf_start = buf; - - while (size) { - int ret = read(fd, buf, size); - - if (ret <= 0) - return ret; - - size -= ret; - buf += ret; - } - - return buf - buf_start; -} - -#define session_done() (*(volatile int *)(&session_done)) -volatile int session_done; - -static int __perf_session__process_pipe_events(struct perf_session *self, - struct perf_event_ops *ops) +int perf_header__read_build_ids(struct perf_header *self, + int input, u64 offset, u64 size) { - event_t event; - uint32_t size; - int skip = 0; - u64 head; - int err; - void *p; + struct build_id_event bev; + char filename[PATH_MAX]; + u64 limit = offset + size; + int err = -1; - perf_event_ops__fill_defaults(ops); + while (offset < limit) { + struct dso *dso; + ssize_t len; + struct list_head *head = &dsos__user; - head = 0; -more: - err = do_read(self->fd, &event, sizeof(struct perf_event_header)); - if (err <= 0) { - if (err == 0) - goto done; + if (read(input, &bev, sizeof(bev)) != sizeof(bev)) + goto out; - pr_err("failed to read event header\n"); - goto out_err; - } + if (self->needs_swap) + perf_event_header__bswap(&bev.header); - if (self->header.needs_swap) - perf_event_header__bswap(&event.header); + len = bev.header.size - sizeof(bev); + if (read(input, filename, len) != len) + goto out; - size = event.header.size; - if (size == 0) - size = 8; - - p = &event; - p += sizeof(struct perf_event_header); + if (bev.header.misc & PERF_RECORD_MISC_KERNEL) + head = &dsos__kernel; - err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); - if (err <= 0) { - if (err == 0) { - pr_err("unexpected end of event stream\n"); - goto done; + dso = __dsos__findnew(head, filename); + if (dso != NULL) { + dso__set_build_id(dso, &bev.build_id); + if (head == &dsos__kernel && filename[0] == '[') + dso->kernel = 1; } - pr_err("failed to read event data\n"); - goto out_err; + offset += bev.header.size; } + err = 0; +out: + return err; +} - if (size == 0 || - (skip = perf_session__process_event(self, &event, ops, - 0, head)) < 0) { - dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", - head, event.header.size, event.header.type); - /* - * assume we lost track of the stream, check alignment, and - * increment a single u64 in the hope to catch on again 'soon'. - */ - if (unlikely(head & 7)) - head &= ~7ULL; +static struct thread *perf_session__register_idle_thread(struct perf_session *self) +{ + struct thread *thread = perf_session__findnew(self, 0); - size = 8; + if (thread == NULL || thread__set_comm(thread, "swapper")) { + pr_err("problem inserting idle task.\n"); + thread = NULL; } - head += size; - - dump_printf("\n%#Lx [%#x]: event: %d\n", - head, event.header.size, event.header.type); - - if (skip > 0) - head += skip; - - if (!session_done()) - goto more; -done: - err = 0; -out_err: - return err; + return thread; } int __perf_session__process_events(struct perf_session *self, @@ -703,10 +396,6 @@ int __perf_session__process_events(struct perf_session *self, event_t *event; uint32_t size; char *buf; - struct ui_progress *progress = ui_progress__new("Processing events...", - self->size); - if (progress == NULL) - return -1; perf_event_ops__fill_defaults(ops); @@ -735,7 +424,6 @@ int __perf_session__process_events(struct perf_session *self, more: event = (event_t *)(buf + head); - ui_progress__update(progress, offset); if (self->header.needs_swap) perf_event_header__bswap(&event->header); @@ -785,11 +473,7 @@ int __perf_session__process_events(struct perf_session *self, goto more; done: err = 0; - /* do the final flush for ordered samples */ - self->ordered_samples.flush_limit = ULLONG_MAX; - flush_sample_queue(self, ops); out_err: - ui_progress__delete(progress); return err; } @@ -818,13 +502,9 @@ int perf_session__process_events(struct perf_session *self, self->cwdlen = strlen(self->cwd); } - if (!self->fd_pipe) - err = __perf_session__process_events(self, - self->header.data_offset, - self->header.data_size, - self->size, ops); - else - err = __perf_session__process_pipe_events(self, ops); + err = __perf_session__process_events(self, self->header.data_offset, + self->header.data_size, + self->size, ops); out_err: return err; } @@ -839,34 +519,56 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg) return true; } -int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, +int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, const char *symbol_name, u64 addr) { char *bracket; enum map_type i; - struct ref_reloc_sym *ref; - ref = zalloc(sizeof(struct ref_reloc_sym)); - if (ref == NULL) + self->ref_reloc_sym.name = strdup(symbol_name); + if (self->ref_reloc_sym.name == NULL) return -ENOMEM; - ref->name = strdup(symbol_name); - if (ref->name == NULL) { - free(ref); - return -ENOMEM; - } - - bracket = strchr(ref->name, ']'); + bracket = strchr(self->ref_reloc_sym.name, ']'); if (bracket) *bracket = '\0'; - ref->addr = addr; + self->ref_reloc_sym.addr = addr; for (i = 0; i < MAP__NR_TYPES; ++i) { - struct kmap *kmap = map__kmap(maps[i]); - kmap->ref_reloc_sym = ref; + struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); + kmap->ref_reloc_sym = &self->ref_reloc_sym; } return 0; } + +static u64 map__reloc_map_ip(struct map *map, u64 ip) +{ + return ip + (s64)map->pgoff; +} + +static u64 map__reloc_unmap_ip(struct map *map, u64 ip) +{ + return ip - (s64)map->pgoff; +} + +void map__reloc_vmlinux(struct map *self) +{ + struct kmap *kmap = map__kmap(self); + s64 reloc; + + if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) + return; + + reloc = (kmap->ref_reloc_sym->unrelocated_addr - + kmap->ref_reloc_sym->addr); + + if (!reloc) + return; + + self->map_ip = map__reloc_map_ip; + self->unmap_ip = map__reloc_unmap_ip; + self->pgoff = reloc; +} diff --git a/trunk/tools/perf/util/session.h b/trunk/tools/perf/util/session.h index f2b2c6a3a49d..5c33417eebb3 100644 --- a/trunk/tools/perf/util/session.h +++ b/trunk/tools/perf/util/session.h @@ -8,36 +8,27 @@ #include #include "../../../include/linux/perf_event.h" -struct sample_queue; struct ip_callchain; struct thread; -struct ordered_samples { - u64 last_flush; - u64 flush_limit; - struct list_head samples_head; - struct sample_queue *last_inserted; -}; - struct perf_session { struct perf_header header; unsigned long size; unsigned long mmap_window; + struct map_groups kmaps; struct rb_root threads; struct thread *last_match; - struct rb_root machines; + struct map *vmlinux_maps[MAP__NR_TYPES]; struct events_stats events_stats; struct rb_root stats_by_id; unsigned long event_total[PERF_RECORD_MAX]; unsigned long unknown_events; struct rb_root hists; u64 sample_type; + struct ref_reloc_sym ref_reloc_sym; int fd; - bool fd_pipe; - bool repipe; int cwdlen; char *cwd; - struct ordered_samples ordered_samples; char filename[0]; }; @@ -52,15 +43,10 @@ struct perf_event_ops { lost, read, throttle, - unthrottle, - attr, - event_type, - tracing_data, - build_id; - bool ordered_samples; + unthrottle; }; -struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); +struct perf_session *perf_session__new(const char *filename, int mode, bool force); void perf_session__delete(struct perf_session *self); void perf_event_header__bswap(struct perf_event_header *self); @@ -71,74 +57,33 @@ int __perf_session__process_events(struct perf_session *self, int perf_session__process_events(struct perf_session *self, struct perf_event_ops *event_ops); -struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent); +struct symbol **perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent); bool perf_session__has_traces(struct perf_session *self, const char *msg); -int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, +int perf_header__read_build_ids(struct perf_header *self, int input, + u64 offset, u64 file_size); + +int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, const char *symbol_name, u64 addr); void mem_bswap_64(void *src, int byte_size); -int perf_session__create_kernel_maps(struct perf_session *self); - -int do_read(int fd, void *buf, size_t size); -void perf_session__update_sample_type(struct perf_session *self); - -#ifdef NO_NEWT_SUPPORT -static inline int perf_session__browse_hists(struct rb_root *hists __used, - u64 nr_hists __used, - u64 session_total __used, - const char *helpline __used, - const char *input_name __used) -{ - return 0; -} -#else -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name); -#endif - -static inline -struct machine *perf_session__find_host_machine(struct perf_session *self) -{ - return machines__find_host(&self->machines); -} - -static inline -struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) -{ - return machines__find(&self->machines, pid); -} - -static inline -struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) -{ - return machines__findnew(&self->machines, pid); -} - -static inline -void perf_session__process_machines(struct perf_session *self, - machine__process_t process) -{ - return machines__process(&self->machines, process, self); -} - -static inline -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +static inline int __perf_session__create_kernel_maps(struct perf_session *self, + struct dso *kernel) { - return machines__fprintf_dsos(&self->machines, fp); + return __map_groups__create_kernel_maps(&self->kmaps, + self->vmlinux_maps, kernel); } -static inline -size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, - bool with_hits) +static inline struct map * + perf_session__new_module_map(struct perf_session *self, + u64 start, const char *filename) { - return machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); + return map_groups__new_module(&self->kmaps, start, filename); } #endif /* __PERF_SESSION_H */ diff --git a/trunk/tools/perf/util/sort.c b/trunk/tools/perf/util/sort.c index da30b305fba0..cb0f327de9e8 100644 --- a/trunk/tools/perf/util/sort.c +++ b/trunk/tools/perf/util/sort.c @@ -18,50 +18,39 @@ char * field_sep; LIST_HEAD(hist_entry__sort_list); -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width); -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width); -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width); -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width); -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width); - struct sort_entry sort_thread = { - .se_header = "Command: Pid", - .se_cmp = sort__thread_cmp, - .se_snprintf = hist_entry__thread_snprintf, - .se_width = &threads__col_width, + .header = "Command: Pid", + .cmp = sort__thread_cmp, + .print = sort__thread_print, + .width = &threads__col_width, }; struct sort_entry sort_comm = { - .se_header = "Command", - .se_cmp = sort__comm_cmp, - .se_collapse = sort__comm_collapse, - .se_snprintf = hist_entry__comm_snprintf, - .se_width = &comms__col_width, + .header = "Command", + .cmp = sort__comm_cmp, + .collapse = sort__comm_collapse, + .print = sort__comm_print, + .width = &comms__col_width, }; struct sort_entry sort_dso = { - .se_header = "Shared Object", - .se_cmp = sort__dso_cmp, - .se_snprintf = hist_entry__dso_snprintf, - .se_width = &dsos__col_width, + .header = "Shared Object", + .cmp = sort__dso_cmp, + .print = sort__dso_print, + .width = &dsos__col_width, }; struct sort_entry sort_sym = { - .se_header = "Symbol", - .se_cmp = sort__sym_cmp, - .se_snprintf = hist_entry__sym_snprintf, + .header = "Symbol", + .cmp = sort__sym_cmp, + .print = sort__sym_print, }; struct sort_entry sort_parent = { - .se_header = "Parent symbol", - .se_cmp = sort__parent_cmp, - .se_snprintf = hist_entry__parent_snprintf, - .se_width = &parent_symbol__col_width, + .header = "Parent symbol", + .cmp = sort__parent_cmp, + .print = sort__parent_print, + .width = &parent_symbol__col_width, }; struct sort_dimension { @@ -96,38 +85,45 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) return right->thread->pid - left->thread->pid; } -static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) +int repsep_fprintf(FILE *fp, const char *fmt, ...) { int n; va_list ap; va_start(ap, fmt); - n = vsnprintf(bf, size, fmt, ap); - if (field_sep && n > 0) { - char *sep = bf; - - while (1) { - sep = strchr(sep, *field_sep); - if (sep == NULL) - break; - *sep = '.'; + if (!field_sep) + n = vfprintf(fp, fmt, ap); + else { + char *bf = NULL; + n = vasprintf(&bf, fmt, ap); + if (n > 0) { + char *sep = bf; + + while (1) { + sep = strchr(sep, *field_sep); + if (sep == NULL) + break; + *sep = '.'; + } } + fputs(bf, fp); + free(bf); } va_end(ap); return n; } -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width) +size_t +sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) { - return repsep_snprintf(bf, size, "%*s:%5d", width, + return repsep_fprintf(fp, "%*s:%5d", width - 6, self->thread->comm ?: "", self->thread->pid); } -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width) +size_t +sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) { - return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); + return repsep_fprintf(fp, "%*s", width, self->thread->comm); } /* --sort dso */ @@ -135,8 +131,8 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, int64_t sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) { - struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; - struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; + struct dso *dso_l = left->map ? left->map->dso : NULL; + struct dso *dso_r = right->map ? right->map->dso : NULL; const char *dso_name_l, *dso_name_r; if (!dso_l || !dso_r) @@ -153,16 +149,16 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(dso_name_l, dso_name_r); } -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width) +size_t +sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) { - if (self->ms.map && self->ms.map->dso) { - const char *dso_name = !verbose ? self->ms.map->dso->short_name : - self->ms.map->dso->long_name; - return repsep_snprintf(bf, size, "%-*s", width, dso_name); + if (self->map && self->map->dso) { + const char *dso_name = !verbose ? self->map->dso->short_name : + self->map->dso->long_name; + return repsep_fprintf(fp, "%-*s", width, dso_name); } - return repsep_snprintf(bf, size, "%*Lx", width, self->ip); + return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); } /* --sort symbol */ @@ -172,31 +168,31 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) { u64 ip_l, ip_r; - if (left->ms.sym == right->ms.sym) + if (left->sym == right->sym) return 0; - ip_l = left->ms.sym ? left->ms.sym->start : left->ip; - ip_r = right->ms.sym ? right->ms.sym->start : right->ip; + ip_l = left->sym ? left->sym->start : left->ip; + ip_r = right->sym ? right->sym->start : right->ip; return (int64_t)(ip_r - ip_l); } -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width __used) + +size_t +sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) { size_t ret = 0; if (verbose) { - char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; - ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); + char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; + ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); } - ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); - if (self->ms.sym) - ret += repsep_snprintf(bf + ret, size - ret, "%s", - self->ms.sym->name); + ret += repsep_fprintf(fp, "[%c] ", self->level); + if (self->sym) + ret += repsep_fprintf(fp, "%s", self->sym->name); else - ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); + ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); return ret; } @@ -235,10 +231,10 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(sym_l->name, sym_r->name); } -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, - size_t size, unsigned int width) +size_t +sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) { - return repsep_snprintf(bf, size, "%-*s", width, + return repsep_fprintf(fp, "%-*s", width, self->parent ? self->parent->name : "[other]"); } @@ -255,7 +251,7 @@ int sort_dimension__add(const char *tok) if (strncasecmp(tok, sd->name, strlen(tok))) continue; - if (sd->entry->se_collapse) + if (sd->entry->collapse) sort__need_collapse = 1; if (sd->entry == &sort_parent) { @@ -264,8 +260,9 @@ int sort_dimension__add(const char *tok) char err[BUFSIZ]; regerror(ret, &parent_regex, err, sizeof(err)); - pr_err("Invalid regex: %s\n%s", parent_pattern, err); - return -EINVAL; + fprintf(stderr, "Invalid regex: %s\n%s", + parent_pattern, err); + exit(-1); } sort__has_parent = 1; } diff --git a/trunk/tools/perf/util/sort.h b/trunk/tools/perf/util/sort.h index b7c54eeed9c9..753f9ea99fb0 100644 --- a/trunk/tools/perf/util/sort.h +++ b/trunk/tools/perf/util/sort.h @@ -44,28 +44,18 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; u64 count; - u64 count_sys; - u64 count_us; - u64 count_guest_sys; - u64 count_guest_us; - - /* - * XXX WARNING! - * thread _has_ to come after ms, see - * hist_browser__selected_thread in util/newt.c - */ - struct map_symbol ms; struct thread *thread; + struct map *map; + struct symbol *sym; u64 ip; char level; - u8 filtered; - struct symbol *parent; + struct symbol *parent; + struct callchain_node callchain; union { unsigned long position; struct hist_entry *pair; struct rb_root sorted_chain; }; - struct callchain_node callchain[0]; }; enum sort_type { @@ -83,13 +73,12 @@ enum sort_type { struct sort_entry { struct list_head list; - const char *se_header; + const char *header; - int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); - int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); - int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, - unsigned int width); - unsigned int *se_width; + int64_t (*cmp)(struct hist_entry *, struct hist_entry *); + int64_t (*collapse)(struct hist_entry *, struct hist_entry *); + size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); + unsigned int *width; bool elide; }; @@ -98,6 +87,7 @@ extern struct list_head hist_entry__sort_list; void setup_sorting(const char * const usagestr[], const struct option *opts); +extern int repsep_fprintf(FILE *fp, const char *fmt, ...); extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); diff --git a/trunk/tools/perf/util/string.c b/trunk/tools/perf/util/string.c index 0409fc7c0058..a175949ed216 100644 --- a/trunk/tools/perf/util/string.c +++ b/trunk/tools/perf/util/string.c @@ -1,5 +1,48 @@ -#include "util.h" #include "string.h" +#include "util.h" + +static int hex(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + if ((ch >= 'A') && (ch <= 'F')) + return ch - 'A' + 10; + return -1; +} + +/* + * While we find nice hex chars, build a long_val. + * Return number of chars processed. + */ +int hex2u64(const char *ptr, u64 *long_val) +{ + const char *p = ptr; + *long_val = 0; + + while (*p) { + const int hex_val = hex(*p); + + if (hex_val < 0) + break; + + *long_val = (*long_val << 4) | hex_val; + p++; + } + + return p - ptr; +} + +char *strxfrchar(char *s, char from, char to) +{ + char *p = s; + + while ((p = strchr(p, from)) != NULL) + *p++ = to; + + return s; +} #define K 1024LL /* diff --git a/trunk/tools/perf/util/string.h b/trunk/tools/perf/util/string.h new file mode 100644 index 000000000000..542e44de3719 --- /dev/null +++ b/trunk/tools/perf/util/string.h @@ -0,0 +1,18 @@ +#ifndef __PERF_STRING_H_ +#define __PERF_STRING_H_ + +#include +#include "types.h" + +int hex2u64(const char *ptr, u64 *val); +char *strxfrchar(char *s, char from, char to); +s64 perf_atoll(const char *str); +char **argv_split(const char *str, int *argcp); +void argv_free(char **argv); +bool strglobmatch(const char *str, const char *pat); +bool strlazymatch(const char *str, const char *pat); + +#define _STR(x) #x +#define STR(x) _STR(x) + +#endif /* __PERF_STRING_H */ diff --git a/trunk/tools/perf/util/symbol.c b/trunk/tools/perf/util/symbol.c index 4c0146a49063..c458c4a371d1 100644 --- a/trunk/tools/perf/util/symbol.c +++ b/trunk/tools/perf/util/symbol.c @@ -1,19 +1,13 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "util.h" +#include "../perf.h" +#include "sort.h" +#include "string.h" #include "symbol.h" -#include "strlist.h" +#include "thread.h" +#include "debug.h" + +#include #include #include #include @@ -24,12 +18,22 @@ #define NT_GNU_BUILD_ID 3 #endif +enum dso_origin { + DSO__ORIG_KERNEL = 0, + DSO__ORIG_JAVA_JIT, + DSO__ORIG_BUILD_ID_CACHE, + DSO__ORIG_FEDORA, + DSO__ORIG_UBUNTU, + DSO__ORIG_BUILDID, + DSO__ORIG_DSO, + DSO__ORIG_KMODULE, + DSO__ORIG_NOT_FOUND, +}; + static void dsos__add(struct list_head *head, struct dso *dso); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); static int dso__load_kernel_sym(struct dso *self, struct map *map, symbol_filter_t filter); -static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, - symbol_filter_t filter); static int vmlinux_path__nr_entries; static char **vmlinux_path; @@ -122,8 +126,8 @@ static void map_groups__fixup_end(struct map_groups *self) static struct symbol *symbol__new(u64 start, u64 len, const char *name) { size_t namelen = strlen(name) + 1; - struct symbol *self = calloc(1, (symbol_conf.priv_size + - sizeof(*self) + namelen)); + struct symbol *self = zalloc(symbol_conf.priv_size + + sizeof(*self) + namelen); if (self == NULL) return NULL; @@ -174,7 +178,7 @@ static void dso__set_basename(struct dso *self) struct dso *dso__new(const char *name) { - struct dso *self = calloc(1, sizeof(*self) + strlen(name) + 1); + struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1); if (self != NULL) { int i; @@ -188,8 +192,6 @@ struct dso *dso__new(const char *name) self->loaded = 0; self->sorted_by_name = 0; self->has_build_id = 0; - self->kernel = DSO_TYPE_USER; - INIT_LIST_HEAD(&self->node); } return self; @@ -406,9 +408,12 @@ int kallsyms__parse(const char *filename, void *arg, char *symbol_name; line_len = getline(&line, &n, file); - if (line_len < 0 || !line) + if (line_len < 0) break; + if (!line) + goto out_failure; + line[--line_len] = '\0'; /* \n */ len = hex2u64(line, &start); @@ -460,7 +465,6 @@ static int map__process_kallsym_symbol(void *arg, const char *name, * map__split_kallsyms, when we have split the maps per module */ symbols__insert(root, sym); - return 0; } @@ -485,7 +489,6 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, symbol_filter_t filter) { struct map_groups *kmaps = map__kmap(map)->kmaps; - struct machine *machine = kmaps->machine; struct map *curr_map = map; struct symbol *pos; int count = 0; @@ -507,33 +510,15 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, *module++ = '\0'; if (strcmp(curr_map->dso->short_name, module)) { - if (curr_map != map && - self->kernel == DSO_TYPE_GUEST_KERNEL && - machine__is_default_guest(machine)) { - /* - * We assume all symbols of a module are - * continuous in * kallsyms, so curr_map - * points to a module and all its - * symbols are in its kmap. Mark it as - * loaded. - */ - dso__set_loaded(curr_map->dso, - curr_map->type); - } - - curr_map = map_groups__find_by_name(kmaps, - map->type, module); + curr_map = map_groups__find_by_name(kmaps, map->type, module); if (curr_map == NULL) { - pr_err("%s/proc/{kallsyms,modules} " + pr_debug("/proc/{kallsyms,modules} " "inconsistency while looking " - "for \"%s\" module!\n", - machine->root_dir, module); - curr_map = map; - goto discard_symbol; + "for \"%s\" module!\n", module); + return -1; } - if (curr_map->dso->loaded && - !machine__is_default_guest(machine)) + if (curr_map->dso->loaded) goto discard_symbol; } /* @@ -546,21 +531,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, char dso_name[PATH_MAX]; struct dso *dso; - if (self->kernel == DSO_TYPE_GUEST_KERNEL) - snprintf(dso_name, sizeof(dso_name), - "[guest.kernel].%d", - kernel_range++); - else - snprintf(dso_name, sizeof(dso_name), - "[kernel].%d", - kernel_range++); + snprintf(dso_name, sizeof(dso_name), "[kernel].%d", + kernel_range++); dso = dso__new(dso_name); if (dso == NULL) return -1; - dso->kernel = self->kernel; - curr_map = map__new2(pos->start, dso, map->type); if (curr_map == NULL) { dso__delete(dso); @@ -584,12 +561,6 @@ discard_symbol: rb_erase(&pos->rb_node, root); } } - if (curr_map != map && - self->kernel == DSO_TYPE_GUEST_KERNEL && - machine__is_default_guest(kmaps->machine)) { - dso__set_loaded(curr_map->dso, curr_map->type); - } - return count; } @@ -600,10 +571,7 @@ int dso__load_kallsyms(struct dso *self, const char *filename, return -1; symbols__fixup_end(&self->symbols[map->type]); - if (self->kernel == DSO_TYPE_GUEST_KERNEL) - self->origin = DSO__ORIG_GUEST_KERNEL; - else - self->origin = DSO__ORIG_KERNEL; + self->origin = DSO__ORIG_KERNEL; return dso__split_kallsyms(self, map, filter); } @@ -902,8 +870,8 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, if (err == 0) return nr; out: - pr_debug("%s: problems reading %s PLT info.\n", - __func__, self->long_name); + pr_warning("%s: problems reading %s PLT info.\n", + __func__, self->long_name); return 0; } @@ -990,7 +958,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, nr_syms = shdr.sh_size / shdr.sh_entsize; memset(&sym, 0, sizeof(sym)); - if (self->kernel == DSO_TYPE_USER) { + if (!self->kernel) { self->adjust_symbols = (ehdr.e_type == ET_EXEC || elf_section_by_name(elf, &ehdr, &shdr, ".gnu.prelink_undo", @@ -1022,7 +990,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, section_name = elf_sec__name(&shdr, secstrs); - if (self->kernel != DSO_TYPE_USER || kmodule) { + if (self->kernel || kmodule) { char dso_name[PATH_MAX]; if (strcmp(section_name, @@ -1049,7 +1017,6 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, curr_dso = dso__new(dso_name); if (curr_dso == NULL) goto out_elf_end; - curr_dso->kernel = self->kernel; curr_map = map__new2(start, curr_dso, map->type); if (curr_map == NULL) { @@ -1058,9 +1025,9 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, } curr_map->map_ip = identity__map_ip; curr_map->unmap_ip = identity__map_ip; - curr_dso->origin = self->origin; + curr_dso->origin = DSO__ORIG_KERNEL; map_groups__insert(kmap->kmaps, curr_map); - dsos__add(&self->node, curr_dso); + dsos__add(&dsos__kernel, curr_dso); dso__set_loaded(curr_dso, map->type); } else curr_dso = curr_map->dso; @@ -1122,7 +1089,7 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id) return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; } -bool __dsos__read_build_ids(struct list_head *head, bool with_hits) +static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) { bool have_build_id = false; struct dso *pos; @@ -1140,6 +1107,13 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) return have_build_id; } +bool dsos__read_build_ids(bool with_hits) +{ + bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), + ubuildids = __dsos__read_build_ids(&dsos__user, with_hits); + return kbuildids || ubuildids; +} + /* * Align offset to 4 bytes as needed for note name and descriptor data. */ @@ -1274,8 +1248,6 @@ char dso__symtab_origin(const struct dso *self) [DSO__ORIG_BUILDID] = 'b', [DSO__ORIG_DSO] = 'd', [DSO__ORIG_KMODULE] = 'K', - [DSO__ORIG_GUEST_KERNEL] = 'g', - [DSO__ORIG_GUEST_KMODULE] = 'G', }; if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) @@ -1291,20 +1263,11 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = -1; int fd; - struct machine *machine; - const char *root_dir; dso__set_loaded(self, map->type); - if (self->kernel == DSO_TYPE_KERNEL) + if (self->kernel) return dso__load_kernel_sym(self, map, filter); - else if (self->kernel == DSO_TYPE_GUEST_KERNEL) - return dso__load_guest_kernel_sym(self, map, filter); - - if (map->groups && map->groups->machine) - machine = map->groups->machine; - else - machine = NULL; name = malloc(size); if (!name) @@ -1358,13 +1321,6 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) case DSO__ORIG_DSO: snprintf(name, size, "%s", self->long_name); break; - case DSO__ORIG_GUEST_KMODULE: - if (map->groups && map->groups->machine) - root_dir = map->groups->machine->root_dir; - else - root_dir = ""; - snprintf(name, size, "%s%s", root_dir, self->long_name); - break; default: goto out; @@ -1418,8 +1374,7 @@ struct map *map_groups__find_by_name(struct map_groups *self, return NULL; } -static int dso__kernel_module_get_build_id(struct dso *self, - const char *root_dir) +static int dso__kernel_module_get_build_id(struct dso *self) { char filename[PATH_MAX]; /* @@ -1429,8 +1384,8 @@ static int dso__kernel_module_get_build_id(struct dso *self, const char *name = self->short_name + 1; snprintf(filename, sizeof(filename), - "%s/sys/module/%.*s/notes/.note.gnu.build-id", - root_dir, (int)strlen(name) - 1, name); + "/sys/module/%.*s/notes/.note.gnu.build-id", + (int)strlen(name - 1), name); if (sysfs__read_build_id(filename, self->build_id, sizeof(self->build_id)) == 0) @@ -1439,33 +1394,26 @@ static int dso__kernel_module_get_build_id(struct dso *self, return 0; } -static int map_groups__set_modules_path_dir(struct map_groups *self, - const char *dir_name) +static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirname) { struct dirent *dent; - DIR *dir = opendir(dir_name); + DIR *dir = opendir(dirname); if (!dir) { - pr_debug("%s: cannot open %s dir\n", __func__, dir_name); + pr_debug("%s: cannot open %s dir\n", __func__, dirname); return -1; } while ((dent = readdir(dir)) != NULL) { char path[PATH_MAX]; - struct stat st; - /*sshfs might return bad dent->d_type, so we have to stat*/ - sprintf(path, "%s/%s", dir_name, dent->d_name); - if (stat(path, &st)) - continue; - - if (S_ISDIR(st.st_mode)) { + if (dent->d_type == DT_DIR) { if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; snprintf(path, sizeof(path), "%s/%s", - dir_name, dent->d_name); + dirname, dent->d_name); if (map_groups__set_modules_path_dir(self, path) < 0) goto failure; } else { @@ -1485,13 +1433,13 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, continue; snprintf(path, sizeof(path), "%s/%s", - dir_name, dent->d_name); + dirname, dent->d_name); long_name = strdup(path); if (long_name == NULL) goto failure; dso__set_long_name(map->dso, long_name); - dso__kernel_module_get_build_id(map->dso, ""); + dso__kernel_module_get_build_id(map->dso); } } @@ -1501,47 +1449,18 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, return -1; } -static char *get_kernel_version(const char *root_dir) -{ - char version[PATH_MAX]; - FILE *file; - char *name, *tmp; - const char *prefix = "Linux version "; - - sprintf(version, "%s/proc/version", root_dir); - file = fopen(version, "r"); - if (!file) - return NULL; - - version[0] = '\0'; - tmp = fgets(version, sizeof(version), file); - fclose(file); - - name = strstr(version, prefix); - if (!name) - return NULL; - name += strlen(prefix); - tmp = strchr(name, ' '); - if (tmp) - *tmp = '\0'; - - return strdup(name); -} - -static int machine__set_modules_path(struct machine *self) +static int map_groups__set_modules_path(struct map_groups *self) { - char *version; + struct utsname uts; char modules_path[PATH_MAX]; - version = get_kernel_version(self->root_dir); - if (!version) + if (uname(&uts) < 0) return -1; - snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", - self->root_dir, version); - free(version); + snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", + uts.release); - return map_groups__set_modules_path_dir(&self->kmaps, modules_path); + return map_groups__set_modules_path_dir(self, modules_path); } /* @@ -1551,8 +1470,8 @@ static int machine__set_modules_path(struct machine *self) */ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) { - struct map *self = calloc(1, (sizeof(*self) + - (dso->kernel ? sizeof(struct kmap) : 0))); + struct map *self = zalloc(sizeof(*self) + + (dso->kernel ? sizeof(struct kmap) : 0)); if (self != NULL) { /* * ->end will be filled after we load all the symbols @@ -1563,11 +1482,11 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) return self; } -struct map *machine__new_module(struct machine *self, u64 start, - const char *filename) +struct map *map_groups__new_module(struct map_groups *self, u64 start, + const char *filename) { struct map *map; - struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename); + struct dso *dso = __dsos__findnew(&dsos__kernel, filename); if (dso == NULL) return NULL; @@ -1576,31 +1495,18 @@ struct map *machine__new_module(struct machine *self, u64 start, if (map == NULL) return NULL; - if (machine__is_host(self)) - dso->origin = DSO__ORIG_KMODULE; - else - dso->origin = DSO__ORIG_GUEST_KMODULE; - map_groups__insert(&self->kmaps, map); + dso->origin = DSO__ORIG_KMODULE; + map_groups__insert(self, map); return map; } -static int machine__create_modules(struct machine *self) +static int map_groups__create_modules(struct map_groups *self) { char *line = NULL; size_t n; - FILE *file; + FILE *file = fopen("/proc/modules", "r"); struct map *map; - const char *modules; - char path[PATH_MAX]; - - if (machine__is_default_guest(self)) - modules = symbol_conf.default_guest_modules; - else { - sprintf(path, "%s/proc/modules", self->root_dir); - modules = path; - } - file = fopen(modules, "r"); if (file == NULL) return -1; @@ -1632,16 +1538,16 @@ static int machine__create_modules(struct machine *self) *sep = '\0'; snprintf(name, sizeof(name), "[%s]", line); - map = machine__new_module(self, start, name); + map = map_groups__new_module(self, start, name); if (map == NULL) goto out_delete_line; - dso__kernel_module_get_build_id(map->dso, self->root_dir); + dso__kernel_module_get_build_id(map->dso); } free(line); fclose(file); - return machine__set_modules_path(self); + return map_groups__set_modules_path(self); out_delete_line: free(line); @@ -1808,56 +1714,8 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, return err; } -static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, - symbol_filter_t filter) -{ - int err; - const char *kallsyms_filename = NULL; - struct machine *machine; - char path[PATH_MAX]; - - if (!map->groups) { - pr_debug("Guest kernel map hasn't the point to groups\n"); - return -1; - } - machine = map->groups->machine; - - if (machine__is_default_guest(machine)) { - /* - * if the user specified a vmlinux filename, use it and only - * it, reporting errors to the user if it cannot be used. - * Or use file guest_kallsyms inputted by user on commandline - */ - if (symbol_conf.default_guest_vmlinux_name != NULL) { - err = dso__load_vmlinux(self, map, - symbol_conf.default_guest_vmlinux_name, filter); - goto out_try_fixup; - } - - kallsyms_filename = symbol_conf.default_guest_kallsyms; - if (!kallsyms_filename) - return -1; - } else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); - kallsyms_filename = path; - } - - err = dso__load_kallsyms(self, kallsyms_filename, map, filter); - if (err > 0) - pr_debug("Using %s for symbols\n", kallsyms_filename); - -out_try_fixup: - if (err > 0) { - if (kallsyms_filename != NULL) { - machine__mmap_name(machine, path, sizeof(path)); - dso__set_long_name(self, strdup(path)); - } - map__fixup_start(map); - map__fixup_end(map); - } - - return err; -} +LIST_HEAD(dsos__user); +LIST_HEAD(dsos__kernel); static void dsos__add(struct list_head *head, struct dso *dso) { @@ -1889,32 +1747,21 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name) return dso; } -static size_t __dsos__fprintf(struct list_head *head, FILE *fp) +static void __dsos__fprintf(struct list_head *head, FILE *fp) { struct dso *pos; - size_t ret = 0; list_for_each_entry(pos, head, node) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) - ret += dso__fprintf(pos, i, fp); + dso__fprintf(pos, i, fp); } - - return ret; } -size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp) +void dsos__fprintf(FILE *fp) { - struct rb_node *nd; - size_t ret = 0; - - for (nd = rb_first(self); nd; nd = rb_next(nd)) { - struct machine *pos = rb_entry(nd, struct machine, rb_node); - ret += __dsos__fprintf(&pos->kernel_dsos, fp); - ret += __dsos__fprintf(&pos->user_dsos, fp); - } - - return ret; + __dsos__fprintf(&dsos__kernel, fp); + __dsos__fprintf(&dsos__user, fp); } static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, @@ -1932,17 +1779,10 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, return ret; } -size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits) +size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) { - struct rb_node *nd; - size_t ret = 0; - - for (nd = rb_first(self); nd; nd = rb_next(nd)) { - struct machine *pos = rb_entry(nd, struct machine, rb_node); - ret += __dsos__fprintf_buildid(&pos->kernel_dsos, fp, with_hits); - ret += __dsos__fprintf_buildid(&pos->user_dsos, fp, with_hits); - } - return ret; + return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + + __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); } struct dso *dso__new_kernel(const char *name) @@ -1951,98 +1791,55 @@ struct dso *dso__new_kernel(const char *name) if (self != NULL) { dso__set_short_name(self, "[kernel]"); - self->kernel = DSO_TYPE_KERNEL; + self->kernel = 1; } return self; } -static struct dso *dso__new_guest_kernel(struct machine *machine, - const char *name) +void dso__read_running_kernel_build_id(struct dso *self) { - char bf[PATH_MAX]; - struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf))); - - if (self != NULL) { - dso__set_short_name(self, "[guest.kernel]"); - self->kernel = DSO_TYPE_GUEST_KERNEL; - } - - return self; -} - -void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine) -{ - char path[PATH_MAX]; - - if (machine__is_default_guest(machine)) - return; - sprintf(path, "%s/sys/kernel/notes", machine->root_dir); - if (sysfs__read_build_id(path, self->build_id, + if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, sizeof(self->build_id)) == 0) self->has_build_id = true; } -static struct dso *machine__create_kernel(struct machine *self) +static struct dso *dsos__create_kernel(const char *vmlinux) { - const char *vmlinux_name = NULL; - struct dso *kernel; - - if (machine__is_host(self)) { - vmlinux_name = symbol_conf.vmlinux_name; - kernel = dso__new_kernel(vmlinux_name); - } else { - if (machine__is_default_guest(self)) - vmlinux_name = symbol_conf.default_guest_vmlinux_name; - kernel = dso__new_guest_kernel(self, vmlinux_name); - } + struct dso *kernel = dso__new_kernel(vmlinux); if (kernel != NULL) { - dso__read_running_kernel_build_id(kernel, self); - dsos__add(&self->kernel_dsos, kernel); + dso__read_running_kernel_build_id(kernel); + dsos__add(&dsos__kernel, kernel); } + return kernel; } -int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) +int __map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES], + struct dso *kernel) { enum map_type type; for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; - self->vmlinux_maps[type] = map__new2(0, kernel, type); - if (self->vmlinux_maps[type] == NULL) + vmlinux_maps[type] = map__new2(0, kernel, type); + if (vmlinux_maps[type] == NULL) return -1; - self->vmlinux_maps[type]->map_ip = - self->vmlinux_maps[type]->unmap_ip = identity__map_ip; + vmlinux_maps[type]->map_ip = + vmlinux_maps[type]->unmap_ip = identity__map_ip; - kmap = map__kmap(self->vmlinux_maps[type]); - kmap->kmaps = &self->kmaps; - map_groups__insert(&self->kmaps, self->vmlinux_maps[type]); + kmap = map__kmap(vmlinux_maps[type]); + kmap->kmaps = self; + map_groups__insert(self, vmlinux_maps[type]); } return 0; } -int machine__create_kernel_maps(struct machine *self) -{ - struct dso *kernel = machine__create_kernel(self); - - if (kernel == NULL || - __machine__create_kernel_maps(self, kernel) < 0) - return -1; - - if (symbol_conf.use_modules && machine__create_modules(self) < 0) - pr_debug("Problems creating module maps, continuing anyway...\n"); - /* - * Now that we have all the maps created, just set the ->end of them: - */ - map_groups__fixup_end(&self->kmaps); - return 0; -} - static void vmlinux_path__exit(void) { while (--vmlinux_path__nr_entries >= 0) { @@ -2098,17 +1895,6 @@ static int vmlinux_path__init(void) return -1; } -size_t vmlinux_path__fprintf(FILE *fp) -{ - int i; - size_t printed = 0; - - for (i = 0; i < vmlinux_path__nr_entries; ++i) - printed += fprintf(fp, "[%d] %s\n", i, vmlinux_path[i]); - - return printed; -} - static int setup_list(struct strlist **list, const char *list_str, const char *list_name) { @@ -2159,129 +1945,22 @@ int symbol__init(void) return -1; } -int machines__create_kernel_maps(struct rb_root *self, pid_t pid) +int map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES]) { - struct machine *machine = machines__findnew(self, pid); + struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); - if (machine == NULL) + if (kernel == NULL) return -1; - return machine__create_kernel_maps(machine); -} - -static int hex(char ch) -{ - if ((ch >= '0') && (ch <= '9')) - return ch - '0'; - if ((ch >= 'a') && (ch <= 'f')) - return ch - 'a' + 10; - if ((ch >= 'A') && (ch <= 'F')) - return ch - 'A' + 10; - return -1; -} - -/* - * While we find nice hex chars, build a long_val. - * Return number of chars processed. - */ -int hex2u64(const char *ptr, u64 *long_val) -{ - const char *p = ptr; - *long_val = 0; - - while (*p) { - const int hex_val = hex(*p); - - if (hex_val < 0) - break; - - *long_val = (*long_val << 4) | hex_val; - p++; - } - - return p - ptr; -} - -char *strxfrchar(char *s, char from, char to) -{ - char *p = s; - - while ((p = strchr(p, from)) != NULL) - *p++ = to; - - return s; -} - -int machines__create_guest_kernel_maps(struct rb_root *self) -{ - int ret = 0; - struct dirent **namelist = NULL; - int i, items = 0; - char path[PATH_MAX]; - pid_t pid; - - if (symbol_conf.default_guest_vmlinux_name || - symbol_conf.default_guest_modules || - symbol_conf.default_guest_kallsyms) { - machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID); - } - - if (symbol_conf.guestmount) { - items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); - if (items <= 0) - return -ENOENT; - for (i = 0; i < items; i++) { - if (!isdigit(namelist[i]->d_name[0])) { - /* Filter out . and .. */ - continue; - } - pid = atoi(namelist[i]->d_name); - sprintf(path, "%s/%s/proc/kallsyms", - symbol_conf.guestmount, - namelist[i]->d_name); - ret = access(path, R_OK); - if (ret) { - pr_debug("Can't access file %s\n", path); - goto failure; - } - machines__create_kernel_maps(self, pid); - } -failure: - free(namelist); - } - - return ret; -} - -int machine__load_kallsyms(struct machine *self, const char *filename, - enum map_type type, symbol_filter_t filter) -{ - struct map *map = self->vmlinux_maps[type]; - int ret = dso__load_kallsyms(map->dso, filename, map, filter); - - if (ret > 0) { - dso__set_loaded(map->dso, type); - /* - * Since /proc/kallsyms will have multiple sessions for the - * kernel, with modules between them, fixup the end of all - * sections. - */ - __map_groups__fixup_end(&self->kmaps, type); - } - - return ret; -} - -int machine__load_vmlinux_path(struct machine *self, enum map_type type, - symbol_filter_t filter) -{ - struct map *map = self->vmlinux_maps[type]; - int ret = dso__load_vmlinux_path(map->dso, map, filter); - - if (ret > 0) { - dso__set_loaded(map->dso, type); - map__reloc_vmlinux(map); - } + if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) + return -1; - return ret; + if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) + pr_debug("Problems creating module maps, continuing anyway...\n"); + /* + * Now that we have all the maps created, just set the ->end of them: + */ + map_groups__fixup_end(self); + return 0; } diff --git a/trunk/tools/perf/util/symbol.h b/trunk/tools/perf/util/symbol.h index a517c17407b7..f30a37428919 100644 --- a/trunk/tools/perf/util/symbol.h +++ b/trunk/tools/perf/util/symbol.h @@ -3,11 +3,10 @@ #include #include -#include -#include "map.h" +#include "types.h" #include #include -#include +#include "event.h" #define DEBUG_CACHE_DIR ".debug" @@ -30,9 +29,6 @@ static inline char *bfd_demangle(void __used *v, const char __used *c, #endif #endif -int hex2u64(const char *ptr, u64 *val); -char *strxfrchar(char *s, char from, char to); - /* * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; * for newer versions we can use mmap to reduce memory usage: @@ -48,8 +44,6 @@ char *strxfrchar(char *s, char from, char to); #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ #endif -#define BUILD_ID_SIZE 20 - struct symbol { struct rb_node rb_node; u64 start; @@ -69,15 +63,10 @@ struct symbol_conf { show_nr_samples, use_callchain, exclude_other, - full_paths, - show_cpu_utilization; + full_paths; const char *vmlinux_name, *field_sep; - const char *default_guest_vmlinux_name, - *default_guest_kallsyms, - *default_guest_modules; - const char *guestmount; - char *dso_list_str, + char *dso_list_str, *comm_list_str, *sym_list_str, *col_width_list_str; @@ -99,11 +88,6 @@ struct ref_reloc_sym { u64 unrelocated_addr; }; -struct map_symbol { - struct map *map; - struct symbol *sym; -}; - struct addr_location { struct thread *thread; struct map *map; @@ -111,13 +95,6 @@ struct addr_location { u64 addr; char level; bool filtered; - unsigned int cpumode; -}; - -enum dso_kernel_type { - DSO_TYPE_USER = 0, - DSO_TYPE_KERNEL, - DSO_TYPE_GUEST_KERNEL }; struct dso { @@ -127,9 +104,8 @@ struct dso { u8 adjust_symbols:1; u8 slen_calculated:1; u8 has_build_id:1; - enum dso_kernel_type kernel; + u8 kernel:1; u8 hit:1; - u8 annotate_warned:1; unsigned char origin; u8 sorted_by_name; u8 loaded; @@ -155,63 +131,42 @@ static inline void dso__set_loaded(struct dso *self, enum map_type type) void dso__sort_by_name(struct dso *self, enum map_type type); +extern struct list_head dsos__user, dsos__kernel; + struct dso *__dsos__findnew(struct list_head *head, const char *name); +static inline struct dso *dsos__findnew(const char *name) +{ + return __dsos__findnew(&dsos__user, name); +} + int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_vmlinux_path(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, symbol_filter_t filter); -int machine__load_kallsyms(struct machine *self, const char *filename, - enum map_type type, symbol_filter_t filter); -int machine__load_vmlinux_path(struct machine *self, enum map_type type, - symbol_filter_t filter); - -size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp); -size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); +void dsos__fprintf(FILE *fp); +size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); - -enum dso_origin { - DSO__ORIG_KERNEL = 0, - DSO__ORIG_GUEST_KERNEL, - DSO__ORIG_JAVA_JIT, - DSO__ORIG_BUILD_ID_CACHE, - DSO__ORIG_FEDORA, - DSO__ORIG_UBUNTU, - DSO__ORIG_BUILDID, - DSO__ORIG_DSO, - DSO__ORIG_GUEST_KMODULE, - DSO__ORIG_KMODULE, - DSO__ORIG_NOT_FOUND, -}; - char dso__symtab_origin(const struct dso *self); void dso__set_long_name(struct dso *self, char *name); void dso__set_build_id(struct dso *self, void *build_id); -void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine); +void dso__read_running_kernel_build_id(struct dso *self); struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, const char *name); int filename__read_build_id(const char *filename, void *bf, size_t size); int sysfs__read_build_id(const char *filename, void *bf, size_t size); -bool __dsos__read_build_ids(struct list_head *head, bool with_hits); +bool dsos__read_build_ids(bool with_hits); int build_id__sprintf(const u8 *self, int len, char *bf); int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); -int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); -int machine__create_kernel_maps(struct machine *self); - -int machines__create_kernel_maps(struct rb_root *self, pid_t pid); -int machines__create_guest_kernel_maps(struct rb_root *self); - int symbol__init(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); -size_t vmlinux_path__fprintf(FILE *fp); - #endif /* __PERF_SYMBOL */ diff --git a/trunk/tools/perf/util/thread.c b/trunk/tools/perf/util/thread.c index 1f7ecd47f499..fa968312ee7d 100644 --- a/trunk/tools/perf/util/thread.c +++ b/trunk/tools/perf/util/thread.c @@ -7,35 +7,13 @@ #include "util.h" #include "debug.h" -int find_all_tid(int pid, pid_t ** all_tid) +void map_groups__init(struct map_groups *self) { - char name[256]; - int items; - struct dirent **namelist = NULL; - int ret = 0; int i; - - sprintf(name, "/proc/%d/task", pid); - items = scandir(name, &namelist, NULL, NULL); - if (items <= 0) - return -ENOENT; - *all_tid = malloc(sizeof(pid_t) * items); - if (!*all_tid) { - ret = -ENOMEM; - goto failure; + for (i = 0; i < MAP__NR_TYPES; ++i) { + self->maps[i] = RB_ROOT; + INIT_LIST_HEAD(&self->removed_maps[i]); } - - for (i = 0; i < items; i++) - (*all_tid)[i] = atoi(namelist[i]->d_name); - - ret = items; - -failure: - for (i=0; imaps[type]; + struct rb_node *next = rb_first(root); + + while (next) { + struct map *pos = rb_entry(next, struct map, rb_node); + next = rb_next(&pos->rb_node); + rb_erase(&pos->rb_node, root); + /* + * We may have references to this map, for + * instance in some hist_entry instances, so + * just move them to a separate list. + */ + list_add_tail(&pos->node, &self->removed_maps[pos->type]); + } + } +} + int thread__set_comm(struct thread *self, const char *comm) { int err; @@ -79,10 +79,69 @@ int thread__comm_len(struct thread *self) return self->comm_len; } +size_t __map_groups__fprintf_maps(struct map_groups *self, + enum map_type type, FILE *fp) +{ + size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); + struct rb_node *nd; + + for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node); + printed += fprintf(fp, "Map:"); + printed += map__fprintf(pos, fp); + if (verbose > 2) { + printed += dso__fprintf(pos->dso, type, fp); + printed += fprintf(fp, "--\n"); + } + } + + return printed; +} + +size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) +{ + size_t printed = 0, i; + for (i = 0; i < MAP__NR_TYPES; ++i) + printed += __map_groups__fprintf_maps(self, i, fp); + return printed; +} + +static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, + enum map_type type, FILE *fp) +{ + struct map *pos; + size_t printed = 0; + + list_for_each_entry(pos, &self->removed_maps[type], node) { + printed += fprintf(fp, "Map:"); + printed += map__fprintf(pos, fp); + if (verbose > 1) { + printed += dso__fprintf(pos->dso, type, fp); + printed += fprintf(fp, "--\n"); + } + } + return printed; +} + +static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) +{ + size_t printed = 0, i; + for (i = 0; i < MAP__NR_TYPES; ++i) + printed += __map_groups__fprintf_removed_maps(self, i, fp); + return printed; +} + +static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) +{ + size_t printed = map_groups__fprintf_maps(self, fp); + printed += fprintf(fp, "Removed maps:\n"); + return printed + map_groups__fprintf_removed_maps(self, fp); +} + static size_t thread__fprintf(struct thread *self, FILE *fp) { return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + - map_groups__fprintf(&self->mg, verbose, fp); + map_groups__fprintf(&self->mg, fp); } struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) @@ -124,12 +183,127 @@ struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) return th; } +static int map_groups__fixup_overlappings(struct map_groups *self, + struct map *map) +{ + struct rb_root *root = &self->maps[map->type]; + struct rb_node *next = rb_first(root); + + while (next) { + struct map *pos = rb_entry(next, struct map, rb_node); + next = rb_next(&pos->rb_node); + + if (!map__overlap(pos, map)) + continue; + + if (verbose >= 2) { + fputs("overlapping maps:\n", stderr); + map__fprintf(map, stderr); + map__fprintf(pos, stderr); + } + + rb_erase(&pos->rb_node, root); + /* + * We may have references to this map, for instance in some + * hist_entry instances, so just move them to a separate + * list. + */ + list_add_tail(&pos->node, &self->removed_maps[map->type]); + /* + * Now check if we need to create new maps for areas not + * overlapped by the new map: + */ + if (map->start > pos->start) { + struct map *before = map__clone(pos); + + if (before == NULL) + return -ENOMEM; + + before->end = map->start - 1; + map_groups__insert(self, before); + if (verbose >= 2) + map__fprintf(before, stderr); + } + + if (map->end < pos->end) { + struct map *after = map__clone(pos); + + if (after == NULL) + return -ENOMEM; + + after->start = map->end + 1; + map_groups__insert(self, after); + if (verbose >= 2) + map__fprintf(after, stderr); + } + } + + return 0; +} + +void maps__insert(struct rb_root *maps, struct map *map) +{ + struct rb_node **p = &maps->rb_node; + struct rb_node *parent = NULL; + const u64 ip = map->start; + struct map *m; + + while (*p != NULL) { + parent = *p; + m = rb_entry(parent, struct map, rb_node); + if (ip < m->start) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&map->rb_node, parent, p); + rb_insert_color(&map->rb_node, maps); +} + +struct map *maps__find(struct rb_root *maps, u64 ip) +{ + struct rb_node **p = &maps->rb_node; + struct rb_node *parent = NULL; + struct map *m; + + while (*p != NULL) { + parent = *p; + m = rb_entry(parent, struct map, rb_node); + if (ip < m->start) + p = &(*p)->rb_left; + else if (ip > m->end) + p = &(*p)->rb_right; + else + return m; + } + + return NULL; +} + void thread__insert_map(struct thread *self, struct map *map) { - map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); + map_groups__fixup_overlappings(&self->mg, map); map_groups__insert(&self->mg, map); } +/* + * XXX This should not really _copy_ te maps, but refcount them. + */ +static int map_groups__clone(struct map_groups *self, + struct map_groups *parent, enum map_type type) +{ + struct rb_node *nd; + for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { + struct map *map = rb_entry(nd, struct map, rb_node); + struct map *new = map__clone(map); + if (new == NULL) + return -ENOMEM; + map_groups__insert(self, new); + } + return 0; +} + int thread__fork(struct thread *self, struct thread *parent) { int i; @@ -162,3 +336,15 @@ size_t perf_session__fprintf(struct perf_session *self, FILE *fp) return ret; } + +struct symbol *map_groups__find_symbol(struct map_groups *self, + enum map_type type, u64 addr, + symbol_filter_t filter) +{ + struct map *map = map_groups__find(self, type, addr); + + if (map != NULL) + return map__find_symbol(map, map->map_ip(map, addr), filter); + + return NULL; +} diff --git a/trunk/tools/perf/util/thread.h b/trunk/tools/perf/util/thread.h index 1dfd9ff8bdcd..dcf70303e58e 100644 --- a/trunk/tools/perf/util/thread.h +++ b/trunk/tools/perf/util/thread.h @@ -5,6 +5,14 @@ #include #include "symbol.h" +struct map_groups { + struct rb_root maps[MAP__NR_TYPES]; + struct list_head removed_maps[MAP__NR_TYPES]; +}; + +size_t __map_groups__fprintf_maps(struct map_groups *self, + enum map_type type, FILE *fp); + struct thread { struct rb_node rb_node; struct map_groups mg; @@ -15,16 +23,29 @@ struct thread { int comm_len; }; -struct perf_session; - -int find_all_tid(int pid, pid_t ** all_tid); +void map_groups__init(struct map_groups *self); int thread__set_comm(struct thread *self, const char *comm); int thread__comm_len(struct thread *self); struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); void thread__insert_map(struct thread *self, struct map *map); int thread__fork(struct thread *self, struct thread *parent); +size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); size_t perf_session__fprintf(struct perf_session *self, FILE *fp); +void maps__insert(struct rb_root *maps, struct map *map); +struct map *maps__find(struct rb_root *maps, u64 addr); + +static inline void map_groups__insert(struct map_groups *self, struct map *map) +{ + maps__insert(&self->maps[map->type], map); +} + +static inline struct map *map_groups__find(struct map_groups *self, + enum map_type type, u64 addr) +{ + return maps__find(&self->maps[type], addr); +} + static inline struct map *thread__find_map(struct thread *self, enum map_type type, u64 addr) { @@ -33,12 +54,34 @@ static inline struct map *thread__find_map(struct thread *self, void thread__find_addr_map(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, pid_t pid, u64 addr, + enum map_type type, u64 addr, struct addr_location *al); void thread__find_addr_location(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, pid_t pid, u64 addr, + enum map_type type, u64 addr, struct addr_location *al, symbol_filter_t filter); +struct symbol *map_groups__find_symbol(struct map_groups *self, + enum map_type type, u64 addr, + symbol_filter_t filter); + +static inline struct symbol *map_groups__find_function(struct map_groups *self, + u64 addr, + symbol_filter_t filter) +{ + return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); +} + +struct map *map_groups__find_by_name(struct map_groups *self, + enum map_type type, const char *name); + +int __map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES], + struct dso *kernel); +int map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES]); + +struct map *map_groups__new_module(struct map_groups *self, u64 start, + const char *filename); #endif /* __PERF_THREAD_H */ diff --git a/trunk/tools/perf/util/trace-event-info.c b/trunk/tools/perf/util/trace-event-info.c index b1572601286c..5ea8973ad331 100644 --- a/trunk/tools/perf/util/trace-event-info.c +++ b/trunk/tools/perf/util/trace-event-info.c @@ -154,17 +154,10 @@ static void put_tracing_file(char *file) free(file); } -static ssize_t calc_data_size; - static ssize_t write_or_die(const void *buf, size_t len) { int ret; - if (calc_data_size) { - calc_data_size += len; - return len; - } - ret = write(output_fd, buf, len); if (ret < 0) die("writing to '%s'", output_file); @@ -487,17 +480,6 @@ get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) return nr_tracepoints > 0 ? path.next : NULL; } -bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events) -{ - int i; - - for (i = 0; i < nb_events; i++) - if (pattrs[i].type == PERF_TYPE_TRACEPOINT) - return true; - - return false; -} - int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) { char buf[BUFSIZ]; @@ -544,20 +526,3 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) return 0; } - -ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs, - int nb_events) -{ - ssize_t size; - int err = 0; - - calc_data_size = 1; - err = read_tracing_data(fd, pattrs, nb_events); - size = calc_data_size - 1; - calc_data_size = 0; - - if (err < 0) - return err; - - return size; -} diff --git a/trunk/tools/perf/util/trace-event-parse.c b/trunk/tools/perf/util/trace-event-parse.c index 069f261b225c..9b3c20f42f98 100644 --- a/trunk/tools/perf/util/trace-event-parse.c +++ b/trunk/tools/perf/util/trace-event-parse.c @@ -37,12 +37,10 @@ int header_page_ts_offset; int header_page_ts_size; int header_page_size_offset; int header_page_size_size; -int header_page_overwrite_offset; -int header_page_overwrite_size; int header_page_data_offset; int header_page_data_size; -bool latency_format; +int latency_format; static char *input_buf; static unsigned long long input_buf_ptr; @@ -630,32 +628,23 @@ static int test_type(enum event_type type, enum event_type expect) return 0; } -static int __test_type_token(enum event_type type, char *token, - enum event_type expect, const char *expect_tok, - bool warn) +static int test_type_token(enum event_type type, char *token, + enum event_type expect, const char *expect_tok) { if (type != expect) { - if (warn) - warning("Error: expected type %d but read %d", - expect, type); + warning("Error: expected type %d but read %d", + expect, type); return -1; } if (strcmp(token, expect_tok) != 0) { - if (warn) - warning("Error: expected '%s' but read '%s'", - expect_tok, token); + warning("Error: expected '%s' but read '%s'", + expect_tok, token); return -1; } return 0; } -static int test_type_token(enum event_type type, char *token, - enum event_type expect, const char *expect_tok) -{ - return __test_type_token(type, token, expect, expect_tok, true); -} - static int __read_expect_type(enum event_type expect, char **tok, int newline_ok) { enum event_type type; @@ -672,8 +661,7 @@ static int read_expect_type(enum event_type expect, char **tok) return __read_expect_type(expect, tok, 1); } -static int __read_expected(enum event_type expect, const char *str, - int newline_ok, bool warn) +static int __read_expected(enum event_type expect, const char *str, int newline_ok) { enum event_type type; char *token; @@ -684,7 +672,7 @@ static int __read_expected(enum event_type expect, const char *str, else type = read_token_item(&token); - ret = __test_type_token(type, token, expect, str, warn); + ret = test_type_token(type, token, expect, str); free_token(token); @@ -693,12 +681,12 @@ static int __read_expected(enum event_type expect, const char *str, static int read_expected(enum event_type expect, const char *str) { - return __read_expected(expect, str, 1, true); + return __read_expected(expect, str, 1); } static int read_expected_item(enum event_type expect, const char *str) { - return __read_expected(expect, str, 0, true); + return __read_expected(expect, str, 0); } static char *event_read_name(void) @@ -756,7 +744,7 @@ static int field_is_string(struct format_field *field) static int field_is_dynamic(struct format_field *field) { - if (!strncmp(field->type, "__data_loc", 10)) + if (!strcmp(field->type, "__data_loc")) return 1; return 0; @@ -3099,6 +3087,88 @@ static void print_args(struct print_arg *args) } } +static void parse_header_field(const char *field, + int *offset, int *size) +{ + char *token; + int type; + + if (read_expected(EVENT_ITEM, "field") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + + /* type */ + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + free_token(token); + + if (read_expected(EVENT_ITEM, field) < 0) + return; + if (read_expected(EVENT_OP, ";") < 0) + return; + if (read_expected(EVENT_ITEM, "offset") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + *offset = atoi(token); + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + if (read_expected(EVENT_ITEM, "size") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + *size = atoi(token); + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + type = read_token(&token); + if (type != EVENT_NEWLINE) { + /* newer versions of the kernel have a "signed" type */ + if (type != EVENT_ITEM) + goto fail; + + if (strcmp(token, "signed") != 0) + goto fail; + + free_token(token); + + if (read_expected(EVENT_OP, ":") < 0) + return; + + if (read_expect_type(EVENT_ITEM, &token)) + goto fail; + + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + + if (read_expect_type(EVENT_NEWLINE, &token)) + goto fail; + } + fail: + free_token(token); +} + +int parse_header_page(char *buf, unsigned long size) +{ + init_input_buf(buf, size); + + parse_header_field("timestamp", &header_page_ts_offset, + &header_page_ts_size); + parse_header_field("commit", &header_page_size_offset, + &header_page_size_size); + parse_header_field("data", &header_page_data_offset, + &header_page_data_size); + + return 0; +} + int parse_ftrace_file(char *buf, unsigned long size) { struct format_field *field; diff --git a/trunk/tools/perf/util/trace-event-read.c b/trunk/tools/perf/util/trace-event-read.c index cb54cd002f49..7cd1193918c7 100644 --- a/trunk/tools/perf/util/trace-event-read.c +++ b/trunk/tools/perf/util/trace-event-read.c @@ -50,51 +50,14 @@ static int long_size; static unsigned long page_size; -static ssize_t calc_data_size; -static bool repipe; - -/* If it fails, the next read will report it */ -static void skip(int size) -{ - lseek(input_fd, size, SEEK_CUR); -} - -static int do_read(int fd, void *buf, int size) -{ - int rsize = size; - - while (size) { - int ret = read(fd, buf, size); - - if (ret <= 0) - return -1; - - if (repipe) { - int retw = write(STDOUT_FILENO, buf, ret); - - if (retw <= 0 || retw != ret) - die("repiping input file"); - } - - size -= ret; - buf += ret; - } - - return rsize; -} - static int read_or_die(void *data, int size) { int r; - r = do_read(input_fd, data, size); - if (r <= 0) + r = read(input_fd, data, size); + if (r != size) die("reading input file (size expected=%d received=%d)", size, r); - - if (calc_data_size) - calc_data_size += r; - return r; } @@ -119,35 +82,56 @@ static char *read_string(void) char buf[BUFSIZ]; char *str = NULL; int size = 0; + int i; off_t r; - char c; for (;;) { - r = read(input_fd, &c, 1); + r = read(input_fd, buf, BUFSIZ); if (r < 0) die("reading input file"); if (!r) die("no data"); - if (repipe) { - int retw = write(STDOUT_FILENO, &c, 1); - - if (retw <= 0 || retw != r) - die("repiping input file string"); + for (i = 0; i < r; i++) { + if (!buf[i]) + break; } - - buf[size++] = c; - - if (!c) + if (i < r) break; - } - if (calc_data_size) - calc_data_size += size; + if (str) { + size += BUFSIZ; + str = realloc(str, size); + if (!str) + die("malloc of size %d", size); + memcpy(str + (size - BUFSIZ), buf, BUFSIZ); + } else { + size = BUFSIZ; + str = malloc_or_die(size); + memcpy(str, buf, size); + } + } - str = malloc_or_die(size); - memcpy(str, buf, size); + /* trailing \0: */ + i++; + + /* move the file descriptor to the end of the string */ + r = lseek(input_fd, -(r - i), SEEK_CUR); + if (r == (off_t)-1) + die("lseek"); + + if (str) { + size += i; + str = realloc(str, size); + if (!str) + die("malloc of size %d", size); + memcpy(str + (size - i), buf, i); + } else { + size = i; + str = malloc_or_die(i); + memcpy(str, buf, i); + } return str; } @@ -190,6 +174,7 @@ static void read_ftrace_printk(void) static void read_header_files(void) { unsigned long long size; + char *header_page; char *header_event; char buf[BUFSIZ]; @@ -199,7 +184,10 @@ static void read_header_files(void) die("did not read header page"); size = read8(); - skip(size); + header_page = malloc_or_die(size); + read_or_die(header_page, size); + parse_header_page(header_page, size); + free(header_page); /* * The size field in the page is of type long, @@ -471,7 +459,7 @@ struct record *trace_read_data(int cpu) return data; } -ssize_t trace_report(int fd, bool __repipe) +void trace_report(int fd) { char buf[BUFSIZ]; char test[] = { 23, 8, 68 }; @@ -479,10 +467,6 @@ ssize_t trace_report(int fd, bool __repipe) int show_version = 0; int show_funcs = 0; int show_printk = 0; - ssize_t size; - - calc_data_size = 1; - repipe = __repipe; input_fd = fd; @@ -515,18 +499,14 @@ ssize_t trace_report(int fd, bool __repipe) read_proc_kallsyms(); read_ftrace_printk(); - size = calc_data_size - 1; - calc_data_size = 0; - repipe = false; - if (show_funcs) { print_funcs(); - return size; + return; } if (show_printk) { print_printk(); - return size; + return; } - return size; + return; } diff --git a/trunk/tools/perf/util/trace-event.h b/trunk/tools/perf/util/trace-event.h index 406d452956db..c3269b937db4 100644 --- a/trunk/tools/perf/util/trace-event.h +++ b/trunk/tools/perf/util/trace-event.h @@ -1,7 +1,6 @@ #ifndef __PERF_TRACE_EVENTS_H #define __PERF_TRACE_EVENTS_H -#include #include "parse-events.h" #define __unused __attribute__((unused)) @@ -163,7 +162,7 @@ struct record *trace_read_data(int cpu); void parse_set_info(int nr_cpus, int long_sz); -ssize_t trace_report(int fd, bool repipe); +void trace_report(int fd); void *malloc_or_die(unsigned int size); @@ -242,8 +241,9 @@ extern int header_page_size_size; extern int header_page_data_offset; extern int header_page_data_size; -extern bool latency_format; +extern int latency_format; +int parse_header_page(char *buf, unsigned long size); int trace_parse_common_type(void *data); int trace_parse_common_pid(void *data); int parse_common_pc(void *data); @@ -258,8 +258,6 @@ void *raw_field_ptr(struct event *event, const char *name, void *data); unsigned long long eval_flag(const char *flag); int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events); -ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs, - int nb_events); /* taken from kernel/trace/trace.h */ enum trace_flag_type { diff --git a/trunk/tools/perf/util/util.h b/trunk/tools/perf/util/util.h index fbf45d1b26f7..0f5b2a6f1080 100644 --- a/trunk/tools/perf/util/util.h +++ b/trunk/tools/perf/util/util.h @@ -42,14 +42,12 @@ #define _ALL_SOURCE 1 #define _GNU_SOURCE 1 #define _BSD_SOURCE 1 -#define HAS_BOOL #include #include #include #include #include -#include #include #include #include @@ -80,7 +78,6 @@ #include #include #include "../../../include/linux/magic.h" -#include "types.h" #ifndef NO_ICONV @@ -298,13 +295,6 @@ extern void *xmemdupz(const void *data, size_t len); extern char *xstrndup(const char *str, size_t len); extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); -static inline void *xzalloc(size_t size) -{ - void *buf = xmalloc(size); - - return memset(buf, 0, size); -} - static inline void *zalloc(size_t size) { return calloc(1, size); @@ -319,7 +309,6 @@ static inline int has_extension(const char *filename, const char *ext) { size_t len = strlen(filename); size_t extlen = strlen(ext); - return len > extlen && !memcmp(filename + len - extlen, ext, extlen); } @@ -333,7 +322,6 @@ static inline int has_extension(const char *filename, const char *ext) #undef isalnum #undef tolower #undef toupper - extern unsigned char sane_ctype[256]; #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 @@ -418,13 +406,4 @@ void git_qsort(void *base, size_t nmemb, size_t size, int mkdir_p(char *path, mode_t mode); int copyfile(const char *from, const char *to); -s64 perf_atoll(const char *str); -char **argv_split(const char *str, int *argcp); -void argv_free(char **argv); -bool strglobmatch(const char *str, const char *pat); -bool strlazymatch(const char *str, const char *pat); - -#define _STR(x) #x -#define STR(x) _STR(x) - #endif