diff options
265 files changed, 7536 insertions, 3816 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index d99940dcfc4..ac3b4a726a1 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -187,7 +187,7 @@ tcp_cookie_size - INTEGER tcp_dsack - BOOLEAN Allows TCP to send "duplicate" SACKs. -tcp_ecn - BOOLEAN +tcp_ecn - INTEGER Enable Explicit Congestion Notification (ECN) in TCP. ECN is only used when both ends of the TCP flow support it. It is useful to avoid losses due to congestion (when the bottleneck router supports diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 16ae4300c74..0caf77e59be 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -296,6 +296,7 @@ Conexant 5066 ============= laptop Basic Laptop config (default) hp-laptop HP laptops, e g G60 + asus Asus K52JU, Lenovo G560 dell-laptop Dell laptops dell-vostro Dell Vostro olpc-xo-1_5 OLPC XO 1.5 diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt index 5f77d94598d..6d27ab8d6e9 100644 --- a/Documentation/trace/kprobetrace.txt +++ b/Documentation/trace/kprobetrace.txt @@ -42,11 +42,25 @@ Synopsis of kprobe_events +|-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) and string are supported. + (u8/u16/u32/u64/s8/s16/s32/s64), "string" and bitfield + are supported. (*) only for return probe. (**) this is useful for fetching a field of data structures. +Types +----- +Several types are supported for fetch-args. Kprobe tracer will access memory +by given type. Prefix 's' and 'u' means those types are signed and unsigned +respectively. Traced arguments are shown in decimal (signed) or hex (unsigned). +String type is a special type, which fetches a "null-terminated" string from +kernel space. This means it will fail and store NULL if the string container +has been paged out. +Bitfield is another special type, which takes 3 parameters, bit-width, bit- +offset, and container-size (usually 32). The syntax is; + + b<bit-width>@<bit-offset>/<container-size> + Per-Probe Event Filtering ------------------------- diff --git a/MAINTAINERS b/MAINTAINERS index 9511bff301c..531c5cf150b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5551,12 +5551,11 @@ S: Supported F: drivers/scsi/be2iscsi/ SERVER ENGINES 10Gbps NIC - BladeEngine 2 DRIVER -M: Sathya Perla <sathyap@serverengines.com> -M: Subbu Seetharaman <subbus@serverengines.com> -M: Sarveshwar Bandi <sarveshwarb@serverengines.com> -M: Ajit Khaparde <ajitk@serverengines.com> +M: Sathya Perla <sathya.perla@emulex.com> +M: Subbu Seetharaman <subbu.seetharaman@emulex.com> +M: Ajit Khaparde <ajit.khaparde@emulex.com> L: netdev@vger.kernel.org -W: http://www.serverengines.com +W: http://www.emulex.com S: Supported F: drivers/net/benet/ @@ -6787,12 +6786,12 @@ S: Maintained F: drivers/net/wireless/wl1251/* WL1271 WIRELESS DRIVER -M: Luciano Coelho <luciano.coelho@nokia.com> +M: Luciano Coelho <coelho@ti.com> L: linux-wireless@vger.kernel.org -W: http://wireless.kernel.org +W: http://wireless.kernel.org/en/users/Drivers/wl12xx T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git S: Maintained -F: drivers/net/wireless/wl12xx/wl1271* +F: drivers/net/wireless/wl12xx/ F: include/linux/wl12xx.h WL3501 WIRELESS PCMCIA CARD DRIVER @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc4 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index ffdf87be295..82079545adc 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -838,7 +838,7 @@ EXPORT_SYMBOL(ep93xx_i2s_release); static struct resource ep93xx_ac97_resources[] = { { .start = EP93XX_AAC_PHYS_BASE, - .end = EP93XX_AAC_PHYS_BASE + 0xb0 - 1, + .end = EP93XX_AAC_PHYS_BASE + 0xac - 1, .flags = IORESOURCE_MEM, }, { diff --git a/arch/arm/mach-imx/mach-mx25_3ds.c b/arch/arm/mach-imx/mach-mx25_3ds.c index aa76cfd9f34..8382e790207 100644 --- a/arch/arm/mach-imx/mach-mx25_3ds.c +++ b/arch/arm/mach-imx/mach-mx25_3ds.c @@ -180,7 +180,7 @@ static const uint32_t mx25pdk_keymap[] = { KEY(3, 3, KEY_POWER), }; -static const struct matrix_keymap_data mx25pdk_keymap_data __initdata = { +static const struct matrix_keymap_data mx25pdk_keymap_data __initconst = { .keymap = mx25pdk_keymap, .keymap_size = ARRAY_SIZE(mx25pdk_keymap), }; diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 4dc68d6bb6b..9fd894271d5 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -432,7 +432,7 @@ static struct clocksource clocksource_ixp4xx = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -unsigned long ixp4xx_timer_freq = FREQ; +unsigned long ixp4xx_timer_freq = IXP4XX_TIMER_FREQ; EXPORT_SYMBOL(ixp4xx_timer_freq); static void __init ixp4xx_clocksource_init(void) { @@ -496,7 +496,7 @@ static struct clock_event_device clockevent_ixp4xx = { static void __init ixp4xx_clockevent_init(void) { - clockevent_ixp4xx.mult = div_sc(FREQ, NSEC_PER_SEC, + clockevent_ixp4xx.mult = div_sc(IXP4XX_TIMER_FREQ, NSEC_PER_SEC, clockevent_ixp4xx.shift); clockevent_ixp4xx.max_delta_ns = clockevent_delta2ns(0xfffffffe, &clockevent_ixp4xx); diff --git a/arch/arm/mach-ixp4xx/include/mach/timex.h b/arch/arm/mach-ixp4xx/include/mach/timex.h index 2c3f93c3eb7..c9e930f2933 100644 --- a/arch/arm/mach-ixp4xx/include/mach/timex.h +++ b/arch/arm/mach-ixp4xx/include/mach/timex.h @@ -10,6 +10,7 @@ * 66.66... MHz. We do a convulted calculation of CLOCK_TICK_RATE b/c the * timer register ignores the bottom 2 bits of the LATCH value. */ -#define FREQ 66666000 -#define CLOCK_TICK_RATE (((FREQ / HZ & ~IXP4XX_OST_RELOAD_MASK) + 1) * HZ) +#define IXP4XX_TIMER_FREQ 66666000 +#define CLOCK_TICK_RATE \ + (((IXP4XX_TIMER_FREQ / HZ & ~IXP4XX_OST_RELOAD_MASK) + 1) * HZ) diff --git a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c index bfdbe4b5a3c..852f7c9f87d 100644 --- a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c +++ b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c @@ -265,6 +265,11 @@ void qmgr_release_queue(unsigned int queue) qmgr_queue_descs[queue], queue); qmgr_queue_descs[queue][0] = '\x0'; #endif + + while ((addr = qmgr_get_entry(queue))) + printk(KERN_ERR "qmgr: released queue %i not empty: 0x%08X\n", + queue, addr); + __raw_writel(0, &qmgr_regs->sram[queue]); used_sram_bitmap[0] &= ~mask[0]; @@ -275,10 +280,6 @@ void qmgr_release_queue(unsigned int queue) spin_unlock_irq(&qmgr_lock); module_put(THIS_MODULE); - - while ((addr = qmgr_get_entry(queue))) - printk(KERN_ERR "qmgr: released queue %i not empty: 0x%08X\n", - queue, addr); } static int qmgr_init(void) diff --git a/arch/arm/mach-mxs/clock-mx23.c b/arch/arm/mach-mxs/clock-mx23.c index b1a362ebfde..ca72a05ed9c 100644 --- a/arch/arm/mach-mxs/clock-mx23.c +++ b/arch/arm/mach-mxs/clock-mx23.c @@ -304,7 +304,7 @@ static int name##_set_rate(struct clk *clk, unsigned long rate) \ reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_##dr); \ reg &= ~BM_CLKCTRL_##dr##_DIV; \ reg |= div << BP_CLKCTRL_##dr##_DIV; \ - if (reg | (1 << clk->enable_shift)) { \ + if (reg & (1 << clk->enable_shift)) { \ pr_err("%s: clock is gated\n", __func__); \ return -EINVAL; \ } \ @@ -347,7 +347,7 @@ static int name##_set_parent(struct clk *clk, struct clk *parent) \ { \ if (parent != clk->parent) { \ __raw_writel(BM_CLKCTRL_CLKSEQ_BYPASS_##bit, \ - HW_CLKCTRL_CLKSEQ_TOG); \ + CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ_TOG); \ clk->parent = parent; \ } \ \ diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c index 56312c092a9..fd1c4c54b8e 100644 --- a/arch/arm/mach-mxs/clock-mx28.c +++ b/arch/arm/mach-mxs/clock-mx28.c @@ -355,12 +355,12 @@ static int name##_set_rate(struct clk *clk, unsigned long rate) \ } else { \ reg &= ~BM_CLKCTRL_##dr##_DIV; \ reg |= div << BP_CLKCTRL_##dr##_DIV; \ - if (reg | (1 << clk->enable_shift)) { \ + if (reg & (1 << clk->enable_shift)) { \ pr_err("%s: clock is gated\n", __func__); \ return -EINVAL; \ } \ } \ - __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_CPU); \ + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_##dr); \ \ for (i = 10000; i; i--) \ if (!(__raw_readl(CLKCTRL_BASE_ADDR + \ @@ -483,7 +483,7 @@ static int name##_set_parent(struct clk *clk, struct clk *parent) \ { \ if (parent != clk->parent) { \ __raw_writel(BM_CLKCTRL_CLKSEQ_BYPASS_##bit, \ - HW_CLKCTRL_CLKSEQ_TOG); \ + CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ_TOG); \ clk->parent = parent; \ } \ \ @@ -609,7 +609,6 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("duart", NULL, uart_clk) _REGISTER_CLOCK("imx28-fec.0", NULL, fec_clk) _REGISTER_CLOCK("imx28-fec.1", NULL, fec_clk) - _REGISTER_CLOCK("fec.0", NULL, fec_clk) _REGISTER_CLOCK("rtc", NULL, rtc_clk) _REGISTER_CLOCK("pll2", NULL, pll2_clk) _REGISTER_CLOCK(NULL, "hclk", hbus_clk) diff --git a/arch/arm/mach-mxs/clock.c b/arch/arm/mach-mxs/clock.c index e7d2269cf70..a7093c88e6a 100644 --- a/arch/arm/mach-mxs/clock.c +++ b/arch/arm/mach-mxs/clock.c @@ -57,7 +57,6 @@ static void __clk_disable(struct clk *clk) if (clk->disable) clk->disable(clk); __clk_disable(clk->parent); - __clk_disable(clk->secondary); } } @@ -68,7 +67,6 @@ static int __clk_enable(struct clk *clk) if (clk->usecount++ == 0) { __clk_enable(clk->parent); - __clk_enable(clk->secondary); if (clk->enable) clk->enable(clk); diff --git a/arch/arm/mach-mxs/gpio.c b/arch/arm/mach-mxs/gpio.c index d7ad7a61366..cb0c0e83a52 100644 --- a/arch/arm/mach-mxs/gpio.c +++ b/arch/arm/mach-mxs/gpio.c @@ -139,6 +139,8 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) struct mxs_gpio_port *port = (struct mxs_gpio_port *)get_irq_data(irq); u32 gpio_irq_no_base = port->virtual_irq_start; + desc->irq_data.chip->irq_ack(&desc->irq_data); + irq_stat = __raw_readl(port->base + PINCTRL_IRQSTAT(port->id)) & __raw_readl(port->base + PINCTRL_IRQEN(port->id)); diff --git a/arch/arm/mach-mxs/include/mach/clock.h b/arch/arm/mach-mxs/include/mach/clock.h index 041e276d8a3..592c9ab5d76 100644 --- a/arch/arm/mach-mxs/include/mach/clock.h +++ b/arch/arm/mach-mxs/include/mach/clock.h @@ -29,8 +29,6 @@ struct clk { int id; /* Source clock this clk depends on */ struct clk *parent; - /* Secondary clock to enable/disable with this clock */ - struct clk *secondary; /* Reference count of clock enable/disable */ __s8 usecount; /* Register bit position for clock's enable/disable control. */ diff --git a/arch/arm/mach-omap1/lcd_dma.c b/arch/arm/mach-omap1/lcd_dma.c index c9088d85da0..453809359ba 100644 --- a/arch/arm/mach-omap1/lcd_dma.c +++ b/arch/arm/mach-omap1/lcd_dma.c @@ -37,7 +37,7 @@ int omap_lcd_dma_running(void) * On OMAP1510, internal LCD controller will start the transfer * when it gets enabled, so assume DMA running if LCD enabled. */ - if (cpu_is_omap1510()) + if (cpu_is_omap15xx()) if (omap_readw(OMAP_LCDC_CONTROL) & OMAP_LCDC_CTRL_LCD_EN) return 1; @@ -95,7 +95,7 @@ EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); void omap_set_lcd_dma_b1_rotation(int rotate) { - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n"); BUG(); return; @@ -106,7 +106,7 @@ EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation); void omap_set_lcd_dma_b1_mirror(int mirror) { - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n"); BUG(); } @@ -116,7 +116,7 @@ EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror); void omap_set_lcd_dma_b1_vxres(unsigned long vxres) { - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { printk(KERN_ERR "DMA virtual resulotion is not supported " "in 1510 mode\n"); BUG(); @@ -127,7 +127,7 @@ EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres); void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale) { - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { printk(KERN_ERR "DMA scale is not supported in 1510 mode\n"); BUG(); } @@ -177,7 +177,7 @@ static void set_b1_regs(void) bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); /* 1510 DMA requires the bottom address to be 2 more * than the actual last memory access location. */ - if (cpu_is_omap1510() && + if (cpu_is_omap15xx() && lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32) bottom += 2; ei = PIXSTEP(0, 0, 1, 0); @@ -241,7 +241,7 @@ static void set_b1_regs(void) return; /* Suppress warning about uninitialized vars */ } - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U); omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L); omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U); @@ -343,7 +343,7 @@ void omap_free_lcd_dma(void) BUG(); return; } - if (!cpu_is_omap1510()) + if (!cpu_is_omap15xx()) omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR); lcd_dma.reserved = 0; @@ -360,7 +360,7 @@ void omap_enable_lcd_dma(void) * connected. Otherwise the OMAP internal controller will * start the transfer when it gets enabled. */ - if (cpu_is_omap1510() || !lcd_dma.ext_ctrl) + if (cpu_is_omap15xx() || !lcd_dma.ext_ctrl) return; w = omap_readw(OMAP1610_DMA_LCD_CTRL); @@ -378,14 +378,14 @@ EXPORT_SYMBOL(omap_enable_lcd_dma); void omap_setup_lcd_dma(void) { BUG_ON(lcd_dma.active); - if (!cpu_is_omap1510()) { + if (!cpu_is_omap15xx()) { /* Set some reasonable defaults */ omap_writew(0x5440, OMAP1610_DMA_LCD_CCR); omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP); omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL); } set_b1_regs(); - if (!cpu_is_omap1510()) { + if (!cpu_is_omap15xx()) { u16 w; w = omap_readw(OMAP1610_DMA_LCD_CCR); @@ -407,7 +407,7 @@ void omap_stop_lcd_dma(void) u16 w; lcd_dma.active = 0; - if (cpu_is_omap1510() || !lcd_dma.ext_ctrl) + if (cpu_is_omap15xx() || !lcd_dma.ext_ctrl) return; w = omap_readw(OMAP1610_DMA_LCD_CCR); diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index f83fc335c61..6885d2fac18 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -44,7 +44,6 @@ #include <linux/clocksource.h> #include <linux/clockchips.h> #include <linux/io.h> -#include <linux/sched.h> #include <asm/system.h> #include <mach/hardware.h> diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c index e906e05bb41..9a2a31e011c 100644 --- a/arch/arm/mach-omap2/board-devkit8000.c +++ b/arch/arm/mach-omap2/board-devkit8000.c @@ -115,9 +115,6 @@ static struct omap2_hsmmc_info mmc[] = { static int devkit8000_panel_enable_lcd(struct omap_dss_device *dssdev) { - twl_i2c_write_u8(TWL4030_MODULE_GPIO, 0x80, REG_GPIODATADIR1); - twl_i2c_write_u8(TWL4030_MODULE_LED, 0x0, 0x0); - if (gpio_is_valid(dssdev->reset_gpio)) gpio_set_value_cansleep(dssdev->reset_gpio, 1); return 0; @@ -247,6 +244,8 @@ static struct gpio_led gpio_leds[]; static int devkit8000_twl_gpio_setup(struct device *dev, unsigned gpio, unsigned ngpio) { + int ret; + omap_mux_init_gpio(29, OMAP_PIN_INPUT); /* gpio + 0 is "mmc0_cd" (input/IRQ) */ mmc[0].gpio_cd = gpio + 0; @@ -255,17 +254,23 @@ static int devkit8000_twl_gpio_setup(struct device *dev, /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; - /* gpio + 1 is "LCD_PWREN" (out, active high) */ - devkit8000_lcd_device.reset_gpio = gpio + 1; - gpio_request(devkit8000_lcd_device.reset_gpio, "LCD_PWREN"); - /* Disable until needed */ - gpio_direction_output(devkit8000_lcd_device.reset_gpio, 0); + /* TWL4030_GPIO_MAX + 0 is "LCD_PWREN" (out, active high) */ + devkit8000_lcd_device.reset_gpio = gpio + TWL4030_GPIO_MAX + 0; + ret = gpio_request_one(devkit8000_lcd_device.reset_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "LCD_PWREN"); + if (ret < 0) { + devkit8000_lcd_device.reset_gpio = -EINVAL; + printk(KERN_ERR "Failed to request GPIO for LCD_PWRN\n"); + } /* gpio + 7 is "DVI_PD" (out, active low) */ devkit8000_dvi_device.reset_gpio = gpio + 7; - gpio_request(devkit8000_dvi_device.reset_gpio, "DVI PowerDown"); - /* Disable until needed */ - gpio_direction_output(devkit8000_dvi_device.reset_gpio, 0); + ret = gpio_request_one(devkit8000_dvi_device.reset_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "DVI PowerDown"); + if (ret < 0) { + devkit8000_dvi_device.reset_gpio = -EINVAL; + printk(KERN_ERR "Failed to request GPIO for DVI PowerDown\n"); + } return 0; } diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index e001a048dc0..e944025d5ef 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -409,8 +409,6 @@ static void __init omap4_panda_init(void) platform_add_devices(panda_devices, ARRAY_SIZE(panda_devices)); omap_serial_init(); omap4_twl6030_hsmmc_init(mmc); - /* OMAP4 Panda uses internal transceiver so register nop transceiver */ - usb_nop_xceiv_register(); omap4_ehci_init(); usb_musb_init(&musb_board_data); } diff --git a/arch/arm/mach-omap2/board-rm680.c b/arch/arm/mach-omap2/board-rm680.c index cb77be7ac44..39a71bb8a30 100644 --- a/arch/arm/mach-omap2/board-rm680.c +++ b/arch/arm/mach-omap2/board-rm680.c @@ -40,9 +40,6 @@ static struct regulator_consumer_supply rm680_vemmc_consumers[] = { static struct regulator_init_data rm680_vemmc = { .constraints = { .name = "rm680_vemmc", - .min_uV = 2900000, - .max_uV = 2900000, - .apply_uV = 1, .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY, .valid_ops_mask = REGULATOR_CHANGE_STATUS diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index fae49d12bc7..98148b6c36e 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -1000,6 +1000,7 @@ int __init omap_mux_init(const char *name, u32 flags, if (!partition->base) { pr_err("%s: Could not ioremap mux partition at 0x%08x\n", __func__, partition->phys); + kfree(partition); return -ENODEV; } diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index a4aa1920a75..2f864e4b085 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -168,9 +168,10 @@ static void omap3_core_restore_context(void) * once during boot sequence, but this works as we are not using secure * services. */ -static void omap3_save_secure_ram_context(u32 target_mpu_state) +static void omap3_save_secure_ram_context(void) { u32 ret; + int mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); if (omap_type() != OMAP2_DEVICE_TYPE_GP) { /* @@ -181,7 +182,7 @@ static void omap3_save_secure_ram_context(u32 target_mpu_state) pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON); ret = _omap_save_secure_sram((u32 *) __pa(omap3_secure_ram_storage)); - pwrdm_set_next_pwrst(mpu_pwrdm, target_mpu_state); + pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state); /* Following is for error tracking, it should not happen */ if (ret) { printk(KERN_ERR "save_secure_sram() returns %08x\n", @@ -1094,7 +1095,7 @@ static int __init omap3_pm_init(void) local_fiq_disable(); omap_dma_global_context_save(); - omap3_save_secure_ram_context(PWRDM_POWER_ON); + omap3_save_secure_ram_context(); omap_dma_global_context_restore(); local_irq_enable(); diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index 77ecebf3fae..c37e823266d 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -780,8 +780,7 @@ static int omap_sr_autocomp_show(void *data, u64 *val) struct omap_sr *sr_info = (struct omap_sr *) data; if (!sr_info) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, sr_info->voltdm->name); + pr_warning("%s: omap_sr struct not found\n", __func__); return -EINVAL; } @@ -795,8 +794,7 @@ static int omap_sr_autocomp_store(void *data, u64 val) struct omap_sr *sr_info = (struct omap_sr *) data; if (!sr_info) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, sr_info->voltdm->name); + pr_warning("%s: omap_sr struct not found\n", __func__); return -EINVAL; } @@ -834,7 +832,8 @@ static int __init omap_sr_probe(struct platform_device *pdev) if (!pdata) { dev_err(&pdev->dev, "%s: platform data missing\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto err_free_devinfo; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -966,7 +965,7 @@ static int __devexit omap_sr_remove(struct platform_device *pdev) } sr_info = _sr_lookup(pdata->voltdm); - if (!sr_info) { + if (IS_ERR(sr_info)) { dev_warn(&pdev->dev, "%s: omap_sr struct not found\n", __func__); return -EINVAL; diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c index ed6079c94c5..12be525b8df 100644 --- a/arch/arm/mach-omap2/voltage.c +++ b/arch/arm/mach-omap2/voltage.c @@ -471,6 +471,7 @@ static void __init vdd_debugfs_init(struct omap_vdd_info *vdd) strcat(name, vdd->voltdm.name); vdd->debug_dir = debugfs_create_dir(name, voltage_dir); + kfree(name); if (IS_ERR(vdd->debug_dir)) { pr_warning("%s: Unable to create debugfs directory for" " vdd_%s\n", __func__, vdd->voltdm.name); diff --git a/arch/arm/plat-mxc/include/mach/uncompress.h b/arch/arm/plat-mxc/include/mach/uncompress.h index 3a70ebf0477..ff469c4f1d7 100644 --- a/arch/arm/plat-mxc/include/mach/uncompress.h +++ b/arch/arm/plat-mxc/include/mach/uncompress.h @@ -95,6 +95,7 @@ static __inline__ void __arch_decomp_setup(unsigned long arch_id) case MACH_TYPE_MX35_3DS: case MACH_TYPE_PCM043: case MACH_TYPE_LILLY1131: + case MACH_TYPE_VPR200: uart_base = MX3X_UART1_BASE_ADDR; break; case MACH_TYPE_MAGX_ZN5: @@ -102,6 +103,7 @@ static __inline__ void __arch_decomp_setup(unsigned long arch_id) break; case MACH_TYPE_MX51_BABBAGE: case MACH_TYPE_EUKREA_CPUIMX51SD: + case MACH_TYPE_MX51_3DS: uart_base = MX51_UART1_BASE_ADDR; break; case MACH_TYPE_MX50_RDP: diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index 2fea897ebeb..9d6feaabbe7 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -12,7 +12,7 @@ # # http://www.arm.linux.org.uk/developer/machines/?action=new # -# Last update: Sun Dec 12 23:24:27 2010 +# Last update: Mon Feb 7 08:59:27 2011 # # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number # @@ -2240,7 +2240,7 @@ arm_ultimator2 MACH_ARM_ULTIMATOR2 ARM_ULTIMATOR2 2250 vs_v210 MACH_VS_V210 VS_V210 2252 vs_v212 MACH_VS_V212 VS_V212 2253 hmt MACH_HMT HMT 2254 -suen3 MACH_SUEN3 SUEN3 2255 +km_kirkwood MACH_KM_KIRKWOOD KM_KIRKWOOD 2255 vesper MACH_VESPER VESPER 2256 str9 MACH_STR9 STR9 2257 omap3_wl_ff MACH_OMAP3_WL_FF OMAP3_WL_FF 2258 @@ -2987,7 +2987,7 @@ pxwnas_500_1000 MACH_PXWNAS_500_1000 PXWNAS_500_1000 3001 ea20 MACH_EA20 EA20 3002 awm2 MACH_AWM2 AWM2 3003 ti8148evm MACH_TI8148EVM TI8148EVM 3004 -tegra_seaboard MACH_TEGRA_SEABOARD TEGRA_SEABOARD 3005 +seaboard MACH_SEABOARD SEABOARD 3005 linkstation_chlv2 MACH_LINKSTATION_CHLV2 LINKSTATION_CHLV2 3006 tera_pro2_rack MACH_TERA_PRO2_RACK TERA_PRO2_RACK 3007 rubys MACH_RUBYS RUBYS 3008 @@ -3190,7 +3190,7 @@ synergy MACH_SYNERGY SYNERGY 3205 ics_if_voip MACH_ICS_IF_VOIP ICS_IF_VOIP 3206 wlf_cragg_6410 MACH_WLF_CRAGG_6410 WLF_CRAGG_6410 3207 punica MACH_PUNICA PUNICA 3208 -sbc_nt250 MACH_SBC_NT250 SBC_NT250 3209 +trimslice MACH_TRIMSLICE TRIMSLICE 3209 mx27_wmultra MACH_MX27_WMULTRA MX27_WMULTRA 3210 mackerel MACH_MACKEREL MACKEREL 3211 fa9x27 MACH_FA9X27 FA9X27 3213 @@ -3219,3 +3219,100 @@ pivicc MACH_PIVICC PIVICC 3235 pcm048 MACH_PCM048 PCM048 3236 dds MACH_DDS DDS 3237 chalten_xa1 MACH_CHALTEN_XA1 CHALTEN_XA1 3238 +ts48xx MACH_TS48XX TS48XX 3239 +tonga2_tfttimer MACH_TONGA2_TFTTIMER TONGA2_TFTTIMER 3240 +whistler MACH_WHISTLER WHISTLER 3241 +asl_phoenix MACH_ASL_PHOENIX ASL_PHOENIX 3242 +at91sam9263otlite MACH_AT91SAM9263OTLITE AT91SAM9263OTLITE 3243 +ddplug MACH_DDPLUG DDPLUG 3244 +d2plug MACH_D2PLUG D2PLUG 3245 +kzm9d MACH_KZM9D KZM9D 3246 +verdi_lte MACH_VERDI_LTE VERDI_LTE 3247 +nanozoom MACH_NANOZOOM NANOZOOM 3248 +dm3730_som_lv MACH_DM3730_SOM_LV DM3730_SOM_LV 3249 +dm3730_torpedo MACH_DM3730_TORPEDO DM3730_TORPEDO 3250 +anchovy MACH_ANCHOVY ANCHOVY 3251 +re2rev20 MACH_RE2REV20 RE2REV20 3253 +re2rev21 MACH_RE2REV21 RE2REV21 3254 +cns21xx MACH_CNS21XX CNS21XX 3255 +rider MACH_RIDER RIDER 3257 +nsk330 MACH_NSK330 NSK330 3258 +cns2133evb MACH_CNS2133EVB CNS2133EVB 3259 +z3_816x_mod MACH_Z3_816X_MOD Z3_816X_MOD 3260 +z3_814x_mod MACH_Z3_814X_MOD Z3_814X_MOD 3261 +beect MACH_BEECT BEECT 3262 +dma_thunderbug MACH_DMA_THUNDERBUG DMA_THUNDERBUG 3263 +omn_at91sam9g20 MACH_OMN_AT91SAM9G20 OMN_AT91SAM9G20 3264 +mx25_e2s_uc MACH_MX25_E2S_UC MX25_E2S_UC 3265 +mione MACH_MIONE MIONE 3266 +top9000_tcu MACH_TOP9000_TCU TOP9000_TCU 3267 +top9000_bsl MACH_TOP9000_BSL TOP9000_BSL 3268 +kingdom MACH_KINGDOM KINGDOM 3269 +armadillo460 MACH_ARMADILLO460 ARMADILLO460 3270 +lq2 MACH_LQ2 LQ2 3271 +sweda_tms2 MACH_SWEDA_TMS2 SWEDA_TMS2 3272 +mx53_loco MACH_MX53_LOCO MX53_LOCO 3273 +acer_a8 MACH_ACER_A8 ACER_A8 3275 +acer_gauguin MACH_ACER_GAUGUIN ACER_GAUGUIN 3276 +guppy MACH_GUPPY GUPPY 3277 +mx61_ard MACH_MX61_ARD MX61_ARD 3278 +tx53 MACH_TX53 TX53 3279 +omapl138_case_a3 MACH_OMAPL138_CASE_A3 OMAPL138_CASE_A3 3280 +uemd MACH_UEMD UEMD 3281 +ccwmx51mut MACH_CCWMX51MUT CCWMX51MUT 3282 +rockhopper MACH_ROCKHOPPER ROCKHOPPER 3283 +nookcolor MACH_NOOKCOLOR NOOKCOLOR 3284 +hkdkc100 MACH_HKDKC100 HKDKC100 3285 +ts42xx MACH_TS42XX TS42XX 3286 +aebl MACH_AEBL AEBL 3287 +wario MACH_WARIO WARIO 3288 +gfs_spm MACH_GFS_SPM GFS_SPM 3289 +cm_t3730 MACH_CM_T3730 CM_T3730 3290 +isc3 MACH_ISC3 ISC3 3291 +rascal MACH_RASCAL RASCAL 3292 +hrefv60 MACH_HREFV60 HREFV60 3293 +tpt_2_0 MACH_TPT_2_0 TPT_2_0 3294 +pyramid_td MACH_PYRAMID_TD PYRAMID_TD 3295 +splendor MACH_SPLENDOR SPLENDOR 3296 +guf_planet MACH_GUF_PLANET GUF_PLANET 3297 +msm8x60_qt MACH_MSM8X60_QT MSM8X60_QT 3298 +htc_hd_mini MACH_HTC_HD_MINI HTC_HD_MINI 3299 +athene MACH_ATHENE ATHENE 3300 +deep_r_ek_1 MACH_DEEP_R_EK_1 DEEP_R_EK_1 3301 +vivow_ct MACH_VIVOW_CT VIVOW_CT 3302 +nery_1000 MACH_NERY_1000 NERY_1000 3303 +rfl109145_ssrv MACH_RFL109145_SSRV RFL109145_SSRV 3304 +nmh MACH_NMH NMH 3305 +wn802t MACH_WN802T WN802T 3306 +dragonet MACH_DRAGONET DRAGONET 3307 +geneva_b MACH_GENEVA_B GENEVA_B 3308 +at91sam9263desk16l MACH_AT91SAM9263DESK16L AT91SAM9263DESK16L 3309 +bcmhana_sv MACH_BCMHANA_SV BCMHANA_SV 3310 +bcmhana_tablet MACH_BCMHANA_TABLET BCMHANA_TABLET 3311 +koi MACH_KOI KOI 3312 +ts4800 MACH_TS4800 TS4800 3313 +tqma9263 MACH_TQMA9263 TQMA9263 3314 +holiday MACH_HOLIDAY HOLIDAY 3315 +dma_6410 MACH_DMA6410 DMA6410 3316 +pcats_overlay MACH_PCATS_OVERLAY PCATS_OVERLAY 3317 +hwgw6410 MACH_HWGW6410 HWGW6410 3318 +shenzhou MACH_SHENZHOU SHENZHOU 3319 +cwme9210 MACH_CWME9210 CWME9210 3320 +cwme9210js MACH_CWME9210JS CWME9210JS 3321 +pgs_v1 MACH_PGS_SITARA PGS_SITARA 3322 +colibri_tegra2 MACH_COLIBRI_TEGRA2 COLIBRI_TEGRA2 3323 +w21 MACH_W21 W21 3324 +polysat1 MACH_POLYSAT1 POLYSAT1 3325 +dataway MACH_DATAWAY DATAWAY 3326 +cobral138 MACH_COBRAL138 COBRAL138 3327 +roverpcs8 MACH_ROVERPCS8 ROVERPCS8 3328 +marvelc MACH_MARVELC MARVELC 3329 +navefihid MACH_NAVEFIHID NAVEFIHID 3330 +dm365_cv100 MACH_DM365_CV100 DM365_CV100 3331 +able MACH_ABLE ABLE 3332 +legacy MACH_LEGACY LEGACY 3333 +icong MACH_ICONG ICONG 3334 +rover_g8 MACH_ROVER_G8 ROVER_G8 3335 +t5388p MACH_T5388P T5388P 3336 +dingo MACH_DINGO DINGO 3337 +goflexhome MACH_GOFLEXHOME GOFLEXHOME 3338 diff --git a/arch/m32r/kernel/irq.c b/arch/m32r/kernel/irq.c index f745c1287f3..76eaf3883fb 100644 --- a/arch/m32r/kernel/irq.c +++ b/arch/m32r/kernel/irq.c @@ -80,7 +80,7 @@ asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) #ifdef CONFIG_DEBUG_STACKOVERFLOW /* FIXME M32R */ #endif - __do_IRQ(irq); + generic_handle_irq(irq); irq_exit(); set_irq_regs(old_regs); diff --git a/arch/powerpc/include/asm/mmu-book3e.h b/arch/powerpc/include/asm/mmu-book3e.h index 8eaed81ea64..17194fcd404 100644 --- a/arch/powerpc/include/asm/mmu-book3e.h +++ b/arch/powerpc/include/asm/mmu-book3e.h @@ -40,8 +40,8 @@ /* MAS registers bit definitions */ -#define MAS0_TLBSEL(x) ((x << 28) & 0x30000000) -#define MAS0_ESEL(x) ((x << 16) & 0x0FFF0000) +#define MAS0_TLBSEL(x) (((x) << 28) & 0x30000000) +#define MAS0_ESEL(x) (((x) << 16) & 0x0FFF0000) #define MAS0_NV(x) ((x) & 0x00000FFF) #define MAS0_HES 0x00004000 #define MAS0_WQ_ALLWAYS 0x00000000 @@ -50,12 +50,12 @@ #define MAS1_VALID 0x80000000 #define MAS1_IPROT 0x40000000 -#define MAS1_TID(x) ((x << 16) & 0x3FFF0000) +#define MAS1_TID(x) (((x) << 16) & 0x3FFF0000) #define MAS1_IND 0x00002000 #define MAS1_TS 0x00001000 #define MAS1_TSIZE_MASK 0x00000f80 #define MAS1_TSIZE_SHIFT 7 -#define MAS1_TSIZE(x) ((x << MAS1_TSIZE_SHIFT) & MAS1_TSIZE_MASK) +#define MAS1_TSIZE(x) (((x) << MAS1_TSIZE_SHIFT) & MAS1_TSIZE_MASK) #define MAS2_EPN 0xFFFFF000 #define MAS2_X0 0x00000040 diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index 53b64be40eb..da4b2000854 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -101,7 +101,7 @@ extern phys_addr_t kernstart_addr; #ifdef CONFIG_FLATMEM #define ARCH_PFN_OFFSET (MEMORY_START >> PAGE_SHIFT) -#define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && (pfn) < (ARCH_PFN_OFFSET + max_mapnr)) +#define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && (pfn) < max_mapnr) #endif #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) diff --git a/arch/powerpc/kernel/cpu_setup_6xx.S b/arch/powerpc/kernel/cpu_setup_6xx.S index 55cba4a8a95..f8cd9fba4d3 100644 --- a/arch/powerpc/kernel/cpu_setup_6xx.S +++ b/arch/powerpc/kernel/cpu_setup_6xx.S @@ -18,7 +18,7 @@ #include <asm/mmu.h> _GLOBAL(__setup_cpu_603) - mflr r4 + mflr r5 BEGIN_MMU_FTR_SECTION li r10,0 mtspr SPRN_SPRG_603_LRU,r10 /* init SW LRU tracking */ @@ -27,60 +27,60 @@ BEGIN_FTR_SECTION bl __init_fpu_registers END_FTR_SECTION_IFCLR(CPU_FTR_FPU_UNAVAILABLE) bl setup_common_caches - mtlr r4 + mtlr r5 blr _GLOBAL(__setup_cpu_604) - mflr r4 + mflr r5 bl setup_common_caches bl setup_604_hid0 - mtlr r4 + mtlr r5 blr _GLOBAL(__setup_cpu_750) - mflr r4 + mflr r5 bl __init_fpu_registers bl setup_common_caches bl setup_750_7400_hid0 - mtlr r4 + mtlr r5 blr _GLOBAL(__setup_cpu_750cx) - mflr r4 + mflr r5 bl __init_fpu_registers bl setup_common_caches bl setup_750_7400_hid0 bl setup_750cx - mtlr r4 + mtlr r5 blr _GLOBAL(__setup_cpu_750fx) - mflr r4 + mflr r5 bl __init_fpu_registers bl setup_common_caches bl setup_750_7400_hid0 bl setup_750fx - mtlr r4 + mtlr r5 blr _GLOBAL(__setup_cpu_7400) - mflr r4 + mflr r5 bl __init_fpu_registers bl setup_7400_workarounds bl setup_common_caches bl setup_750_7400_hid0 - mtlr r4 + mtlr r5 blr _GLOBAL(__setup_cpu_7410) - mflr r4 + mflr r5 bl __init_fpu_registers bl setup_7410_workarounds bl setup_common_caches bl setup_750_7400_hid0 li r3,0 mtspr SPRN_L2CR2,r3 - mtlr r4 + mtlr r5 blr _GLOBAL(__setup_cpu_745x) - mflr r4 + mflr r5 bl setup_common_caches bl setup_745x_specifics - mtlr r4 + mtlr r5 blr /* Enable caches for 603's, 604, 750 & 7400 */ @@ -194,10 +194,10 @@ setup_750cx: cror 4*cr0+eq,4*cr0+eq,4*cr1+eq cror 4*cr0+eq,4*cr0+eq,4*cr2+eq bnelr - lwz r6,CPU_SPEC_FEATURES(r5) + lwz r6,CPU_SPEC_FEATURES(r4) li r7,CPU_FTR_CAN_NAP andc r6,r6,r7 - stw r6,CPU_SPEC_FEATURES(r5) + stw r6,CPU_SPEC_FEATURES(r4) blr /* 750fx specific @@ -225,12 +225,12 @@ BEGIN_FTR_SECTION andis. r11,r11,L3CR_L3E@h beq 1f END_FTR_SECTION_IFSET(CPU_FTR_L3CR) - lwz r6,CPU_SPEC_FEATURES(r5) + lwz r6,CPU_SPEC_FEATURES(r4) andi. r0,r6,CPU_FTR_L3_DISABLE_NAP beq 1f li r7,CPU_FTR_CAN_NAP andc r6,r6,r7 - stw r6,CPU_SPEC_FEATURES(r5) + stw r6,CPU_SPEC_FEATURES(r4) 1: mfspr r11,SPRN_HID0 diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 8d74a24c550..e8e915ce3d8 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -2076,8 +2076,8 @@ static void __init setup_cpu_spec(unsigned long offset, struct cpu_spec *s) * pointer on ppc64 and booke as we are running at 0 in real mode * on ppc64 and reloc_offset is always 0 on booke. */ - if (s->cpu_setup) { - s->cpu_setup(offset, s); + if (t->cpu_setup) { + t->cpu_setup(offset, t); } #endif /* CONFIG_PPC64 || CONFIG_BOOKE */ } diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index bf5cb91f07d..fd481232957 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -186,7 +186,7 @@ static void unmap_cpu_from_node(unsigned long cpu) dbg("removing cpu %lu from node %d\n", cpu, node); if (cpumask_test_cpu(cpu, node_to_cpumask_map[node])) { - cpumask_set_cpu(cpu, node_to_cpumask_map[node]); + cpumask_clear_cpu(cpu, node_to_cpumask_map[node]); } else { printk(KERN_ERR "WARNING: cpu %lu not found in node %d\n", cpu, node); @@ -1289,10 +1289,9 @@ u64 memory_hotplug_max(void) } #endif /* CONFIG_MEMORY_HOTPLUG */ -/* Vrtual Processor Home Node (VPHN) support */ +/* Virtual Processor Home Node (VPHN) support */ #ifdef CONFIG_PPC_SPLPAR -#define VPHN_NR_CHANGE_CTRS (8) -static u8 vphn_cpu_change_counts[NR_CPUS][VPHN_NR_CHANGE_CTRS]; +static u8 vphn_cpu_change_counts[NR_CPUS][MAX_DISTANCE_REF_POINTS]; static cpumask_t cpu_associativity_changes_mask; static int vphn_enabled; static void set_topology_timer(void); @@ -1303,16 +1302,18 @@ static void set_topology_timer(void); */ static void setup_cpu_associativity_change_counters(void) { - int cpu = 0; + int cpu; + + /* The VPHN feature supports a maximum of 8 reference points */ + BUILD_BUG_ON(MAX_DISTANCE_REF_POINTS > 8); for_each_possible_cpu(cpu) { - int i = 0; + int i; u8 *counts = vphn_cpu_change_counts[cpu]; volatile u8 *hypervisor_counts = lppaca[cpu].vphn_assoc_counts; - for (i = 0; i < VPHN_NR_CHANGE_CTRS; i++) { + for (i = 0; i < distance_ref_points_depth; i++) counts[i] = hypervisor_counts[i]; - } } } @@ -1329,7 +1330,7 @@ static void setup_cpu_associativity_change_counters(void) */ static int update_cpu_associativity_changes_mask(void) { - int cpu = 0, nr_cpus = 0; + int cpu, nr_cpus = 0; cpumask_t *changes = &cpu_associativity_changes_mask; cpumask_clear(changes); @@ -1339,8 +1340,8 @@ static int update_cpu_associativity_changes_mask(void) u8 *counts = vphn_cpu_change_counts[cpu]; volatile u8 *hypervisor_counts = lppaca[cpu].vphn_assoc_counts; - for (i = 0; i < VPHN_NR_CHANGE_CTRS; i++) { - if (hypervisor_counts[i] > counts[i]) { + for (i = 0; i < distance_ref_points_depth; i++) { + if (hypervisor_counts[i] != counts[i]) { counts[i] = hypervisor_counts[i]; changed = 1; } @@ -1354,8 +1355,11 @@ static int update_cpu_associativity_changes_mask(void) return nr_cpus; } -/* 6 64-bit registers unpacked into 12 32-bit associativity values */ -#define VPHN_ASSOC_BUFSIZE (6*sizeof(u64)/sizeof(u32)) +/* + * 6 64-bit registers unpacked into 12 32-bit associativity values. To form + * the complete property we have to add the length in the first cell. + */ +#define VPHN_ASSOC_BUFSIZE (6*sizeof(u64)/sizeof(u32) + 1) /* * Convert the associativity domain numbers returned from the hypervisor @@ -1363,15 +1367,14 @@ static int update_cpu_associativity_changes_mask(void) */ static int vphn_unpack_associativity(const long *packed, unsigned int *unpacked) { - int i = 0; - int nr_assoc_doms = 0; + int i, nr_assoc_doms = 0; const u16 *field = (const u16*) packed; #define VPHN_FIELD_UNUSED (0xffff) #define VPHN_FIELD_MSB (0x8000) #define VPHN_FIELD_MASK (~VPHN_FIELD_MSB) - for (i = 0; i < VPHN_ASSOC_BUFSIZE; i++) { + for (i = 1; i < VPHN_ASSOC_BUFSIZE; i++) { if (*field == VPHN_FIELD_UNUSED) { /* All significant fields processed, and remaining * fields contain the reserved value of all 1's. @@ -1379,14 +1382,12 @@ static int vphn_unpack_associativity(const long *packed, unsigned int *unpacked) */ unpacked[i] = *((u32*)field); field += 2; - } - else if (*field & VPHN_FIELD_MSB) { + } else if (*field & VPHN_FIELD_MSB) { /* Data is in the lower 15 bits of this field */ unpacked[i] = *field & VPHN_FIELD_MASK; field++; nr_assoc_doms++; - } - else { + } else { /* Data is in the lower 15 bits of this field * concatenated with the next 16 bit field */ @@ -1396,6 +1397,9 @@ static int vphn_unpack_associativity(const long *packed, unsigned int *unpacked) } } + /* The first cell contains the length of the property */ + unpacked[0] = nr_assoc_doms; + return nr_assoc_doms; } @@ -1405,7 +1409,7 @@ static int vphn_unpack_associativity(const long *packed, unsigned int *unpacked) */ static long hcall_vphn(unsigned long cpu, unsigned int *associativity) { - long rc = 0; + long rc; long retbuf[PLPAR_HCALL9_BUFSIZE] = {0}; u64 flags = 1; int hwcpu = get_hard_smp_processor_id(cpu); @@ -1419,7 +1423,7 @@ static long hcall_vphn(unsigned long cpu, unsigned int *associativity) static long vphn_get_associativity(unsigned long cpu, unsigned int *associativity) { - long rc = 0; + long rc; rc = hcall_vphn(cpu, associativity); @@ -1445,9 +1449,9 @@ static long vphn_get_associativity(unsigned long cpu, */ int arch_update_cpu_topology(void) { - int cpu = 0, nid = 0, old_nid = 0; + int cpu, nid, old_nid; unsigned int associativity[VPHN_ASSOC_BUFSIZE] = {0}; - struct sys_device *sysdev = NULL; + struct sys_device *sysdev; for_each_cpu_mask(cpu, cpu_associativity_changes_mask) { vphn_get_associativity(cpu, associativity); @@ -1512,7 +1516,8 @@ int start_topology_update(void) { int rc = 0; - if (firmware_has_feature(FW_FEATURE_VPHN)) { + if (firmware_has_feature(FW_FEATURE_VPHN) && + get_lppaca()->shared_proc) { vphn_enabled = 1; setup_cpu_associativity_change_counters(); init_timer_deferrable(&topology_timer); diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 5d3ea9f60dd..ca5d5898d32 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -713,6 +713,13 @@ EXPORT_SYMBOL(arch_free_page); /* NB: reg/unreg are called while guarded with the tracepoints_mutex */ extern long hcall_tracepoint_refcount; +/* + * Since the tracing code might execute hcalls we need to guard against + * recursion. One example of this are spinlocks calling H_YIELD on + * shared processor partitions. + */ +static DEFINE_PER_CPU(unsigned int, hcall_trace_depth); + void hcall_tracepoint_regfunc(void) { hcall_tracepoint_refcount++; @@ -725,12 +732,42 @@ void hcall_tracepoint_unregfunc(void) void __trace_hcall_entry(unsigned long opcode, unsigned long *args) { + unsigned long flags; + unsigned int *depth; + + local_irq_save(flags); + + depth = &__get_cpu_var(hcall_trace_depth); + + if (*depth) + goto out; + + (*depth)++; trace_hcall_entry(opcode, args); + (*depth)--; + +out: + local_irq_restore(flags); } void __trace_hcall_exit(long opcode, unsigned long retval, unsigned long *retbuf) { + unsigned long flags; + unsigned int *depth; + + local_irq_save(flags); + + depth = &__get_cpu_var(hcall_trace_depth); + + if (*depth) + goto out; + + (*depth)++; trace_hcall_exit(opcode, retval, retbuf); + (*depth)--; + +out: + local_irq_restore(flags); } #endif diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 4a2d4e0c18d..8b5393ec108 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -36,8 +36,6 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, unsigned cpu = smp_processor_id(); if (likely(prev != next)) { - /* stop flush ipis for the previous mm */ - cpumask_clear_cpu(cpu, mm_cpumask(prev)); #ifdef CONFIG_SMP percpu_write(cpu_tlbstate.state, TLBSTATE_OK); percpu_write(cpu_tlbstate.active_mm, next); @@ -47,6 +45,9 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, /* Re-load page tables */ load_cr3(next->pgd); + /* stop flush ipis for the previous mm */ + cpumask_clear_cpu(cpu, mm_cpumask(prev)); + /* * load the LDT, if the LDT is different: */ diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h index 4c2f63c7fc1..1f469513677 100644 --- a/arch/x86/include/asm/smp.h +++ b/arch/x86/include/asm/smp.h @@ -40,10 +40,7 @@ DECLARE_EARLY_PER_CPU(u16, x86_cpu_to_apicid); DECLARE_EARLY_PER_CPU(u16, x86_bios_cpu_apicid); /* Static state in head.S used to set up a CPU */ -extern struct { - void *sp; - unsigned short ss; -} stack_start; +extern unsigned long stack_start; /* Initial stack pointer address */ struct smp_ops { void (*smp_prepare_boot_cpu)(void); diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index 69fd72aa559..68d1537b8c8 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -12,10 +12,8 @@ #include <linux/cpumask.h> #include <asm/segment.h> #include <asm/desc.h> - -#ifdef CONFIG_X86_32 #include <asm/pgtable.h> -#endif +#include <asm/cacheflush.h> #include "realmode/wakeup.h" #include "sleep.h" @@ -100,7 +98,7 @@ int acpi_save_state_mem(void) #else /* CONFIG_64BIT */ header->trampoline_segment = setup_trampoline() >> 4; #ifdef CONFIG_SMP - stack_start.sp = temp_stack + sizeof(temp_stack); + stack_start = (unsigned long)temp_stack + sizeof(temp_stack); early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(smp_processor_id()); initial_gs = per_cpu_offset(smp_processor_id()); @@ -149,6 +147,15 @@ void __init acpi_reserve_wakeup_memory(void) memblock_x86_reserve_range(mem, mem + WAKEUP_SIZE, "ACPI WAKEUP"); } +int __init acpi_configure_wakeup_memory(void) +{ + if (acpi_realmode) + set_memory_x(acpi_realmode, WAKEUP_SIZE >> PAGE_SHIFT); + + return 0; +} +arch_initcall(acpi_configure_wakeup_memory); + static int __init acpi_sleep_setup(char *str) { diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index 01c0f3ee6cc..bebabec5b44 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c @@ -793,13 +793,21 @@ void set_mtrr_aps_delayed_init(void) } /* - * MTRR initialization for all AP's + * Delayed MTRR initialization for all AP's */ void mtrr_aps_init(void) { if (!use_intel()) return; + /* + * Check if someone has requested the delay of AP MTRR initialization, + * by doing set_mtrr_aps_delayed_init(), prior to this point. If not, + * then we are done. + */ + if (!mtrr_aps_delayed_init) + return; + set_mtrr(~0U, 0, 0, 0); mtrr_aps_delayed_init = false; } diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 9d977a2ea69..4d98789b066 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1389,7 +1389,7 @@ static void __init pmu_check_apic(void) pr_info("no hardware sampling interrupt available.\n"); } -int __init init_hw_perf_events(void) +static int __init init_hw_perf_events(void) { struct event_constraint *c; int err; @@ -1608,7 +1608,7 @@ out: return ret; } -int x86_pmu_event_init(struct perf_event *event) +static int x86_pmu_event_init(struct perf_event *event) { struct pmu *tmp; int err; diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S index fc293dc8dc3..767d6c43de3 100644 --- a/arch/x86/kernel/head_32.S +++ b/arch/x86/kernel/head_32.S @@ -85,6 +85,8 @@ RESERVE_BRK(pagetables, INIT_MAP_SIZE) */ __HEAD ENTRY(startup_32) + movl pa(stack_start),%ecx + /* test KEEP_SEGMENTS flag to see if the bootloader is asking us to not reload segments */ testb $(1<<6), BP_loadflags(%esi) @@ -99,7 +101,9 @@ ENTRY(startup_32) movl %eax,%es movl %eax,%fs movl %eax,%gs + movl %eax,%ss 2: + leal -__PAGE_OFFSET(%ecx),%esp /* * Clear BSS first so that there are no surprises... @@ -145,8 +149,6 @@ ENTRY(startup_32) * _brk_end is set up to point to the first "safe" location. * Mappings are created both at virtual address 0 (identity mapping) * and PAGE_OFFSET for up to _end. - * - * Note that the stack is not yet set up! */ #ifdef CONFIG_X86_PAE @@ -282,6 +284,9 @@ ENTRY(startup_32_smp) movl %eax,%es movl %eax,%fs movl %eax,%gs + movl pa(stack_start),%ecx + movl %eax,%ss + leal -__PAGE_OFFSET(%ecx),%esp #endif /* CONFIG_SMP */ default_entry: @@ -347,8 +352,8 @@ default_entry: movl %eax,%cr0 /* ..and set paging (PG) bit */ ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */ 1: - /* Set up the stack pointer */ - lss stack_start,%esp + /* Shift the stack pointer to a virtual address */ + addl $__PAGE_OFFSET, %esp /* * Initialize eflags. Some BIOS's leave bits like NT set. This would @@ -360,9 +365,7 @@ default_entry: #ifdef CONFIG_SMP cmpb $0, ready - jz 1f /* Initial CPU cleans BSS */ - jmp checkCPUtype -1: + jnz checkCPUtype #endif /* CONFIG_SMP */ /* @@ -470,14 +473,7 @@ is386: movl $2,%ecx # set MP cld # gcc2 wants the direction flag cleared at all times pushl $0 # fake return address for unwinder -#ifdef CONFIG_SMP - movb ready, %cl movb $1, ready - cmpb $0,%cl # the first CPU calls start_kernel - je 1f - movl (stack_start), %esp -1: -#endif /* CONFIG_SMP */ jmp *(initial_code) /* @@ -670,15 +666,15 @@ ENTRY(initial_page_table) #endif .data +.balign 4 ENTRY(stack_start) .long init_thread_union+THREAD_SIZE - .long __BOOT_DS - -ready: .byte 0 early_recursion_flag: .long 0 +ready: .byte 0 + int_msg: .asciz "Unknown interrupt or fault at: %p %p %p\n" diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 0cbe8c0b35e..03273b6c272 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -638,7 +638,7 @@ wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip) * target processor state. */ startup_ipi_hook(phys_apicid, (unsigned long) start_secondary, - (unsigned long)stack_start.sp); + stack_start); /* * Run STARTUP IPI loop. @@ -785,7 +785,7 @@ do_rest: #endif early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu); initial_code = (unsigned long)start_secondary; - stack_start.sp = (void *) c_idle.idle->thread.sp; + stack_start = c_idle.idle->thread.sp; /* start_ip had better be page-aligned! */ start_ip = setup_trampoline(); diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 8b830ca14ac..d343b3c81f3 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -256,7 +256,6 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, unsigned long pfn) { pgprot_t forbidden = __pgprot(0); - pgprot_t required = __pgprot(0); /* * The BIOS area between 640k and 1Mb needs to be executable for @@ -282,12 +281,6 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT, __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) pgprot_val(forbidden) |= _PAGE_RW; - /* - * .data and .bss should always be writable. - */ - if (within(address, (unsigned long)_sdata, (unsigned long)_edata) || - within(address, (unsigned long)__bss_start, (unsigned long)__bss_stop)) - pgprot_val(required) |= _PAGE_RW; #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) /* @@ -327,7 +320,6 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, #endif prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); - prot = __pgprot(pgprot_val(prot) | pgprot_val(required)); return prot; } diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig index fcd867d923b..d8b1b576556 100644 --- a/drivers/char/agp/Kconfig +++ b/drivers/char/agp/Kconfig @@ -50,7 +50,7 @@ config AGP_ATI config AGP_AMD tristate "AMD Irongate, 761, and 762 chipset support" - depends on AGP && (X86_32 || ALPHA) + depends on AGP && X86_32 help This option gives you AGP support for the GLX component of X on AMD Irongate, 761, and 762 chipsets. diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index b1b4362bc64..45681c0ff3b 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -41,22 +41,8 @@ static int amd_create_page_map(struct amd_page_map *page_map) if (page_map->real == NULL) return -ENOMEM; -#ifndef CONFIG_X86 - SetPageReserved(virt_to_page(page_map->real)); - global_cache_flush(); - page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), - PAGE_SIZE); - if (page_map->remapped == NULL) { - ClearPageReserved(virt_to_page(page_map->real)); - free_page((unsigned long) page_map->real); - page_map->real = NULL; - return -ENOMEM; - } - global_cache_flush(); -#else set_memory_uc((unsigned long)page_map->real, 1); page_map->remapped = page_map->real; -#endif for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { writel(agp_bridge->scratch_page, page_map->remapped+i); @@ -68,12 +54,7 @@ static int amd_create_page_map(struct amd_page_map *page_map) static void amd_free_page_map(struct amd_page_map *page_map) { -#ifndef CONFIG_X86 - iounmap(page_map->remapped); - ClearPageReserved(virt_to_page(page_map->real)); -#else set_memory_wb((unsigned long)page_map->real, 1); -#endif free_page((unsigned long) page_map->real); } diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 857df10c042..b0a0dccc98c 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -774,20 +774,14 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "Intel %s Chipset\n", intel_agp_chipsets[i].name); /* - * If the device has not been properly setup, the following will catch - * the problem and should stop the system from crashing. - * 20030610 - hamish@zot.org - */ - if (pci_enable_device(pdev)) { - dev_err(&pdev->dev, "can't enable PCI device\n"); - agp_put_bridge(bridge); - return -ENODEV; - } - - /* * The following fixes the case where the BIOS has "forgotten" to * provide an address range for the GART. * 20030610 - hamish@zot.org + * This happens before pci_enable_device() intentionally; + * calling pci_enable_device() before assigning the resource + * will result in the GART being disabled on machines with such + * BIOSs (the GART ends up with a BAR starting at 0, which + * conflicts a lot of other devices). */ r = &pdev->resource[0]; if (!r->start && r->end) { @@ -798,6 +792,17 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, } } + /* + * If the device has not been properly setup, the following will catch + * the problem and should stop the system from crashing. + * 20030610 - hamish@zot.org + */ + if (pci_enable_device(pdev)) { + dev_err(&pdev->dev, "can't enable PCI device\n"); + agp_put_bridge(bridge); + return -ENODEV; + } + /* Fill in the mode register */ if (cap_ptr) { pci_read_config_dword(pdev, diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2baa6708e44..654faa803dc 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2674,3 +2674,23 @@ out: mutex_unlock(&dev->mode_config.mutex); return ret; } + +void drm_mode_config_reset(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + if (crtc->funcs->reset) + crtc->funcs->reset(crtc); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + if (encoder->funcs->reset) + encoder->funcs->reset(encoder); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + if (connector->funcs->reset) + connector->funcs->reset(connector); +} +EXPORT_SYMBOL(drm_mode_config_reset); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 952b3d4fb2a..92369655dca 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -343,13 +343,12 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_encoder *encoder; bool ret = true; - adjusted_mode = drm_mode_duplicate(dev, mode); - crtc->enabled = drm_helper_crtc_in_use(crtc); - if (!crtc->enabled) return true; + adjusted_mode = drm_mode_duplicate(dev, mode); + saved_hwmode = crtc->hwmode; saved_mode = crtc->mode; saved_x = crtc->x; @@ -437,10 +436,9 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, */ drm_calc_timestamping_constants(crtc); - /* XXX free adjustedmode */ - drm_mode_destroy(dev, adjusted_mode); /* FIXME: add subpixel order */ done: + drm_mode_destroy(dev, adjusted_mode); if (!ret) { crtc->hwmode = saved_hwmode; crtc->mode = saved_mode; @@ -497,14 +495,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) crtc_funcs = set->crtc->helper_private; + if (!set->mode) + set->fb = NULL; + if (set->fb) { DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", set->crtc->base.id, set->fb->base.id, (int)set->num_connectors, set->x, set->y); } else { - DRM_DEBUG_KMS("[CRTC:%d] [NOFB] #connectors=%d (x y) (%i %i)\n", - set->crtc->base.id, (int)set->num_connectors, - set->x, set->y); + DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); + set->mode = NULL; + set->num_connectors = 0; } dev = set->crtc->dev; @@ -649,8 +650,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) mode_changed = true; if (mode_changed) { - set->crtc->enabled = (set->mode != NULL); - if (set->mode != NULL) { + set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); + if (set->crtc->enabled) { DRM_DEBUG_KMS("attempting to set mode from" " userspace\n"); drm_mode_debug_printmodeline(set->mode); @@ -665,6 +666,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) ret = -EINVAL; goto fail; } + DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); + for (i = 0; i < set->num_connectors; i++) { + DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, + drm_get_connector_name(set->connectors[i])); + set->connectors[i]->dpms = DRM_MODE_DPMS_ON; + } } drm_helper_disable_unused_functions(dev); } else if (fb_changed) { @@ -681,12 +688,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) goto fail; } } - DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); - for (i = 0; i < set->num_connectors; i++) { - DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, - drm_get_connector_name(set->connectors[i])); - set->connectors[i]->dpms = DRM_MODE_DPMS_ON; - } kfree(save_connectors); kfree(save_encoders); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 0054e957203..3dadfa2a852 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1250,7 +1250,7 @@ void drm_handle_vblank_events(struct drm_device *dev, int crtc) * Drivers should call this routine in their vblank interrupt handlers to * update the vblank counter and send any signals that may be pending. */ -void drm_handle_vblank(struct drm_device *dev, int crtc) +bool drm_handle_vblank(struct drm_device *dev, int crtc) { u32 vblcount; s64 diff_ns; @@ -1258,7 +1258,7 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) unsigned long irqflags; if (!dev->num_crtcs) - return; + return false; /* Need timestamp lock to prevent concurrent execution with * vblank enable/disable, as this would cause inconsistent @@ -1269,7 +1269,7 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) /* Vblank irq handling disabled. Nothing to do. */ if (!dev->vblank_enabled[crtc]) { spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); - return; + return false; } /* Fetch corresponding timestamp for this vblank interval from @@ -1311,5 +1311,6 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) drm_handle_vblank_events(dev, crtc); spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); + return true; } EXPORT_SYMBOL(drm_handle_vblank); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 66796bb82d3..cfb56d0ff36 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -354,6 +354,7 @@ static int i915_drm_thaw(struct drm_device *dev) error = i915_gem_init_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); + drm_mode_config_reset(dev); drm_irq_install(dev); /* Resume the modeset for every activated CRTC */ @@ -542,6 +543,7 @@ int i915_reset(struct drm_device *dev, u8 flags) mutex_unlock(&dev->struct_mutex); drm_irq_uninstall(dev); + drm_mode_config_reset(dev); drm_irq_install(dev); mutex_lock(&dev->struct_mutex); } @@ -566,6 +568,14 @@ int i915_reset(struct drm_device *dev, u8 flags) static int __devinit i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + /* Only bind to function 0 of the device. Early generations + * used function 1 as a placeholder for multi-head. This causes + * us confusion instead, especially on the systems where both + * functions have the same PCI-ID! + */ + if (PCI_FUNC(pdev->devfn)) + return -ENODEV; + return drm_get_pci_dev(pdev, ent, &driver); } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 062f353497e..97f946dcc1a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1196,18 +1196,18 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) intel_finish_page_flip_plane(dev, 1); } - if (pipea_stats & vblank_status) { + if (pipea_stats & vblank_status && + drm_handle_vblank(dev, 0)) { vblank++; - drm_handle_vblank(dev, 0); if (!dev_priv->flip_pending_is_done) { i915_pageflip_stall_check(dev, 0); intel_finish_page_flip(dev, 0); } } - if (pipeb_stats & vblank_status) { + if (pipeb_stats & vblank_status && + drm_handle_vblank(dev, 1)) { vblank++; - drm_handle_vblank(dev, 1); if (!dev_priv->flip_pending_is_done) { i915_pageflip_stall_check(dev, 1); intel_finish_page_flip(dev, 1); diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 17035b87ee4..8a77ff4a723 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -535,6 +535,15 @@ static int intel_crt_set_property(struct drm_connector *connector, return 0; } +static void intel_crt_reset(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct intel_crt *crt = intel_attached_crt(connector); + + if (HAS_PCH_SPLIT(dev)) + crt->force_hotplug_required = 1; +} + /* * Routines for controlling stuff on the analog port */ @@ -548,6 +557,7 @@ static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = { }; static const struct drm_connector_funcs intel_crt_connector_funcs = { + .reset = intel_crt_reset, .dpms = drm_helper_connector_dpms, .detect = intel_crt_detect, .fill_modes = drm_helper_probe_single_connector_modes, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d7f237deaaf..7e42aa58650 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5551,6 +5551,18 @@ cleanup_work: return ret; } +static void intel_crtc_reset(struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + /* Reset flags back to the 'unknown' status so that they + * will be correctly set on the initial modeset. + */ + intel_crtc->cursor_addr = 0; + intel_crtc->dpms_mode = -1; + intel_crtc->active = true; /* force the pipe off on setup_init_config */ +} + static struct drm_crtc_helper_funcs intel_helper_funcs = { .dpms = intel_crtc_dpms, .mode_fixup = intel_crtc_mode_fixup, @@ -5562,6 +5574,7 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = { }; static const struct drm_crtc_funcs intel_crtc_funcs = { + .reset = intel_crtc_reset, .cursor_set = intel_crtc_cursor_set, .cursor_move = intel_crtc_cursor_move, .gamma_set = intel_crtc_gamma_set, @@ -5652,9 +5665,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base; dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base; - intel_crtc->cursor_addr = 0; - intel_crtc->dpms_mode = -1; - intel_crtc->active = true; /* force the pipe off on setup_init_config */ + intel_crtc_reset(&intel_crtc->base); if (HAS_PCH_SPLIT(dev)) { intel_helper_funcs.prepare = ironlake_crtc_prepare; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 45cd37652a3..6a09c1413d6 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -473,20 +473,6 @@ static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd, return false; } - i = 3; - while (status == SDVO_CMD_STATUS_PENDING && i--) { - if (!intel_sdvo_read_byte(intel_sdvo, - SDVO_I2C_CMD_STATUS, - &status)) - return false; - } - if (status != SDVO_CMD_STATUS_SUCCESS) { - DRM_DEBUG_KMS("command returns response %s [%d]\n", - status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP ? cmd_status_names[status] : "???", - status); - return false; - } - return true; } @@ -497,6 +483,8 @@ static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo, u8 status; int i; + DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(intel_sdvo)); + /* * The documentation states that all commands will be * processed within 15µs, and that we need only poll @@ -505,14 +493,19 @@ static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo, * * Check 5 times in case the hardware failed to read the docs. */ - do { + if (!intel_sdvo_read_byte(intel_sdvo, + SDVO_I2C_CMD_STATUS, + &status)) + goto log_fail; + + while (status == SDVO_CMD_STATUS_PENDING && retry--) { + udelay(15); if (!intel_sdvo_read_byte(intel_sdvo, SDVO_I2C_CMD_STATUS, &status)) - return false; - } while (status == SDVO_CMD_STATUS_PENDING && --retry); + goto log_fail; + } - DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(intel_sdvo)); if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) DRM_LOG_KMS("(%s)", cmd_status_names[status]); else @@ -533,7 +526,7 @@ static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo, return true; log_fail: - DRM_LOG_KMS("\n"); + DRM_LOG_KMS("... failed\n"); return false; } @@ -550,6 +543,7 @@ static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) static bool intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo, u8 ddc_bus) { + /* This must be the immediately preceding write before the i2c xfer */ return intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &ddc_bus, 1); @@ -557,7 +551,10 @@ static bool intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_set_value(struct intel_sdvo *intel_sdvo, u8 cmd, const void *data, int len) { - return intel_sdvo_write_cmd(intel_sdvo, cmd, data, len); + if (!intel_sdvo_write_cmd(intel_sdvo, cmd, data, len)) + return false; + + return intel_sdvo_read_response(intel_sdvo, NULL, 0); } static bool @@ -859,18 +856,21 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) intel_dip_infoframe_csum(&avi_if); - if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_INDEX, + if (!intel_sdvo_set_value(intel_sdvo, + SDVO_CMD_SET_HBUF_INDEX, set_buf_index, 2)) return false; for (i = 0; i < sizeof(avi_if); i += 8) { - if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_DATA, + if (!intel_sdvo_set_value(intel_sdvo, + SDVO_CMD_SET_HBUF_DATA, data, 8)) return false; data++; } - return intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_TXRATE, + return intel_sdvo_set_value(intel_sdvo, + SDVO_CMD_SET_HBUF_TXRATE, &tx_rate, 1); } diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index fb846a3fef1..f05c0cddfec 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -443,7 +443,7 @@ nouveau_hwmon_fini(struct drm_device *dev) struct nouveau_pm_engine *pm = &dev_priv->engine.pm; if (pm->hwmon) { - sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup); + sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); hwmon_device_unregister(pm->hwmon); } #endif diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c index 14e24e906ee..0ea090f4244 100644 --- a/drivers/gpu/drm/nouveau/nv50_evo.c +++ b/drivers/gpu/drm/nouveau/nv50_evo.c @@ -283,8 +283,7 @@ nv50_evo_create(struct drm_device *dev) nv50_evo_channel_del(&dev_priv->evo); return ret; } - } else - if (dev_priv->chipset != 0x50) { + } else { ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB16, 0x70, 0x19, 0, 0xffffffff, 0x00010000); if (ret) { diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 842954fe74c..b1537000a10 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -555,6 +555,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, dp_clock = dig_connector->dp_clock; } } +/* this might work properly with the new pll algo */ #if 0 /* doesn't work properly on some laptops */ /* use recommended ref_div for ss */ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { @@ -572,6 +573,11 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, adjusted_clock = mode->clock * 2; if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) pll->flags |= RADEON_PLL_PREFER_CLOSEST_LOWER; + /* rv515 needs more testing with this option */ + if (rdev->family != CHIP_RV515) { + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) + pll->flags |= RADEON_PLL_IS_LCD; + } } else { if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) pll->flags |= RADEON_PLL_NO_ODD_POST_DIV; @@ -951,8 +957,16 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode /* adjust pixel clock as needed */ adjusted_clock = atombios_adjust_pll(crtc, mode, pll, ss_enabled, &ss); - radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, - &ref_div, &post_div); + /* rv515 seems happier with the old algo */ + if (rdev->family == CHIP_RV515) + radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, + &ref_div, &post_div); + else if (ASIC_IS_AVIVO(rdev)) + radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, + &ref_div, &post_div); + else + radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, + &ref_div, &post_div); atombios_crtc_program_ss(crtc, ATOM_DISABLE, radeon_crtc->pll_id, &ss); diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 677af91b555..ffdc8332b76 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -97,26 +97,29 @@ u32 evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base) } /* get temperature in millidegrees */ -u32 evergreen_get_temp(struct radeon_device *rdev) +int evergreen_get_temp(struct radeon_device *rdev) { u32 temp = (RREG32(CG_MULT_THERMAL_STATUS) & ASIC_T_MASK) >> ASIC_T_SHIFT; u32 actual_temp = 0; - if ((temp >> 10) & 1) - actual_temp = 0; - else if ((temp >> 9) & 1) + if (temp & 0x400) + actual_temp = -256; + else if (temp & 0x200) actual_temp = 255; - else - actual_temp = (temp >> 1) & 0xff; + else if (temp & 0x100) { + actual_temp = temp & 0x1ff; + actual_temp |= ~0x1ff; + } else + actual_temp = temp & 0xff; - return actual_temp * 1000; + return (actual_temp * 1000) / 2; } -u32 sumo_get_temp(struct radeon_device *rdev) +int sumo_get_temp(struct radeon_device *rdev) { u32 temp = RREG32(CG_THERMAL_STATUS) & 0xff; - u32 actual_temp = (temp >> 1) & 0xff; + int actual_temp = temp - 49; return actual_temp * 1000; } @@ -1182,6 +1185,18 @@ static void evergreen_mc_program(struct radeon_device *rdev) /* * CP. */ +void evergreen_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + /* set to DX10/11 mode */ + radeon_ring_write(rdev, PACKET3(PACKET3_MODE_CONTROL, 0)); + radeon_ring_write(rdev, 1); + /* FIXME: implement */ + radeon_ring_write(rdev, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); + radeon_ring_write(rdev, ib->gpu_addr & 0xFFFFFFFC); + radeon_ring_write(rdev, upper_32_bits(ib->gpu_addr) & 0xFF); + radeon_ring_write(rdev, ib->length_dw); +} + static int evergreen_cp_load_microcode(struct radeon_device *rdev) { @@ -1233,7 +1248,7 @@ static int evergreen_cp_start(struct radeon_device *rdev) cp_me = 0xff; WREG32(CP_ME_CNTL, cp_me); - r = radeon_ring_lock(rdev, evergreen_default_size + 15); + r = radeon_ring_lock(rdev, evergreen_default_size + 19); if (r) { DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); return r; @@ -1266,6 +1281,11 @@ static int evergreen_cp_start(struct radeon_device *rdev) radeon_ring_write(rdev, 0xffffffff); radeon_ring_write(rdev, 0xffffffff); + radeon_ring_write(rdev, 0xc0026900); + radeon_ring_write(rdev, 0x00000316); + radeon_ring_write(rdev, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + radeon_ring_write(rdev, 0x00000010); /* */ + radeon_ring_unlock_commit(rdev); return 0; @@ -2072,6 +2092,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) WREG32(VGT_CACHE_INVALIDATION, vgt_cache_invalidation); WREG32(VGT_GS_VERTEX_REUSE, 16); + WREG32(PA_SU_LINE_STIPPLE_VALUE, 0); WREG32(PA_SC_LINE_STIPPLE_STATE, 0); WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, 14); diff --git a/drivers/gpu/drm/radeon/evergreen_blit_kms.c b/drivers/gpu/drm/radeon/evergreen_blit_kms.c index d4d4db49a8b..a1ba4b3053d 100644 --- a/drivers/gpu/drm/radeon/evergreen_blit_kms.c +++ b/drivers/gpu/drm/radeon/evergreen_blit_kms.c @@ -232,7 +232,7 @@ draw_auto(struct radeon_device *rdev) } -/* emits 34 */ +/* emits 36 */ static void set_default_state(struct radeon_device *rdev) { @@ -499,6 +499,10 @@ set_default_state(struct radeon_device *rdev) radeon_ring_write(rdev, 0x00000000); radeon_ring_write(rdev, 0x00000000); + /* set to DX10/11 mode */ + radeon_ring_write(rdev, PACKET3(PACKET3_MODE_CONTROL, 0)); + radeon_ring_write(rdev, 1); + /* emit an IB pointing at default state */ dwords = ALIGN(rdev->r600_blit.state_len, 0x10); gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.state_offset; @@ -679,7 +683,7 @@ int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes) /* calculate number of loops correctly */ ring_size = num_loops * dwords_per_loop; /* set default + shaders */ - ring_size += 50; /* shaders + def state */ + ring_size += 52; /* shaders + def state */ ring_size += 10; /* fence emit for VB IB */ ring_size += 5; /* done copy */ ring_size += 10; /* fence emit for done copy */ @@ -687,7 +691,7 @@ int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes) if (r) return r; - set_default_state(rdev); /* 34 */ + set_default_state(rdev); /* 36 */ set_shaders(rdev); /* 16 */ return 0; } diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 36d32d83d86..afec1aca2a7 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -240,6 +240,7 @@ #define FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) #define FORCE_EOV_MAX_REZ_CNT(x) ((x) << 16) #define PA_SC_LINE_STIPPLE 0x28A0C +#define PA_SU_LINE_STIPPLE_VALUE 0x8A60 #define PA_SC_LINE_STIPPLE_STATE 0x8B10 #define SCRATCH_REG0 0x8500 @@ -652,6 +653,7 @@ #define PACKET3_DISPATCH_DIRECT 0x15 #define PACKET3_DISPATCH_INDIRECT 0x16 #define PACKET3_INDIRECT_BUFFER_END 0x17 +#define PACKET3_MODE_CONTROL 0x18 #define PACKET3_SET_PREDICATION 0x20 #define PACKET3_REG_RMW 0x21 #define PACKET3_COND_EXEC 0x22 diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 1e10e3e2ba2..650672a0f5a 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -97,12 +97,16 @@ void r600_irq_disable(struct radeon_device *rdev); static void r600_pcie_gen2_enable(struct radeon_device *rdev); /* get temperature in millidegrees */ -u32 rv6xx_get_temp(struct radeon_device *rdev) +int rv6xx_get_temp(struct radeon_device *rdev) { u32 temp = (RREG32(CG_THERMAL_STATUS) & ASIC_T_MASK) >> ASIC_T_SHIFT; + int actual_temp = temp & 0xff; - return temp * 1000; + if (temp & 0x100) + actual_temp -= 256; + + return actual_temp * 1000; } void r600_pm_get_dynpm_state(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 71d2a554bbe..56c48b67ef3 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -179,10 +179,10 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev); void radeon_atombios_get_power_modes(struct radeon_device *rdev); void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level); void rs690_pm_info(struct radeon_device *rdev); -extern u32 rv6xx_get_temp(struct radeon_device *rdev); -extern u32 rv770_get_temp(struct radeon_device *rdev); -extern u32 evergreen_get_temp(struct radeon_device *rdev); -extern u32 sumo_get_temp(struct radeon_device *rdev); +extern int rv6xx_get_temp(struct radeon_device *rdev); +extern int rv770_get_temp(struct radeon_device *rdev); +extern int evergreen_get_temp(struct radeon_device *rdev); +extern int sumo_get_temp(struct radeon_device *rdev); /* * Fences. @@ -812,8 +812,7 @@ struct radeon_pm { fixed20_12 sclk; fixed20_12 mclk; fixed20_12 needed_bandwidth; - /* XXX: use a define for num power modes */ - struct radeon_power_state power_state[8]; + struct radeon_power_state *power_state; /* number of valid power states */ int num_power_states; int current_power_state_index; diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 3a1b1618622..e75d63b8e21 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -759,7 +759,7 @@ static struct radeon_asic evergreen_asic = { .gart_tlb_flush = &evergreen_pcie_gart_tlb_flush, .gart_set_page = &rs600_gart_set_page, .ring_test = &r600_ring_test, - .ring_ib_execute = &r600_ring_ib_execute, + .ring_ib_execute = &evergreen_ring_ib_execute, .irq_set = &evergreen_irq_set, .irq_process = &evergreen_irq_process, .get_vblank_counter = &evergreen_get_vblank_counter, @@ -805,7 +805,7 @@ static struct radeon_asic sumo_asic = { .gart_tlb_flush = &evergreen_pcie_gart_tlb_flush, .gart_set_page = &rs600_gart_set_page, .ring_test = &r600_ring_test, - .ring_ib_execute = &r600_ring_ib_execute, + .ring_ib_execute = &evergreen_ring_ib_execute, .irq_set = &evergreen_irq_set, .irq_process = &evergreen_irq_process, .get_vblank_counter = &evergreen_get_vblank_counter, @@ -848,7 +848,7 @@ static struct radeon_asic btc_asic = { .gart_tlb_flush = &evergreen_pcie_gart_tlb_flush, .gart_set_page = &rs600_gart_set_page, .ring_test = &r600_ring_test, - .ring_ib_execute = &r600_ring_ib_execute, + .ring_ib_execute = &evergreen_ring_ib_execute, .irq_set = &evergreen_irq_set, .irq_process = &evergreen_irq_process, .get_vblank_counter = &evergreen_get_vblank_counter, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index e01f0771853..c59bd98a202 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -355,6 +355,7 @@ int evergreen_resume(struct radeon_device *rdev); bool evergreen_gpu_is_lockup(struct radeon_device *rdev); int evergreen_asic_reset(struct radeon_device *rdev); void evergreen_bandwidth_update(struct radeon_device *rdev); +void evergreen_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); int evergreen_copy_blit(struct radeon_device *rdev, uint64_t src_offset, uint64_t dst_offset, unsigned num_pages, struct radeon_fence *fence); diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 52777902bbc..5c1cc7ad9a1 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1163,16 +1163,6 @@ bool radeon_atom_get_clock_info(struct drm_device *dev) p1pll->pll_out_min = 64800; else p1pll->pll_out_min = 20000; - } else if (p1pll->pll_out_min > 64800) { - /* Limiting the pll output range is a good thing generally as - * it limits the number of possible pll combinations for a given - * frequency presumably to the ones that work best on each card. - * However, certain duallink DVI monitors seem to like - * pll combinations that would be limited by this at least on - * pre-DCE 3.0 r6xx hardware. This might need to be adjusted per - * family. - */ - p1pll->pll_out_min = 64800; } p1pll->pll_in_min = @@ -1987,6 +1977,9 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev) num_modes = power_info->info.ucNumOfPowerModeEntries; if (num_modes > ATOM_MAX_NUMBEROF_POWER_BLOCK) num_modes = ATOM_MAX_NUMBEROF_POWER_BLOCK; + rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * num_modes, GFP_KERNEL); + if (!rdev->pm.power_state) + return state_index; /* last mode is usually default, array is low to high */ for (i = 0; i < num_modes; i++) { rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; @@ -2338,6 +2331,10 @@ static int radeon_atombios_parse_power_table_4_5(struct radeon_device *rdev) power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); radeon_atombios_add_pplib_thermal_controller(rdev, &power_info->pplib.sThermalController); + rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * + power_info->pplib.ucNumStates, GFP_KERNEL); + if (!rdev->pm.power_state) + return state_index; /* first mode is usually default, followed by low to high */ for (i = 0; i < power_info->pplib.ucNumStates; i++) { mode_index = 0; @@ -2418,6 +2415,10 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev) non_clock_info_array = (struct NonClockInfoArray *) (mode_info->atom_context->bios + data_offset + power_info->pplib.usNonClockInfoArrayOffset); + rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * + state_array->ucNumEntries, GFP_KERNEL); + if (!rdev->pm.power_state) + return state_index; for (i = 0; i < state_array->ucNumEntries; i++) { mode_index = 0; power_state = (union pplib_power_state *)&state_array->states[i]; @@ -2491,19 +2492,22 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) break; } } else { - /* add the default mode */ - rdev->pm.power_state[state_index].type = - POWER_STATE_TYPE_DEFAULT; - rdev->pm.power_state[state_index].num_clock_modes = 1; - rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk; - rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk; - rdev->pm.power_state[state_index].default_clock_mode = - &rdev->pm.power_state[state_index].clock_info[0]; - rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; - rdev->pm.power_state[state_index].pcie_lanes = 16; - rdev->pm.default_power_state_index = state_index; - rdev->pm.power_state[state_index].flags = 0; - state_index++; + rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state), GFP_KERNEL); + if (rdev->pm.power_state) { + /* add the default mode */ + rdev->pm.power_state[state_index].type = + POWER_STATE_TYPE_DEFAULT; + rdev->pm.power_state[state_index].num_clock_modes = 1; + rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk; + rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk; + rdev->pm.power_state[state_index].default_clock_mode = + &rdev->pm.power_state[state_index].clock_info[0]; + rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE; + rdev->pm.power_state[state_index].pcie_lanes = 16; + rdev->pm.default_power_state_index = state_index; + rdev->pm.power_state[state_index].flags = 0; + state_index++; + } } rdev->pm.num_power_states = state_index; @@ -2619,7 +2623,7 @@ void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev) bios_2_scratch &= ~ATOM_S2_VRI_BRIGHT_ENABLE; /* tell the bios not to handle mode switching */ - bios_6_scratch |= (ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH | ATOM_S6_ACC_MODE); + bios_6_scratch |= ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH; if (rdev->family >= CHIP_R600) { WREG32(R600_BIOS_2_SCRATCH, bios_2_scratch); @@ -2670,10 +2674,13 @@ void radeon_atom_output_lock(struct drm_encoder *encoder, bool lock) else bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); - if (lock) + if (lock) { bios_6_scratch |= ATOM_S6_CRITICAL_STATE; - else + bios_6_scratch &= ~ATOM_S6_ACC_MODE; + } else { bios_6_scratch &= ~ATOM_S6_CRITICAL_STATE; + bios_6_scratch |= ATOM_S6_ACC_MODE; + } if (rdev->family >= CHIP_R600) WREG32(R600_BIOS_6_SCRATCH, bios_6_scratch); diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 591fcae8f22..d27ef74590c 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -2442,6 +2442,17 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev) rdev->pm.default_power_state_index = -1; + /* allocate 2 power states */ + rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * 2, GFP_KERNEL); + if (!rdev->pm.power_state) { + rdev->pm.default_power_state_index = state_index; + rdev->pm.num_power_states = 0; + + rdev->pm.current_power_state_index = rdev->pm.default_power_state_index; + rdev->pm.current_clock_mode_index = 0; + return; + } + if (rdev->flags & RADEON_IS_MOBILITY) { offset = combios_get_table_offset(dev, COMBIOS_POWERPLAY_INFO_TABLE); if (offset) { diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index d26dabf878d..2eff98cfd72 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -780,6 +780,115 @@ static int radeon_ddc_dump(struct drm_connector *connector) return ret; } +/* avivo */ +static void avivo_get_fb_div(struct radeon_pll *pll, + u32 target_clock, + u32 post_div, + u32 ref_div, + u32 *fb_div, + u32 *frac_fb_div) +{ + u32 tmp = post_div * ref_div; + + tmp *= target_clock; + *fb_div = tmp / pll->reference_freq; + *frac_fb_div = tmp % pll->reference_freq; +} + +static u32 avivo_get_post_div(struct radeon_pll *pll, + u32 target_clock) +{ + u32 vco, post_div, tmp; + + if (pll->flags & RADEON_PLL_USE_POST_DIV) + return pll->post_div; + + if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { + if (pll->flags & RADEON_PLL_IS_LCD) + vco = pll->lcd_pll_out_min; + else + vco = pll->pll_out_min; + } else { + if (pll->flags & RADEON_PLL_IS_LCD) + vco = pll->lcd_pll_out_max; + else + vco = pll->pll_out_max; + } + + post_div = vco / target_clock; + tmp = vco % target_clock; + + if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { + if (tmp) + post_div++; + } else { + if (!tmp) + post_div--; + } + + return post_div; +} + +#define MAX_TOLERANCE 10 + +void radeon_compute_pll_avivo(struct radeon_pll *pll, + u32 freq, + u32 *dot_clock_p, + u32 *fb_div_p, + u32 *frac_fb_div_p, + u32 *ref_div_p, + u32 *post_div_p) +{ + u32 target_clock = freq / 10; + u32 post_div = avivo_get_post_div(pll, target_clock); + u32 ref_div = pll->min_ref_div; + u32 fb_div = 0, frac_fb_div = 0, tmp; + + if (pll->flags & RADEON_PLL_USE_REF_DIV) + ref_div = pll->reference_div; + + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { + avivo_get_fb_div(pll, target_clock, post_div, ref_div, &fb_div, &frac_fb_div); + frac_fb_div = (100 * frac_fb_div) / pll->reference_freq; + if (frac_fb_div >= 5) { + frac_fb_div -= 5; + frac_fb_div = frac_fb_div / 10; + frac_fb_div++; + } + if (frac_fb_div >= 10) { + fb_div++; + frac_fb_div = 0; + } + } else { + while (ref_div <= pll->max_ref_div) { + avivo_get_fb_div(pll, target_clock, post_div, ref_div, + &fb_div, &frac_fb_div); + if (frac_fb_div >= (pll->reference_freq / 2)) + fb_div++; + frac_fb_div = 0; + tmp = (pll->reference_freq * fb_div) / (post_div * ref_div); + tmp = (tmp * 10000) / target_clock; + + if (tmp > (10000 + MAX_TOLERANCE)) + ref_div++; + else if (tmp >= (10000 - MAX_TOLERANCE)) + break; + else + ref_div++; + } + } + + *dot_clock_p = ((pll->reference_freq * fb_div * 10) + (pll->reference_freq * frac_fb_div)) / + (ref_div * post_div * 10); + *fb_div_p = fb_div; + *frac_fb_div_p = frac_fb_div; + *ref_div_p = ref_div; + *post_div_p = post_div; + DRM_DEBUG_KMS("%d, pll dividers - fb: %d.%d ref: %d, post %d\n", + *dot_clock_p, fb_div, frac_fb_div, ref_div, post_div); +} + +/* pre-avivo */ static inline uint32_t radeon_div(uint64_t n, uint32_t d) { uint64_t mod; @@ -790,13 +899,13 @@ static inline uint32_t radeon_div(uint64_t n, uint32_t d) return n; } -void radeon_compute_pll(struct radeon_pll *pll, - uint64_t freq, - uint32_t *dot_clock_p, - uint32_t *fb_div_p, - uint32_t *frac_fb_div_p, - uint32_t *ref_div_p, - uint32_t *post_div_p) +void radeon_compute_pll_legacy(struct radeon_pll *pll, + uint64_t freq, + uint32_t *dot_clock_p, + uint32_t *fb_div_p, + uint32_t *frac_fb_div_p, + uint32_t *ref_div_p, + uint32_t *post_div_p) { uint32_t min_ref_div = pll->min_ref_div; uint32_t max_ref_div = pll->max_ref_div; @@ -826,6 +935,9 @@ void radeon_compute_pll(struct radeon_pll *pll, pll_out_max = pll->pll_out_max; } + if (pll_out_min > 64800) + pll_out_min = 64800; + if (pll->flags & RADEON_PLL_USE_REF_DIV) min_ref_div = max_ref_div = pll->reference_div; else { @@ -849,7 +961,7 @@ void radeon_compute_pll(struct radeon_pll *pll, max_fractional_feed_div = pll->max_frac_feedback_div; } - for (post_div = max_post_div; post_div >= min_post_div; --post_div) { + for (post_div = min_post_div; post_div <= max_post_div; ++post_div) { uint32_t ref_div; if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) @@ -965,6 +1077,10 @@ void radeon_compute_pll(struct radeon_pll *pll, *frac_fb_div_p = best_frac_feedback_div; *ref_div_p = best_ref_div; *post_div_p = best_post_div; + DRM_DEBUG_KMS("%d %d, pll dividers - fb: %d.%d ref: %d, post %d\n", + freq, best_freq / 1000, best_feedback_div, best_frac_feedback_div, + best_ref_div, best_post_div); + } static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb) diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 5e90984d5ad..d4a54224761 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -1063,7 +1063,7 @@ atombios_set_edp_panel_power(struct drm_connector *connector, int action) if (!ASIC_IS_DCE4(rdev)) return; - if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) || + if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) && (action != ATOM_TRANSMITTER_ACTION_POWER_OFF)) return; diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index ace2e6384d4..cf0638c3b7c 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -778,9 +778,9 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) DRM_DEBUG_KMS("\n"); if (!use_bios_divs) { - radeon_compute_pll(pll, mode->clock, - &freq, &feedback_div, &frac_fb_div, - &reference_div, &post_divider); + radeon_compute_pll_legacy(pll, mode->clock, + &freq, &feedback_div, &frac_fb_div, + &reference_div, &post_divider); for (post_div = &post_divs[0]; post_div->divider; ++post_div) { if (post_div->divider == post_divider) diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 12bdeab91c8..6794cdf91f2 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -149,6 +149,7 @@ struct radeon_tmds_pll { #define RADEON_PLL_PREFER_CLOSEST_LOWER (1 << 11) #define RADEON_PLL_USE_POST_DIV (1 << 12) #define RADEON_PLL_IS_LCD (1 << 13) +#define RADEON_PLL_PREFER_MINM_OVER_MAXP (1 << 14) struct radeon_pll { /* reference frequency */ @@ -510,13 +511,21 @@ extern bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev, struct radeon_atom_ss *ss, int id, u32 clock); -extern void radeon_compute_pll(struct radeon_pll *pll, - uint64_t freq, - uint32_t *dot_clock_p, - uint32_t *fb_div_p, - uint32_t *frac_fb_div_p, - uint32_t *ref_div_p, - uint32_t *post_div_p); +extern void radeon_compute_pll_legacy(struct radeon_pll *pll, + uint64_t freq, + uint32_t *dot_clock_p, + uint32_t *fb_div_p, + uint32_t *frac_fb_div_p, + uint32_t *ref_div_p, + uint32_t *post_div_p); + +extern void radeon_compute_pll_avivo(struct radeon_pll *pll, + u32 freq, + u32 *dot_clock_p, + u32 *fb_div_p, + u32 *frac_fb_div_p, + u32 *ref_div_p, + u32 *post_div_p); extern void radeon_setup_encoder_clones(struct drm_device *dev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 3b1b2bf9cdd..2aed03bde4b 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -430,7 +430,7 @@ static ssize_t radeon_hwmon_show_temp(struct device *dev, { struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); struct radeon_device *rdev = ddev->dev_private; - u32 temp; + int temp; switch (rdev->pm.int_thermal_type) { case THERMAL_TYPE_RV6XX: @@ -646,6 +646,9 @@ void radeon_pm_fini(struct radeon_device *rdev) #endif } + if (rdev->pm.power_state) + kfree(rdev->pm.power_state); + radeon_hwmon_fini(rdev); } diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 491dc900065..2211a323db4 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -78,18 +78,23 @@ u32 rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base) } /* get temperature in millidegrees */ -u32 rv770_get_temp(struct radeon_device *rdev) +int rv770_get_temp(struct radeon_device *rdev) { u32 temp = (RREG32(CG_MULT_THERMAL_STATUS) & ASIC_T_MASK) >> ASIC_T_SHIFT; - u32 actual_temp = 0; - - if ((temp >> 9) & 1) - actual_temp = 0; - else - actual_temp = (temp >> 1) & 0xff; - - return actual_temp * 1000; + int actual_temp; + + if (temp & 0x400) + actual_temp = -256; + else if (temp & 0x200) + actual_temp = 255; + else if (temp & 0x100) { + actual_temp = temp & 0x1ff; + actual_temp |= ~0x1ff; + } else + actual_temp = temp & 0xff; + + return (actual_temp * 1000) / 2; } void rv770_pm_misc(struct radeon_device *rdev) diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index e38be1bcc01..fbbfa24cf57 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -1079,7 +1079,7 @@ static void ib_sa_remove_one(struct ib_device *device) ib_unregister_event_handler(&sa_dev->event_handler); - flush_scheduled_work(); + flush_workqueue(ib_wq); for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) { if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND) { diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index ca12acf3837..ec1e9da1488 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -636,6 +636,16 @@ static void ucma_copy_iboe_route(struct rdma_ucm_query_route_resp *resp, } } +static void ucma_copy_iw_route(struct rdma_ucm_query_route_resp *resp, + struct rdma_route *route) +{ + struct rdma_dev_addr *dev_addr; + + dev_addr = &route->addr.dev_addr; + rdma_addr_get_dgid(dev_addr, (union ib_gid *) &resp->ib_route[0].dgid); + rdma_addr_get_sgid(dev_addr, (union ib_gid *) &resp->ib_route[0].sgid); +} + static ssize_t ucma_query_route(struct ucma_file *file, const char __user *inbuf, int in_len, int out_len) @@ -670,8 +680,10 @@ static ssize_t ucma_query_route(struct ucma_file *file, resp.node_guid = (__force __u64) ctx->cm_id->device->node_guid; resp.port_num = ctx->cm_id->port_num; - if (rdma_node_get_transport(ctx->cm_id->device->node_type) == RDMA_TRANSPORT_IB) { - switch (rdma_port_get_link_layer(ctx->cm_id->device, ctx->cm_id->port_num)) { + switch (rdma_node_get_transport(ctx->cm_id->device->node_type)) { + case RDMA_TRANSPORT_IB: + switch (rdma_port_get_link_layer(ctx->cm_id->device, + ctx->cm_id->port_num)) { case IB_LINK_LAYER_INFINIBAND: ucma_copy_ib_route(&resp, &ctx->cm_id->route); break; @@ -681,6 +693,12 @@ static ssize_t ucma_query_route(struct ucma_file *file, default: break; } + break; + case RDMA_TRANSPORT_IWARP: + ucma_copy_iw_route(&resp, &ctx->cm_id->route); + break; + default: + break; } out: diff --git a/drivers/infiniband/hw/amso1100/c2_vq.c b/drivers/infiniband/hw/amso1100/c2_vq.c index 9ce7819b7b2..2ec716fb2ed 100644 --- a/drivers/infiniband/hw/amso1100/c2_vq.c +++ b/drivers/infiniband/hw/amso1100/c2_vq.c @@ -107,7 +107,7 @@ struct c2_vq_req *vq_req_alloc(struct c2_dev *c2dev) r = kmalloc(sizeof(struct c2_vq_req), GFP_KERNEL); if (r) { init_waitqueue_head(&r->wait_object); - r->reply_msg = (u64) NULL; + r->reply_msg = 0; r->event = 0; r->cm_id = NULL; r->qp = NULL; @@ -123,7 +123,7 @@ struct c2_vq_req *vq_req_alloc(struct c2_dev *c2dev) */ void vq_req_free(struct c2_dev *c2dev, struct c2_vq_req *r) { - r->reply_msg = (u64) NULL; + r->reply_msg = 0; if (atomic_dec_and_test(&r->refcnt)) { kfree(r); } @@ -151,7 +151,7 @@ void vq_req_get(struct c2_dev *c2dev, struct c2_vq_req *r) void vq_req_put(struct c2_dev *c2dev, struct c2_vq_req *r) { if (atomic_dec_and_test(&r->refcnt)) { - if (r->reply_msg != (u64) NULL) + if (r->reply_msg != 0) vq_repbuf_free(c2dev, (void *) (unsigned long) r->reply_msg); kfree(r); diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 0dc62b1438b..8b00e6c46f0 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -380,7 +380,7 @@ static void send_flowc(struct c4iw_ep *ep, struct sk_buff *skb) 16)) | FW_WR_FLOWID(ep->hwtid)); flowc->mnemval[0].mnemonic = FW_FLOWC_MNEM_PFNVFN; - flowc->mnemval[0].val = cpu_to_be32(0); + flowc->mnemval[0].val = cpu_to_be32(PCI_FUNC(ep->com.dev->rdev.lldi.pdev->devfn) << 8); flowc->mnemval[1].mnemonic = FW_FLOWC_MNEM_CH; flowc->mnemval[1].val = cpu_to_be32(ep->tx_chan); flowc->mnemval[2].mnemonic = FW_FLOWC_MNEM_PORT; diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 20800900ef3..4f0be25cab1 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -220,7 +220,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, V_FW_RI_RES_WR_DCAEN(0) | V_FW_RI_RES_WR_DCACPU(0) | V_FW_RI_RES_WR_FBMIN(2) | - V_FW_RI_RES_WR_FBMAX(3) | + V_FW_RI_RES_WR_FBMAX(2) | V_FW_RI_RES_WR_CIDXFTHRESHO(0) | V_FW_RI_RES_WR_CIDXFTHRESH(0) | V_FW_RI_RES_WR_EQSIZE(eqsize)); @@ -243,7 +243,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, V_FW_RI_RES_WR_DCAEN(0) | V_FW_RI_RES_WR_DCACPU(0) | V_FW_RI_RES_WR_FBMIN(2) | - V_FW_RI_RES_WR_FBMAX(3) | + V_FW_RI_RES_WR_FBMAX(2) | V_FW_RI_RES_WR_CIDXFTHRESHO(0) | V_FW_RI_RES_WR_CIDXFTHRESH(0) | V_FW_RI_RES_WR_EQSIZE(eqsize)); diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index 50cceb3ab88..b01809a82cb 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -623,7 +623,6 @@ struct qib_chippport_specific { u8 ibmalfusesnap; struct qib_qsfp_data qsfp_data; char epmsgbuf[192]; /* for port error interrupt msg buffer */ - u8 bounced; }; static struct { @@ -1881,23 +1880,7 @@ static noinline void handle_7322_p_errors(struct qib_pportdata *ppd) IB_PHYSPORTSTATE_DISABLED) qib_set_ib_7322_lstate(ppd, 0, QLOGIC_IB_IBCC_LINKINITCMD_DISABLE); - else { - u32 lstate; - /* - * We need the current logical link state before - * lflags are set in handle_e_ibstatuschanged. - */ - lstate = qib_7322_iblink_state(ibcs); - - if (IS_QMH(dd) && !ppd->cpspec->bounced && - ltstate == IB_PHYSPORTSTATE_LINKUP && - (lstate >= IB_PORT_INIT && - lstate <= IB_PORT_ACTIVE)) { - ppd->cpspec->bounced = 1; - qib_7322_set_ib_cfg(ppd, QIB_IB_CFG_LSTATE, - IB_LINKCMD_DOWN | IB_LINKINITCMD_POLL); - } - + else /* * Since going into a recovery state causes the link * state to go down and since recovery is transitory, @@ -1911,7 +1894,6 @@ static noinline void handle_7322_p_errors(struct qib_pportdata *ppd) ltstate != IB_PHYSPORTSTATE_RECOVERY_WAITRMT && ltstate != IB_PHYSPORTSTATE_RECOVERY_IDLE) qib_handle_e_ibstatuschanged(ppd, ibcs); - } } if (*msg && iserr) qib_dev_porterr(dd, ppd->port, "%s error\n", msg); @@ -2381,6 +2363,11 @@ static int qib_7322_bringup_serdes(struct qib_pportdata *ppd) qib_write_kreg_port(ppd, krp_rcvctrl, ppd->p_rcvctrl); spin_unlock_irqrestore(&dd->cspec->rcvmod_lock, flags); + /* Hold the link state machine for mezz boards */ + if (IS_QMH(dd) || IS_QME(dd)) + qib_set_ib_7322_lstate(ppd, 0, + QLOGIC_IB_IBCC_LINKINITCMD_DISABLE); + /* Also enable IBSTATUSCHG interrupt. */ val = qib_read_kreg_port(ppd, krp_errmask); qib_write_kreg_port(ppd, krp_errmask, @@ -5702,6 +5689,11 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change) ppd->cpspec->h1_val = h1; /* now change the IBC and serdes, overriding generic */ init_txdds_table(ppd, 1); + /* Re-enable the physical state machine on mezz boards + * now that the correct settings have been set. */ + if (IS_QMH(dd) || IS_QME(dd)) + qib_set_ib_7322_lstate(ppd, 0, + QLOGIC_IB_IBCC_LINKINITCMD_SLEEP); any++; } if (*nxt == '\n') diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 9dfd6e5f786..1f38302a595 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -69,11 +69,7 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned } if (value > 20 && value < 32767) -#ifndef FREQ - count = (ixp4xx_get_board_tick_rate() / (value * 4)) - 1; -#else - count = (FREQ / (value * 4)) - 1; -#endif + count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1; ixp4xx_spkr_control(pin, count); diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index f2b5bab5e6a..1f355bb85e5 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1627,7 +1627,7 @@ __setup("icn=", icn_setup); static int __init icn_init(void) { char *p; - char rev[20]; + char rev[21]; memset(&dev, 0, sizeof(icn_dev)); dev.memaddr = (membase & 0x0ffc000); @@ -1638,6 +1638,7 @@ static int __init icn_init(void) if ((p = strchr(revision, ':'))) { strncpy(rev, p + 1, 20); + rev[20] = '\0'; p = strchr(rev, '$'); if (p) *p = 0; diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index a699bbf20eb..3824382faec 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -48,6 +48,7 @@ static DEFINE_PCI_DEVICE_TABLE(atl1c_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B2)}, {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D)}, + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D_2_0)}, /* required last entry */ { 0 } }; diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index de40d3b7152..28a32a6c8bf 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -312,11 +312,9 @@ void be_link_status_update(struct be_adapter *adapter, bool link_up) if (adapter->link_up != link_up) { adapter->link_speed = -1; if (link_up) { - netif_start_queue(netdev); netif_carrier_on(netdev); printk(KERN_INFO "%s: Link up\n", netdev->name); } else { - netif_stop_queue(netdev); netif_carrier_off(netdev); printk(KERN_INFO "%s: Link down\n", netdev->name); } @@ -2628,8 +2626,6 @@ static void be_netdev_init(struct net_device *netdev) netif_napi_add(netdev, &adapter->tx_eq.napi, be_poll_tx_mcc, BE_NAPI_WEIGHT); - - netif_stop_queue(netdev); } static void be_unmap_pci_bars(struct be_adapter *adapter) diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/bnx2x/bnx2x.h index 8e4183717d9..653c62475cb 100644 --- a/drivers/net/bnx2x/bnx2x.h +++ b/drivers/net/bnx2x/bnx2x.h @@ -22,8 +22,8 @@ * (you will need to reboot afterwards) */ /* #define BNX2X_STOP_ON_ERROR */ -#define DRV_MODULE_VERSION "1.62.00-4" -#define DRV_MODULE_RELDATE "2011/01/18" +#define DRV_MODULE_VERSION "1.62.00-5" +#define DRV_MODULE_RELDATE "2011/01/30" #define BNX2X_BC_VER 0x040200 #define BNX2X_MULTI_QUEUE diff --git a/drivers/net/bnx2x/bnx2x_link.c b/drivers/net/bnx2x/bnx2x_link.c index 7160ec51093..dd1210fddff 100644 --- a/drivers/net/bnx2x/bnx2x_link.c +++ b/drivers/net/bnx2x/bnx2x_link.c @@ -3948,48 +3948,6 @@ static u8 bnx2x_8073_8727_external_rom_boot(struct bnx2x *bp, return rc; } -static void bnx2x_8073_set_xaui_low_power_mode(struct bnx2x *bp, - struct bnx2x_phy *phy) -{ - u16 val; - bnx2x_cl45_read(bp, phy, - MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &val); - - if (val == 0) { - /* Mustn't set low power mode in 8073 A0 */ - return; - } - - /* Disable PLL sequencer (use read-modify-write to clear bit 13) */ - bnx2x_cl45_read(bp, phy, - MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); - val &= ~(1<<13); - bnx2x_cl45_write(bp, phy, - MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); - - /* PLL controls */ - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x805E, 0x1077); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x805D, 0x0000); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x805C, 0x030B); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x805B, 0x1240); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x805A, 0x2490); - - /* Tx Controls */ - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x80A7, 0x0C74); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x80A6, 0x9041); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x80A5, 0x4640); - - /* Rx Controls */ - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x80FE, 0x01C4); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x80FD, 0x9249); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, 0x80FC, 0x2015); - - /* Enable PLL sequencer (use read-modify-write to set bit 13) */ - bnx2x_cl45_read(bp, phy, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); - val |= (1<<13); - bnx2x_cl45_write(bp, phy, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); -} - /******************************************************************/ /* BCM8073 PHY SECTION */ /******************************************************************/ @@ -4148,8 +4106,6 @@ static u8 bnx2x_8073_config_init(struct bnx2x_phy *phy, bnx2x_8073_set_pause_cl37(params, phy, vars); - bnx2x_8073_set_xaui_low_power_mode(bp, phy); - bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &tmp1); @@ -6519,6 +6475,18 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED1_MASK, 0x80); + + /* Tell LED3 to blink on source */ + bnx2x_cl45_read(bp, phy, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_8481_LINK_SIGNAL, + &val); + val &= ~(7<<6); + val |= (1<<6); /* A83B[8:6]= 1 */ + bnx2x_cl45_write(bp, phy, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_8481_LINK_SIGNAL, + val); } break; } @@ -7720,10 +7688,13 @@ static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp, struct bnx2x_phy phy[PORT_MAX]; struct bnx2x_phy *phy_blk[PORT_MAX]; u16 val; - s8 port; + s8 port = 0; s8 port_of_path = 0; - - bnx2x_ext_phy_hw_reset(bp, 0); + u32 swap_val, swap_override; + swap_val = REG_RD(bp, NIG_REG_PORT_SWAP); + swap_override = REG_RD(bp, NIG_REG_STRAP_OVERRIDE); + port ^= (swap_val && swap_override); + bnx2x_ext_phy_hw_reset(bp, port); /* PART1 - Reset both phys */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { u32 shmem_base, shmem2_base; diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c index 8cdcf5b39d1..f40740e68ea 100644 --- a/drivers/net/bnx2x/bnx2x_main.c +++ b/drivers/net/bnx2x/bnx2x_main.c @@ -2301,15 +2301,10 @@ static void bnx2x_rxq_set_mac_filters(struct bnx2x *bp, u16 cl_id, u32 filters) /* accept matched ucast */ drop_all_ucast = 0; } - if (filters & BNX2X_ACCEPT_MULTICAST) { + if (filters & BNX2X_ACCEPT_MULTICAST) /* accept matched mcast */ drop_all_mcast = 0; - if (IS_MF_SI(bp)) - /* since mcast addresses won't arrive with ovlan, - * fw needs to accept all of them in - * switch-independent mode */ - accp_all_mcast = 1; - } + if (filters & BNX2X_ACCEPT_ALL_UNICAST) { /* accept all mcast */ drop_all_ucast = 0; @@ -5296,10 +5291,6 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code) } } - bp->port.need_hw_lock = bnx2x_hw_lock_required(bp, - bp->common.shmem_base, - bp->common.shmem2_base); - bnx2x_setup_fan_failure_detection(bp); /* clear PXP2 attentions */ @@ -5503,9 +5494,6 @@ static int bnx2x_init_hw_port(struct bnx2x *bp) bnx2x_init_block(bp, MCP_BLOCK, init_stage); bnx2x_init_block(bp, DMAE_BLOCK, init_stage); - bp->port.need_hw_lock = bnx2x_hw_lock_required(bp, - bp->common.shmem_base, - bp->common.shmem2_base); if (bnx2x_fan_failure_det_req(bp, bp->common.shmem_base, bp->common.shmem2_base, port)) { u32 reg_addr = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : @@ -8379,6 +8367,17 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN)) bp->mdio.prtad = XGXS_EXT_PHY_ADDR(ext_phy_config); + + /* + * Check if hw lock is required to access MDC/MDIO bus to the PHY(s) + * In MF mode, it is set to cover self test cases + */ + if (IS_MF(bp)) + bp->port.need_hw_lock = 1; + else + bp->port.need_hw_lock = bnx2x_hw_lock_required(bp, + bp->common.shmem_base, + bp->common.shmem2_base); } static void __devinit bnx2x_get_mac_hwinfo(struct bnx2x *bp) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 986195eaa57..5dec456fd4a 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -23,7 +23,7 @@ config CAN_SLCAN As only the sending and receiving of CAN frames is implemented, this driver should work with the (serial/USB) CAN hardware from: - www.canusb.com / www.can232.com / www.mictronic.com / www.canhack.de + www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de Userspace tools to attach the SLCAN line discipline (slcan_attach, slcand) can be found in the can-utils at the SocketCAN SVN, see diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 2532b963153..57d2ffbbb43 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1109,7 +1109,7 @@ static ssize_t at91_sysfs_set_mb0_id(struct device *dev, return ret; } -static DEVICE_ATTR(mb0_id, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(mb0_id, S_IWUSR | S_IRUGO, at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id); static struct attribute *at91_sysfs_attrs[] = { diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index b9a6d7a5a73..366f5cc050a 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -1618,7 +1618,7 @@ static ssize_t ican3_sysfs_set_term(struct device *dev, return count; } -static DEVICE_ATTR(termination, S_IWUGO | S_IRUGO, ican3_sysfs_show_term, +static DEVICE_ATTR(termination, S_IWUSR | S_IRUGO, ican3_sysfs_show_term, ican3_sysfs_set_term); static struct attribute *ican3_sysfs_attrs[] = { diff --git a/drivers/net/can/softing/Kconfig b/drivers/net/can/softing/Kconfig index 92bd6bdde5e..8ba81b3ddd9 100644 --- a/drivers/net/can/softing/Kconfig +++ b/drivers/net/can/softing/Kconfig @@ -1,6 +1,6 @@ config CAN_SOFTING tristate "Softing Gmbh CAN generic support" - depends on CAN_DEV + depends on CAN_DEV && HAS_IOMEM ---help--- Support for CAN cards from Softing Gmbh & some cards from Vector Gmbh. diff --git a/drivers/net/depca.c b/drivers/net/depca.c index 1b48b68ad4f..8b0084d17c8 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -1094,7 +1094,7 @@ static int depca_rx(struct net_device *dev) } } /* Change buffer ownership for this last frame, back to the adapter */ - for (; lp->rx_old != entry; lp->rx_old = (++lp->rx_old) & lp->rxRingMask) { + for (; lp->rx_old != entry; lp->rx_old = (lp->rx_old + 1) & lp->rxRingMask) { writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN, &lp->rx_ring[lp->rx_old].base); } writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base); @@ -1103,7 +1103,7 @@ static int depca_rx(struct net_device *dev) /* ** Update entry information */ - lp->rx_new = (++lp->rx_new) & lp->rxRingMask; + lp->rx_new = (lp->rx_new + 1) & lp->rxRingMask; } return 0; @@ -1148,7 +1148,7 @@ static int depca_tx(struct net_device *dev) } /* Update all the pointers */ - lp->tx_old = (++lp->tx_old) & lp->txRingMask; + lp->tx_old = (lp->tx_old + 1) & lp->txRingMask; } return 0; diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index e1a8216ff69..c05db604605 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -1753,8 +1753,6 @@ rio_close (struct net_device *dev) /* Free all the skbuffs in the queue. */ for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].status = 0; - np->rx_ring[i].fraginfo = 0; skb = np->rx_skbuff[i]; if (skb) { pci_unmap_single(np->pdev, @@ -1763,6 +1761,8 @@ rio_close (struct net_device *dev) dev_kfree_skb (skb); np->rx_skbuff[i] = NULL; } + np->rx_ring[i].status = 0; + np->rx_ring[i].fraginfo = 0; } for (i = 0; i < TX_RING_SIZE; i++) { skb = np->tx_skbuff[i]; diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index 112c5aa9af7..907b05a1c65 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c @@ -812,7 +812,7 @@ static void enc28j60_read_tsv(struct enc28j60_net *priv, u8 tsv[TSV_SIZE]) if (netif_msg_hw(priv)) printk(KERN_DEBUG DRV_NAME ": reading TSV at addr:0x%04x\n", endptr + 1); - enc28j60_mem_read(priv, endptr + 1, sizeof(tsv), tsv); + enc28j60_mem_read(priv, endptr + 1, TSV_SIZE, tsv); } static void enc28j60_dump_tsv(struct enc28j60_net *priv, const char *msg, diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index 4ffdc18fcb8..2765a3ce9c2 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -1286,6 +1286,21 @@ static DEFINE_PCI_DEVICE_TABLE(mlx4_pci_table) = { { PCI_VDEVICE(MELLANOX, 0x6764) }, /* MT26468 ConnectX EN 10GigE PCIe gen2*/ { PCI_VDEVICE(MELLANOX, 0x6746) }, /* MT26438 ConnectX EN 40GigE PCIe gen2 5GT/s */ { PCI_VDEVICE(MELLANOX, 0x676e) }, /* MT26478 ConnectX2 40GigE PCIe gen2 */ + { PCI_VDEVICE(MELLANOX, 0x1002) }, /* MT25400 Family [ConnectX-2 Virtual Function] */ + { PCI_VDEVICE(MELLANOX, 0x1003) }, /* MT27500 Family [ConnectX-3] */ + { PCI_VDEVICE(MELLANOX, 0x1004) }, /* MT27500 Family [ConnectX-3 Virtual Function] */ + { PCI_VDEVICE(MELLANOX, 0x1005) }, /* MT27510 Family */ + { PCI_VDEVICE(MELLANOX, 0x1006) }, /* MT27511 Family */ + { PCI_VDEVICE(MELLANOX, 0x1007) }, /* MT27520 Family */ + { PCI_VDEVICE(MELLANOX, 0x1008) }, /* MT27521 Family */ + { PCI_VDEVICE(MELLANOX, 0x1009) }, /* MT27530 Family */ + { PCI_VDEVICE(MELLANOX, 0x100a) }, /* MT27531 Family */ + { PCI_VDEVICE(MELLANOX, 0x100b) }, /* MT27540 Family */ + { PCI_VDEVICE(MELLANOX, 0x100c) }, /* MT27541 Family */ + { PCI_VDEVICE(MELLANOX, 0x100d) }, /* MT27550 Family */ + { PCI_VDEVICE(MELLANOX, 0x100e) }, /* MT27551 Family */ + { PCI_VDEVICE(MELLANOX, 0x100f) }, /* MT27560 Family */ + { PCI_VDEVICE(MELLANOX, 0x1010) }, /* MT27561 Family */ { 0, } }; diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 2541321bad8..9fb59d3f9c9 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -4489,6 +4489,9 @@ static int niu_alloc_channels(struct niu *np) { struct niu_parent *parent = np->parent; int first_rx_channel, first_tx_channel; + int num_rx_rings, num_tx_rings; + struct rx_ring_info *rx_rings; + struct tx_ring_info *tx_rings; int i, port, err; port = np->port; @@ -4498,18 +4501,21 @@ static int niu_alloc_channels(struct niu *np) first_tx_channel += parent->txchan_per_port[i]; } - np->num_rx_rings = parent->rxchan_per_port[port]; - np->num_tx_rings = parent->txchan_per_port[port]; + num_rx_rings = parent->rxchan_per_port[port]; + num_tx_rings = parent->txchan_per_port[port]; - netif_set_real_num_rx_queues(np->dev, np->num_rx_rings); - netif_set_real_num_tx_queues(np->dev, np->num_tx_rings); - - np->rx_rings = kcalloc(np->num_rx_rings, sizeof(struct rx_ring_info), - GFP_KERNEL); + rx_rings = kcalloc(num_rx_rings, sizeof(struct rx_ring_info), + GFP_KERNEL); err = -ENOMEM; - if (!np->rx_rings) + if (!rx_rings) goto out_err; + np->num_rx_rings = num_rx_rings; + smp_wmb(); + np->rx_rings = rx_rings; + + netif_set_real_num_rx_queues(np->dev, num_rx_rings); + for (i = 0; i < np->num_rx_rings; i++) { struct rx_ring_info *rp = &np->rx_rings[i]; @@ -4538,12 +4544,18 @@ static int niu_alloc_channels(struct niu *np) return err; } - np->tx_rings = kcalloc(np->num_tx_rings, sizeof(struct tx_ring_info), - GFP_KERNEL); + tx_rings = kcalloc(num_tx_rings, sizeof(struct tx_ring_info), + GFP_KERNEL); err = -ENOMEM; - if (!np->tx_rings) + if (!tx_rings) goto out_err; + np->num_tx_rings = num_tx_rings; + smp_wmb(); + np->tx_rings = tx_rings; + + netif_set_real_num_tx_queues(np->dev, num_tx_rings); + for (i = 0; i < np->num_tx_rings; i++) { struct tx_ring_info *rp = &np->tx_rings[i]; @@ -6246,11 +6258,17 @@ static void niu_sync_mac_stats(struct niu *np) static void niu_get_rx_stats(struct niu *np) { unsigned long pkts, dropped, errors, bytes; + struct rx_ring_info *rx_rings; int i; pkts = dropped = errors = bytes = 0; + + rx_rings = ACCESS_ONCE(np->rx_rings); + if (!rx_rings) + goto no_rings; + for (i = 0; i < np->num_rx_rings; i++) { - struct rx_ring_info *rp = &np->rx_rings[i]; + struct rx_ring_info *rp = &rx_rings[i]; niu_sync_rx_discard_stats(np, rp, 0); @@ -6259,6 +6277,8 @@ static void niu_get_rx_stats(struct niu *np) dropped += rp->rx_dropped; errors += rp->rx_errors; } + +no_rings: np->dev->stats.rx_packets = pkts; np->dev->stats.rx_bytes = bytes; np->dev->stats.rx_dropped = dropped; @@ -6268,16 +6288,24 @@ static void niu_get_rx_stats(struct niu *np) static void niu_get_tx_stats(struct niu *np) { unsigned long pkts, errors, bytes; + struct tx_ring_info *tx_rings; int i; pkts = errors = bytes = 0; + + tx_rings = ACCESS_ONCE(np->tx_rings); + if (!tx_rings) + goto no_rings; + for (i = 0; i < np->num_tx_rings; i++) { - struct tx_ring_info *rp = &np->tx_rings[i]; + struct tx_ring_info *rp = &tx_rings[i]; pkts += rp->tx_packets; bytes += rp->tx_bytes; errors += rp->tx_errors; } + +no_rings: np->dev->stats.tx_packets = pkts; np->dev->stats.tx_bytes = bytes; np->dev->stats.tx_errors = errors; @@ -6287,9 +6315,10 @@ static struct net_device_stats *niu_get_stats(struct net_device *dev) { struct niu *np = netdev_priv(dev); - niu_get_rx_stats(np); - niu_get_tx_stats(np); - + if (netif_running(dev)) { + niu_get_rx_stats(np); + niu_get_tx_stats(np); + } return &dev->stats; } diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 1f42f6ac855..d3cb7720586 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -1488,12 +1488,10 @@ static void ei_rx_overrun(struct net_device *dev) /* * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. - * Early datasheets said to poll the reset bit, but now they say that - * it "is not a reliable indicator and subsequently should be ignored." - * We wait at least 10ms. + * We wait at least 2ms. */ - mdelay(10); + mdelay(2); /* * Reset RBCR[01] back to zero as per magic incantation. diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index bde7d61f193..59ccf0c5c61 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -973,7 +973,8 @@ static void __rtl8169_check_link_status(struct net_device *dev, if (pm) pm_request_resume(&tp->pci_dev->dev); netif_carrier_on(dev); - netif_info(tp, ifup, dev, "link up\n"); + if (net_ratelimit()) + netif_info(tp, ifup, dev, "link up\n"); } else { netif_carrier_off(dev); netif_info(tp, ifdown, dev, "link down\n"); @@ -3757,7 +3758,8 @@ static void rtl_hw_start_8168(struct net_device *dev) RTL_W16(IntrMitigate, 0x5151); /* Work around for RxFIFO overflow. */ - if (tp->mac_version == RTL_GIGA_MAC_VER_11) { + if (tp->mac_version == RTL_GIGA_MAC_VER_11 || + tp->mac_version == RTL_GIGA_MAC_VER_22) { tp->intr_event |= RxFIFOOver | PCSTimeout; tp->intr_event &= ~RxOverflow; } @@ -4639,12 +4641,33 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) break; } - /* Work around for rx fifo overflow */ - if (unlikely(status & RxFIFOOver) && - (tp->mac_version == RTL_GIGA_MAC_VER_11)) { - netif_stop_queue(dev); - rtl8169_tx_timeout(dev); - break; + if (unlikely(status & RxFIFOOver)) { + switch (tp->mac_version) { + /* Work around for rx fifo overflow */ + case RTL_GIGA_MAC_VER_11: + case RTL_GIGA_MAC_VER_22: + case RTL_GIGA_MAC_VER_26: + netif_stop_queue(dev); + rtl8169_tx_timeout(dev); + goto done; + /* Testers needed. */ + case RTL_GIGA_MAC_VER_17: + case RTL_GIGA_MAC_VER_19: + case RTL_GIGA_MAC_VER_20: + case RTL_GIGA_MAC_VER_21: + case RTL_GIGA_MAC_VER_23: + case RTL_GIGA_MAC_VER_24: + case RTL_GIGA_MAC_VER_27: + case RTL_GIGA_MAC_VER_28: + /* Experimental science. Pktgen proof. */ + case RTL_GIGA_MAC_VER_12: + case RTL_GIGA_MAC_VER_25: + if (status == RxFIFOOver) + goto done; + break; + default: + break; + } } if (unlikely(status & SYSErr)) { @@ -4680,7 +4703,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) (status & RxFIFOOver) ? (status | RxOverflow) : status); status = RTL_R16(IntrStatus); } - +done: return IRQ_RETVAL(handled); } diff --git a/drivers/net/vxge/vxge-config.c b/drivers/net/vxge/vxge-config.c index 01c05f53e2f..228d4f7a58a 100644 --- a/drivers/net/vxge/vxge-config.c +++ b/drivers/net/vxge/vxge-config.c @@ -3690,7 +3690,7 @@ __vxge_hw_vpath_rts_table_get(struct __vxge_hw_vpath_handle *vp, if (status != VXGE_HW_OK) goto exit; - if ((rts_table != VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA) || + if ((rts_table != VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_DA) && (rts_table != VXGE_HW_RTS_ACS_STEER_CTRL_DATA_STRUCT_SEL_RTH_MULTI_IT)) *data1 = 0; diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 0064be7ce5c..21091c26a9a 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -838,9 +838,9 @@ int ath5k_hw_dma_stop(struct ath5k_hw *ah) for (i = 0; i < qmax; i++) { err = ath5k_hw_stop_tx_dma(ah, i); /* -EINVAL -> queue inactive */ - if (err != -EINVAL) + if (err && err != -EINVAL) return err; } - return err; + return 0; } diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index e5f2b96a4c6..a702817daf7 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -86,7 +86,7 @@ int ath5k_hw_get_frame_duration(struct ath5k_hw *ah, if (!ah->ah_bwmode) { dur = ieee80211_generic_frame_duration(sc->hw, NULL, len, rate); - return dur; + return le16_to_cpu(dur); } bitrate = rate->bitrate; @@ -265,8 +265,6 @@ static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah) * what rate we should choose to TX ACKs. */ tx_time = ath5k_hw_get_frame_duration(ah, 10, rate); - tx_time = le16_to_cpu(tx_time); - ath5k_hw_reg_write(ah, tx_time, reg); if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index f8a7771faee..f44c84ab5dc 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -426,9 +426,8 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah, } /* WAR for ASPM system hang */ - if (AR_SREV_9280(ah) || AR_SREV_9285(ah) || AR_SREV_9287(ah)) { + if (AR_SREV_9285(ah) || AR_SREV_9287(ah)) val |= (AR_WA_BIT6 | AR_WA_BIT7); - } if (AR_SREV_9285E_20(ah)) val |= AR_WA_BIT23; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 38433f9bfe5..0352f0994ca 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -142,9 +142,6 @@ static void ath9k_deinit_priv(struct ath9k_htc_priv *priv) { ath9k_htc_exit_debug(priv->ah); ath9k_hw_deinit(priv->ah); - tasklet_kill(&priv->swba_tasklet); - tasklet_kill(&priv->rx_tasklet); - tasklet_kill(&priv->tx_tasklet); kfree(priv->ah); priv->ah = NULL; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index f4d576bc3cc..6bb59958f71 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1025,12 +1025,6 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) int ret = 0; u8 cmd_rsp; - /* Cancel all the running timers/work .. */ - cancel_work_sync(&priv->fatal_work); - cancel_work_sync(&priv->ps_work); - cancel_delayed_work_sync(&priv->ath9k_led_blink_work); - ath9k_led_stop_brightness(priv); - mutex_lock(&priv->mutex); if (priv->op_flags & OP_INVALID) { @@ -1044,8 +1038,23 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); + + tasklet_kill(&priv->swba_tasklet); + tasklet_kill(&priv->rx_tasklet); + tasklet_kill(&priv->tx_tasklet); + skb_queue_purge(&priv->tx_queue); + mutex_unlock(&priv->mutex); + + /* Cancel all the running timers/work .. */ + cancel_work_sync(&priv->fatal_work); + cancel_work_sync(&priv->ps_work); + cancel_delayed_work_sync(&priv->ath9k_led_blink_work); + ath9k_led_stop_brightness(priv); + + mutex_lock(&priv->mutex); + /* Remove monitor interface here */ if (ah->opmode == NL80211_IFTYPE_MONITOR) { if (ath9k_htc_remove_monitor_interface(priv)) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 767d8b86f1e..087a6a95edd 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -598,8 +598,6 @@ err_btcoex: err_queues: ath9k_hw_deinit(ah); err_hw: - tasklet_kill(&sc->intr_tq); - tasklet_kill(&sc->bcon_tasklet); kfree(ah); sc->sc_ah = NULL; @@ -807,9 +805,6 @@ static void ath9k_deinit_softc(struct ath_softc *sc) ath9k_hw_deinit(sc->sc_ah); - tasklet_kill(&sc->intr_tq); - tasklet_kill(&sc->bcon_tasklet); - kfree(sc->sc_ah); sc->sc_ah = NULL; } @@ -824,6 +819,8 @@ void ath9k_deinit_device(struct ath_softc *sc) wiphy_rfkill_stop_polling(sc->hw->wiphy); ath_deinit_leds(sc); + ath9k_ps_restore(sc); + for (i = 0; i < sc->num_sec_wiphy; i++) { struct ath_wiphy *aphy = sc->sec_wiphy[i]; if (aphy == NULL) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c79c97be6cd..9040c2ff190 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -325,6 +325,8 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int { struct ieee80211_hw *hw = sc->hw; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_tx_control txctl; int time_left; @@ -342,8 +344,12 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int init_completion(&sc->paprd_complete); sc->paprd_pending = true; txctl.paprd = BIT(chain); - if (ath_tx_start(hw, skb, &txctl) != 0) + + if (ath_tx_start(hw, skb, &txctl) != 0) { + ath_dbg(common, ATH_DBG_XMIT, "PAPRD TX failed\n"); + dev_kfree_skb_any(skb); return false; + } time_left = wait_for_completion_timeout(&sc->paprd_complete, msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); @@ -953,8 +959,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) spin_unlock_bh(&sc->sc_pcu_lock); ath9k_ps_restore(sc); - - ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP); } int ath_reset(struct ath_softc *sc, bool retry_tx) @@ -1309,6 +1313,9 @@ static void ath9k_stop(struct ieee80211_hw *hw) spin_lock_bh(&sc->sc_pcu_lock); + /* prevent tasklets to enable interrupts once we disable them */ + ah->imask &= ~ATH9K_INT_GLOBAL; + /* make sure h/w will not generate any interrupt * before setting the invalid flag. */ ath9k_hw_disable_interrupts(ah); @@ -1326,6 +1333,12 @@ static void ath9k_stop(struct ieee80211_hw *hw) spin_unlock_bh(&sc->sc_pcu_lock); + /* we can now sync irq and kill any running tasklets, since we already + * disabled interrupts and not holding a spin lock */ + synchronize_irq(sc->irq); + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); + ath9k_ps_restore(sc); sc->ps_idle = true; diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c index b8433f3a9bc..62876cd5c41 100644 --- a/drivers/net/wireless/rtlwifi/efuse.c +++ b/drivers/net/wireless/rtlwifi/efuse.c @@ -726,9 +726,9 @@ static int efuse_pg_packet_read(struct ieee80211_hw *hw, u8 offset, u8 *data) } static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, - u8 efuse_data, u8 offset, int *bcontinual, - u8 *write_state, struct pgpkt_struct target_pkt, - int *repeat_times, int *bresult, u8 word_en) + u8 efuse_data, u8 offset, int *bcontinual, + u8 *write_state, struct pgpkt_struct *target_pkt, + int *repeat_times, int *bresult, u8 word_en) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct pgpkt_struct tmp_pkt; @@ -744,8 +744,8 @@ static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, tmp_pkt.word_en = tmp_header & 0x0F; tmp_word_cnts = efuse_calculate_word_cnts(tmp_pkt.word_en); - if (tmp_pkt.offset != target_pkt.offset) { - efuse_addr = efuse_addr + (tmp_word_cnts * 2) + 1; + if (tmp_pkt.offset != target_pkt->offset) { + *efuse_addr = *efuse_addr + (tmp_word_cnts * 2) + 1; *write_state = PG_STATE_HEADER; } else { for (tmpindex = 0; tmpindex < (tmp_word_cnts * 2); tmpindex++) { @@ -756,23 +756,23 @@ static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, } if (bdataempty == false) { - efuse_addr = efuse_addr + (tmp_word_cnts * 2) + 1; + *efuse_addr = *efuse_addr + (tmp_word_cnts * 2) + 1; *write_state = PG_STATE_HEADER; } else { match_word_en = 0x0F; - if (!((target_pkt.word_en & BIT(0)) | + if (!((target_pkt->word_en & BIT(0)) | (tmp_pkt.word_en & BIT(0)))) match_word_en &= (~BIT(0)); - if (!((target_pkt.word_en & BIT(1)) | + if (!((target_pkt->word_en & BIT(1)) | (tmp_pkt.word_en & BIT(1)))) match_word_en &= (~BIT(1)); - if (!((target_pkt.word_en & BIT(2)) | + if (!((target_pkt->word_en & BIT(2)) | (tmp_pkt.word_en & BIT(2)))) match_word_en &= (~BIT(2)); - if (!((target_pkt.word_en & BIT(3)) | + if (!((target_pkt->word_en & BIT(3)) | (tmp_pkt.word_en & BIT(3)))) match_word_en &= (~BIT(3)); @@ -780,7 +780,7 @@ static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, badworden = efuse_word_enable_data_write( hw, *efuse_addr + 1, tmp_pkt.word_en, - target_pkt.data); + target_pkt->data); if (0x0F != (badworden & 0x0F)) { u8 reorg_offset = offset; @@ -791,26 +791,26 @@ static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, } tmp_word_en = 0x0F; - if ((target_pkt.word_en & BIT(0)) ^ + if ((target_pkt->word_en & BIT(0)) ^ (match_word_en & BIT(0))) tmp_word_en &= (~BIT(0)); - if ((target_pkt.word_en & BIT(1)) ^ + if ((target_pkt->word_en & BIT(1)) ^ (match_word_en & BIT(1))) tmp_word_en &= (~BIT(1)); - if ((target_pkt.word_en & BIT(2)) ^ + if ((target_pkt->word_en & BIT(2)) ^ (match_word_en & BIT(2))) tmp_word_en &= (~BIT(2)); - if ((target_pkt.word_en & BIT(3)) ^ + if ((target_pkt->word_en & BIT(3)) ^ (match_word_en & BIT(3))) tmp_word_en &= (~BIT(3)); if ((tmp_word_en & 0x0F) != 0x0F) { *efuse_addr = efuse_get_current_size(hw); - target_pkt.offset = offset; - target_pkt.word_en = tmp_word_en; + target_pkt->offset = offset; + target_pkt->word_en = tmp_word_en; } else *bcontinual = false; *write_state = PG_STATE_HEADER; @@ -821,8 +821,8 @@ static void efuse_write_data_case1(struct ieee80211_hw *hw, u16 *efuse_addr, } } else { *efuse_addr += (2 * tmp_word_cnts) + 1; - target_pkt.offset = offset; - target_pkt.word_en = word_en; + target_pkt->offset = offset; + target_pkt->word_en = word_en; *write_state = PG_STATE_HEADER; } } @@ -938,7 +938,7 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw, efuse_write_data_case1(hw, &efuse_addr, efuse_data, offset, &bcontinual, - &write_state, target_pkt, + &write_state, &target_pkt, &repeat_times, &bresult, word_en); else diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 46714910f98..7145ea54378 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -110,9 +110,8 @@ static void wl1271_spi_reset(struct wl1271 *wl) spi_message_add_tail(&t, &m); spi_sync(wl_to_spi(wl), &m); - kfree(cmd); - wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); + kfree(cmd); } static void wl1271_spi_init(struct wl1271 *wl) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 546de574982..da1f1212034 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -120,6 +120,9 @@ struct netfront_info { unsigned long rx_pfn_array[NET_RX_RING_SIZE]; struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1]; struct mmu_update rx_mmu[NET_RX_RING_SIZE]; + + /* Statistics */ + int rx_gso_checksum_fixup; }; struct netfront_rx_info { @@ -770,11 +773,29 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np, return cons; } -static int skb_checksum_setup(struct sk_buff *skb) +static int checksum_setup(struct net_device *dev, struct sk_buff *skb) { struct iphdr *iph; unsigned char *th; int err = -EPROTO; + int recalculate_partial_csum = 0; + + /* + * A GSO SKB must be CHECKSUM_PARTIAL. However some buggy + * peers can fail to set NETRXF_csum_blank when sending a GSO + * frame. In this case force the SKB to CHECKSUM_PARTIAL and + * recalculate the partial checksum. + */ + if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) { + struct netfront_info *np = netdev_priv(dev); + np->rx_gso_checksum_fixup++; + skb->ip_summed = CHECKSUM_PARTIAL; + recalculate_partial_csum = 1; + } + + /* A non-CHECKSUM_PARTIAL SKB does not require setup. */ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; if (skb->protocol != htons(ETH_P_IP)) goto out; @@ -788,9 +809,23 @@ static int skb_checksum_setup(struct sk_buff *skb) switch (iph->protocol) { case IPPROTO_TCP: skb->csum_offset = offsetof(struct tcphdr, check); + + if (recalculate_partial_csum) { + struct tcphdr *tcph = (struct tcphdr *)th; + tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, + skb->len - iph->ihl*4, + IPPROTO_TCP, 0); + } break; case IPPROTO_UDP: skb->csum_offset = offsetof(struct udphdr, check); + + if (recalculate_partial_csum) { + struct udphdr *udph = (struct udphdr *)th; + udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, + skb->len - iph->ihl*4, + IPPROTO_UDP, 0); + } break; default: if (net_ratelimit()) @@ -829,13 +864,11 @@ static int handle_incoming_queue(struct net_device *dev, /* Ethernet work: Delayed to here as it peeks the header. */ skb->protocol = eth_type_trans(skb, dev); - if (skb->ip_summed == CHECKSUM_PARTIAL) { - if (skb_checksum_setup(skb)) { - kfree_skb(skb); - packets_dropped++; - dev->stats.rx_errors++; - continue; - } + if (checksum_setup(dev, skb)) { + kfree_skb(skb); + packets_dropped++; + dev->stats.rx_errors++; + continue; } dev->stats.rx_packets++; @@ -1632,12 +1665,59 @@ static void netback_changed(struct xenbus_device *dev, } } +static const struct xennet_stat { + char name[ETH_GSTRING_LEN]; + u16 offset; +} xennet_stats[] = { + { + "rx_gso_checksum_fixup", + offsetof(struct netfront_info, rx_gso_checksum_fixup) + }, +}; + +static int xennet_get_sset_count(struct net_device *dev, int string_set) +{ + switch (string_set) { + case ETH_SS_STATS: + return ARRAY_SIZE(xennet_stats); + default: + return -EINVAL; + } +} + +static void xennet_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 * data) +{ + void *np = netdev_priv(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(xennet_stats); i++) + data[i] = *(int *)(np + xennet_stats[i].offset); +} + +static void xennet_get_strings(struct net_device *dev, u32 stringset, u8 * data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(xennet_stats); i++) + memcpy(data + i * ETH_GSTRING_LEN, + xennet_stats[i].name, ETH_GSTRING_LEN); + break; + } +} + static const struct ethtool_ops xennet_ethtool_ops = { .set_tx_csum = ethtool_op_set_tx_csum, .set_sg = xennet_set_sg, .set_tso = xennet_set_tso, .get_link = ethtool_op_get_link, + + .get_sset_count = xennet_get_sset_count, + .get_ethtool_stats = xennet_get_ethtool_stats, + .get_strings = xennet_get_strings, }; #ifdef CONFIG_SYSFS diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 9583cbcc6b7..c404b61386b 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -143,6 +143,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, rtc->id = id; rtc->ops = ops; rtc->owner = owner; + rtc->irq_freq = 1; rtc->max_user_freq = 64; rtc->dev.parent = dev; rtc->dev.class = rtc_class; diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 925006d3310..a0c01967244 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -464,6 +464,9 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) int err = 0; unsigned long flags; + if (freq <= 0) + return -EINVAL; + spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 65ebee0a326..b6a6356d09b 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -565,7 +565,7 @@ static int netiucv_callback_connreq(struct iucv_path *path, struct iucv_event ev; int rc; - if (memcmp(iucvMagic, ipuser, sizeof(ipuser))) + if (memcmp(iucvMagic, ipuser, 16)) /* ipuser must match iucvMagic. */ return -EINVAL; rc = -EINVAL; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 29f848bfc12..019ae58ab91 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -988,16 +988,30 @@ static void qeth_get_channel_path_desc(struct qeth_card *card) chp_dsc = (struct channelPath_dsc *)ccw_device_get_chp_desc(ccwdev, 0); if (chp_dsc != NULL) { /* CHPP field bit 6 == 1 -> single queue */ - if ((chp_dsc->chpp & 0x02) == 0x02) + if ((chp_dsc->chpp & 0x02) == 0x02) { + if ((atomic_read(&card->qdio.state) != + QETH_QDIO_UNINITIALIZED) && + (card->qdio.no_out_queues == 4)) + /* change from 4 to 1 outbound queues */ + qeth_free_qdio_buffers(card); card->qdio.no_out_queues = 1; + if (card->qdio.default_out_queue != 0) + dev_info(&card->gdev->dev, + "Priority Queueing not supported\n"); + card->qdio.default_out_queue = 0; + } else { + if ((atomic_read(&card->qdio.state) != + QETH_QDIO_UNINITIALIZED) && + (card->qdio.no_out_queues == 1)) { + /* change from 1 to 4 outbound queues */ + qeth_free_qdio_buffers(card); + card->qdio.default_out_queue = 2; + } + card->qdio.no_out_queues = 4; + } card->info.func_level = 0x4100 + chp_dsc->desc; kfree(chp_dsc); } - if (card->qdio.no_out_queues == 1) { - card->qdio.default_out_queue = 0; - dev_info(&card->gdev->dev, - "Priority Queueing not supported\n"); - } QETH_DBF_TEXT_(SETUP, 2, "nr:%x", card->qdio.no_out_queues); QETH_DBF_TEXT_(SETUP, 2, "lvl:%02x", card->info.func_level); return; @@ -1832,33 +1846,6 @@ static inline int qeth_get_initial_mtu_for_card(struct qeth_card *card) } } -static inline int qeth_get_max_mtu_for_card(int cardtype) -{ - switch (cardtype) { - - case QETH_CARD_TYPE_UNKNOWN: - case QETH_CARD_TYPE_OSD: - case QETH_CARD_TYPE_OSN: - case QETH_CARD_TYPE_OSM: - case QETH_CARD_TYPE_OSX: - return 61440; - case QETH_CARD_TYPE_IQD: - return 57344; - default: - return 1500; - } -} - -static inline int qeth_get_mtu_out_of_mpc(int cardtype) -{ - switch (cardtype) { - case QETH_CARD_TYPE_IQD: - return 1; - default: - return 0; - } -} - static inline int qeth_get_mtu_outof_framesize(int framesize) { switch (framesize) { @@ -1881,10 +1868,9 @@ static inline int qeth_mtu_is_valid(struct qeth_card *card, int mtu) case QETH_CARD_TYPE_OSD: case QETH_CARD_TYPE_OSM: case QETH_CARD_TYPE_OSX: - return ((mtu >= 576) && (mtu <= 61440)); case QETH_CARD_TYPE_IQD: return ((mtu >= 576) && - (mtu <= card->info.max_mtu + 4096 - 32)); + (mtu <= card->info.max_mtu)); case QETH_CARD_TYPE_OSN: case QETH_CARD_TYPE_UNKNOWN: default: @@ -1907,7 +1893,7 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, memcpy(&card->token.ulp_filter_r, QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data), QETH_MPC_TOKEN_LENGTH); - if (qeth_get_mtu_out_of_mpc(card->info.type)) { + if (card->info.type == QETH_CARD_TYPE_IQD) { memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2); mtu = qeth_get_mtu_outof_framesize(framesize); if (!mtu) { @@ -1915,12 +1901,21 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); return 0; } - card->info.max_mtu = mtu; + if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) { + /* frame size has changed */ + if (card->dev && + ((card->dev->mtu == card->info.initial_mtu) || + (card->dev->mtu > mtu))) + card->dev->mtu = mtu; + qeth_free_qdio_buffers(card); + } card->info.initial_mtu = mtu; + card->info.max_mtu = mtu; card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE; } else { card->info.initial_mtu = qeth_get_initial_mtu_for_card(card); - card->info.max_mtu = qeth_get_max_mtu_for_card(card->info.type); + card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU( + iob->data); card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; } @@ -3775,6 +3770,47 @@ static inline int qeth_get_qdio_q_format(struct qeth_card *card) } } +static void qeth_determine_capabilities(struct qeth_card *card) +{ + int rc; + int length; + char *prcd; + struct ccw_device *ddev; + int ddev_offline = 0; + + QETH_DBF_TEXT(SETUP, 2, "detcapab"); + ddev = CARD_DDEV(card); + if (!ddev->online) { + ddev_offline = 1; + rc = ccw_device_set_online(ddev); + if (rc) { + QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); + goto out; + } + } + + rc = qeth_read_conf_data(card, (void **) &prcd, &length); + if (rc) { + QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", + dev_name(&card->gdev->dev), rc); + QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); + goto out_offline; + } + qeth_configure_unitaddr(card, prcd); + qeth_configure_blkt_default(card, prcd); + kfree(prcd); + + rc = qdio_get_ssqd_desc(ddev, &card->ssqd); + if (rc) + QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + +out_offline: + if (ddev_offline == 1) + ccw_device_set_offline(ddev); +out: + return; +} + static int qeth_qdio_establish(struct qeth_card *card) { struct qdio_initialize init_data; @@ -3905,6 +3941,7 @@ int qeth_core_hardsetup_card(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "hrdsetup"); atomic_set(&card->force_alloc_skb, 0); + qeth_get_channel_path_desc(card); retry: if (retries) QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", @@ -3933,6 +3970,7 @@ retriable: else goto retry; } + qeth_determine_capabilities(card); qeth_init_tokens(card); qeth_init_func_level(card); rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb); @@ -4202,41 +4240,6 @@ void qeth_core_free_discipline(struct qeth_card *card) card->discipline.ccwgdriver = NULL; } -static void qeth_determine_capabilities(struct qeth_card *card) -{ - int rc; - int length; - char *prcd; - - QETH_DBF_TEXT(SETUP, 2, "detcapab"); - rc = ccw_device_set_online(CARD_DDEV(card)); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); - goto out; - } - - - rc = qeth_read_conf_data(card, (void **) &prcd, &length); - if (rc) { - QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", - dev_name(&card->gdev->dev), rc); - QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); - goto out_offline; - } - qeth_configure_unitaddr(card, prcd); - qeth_configure_blkt_default(card, prcd); - kfree(prcd); - - rc = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd); - if (rc) - QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); - -out_offline: - ccw_device_set_offline(CARD_DDEV(card)); -out: - return; -} - static int qeth_core_probe_device(struct ccwgroup_device *gdev) { struct qeth_card *card; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 2ac8f6aff5a..ada0fe78237 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -573,13 +573,13 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card, case IPA_RC_L2_DUP_LAYER3_MAC: dev_warn(&card->gdev->dev, "MAC address %pM already exists\n", - card->dev->dev_addr); + cmd->data.setdelmac.mac); break; case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP: case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP: dev_warn(&card->gdev->dev, "MAC address %pM is not authorized\n", - card->dev->dev_addr); + cmd->data.setdelmac.mac); break; default: break; diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 65e1cf10494..207b7d74244 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -60,7 +60,7 @@ static struct iucv_handler smsg_handler = { static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8], u8 ipuser[16]) { - if (strncmp(ipvmid, "*MSG ", sizeof(ipvmid)) != 0) + if (strncmp(ipvmid, "*MSG ", 8) != 0) return -EINVAL; /* Path pending from *MSG. */ return iucv_path_accept(path, &smsg_handler, "SMSGIUCV ", NULL); diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h index 475c31ae985..77b26f5b9c3 100644 --- a/drivers/scsi/arcmsr/arcmsr.h +++ b/drivers/scsi/arcmsr/arcmsr.h @@ -2,7 +2,7 @@ ******************************************************************************* ** O.S : Linux ** FILE NAME : arcmsr.h -** BY : Erich Chen +** BY : Nick Cheng ** Description: SCSI RAID Device Driver for ** ARECA RAID Host adapter ******************************************************************************* @@ -46,8 +46,12 @@ struct device_attribute; /*The limit of outstanding scsi command that firmware can handle*/ #define ARCMSR_MAX_OUTSTANDING_CMD 256 -#define ARCMSR_MAX_FREECCB_NUM 320 -#define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.15 2010/02/02" +#ifdef CONFIG_XEN + #define ARCMSR_MAX_FREECCB_NUM 160 +#else + #define ARCMSR_MAX_FREECCB_NUM 320 +#endif +#define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.15 2010/08/05" #define ARCMSR_SCSI_INITIATOR_ID 255 #define ARCMSR_MAX_XFER_SECTORS 512 #define ARCMSR_MAX_XFER_SECTORS_B 4096 @@ -60,7 +64,6 @@ struct device_attribute; #define ARCMSR_MAX_HBB_POSTQUEUE 264 #define ARCMSR_MAX_XFER_LEN 0x26000 /* 152K */ #define ARCMSR_CDB_SG_PAGE_LENGTH 256 -#define SCSI_CMD_ARECA_SPECIFIC 0xE1 #ifndef PCI_DEVICE_ID_ARECA_1880 #define PCI_DEVICE_ID_ARECA_1880 0x1880 #endif diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c index a4e04c50c43..acdae33de52 100644 --- a/drivers/scsi/arcmsr/arcmsr_attr.c +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -2,7 +2,7 @@ ******************************************************************************* ** O.S : Linux ** FILE NAME : arcmsr_attr.c -** BY : Erich Chen +** BY : Nick Cheng ** Description: attributes exported to sysfs and device host ******************************************************************************* ** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 1cadcd6b7da..984bd527c6c 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -2,7 +2,7 @@ ******************************************************************************* ** O.S : Linux ** FILE NAME : arcmsr_hba.c -** BY : Erich Chen +** BY : Nick Cheng ** Description: SCSI RAID Device Driver for ** ARECA RAID Host adapter ******************************************************************************* @@ -76,7 +76,7 @@ MODULE_DESCRIPTION("ARECA (ARC11xx/12xx/16xx/1880) SATA/SAS RAID Host Bus Adapte MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(ARCMSR_DRIVER_VERSION); static int sleeptime = 10; -static int retrycount = 30; +static int retrycount = 12; wait_queue_head_t wait_q; static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, struct scsi_cmnd *cmd); @@ -187,7 +187,6 @@ int arcmsr_sleep_for_bus_reset(struct scsi_cmnd *cmd) if (isleep > 0) { msleep(isleep*1000); } - printk(KERN_NOTICE "wake-up\n"); return 0; } @@ -921,7 +920,6 @@ static void arcmsr_report_ccb_state(struct AdapterControlBlock *acb, } static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, struct CommandControlBlock *pCCB, bool error) - { int id, lun; if ((pCCB->acb != acb) || (pCCB->startdone != ARCMSR_CCB_START)) { @@ -948,7 +946,7 @@ static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, struct Comma , pCCB->startdone , atomic_read(&acb->ccboutstandingcount)); return; - } + } arcmsr_report_ccb_state(acb, pCCB, error); } @@ -981,7 +979,7 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb) case ACB_ADAPTER_TYPE_B: { struct MessageUnit_B *reg = acb->pmuB; /*clear all outbound posted Q*/ - writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, ®->iop2drv_doorbell); /* clear doorbell interrupt */ + writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell); /* clear doorbell interrupt */ for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) { if ((flag_ccb = readl(®->done_qbuffer[i])) != 0) { writel(0, ®->done_qbuffer[i]); @@ -1511,7 +1509,6 @@ static void arcmsr_hba_postqueue_isr(struct AdapterControlBlock *acb) arcmsr_drain_donequeue(acb, pCCB, error); } } - static void arcmsr_hbb_postqueue_isr(struct AdapterControlBlock *acb) { uint32_t index; @@ -2106,10 +2103,6 @@ static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd, if (atomic_read(&acb->ccboutstandingcount) >= ARCMSR_MAX_OUTSTANDING_CMD) return SCSI_MLQUEUE_HOST_BUSY; - if ((scsicmd == SCSI_CMD_ARECA_SPECIFIC)) { - printk(KERN_NOTICE "Receiveing SCSI_CMD_ARECA_SPECIFIC command..\n"); - return 0; - } ccb = arcmsr_get_freeccb(acb); if (!ccb) return SCSI_MLQUEUE_HOST_BUSY; @@ -2393,6 +2386,7 @@ static int arcmsr_polling_hbb_ccbdone(struct AdapterControlBlock *acb, int index, rtn; bool error; polling_hbb_ccb_retry: + poll_count++; /* clear doorbell interrupt */ writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell); @@ -2663,6 +2657,7 @@ static void arcmsr_request_hba_device_map(struct AdapterControlBlock *acb) { struct MessageUnit_A __iomem *reg = acb->pmuA; if (unlikely(atomic_read(&acb->rq_map_token) == 0) || ((acb->acb_flags & ACB_F_BUS_RESET) != 0 ) || ((acb->acb_flags & ACB_F_ABORT) != 0 )){ + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); return; } else { acb->fw_flag = FW_NORMAL; @@ -2670,8 +2665,10 @@ static void arcmsr_request_hba_device_map(struct AdapterControlBlock *acb) atomic_set(&acb->rq_map_token, 16); } atomic_set(&acb->ante_token_value, atomic_read(&acb->rq_map_token)); - if (atomic_dec_and_test(&acb->rq_map_token)) + if (atomic_dec_and_test(&acb->rq_map_token)) { + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); return; + } writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, ®->inbound_msgaddr0); mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); } @@ -2682,15 +2679,18 @@ static void arcmsr_request_hbb_device_map(struct AdapterControlBlock *acb) { struct MessageUnit_B __iomem *reg = acb->pmuB; if (unlikely(atomic_read(&acb->rq_map_token) == 0) || ((acb->acb_flags & ACB_F_BUS_RESET) != 0 ) || ((acb->acb_flags & ACB_F_ABORT) != 0 )){ + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); return; } else { acb->fw_flag = FW_NORMAL; if (atomic_read(&acb->ante_token_value) == atomic_read(&acb->rq_map_token)) { - atomic_set(&acb->rq_map_token,16); + atomic_set(&acb->rq_map_token, 16); } atomic_set(&acb->ante_token_value, atomic_read(&acb->rq_map_token)); - if(atomic_dec_and_test(&acb->rq_map_token)) + if (atomic_dec_and_test(&acb->rq_map_token)) { + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); return; + } writel(ARCMSR_MESSAGE_GET_CONFIG, reg->drv2iop_doorbell); mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); } @@ -2701,6 +2701,7 @@ static void arcmsr_request_hbc_device_map(struct AdapterControlBlock *acb) { struct MessageUnit_C __iomem *reg = acb->pmuC; if (unlikely(atomic_read(&acb->rq_map_token) == 0) || ((acb->acb_flags & ACB_F_BUS_RESET) != 0) || ((acb->acb_flags & ACB_F_ABORT) != 0)) { + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); return; } else { acb->fw_flag = FW_NORMAL; @@ -2708,8 +2709,10 @@ static void arcmsr_request_hbc_device_map(struct AdapterControlBlock *acb) atomic_set(&acb->rq_map_token, 16); } atomic_set(&acb->ante_token_value, atomic_read(&acb->rq_map_token)); - if (atomic_dec_and_test(&acb->rq_map_token)) + if (atomic_dec_and_test(&acb->rq_map_token)) { + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); return; + } writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, ®->inbound_msgaddr0); writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, ®->inbound_doorbell); mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); @@ -2897,6 +2900,8 @@ static uint8_t arcmsr_iop_reset(struct AdapterControlBlock *acb) uint32_t intmask_org; uint8_t rtnval = 0x00; int i = 0; + unsigned long flags; + if (atomic_read(&acb->ccboutstandingcount) != 0) { /* disable all outbound interrupt */ intmask_org = arcmsr_disable_outbound_ints(acb); @@ -2907,7 +2912,12 @@ static uint8_t arcmsr_iop_reset(struct AdapterControlBlock *acb) for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) { ccb = acb->pccb_pool[i]; if (ccb->startdone == ARCMSR_CCB_START) { - arcmsr_ccb_complete(ccb); + scsi_dma_unmap(ccb->pcmd); + ccb->startdone = ARCMSR_CCB_DONE; + ccb->ccb_flags = 0; + spin_lock_irqsave(&acb->ccblist_lock, flags); + list_add_tail(&ccb->list, &acb->ccb_free_list); + spin_unlock_irqrestore(&acb->ccblist_lock, flags); } } atomic_set(&acb->ccboutstandingcount, 0); @@ -2920,8 +2930,7 @@ static uint8_t arcmsr_iop_reset(struct AdapterControlBlock *acb) static int arcmsr_bus_reset(struct scsi_cmnd *cmd) { - struct AdapterControlBlock *acb = - (struct AdapterControlBlock *)cmd->device->host->hostdata; + struct AdapterControlBlock *acb; uint32_t intmask_org, outbound_doorbell; int retry_count = 0; int rtn = FAILED; @@ -2971,31 +2980,16 @@ sleep_again: atomic_set(&acb->rq_map_token, 16); atomic_set(&acb->ante_token_value, 16); acb->fw_flag = FW_NORMAL; - init_timer(&acb->eternal_timer); - acb->eternal_timer.expires = jiffies + msecs_to_jiffies(6*HZ); - acb->eternal_timer.data = (unsigned long) acb; - acb->eternal_timer.function = &arcmsr_request_device_map; - add_timer(&acb->eternal_timer); + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); acb->acb_flags &= ~ACB_F_BUS_RESET; rtn = SUCCESS; printk(KERN_ERR "arcmsr: scsi bus reset eh returns with success\n"); } else { acb->acb_flags &= ~ACB_F_BUS_RESET; - if (atomic_read(&acb->rq_map_token) == 0) { - atomic_set(&acb->rq_map_token, 16); - atomic_set(&acb->ante_token_value, 16); - acb->fw_flag = FW_NORMAL; - init_timer(&acb->eternal_timer); - acb->eternal_timer.expires = jiffies + msecs_to_jiffies(6*HZ); - acb->eternal_timer.data = (unsigned long) acb; - acb->eternal_timer.function = &arcmsr_request_device_map; - add_timer(&acb->eternal_timer); - } else { - atomic_set(&acb->rq_map_token, 16); - atomic_set(&acb->ante_token_value, 16); - acb->fw_flag = FW_NORMAL; - mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6*HZ)); - } + atomic_set(&acb->rq_map_token, 16); + atomic_set(&acb->ante_token_value, 16); + acb->fw_flag = FW_NORMAL; + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6*HZ)); rtn = SUCCESS; } break; @@ -3007,21 +3001,10 @@ sleep_again: rtn = FAILED; } else { acb->acb_flags &= ~ACB_F_BUS_RESET; - if (atomic_read(&acb->rq_map_token) == 0) { - atomic_set(&acb->rq_map_token, 16); - atomic_set(&acb->ante_token_value, 16); - acb->fw_flag = FW_NORMAL; - init_timer(&acb->eternal_timer); - acb->eternal_timer.expires = jiffies + msecs_to_jiffies(6*HZ); - acb->eternal_timer.data = (unsigned long) acb; - acb->eternal_timer.function = &arcmsr_request_device_map; - add_timer(&acb->eternal_timer); - } else { - atomic_set(&acb->rq_map_token, 16); - atomic_set(&acb->ante_token_value, 16); - acb->fw_flag = FW_NORMAL; - mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6*HZ)); - } + atomic_set(&acb->rq_map_token, 16); + atomic_set(&acb->ante_token_value, 16); + acb->fw_flag = FW_NORMAL; + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); rtn = SUCCESS; } break; @@ -3067,31 +3050,16 @@ sleep: atomic_set(&acb->rq_map_token, 16); atomic_set(&acb->ante_token_value, 16); acb->fw_flag = FW_NORMAL; - init_timer(&acb->eternal_timer); - acb->eternal_timer.expires = jiffies + msecs_to_jiffies(6 * HZ); - acb->eternal_timer.data = (unsigned long) acb; - acb->eternal_timer.function = &arcmsr_request_device_map; - add_timer(&acb->eternal_timer); + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ)); acb->acb_flags &= ~ACB_F_BUS_RESET; rtn = SUCCESS; printk(KERN_ERR "arcmsr: scsi bus reset eh returns with success\n"); } else { acb->acb_flags &= ~ACB_F_BUS_RESET; - if (atomic_read(&acb->rq_map_token) == 0) { - atomic_set(&acb->rq_map_token, 16); - atomic_set(&acb->ante_token_value, 16); - acb->fw_flag = FW_NORMAL; - init_timer(&acb->eternal_timer); - acb->eternal_timer.expires = jiffies + msecs_to_jiffies(6*HZ); - acb->eternal_timer.data = (unsigned long) acb; - acb->eternal_timer.function = &arcmsr_request_device_map; - add_timer(&acb->eternal_timer); - } else { - atomic_set(&acb->rq_map_token, 16); - atomic_set(&acb->ante_token_value, 16); - acb->fw_flag = FW_NORMAL; - mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6*HZ)); - } + atomic_set(&acb->rq_map_token, 16); + atomic_set(&acb->ante_token_value, 16); + acb->fw_flag = FW_NORMAL; + mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6*HZ)); rtn = SUCCESS; } break; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 5815cbeb27a..9a7aaf5f131 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -646,6 +646,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) spin_lock_irqsave(shost->host_lock, flags); list_splice_init(&shost->eh_cmd_q, &eh_work_q); + shost->host_eh_scheduled = 0; spin_unlock_irqrestore(shost->host_lock, flags); SAS_DPRINTK("Enter %s\n", __func__); diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index b2a817055b8..9ead0399808 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -2176,9 +2176,9 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /* adjust hba_queue_depth, reply_free_queue_depth, * and queue_size */ - ioc->hba_queue_depth -= queue_diff; - ioc->reply_free_queue_depth -= queue_diff; - queue_size -= queue_diff; + ioc->hba_queue_depth -= (queue_diff / 2); + ioc->reply_free_queue_depth -= (queue_diff / 2); + queue_size = facts->MaxReplyDescriptorPostQueueDepth; } ioc->reply_post_queue_depth = queue_size; @@ -3941,6 +3941,8 @@ mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) static void _base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) { + mpt2sas_scsih_reset_handler(ioc, reset_phase); + mpt2sas_ctl_reset_handler(ioc, reset_phase); switch (reset_phase) { case MPT2_IOC_PRE_RESET: dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " @@ -3971,8 +3973,6 @@ _base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); break; } - mpt2sas_scsih_reset_handler(ioc, reset_phase); - mpt2sas_ctl_reset_handler(ioc, reset_phase); } /** @@ -4026,6 +4026,7 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, { int r; unsigned long flags; + u8 pe_complete = ioc->wait_for_port_enable_to_complete; dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); @@ -4068,6 +4069,14 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, if (r) goto out; _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET); + + /* If this hard reset is called while port enable is active, then + * there is no reason to call make_ioc_operational + */ + if (pe_complete) { + r = -EFAULT; + goto out; + } r = _base_make_ioc_operational(ioc, sleep_flag); if (!r) _base_reset_handler(ioc, MPT2_IOC_DONE_RESET); diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index eda347c5797..5ded3db6e31 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -819,7 +819,7 @@ _scsih_is_end_device(u32 device_info) } /** - * mptscsih_get_scsi_lookup - returns scmd entry + * _scsih_scsi_lookup_get - returns scmd entry * @ioc: per adapter object * @smid: system request message index * @@ -832,6 +832,28 @@ _scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) } /** + * _scsih_scsi_lookup_get_clear - returns scmd entry + * @ioc: per adapter object + * @smid: system request message index + * + * Returns the smid stored scmd pointer. + * Then will derefrence the stored scmd pointer. + */ +static inline struct scsi_cmnd * +_scsih_scsi_lookup_get_clear(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + unsigned long flags; + struct scsi_cmnd *scmd; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + scmd = ioc->scsi_lookup[smid - 1].scmd; + ioc->scsi_lookup[smid - 1].scmd = NULL; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + return scmd; +} + +/** * _scsih_scsi_lookup_find_by_scmd - scmd lookup * @ioc: per adapter object * @smid: system request message index @@ -2981,9 +3003,6 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, u16 handle; for (i = 0 ; i < event_data->NumEntries; i++) { - if (event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) - continue; handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); if (!handle) continue; @@ -3210,7 +3229,7 @@ _scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc) u16 count = 0; for (smid = 1; smid <= ioc->scsiio_depth; smid++) { - scmd = _scsih_scsi_lookup_get(ioc, smid); + scmd = _scsih_scsi_lookup_get_clear(ioc, smid); if (!scmd) continue; count++; @@ -3804,7 +3823,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) u32 response_code = 0; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - scmd = _scsih_scsi_lookup_get(ioc, smid); + scmd = _scsih_scsi_lookup_get_clear(ioc, smid); if (scmd == NULL) return 1; @@ -5005,6 +5024,12 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, event_data); #endif + /* In MPI Revision K (0xC), the internal device reset complete was + * implemented, so avoid setting tm_busy flag for older firmware. + */ + if ((ioc->facts.HeaderVersion >> 8) < 0xC) + return; + if (event_data->ReasonCode != MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && event_data->ReasonCode != @@ -5099,6 +5124,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { struct scsi_cmnd *scmd; + struct scsi_device *sdev; u16 smid, handle; u32 lun; struct MPT2SAS_DEVICE *sas_device_priv_data; @@ -5109,12 +5135,17 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data; #endif u16 ioc_status; + unsigned long flags; + int r; + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "broadcast primative: " "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum, event_data->PortWidth)); dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + ioc->broadcast_aen_busy = 0; termination_count = 0; query_count = 0; mpi_reply = ioc->tm_cmds.reply; @@ -5122,7 +5153,8 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, scmd = _scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; - sas_device_priv_data = scmd->device->hostdata; + sdev = scmd->device; + sas_device_priv_data = sdev->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) continue; /* skip hidden raid components */ @@ -5138,6 +5170,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, lun = sas_device_priv_data->lun; query_count++; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, NULL); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; @@ -5147,14 +5180,20 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, (mpi_reply->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED || mpi_reply->ResponseCode == - MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC)) + MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC)) { + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); continue; - - mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30, NULL); + } + r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, + sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, + scmd); + if (r == FAILED) + sdev_printk(KERN_WARNING, sdev, "task abort: FAILED " + "scmd(%p)\n", scmd); termination_count += le32_to_cpu(mpi_reply->TerminationCount); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); } - ioc->broadcast_aen_busy = 0; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - exit, query_count = %d termination_count = %d\n", @@ -6626,6 +6665,7 @@ _scsih_remove(struct pci_dev *pdev) destroy_workqueue(wq); /* release all the volumes */ + _scsih_ir_shutdown(ioc); list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, list) { if (raid_device->starget) { diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 9b3ca103135..f616cefc95b 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -128,8 +128,7 @@ static void handle_tx(struct vhost_net *net) size_t hdr_size; struct socket *sock; - /* TODO: check that we are running from vhost_worker? - * Not sure it's worth it, it's straight-forward enough. */ + /* TODO: check that we are running from vhost_worker? */ sock = rcu_dereference_check(vq->private_data, 1); if (!sock) return; @@ -306,7 +305,8 @@ static void handle_rx_big(struct vhost_net *net) size_t len, total_len = 0; int err; size_t hdr_size; - struct socket *sock = rcu_dereference(vq->private_data); + /* TODO: check that we are running from vhost_worker? */ + struct socket *sock = rcu_dereference_check(vq->private_data, 1); if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue)) return; @@ -415,7 +415,8 @@ static void handle_rx_mergeable(struct vhost_net *net) int err, headcount; size_t vhost_hlen, sock_hlen; size_t vhost_len, sock_len; - struct socket *sock = rcu_dereference(vq->private_data); + /* TODO: check that we are running from vhost_worker? */ + struct socket *sock = rcu_dereference_check(vq->private_data, 1); if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue)) return; diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 2af44b7b1f3..b3363ae3851 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -173,9 +173,9 @@ static inline int vhost_has_feature(struct vhost_dev *dev, int bit) { unsigned acked_features; - acked_features = - rcu_dereference_index_check(dev->acked_features, - lockdep_is_held(&dev->mutex)); + /* TODO: check that we are running from vhost_worker or dev mutex is + * held? */ + acked_features = rcu_dereference_index_check(dev->acked_features, 1); return acked_features & (1 << bit); } diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index 15b5ca2a260..9c949348510 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -37,6 +37,9 @@ static struct posix_acl *btrfs_get_acl(struct inode *inode, int type) char *value = NULL; struct posix_acl *acl; + if (!IS_POSIXACL(inode)) + return NULL; + acl = get_cached_acl(inode, type); if (acl != ACL_NOT_CACHED) return acl; @@ -84,6 +87,9 @@ static int btrfs_xattr_acl_get(struct dentry *dentry, const char *name, struct posix_acl *acl; int ret = 0; + if (!IS_POSIXACL(dentry->d_inode)) + return -EOPNOTSUPP; + acl = btrfs_get_acl(dentry->d_inode, type); if (IS_ERR(acl)) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index f745287fbf2..4d2110eafe2 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -562,7 +562,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, u64 em_len; u64 em_start; struct extent_map *em; - int ret; + int ret = -ENOMEM; u32 *sums; tree = &BTRFS_I(inode)->io_tree; @@ -577,6 +577,9 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, compressed_len = em->block_len; cb = kmalloc(compressed_bio_size(root, compressed_len), GFP_NOFS); + if (!cb) + goto out; + atomic_set(&cb->pending_bios, 0); cb->errors = 0; cb->inode = inode; @@ -597,13 +600,18 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, nr_pages = (compressed_len + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE; - cb->compressed_pages = kmalloc(sizeof(struct page *) * nr_pages, + cb->compressed_pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); + if (!cb->compressed_pages) + goto fail1; + bdev = BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev; for (page_index = 0; page_index < nr_pages; page_index++) { cb->compressed_pages[page_index] = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + if (!cb->compressed_pages[page_index]) + goto fail2; } cb->nr_pages = nr_pages; @@ -614,6 +622,8 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, cb->len = uncompressed_len; comp_bio = compressed_bio_alloc(bdev, cur_disk_byte, GFP_NOFS); + if (!comp_bio) + goto fail2; comp_bio->bi_private = cb; comp_bio->bi_end_io = end_compressed_bio_read; atomic_inc(&cb->pending_bios); @@ -681,6 +691,17 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, bio_put(comp_bio); return 0; + +fail2: + for (page_index = 0; page_index < nr_pages; page_index++) + free_page((unsigned long)cb->compressed_pages[page_index]); + + kfree(cb->compressed_pages); +fail1: + kfree(cb); +out: + free_extent_map(em); + return ret; } static struct list_head comp_idle_workspace[BTRFS_COMPRESS_TYPES]; @@ -900,7 +921,7 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page, return ret; } -void __exit btrfs_exit_compress(void) +void btrfs_exit_compress(void) { free_workspaces(); } diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index b531c36455d..fdce8799b98 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1550,6 +1550,7 @@ static int transaction_kthread(void *arg) spin_unlock(&root->fs_info->new_trans_lock); trans = btrfs_join_transaction(root, 1); + BUG_ON(IS_ERR(trans)); if (transid == trans->transid) { ret = btrfs_commit_transaction(trans, root); BUG_ON(ret); @@ -2453,10 +2454,14 @@ int btrfs_commit_super(struct btrfs_root *root) up_write(&root->fs_info->cleanup_work_sem); trans = btrfs_join_transaction(root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); ret = btrfs_commit_transaction(trans, root); BUG_ON(ret); /* run commit again to drop the original snapshot */ trans = btrfs_join_transaction(root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); btrfs_commit_transaction(trans, root); ret = btrfs_write_and_wait_transaction(NULL, root); BUG_ON(ret); @@ -2554,6 +2559,8 @@ int close_ctree(struct btrfs_root *root) kfree(fs_info->chunk_root); kfree(fs_info->dev_root); kfree(fs_info->csum_root); + kfree(fs_info); + return 0; } diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c index 9786963b07e..ff27d7a477b 100644 --- a/fs/btrfs/export.c +++ b/fs/btrfs/export.c @@ -171,6 +171,8 @@ static struct dentry *btrfs_get_parent(struct dentry *child) int ret; path = btrfs_alloc_path(); + if (!path) + return ERR_PTR(-ENOMEM); if (dir->i_ino == BTRFS_FIRST_FREE_OBJECTID) { key.objectid = root->root_key.objectid; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index b55269340ce..4e7e012ad66 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -320,11 +320,6 @@ static int caching_kthread(void *data) if (!path) return -ENOMEM; - exclude_super_stripes(extent_root, block_group); - spin_lock(&block_group->space_info->lock); - block_group->space_info->bytes_readonly += block_group->bytes_super; - spin_unlock(&block_group->space_info->lock); - last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET); /* @@ -467,8 +462,10 @@ static int cache_block_group(struct btrfs_block_group_cache *cache, cache->cached = BTRFS_CACHE_NO; } spin_unlock(&cache->lock); - if (ret == 1) + if (ret == 1) { + free_excluded_extents(fs_info->extent_root, cache); return 0; + } } if (load_cache_only) @@ -3344,8 +3341,10 @@ static int shrink_delalloc(struct btrfs_trans_handle *trans, u64 reserved; u64 max_reclaim; u64 reclaimed = 0; + long time_left; int pause = 1; int nr_pages = (2 * 1024 * 1024) >> PAGE_CACHE_SHIFT; + int loops = 0; block_rsv = &root->fs_info->delalloc_block_rsv; space_info = block_rsv->space_info; @@ -3358,7 +3357,7 @@ static int shrink_delalloc(struct btrfs_trans_handle *trans, max_reclaim = min(reserved, to_reclaim); - while (1) { + while (loops < 1024) { /* have the flusher threads jump in and do some IO */ smp_mb(); nr_pages = min_t(unsigned long, nr_pages, @@ -3366,8 +3365,12 @@ static int shrink_delalloc(struct btrfs_trans_handle *trans, writeback_inodes_sb_nr_if_idle(root->fs_info->sb, nr_pages); spin_lock(&space_info->lock); - if (reserved > space_info->bytes_reserved) + if (reserved > space_info->bytes_reserved) { + loops = 0; reclaimed += reserved - space_info->bytes_reserved; + } else { + loops++; + } reserved = space_info->bytes_reserved; spin_unlock(&space_info->lock); @@ -3378,7 +3381,12 @@ static int shrink_delalloc(struct btrfs_trans_handle *trans, return -EAGAIN; __set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(pause); + time_left = schedule_timeout(pause); + + /* We were interrupted, exit */ + if (time_left) + break; + pause <<= 1; if (pause > HZ / 10) pause = HZ / 10; @@ -3588,8 +3596,20 @@ void block_rsv_release_bytes(struct btrfs_block_rsv *block_rsv, if (num_bytes > 0) { if (dest) { - block_rsv_add_bytes(dest, num_bytes, 0); - } else { + spin_lock(&dest->lock); + if (!dest->full) { + u64 bytes_to_add; + + bytes_to_add = dest->size - dest->reserved; + bytes_to_add = min(num_bytes, bytes_to_add); + dest->reserved += bytes_to_add; + if (dest->reserved >= dest->size) + dest->full = 1; + num_bytes -= bytes_to_add; + } + spin_unlock(&dest->lock); + } + if (num_bytes) { spin_lock(&space_info->lock); space_info->bytes_reserved -= num_bytes; spin_unlock(&space_info->lock); @@ -4012,6 +4032,7 @@ void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes) num_bytes = ALIGN(num_bytes, root->sectorsize); atomic_dec(&BTRFS_I(inode)->outstanding_extents); + WARN_ON(atomic_read(&BTRFS_I(inode)->outstanding_extents) < 0); spin_lock(&BTRFS_I(inode)->accounting_lock); nr_extents = atomic_read(&BTRFS_I(inode)->outstanding_extents); @@ -5633,6 +5654,7 @@ use_block_rsv(struct btrfs_trans_handle *trans, struct btrfs_root *root, u32 blocksize) { struct btrfs_block_rsv *block_rsv; + struct btrfs_block_rsv *global_rsv = &root->fs_info->global_block_rsv; int ret; block_rsv = get_block_rsv(trans, root); @@ -5640,14 +5662,39 @@ use_block_rsv(struct btrfs_trans_handle *trans, if (block_rsv->size == 0) { ret = reserve_metadata_bytes(trans, root, block_rsv, blocksize, 0); - if (ret) + /* + * If we couldn't reserve metadata bytes try and use some from + * the global reserve. + */ + if (ret && block_rsv != global_rsv) { + ret = block_rsv_use_bytes(global_rsv, blocksize); + if (!ret) + return global_rsv; + return ERR_PTR(ret); + } else if (ret) { return ERR_PTR(ret); + } return block_rsv; } ret = block_rsv_use_bytes(block_rsv, blocksize); if (!ret) return block_rsv; + if (ret) { + WARN_ON(1); + ret = reserve_metadata_bytes(trans, root, block_rsv, blocksize, + 0); + if (!ret) { + spin_lock(&block_rsv->lock); + block_rsv->size += blocksize; + spin_unlock(&block_rsv->lock); + return block_rsv; + } else if (ret && block_rsv != global_rsv) { + ret = block_rsv_use_bytes(global_rsv, blocksize); + if (!ret) + return global_rsv; + } + } return ERR_PTR(-ENOSPC); } @@ -6221,6 +6268,8 @@ int btrfs_drop_snapshot(struct btrfs_root *root, BUG_ON(!wc); trans = btrfs_start_transaction(tree_root, 0); + BUG_ON(IS_ERR(trans)); + if (block_rsv) trans->block_rsv = block_rsv; @@ -6318,6 +6367,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, btrfs_end_transaction_throttle(trans, tree_root); trans = btrfs_start_transaction(tree_root, 0); + BUG_ON(IS_ERR(trans)); if (block_rsv) trans->block_rsv = block_rsv; } @@ -6446,6 +6496,8 @@ static noinline int relocate_inode_pages(struct inode *inode, u64 start, int ret = 0; ra = kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + return -ENOMEM; mutex_lock(&inode->i_mutex); first_index = start >> PAGE_CACHE_SHIFT; @@ -7477,7 +7529,7 @@ int btrfs_drop_dead_reloc_roots(struct btrfs_root *root) BUG_ON(reloc_root->commit_root != NULL); while (1) { trans = btrfs_join_transaction(root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); mutex_lock(&root->fs_info->drop_mutex); ret = btrfs_drop_snapshot(trans, reloc_root); @@ -7535,7 +7587,7 @@ int btrfs_cleanup_reloc_trees(struct btrfs_root *root) if (found) { trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); ret = btrfs_commit_transaction(trans, root); BUG_ON(ret); } @@ -7779,7 +7831,7 @@ static noinline int relocate_one_extent(struct btrfs_root *extent_root, trans = btrfs_start_transaction(extent_root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); if (extent_key->objectid == 0) { ret = del_extent_zero(trans, extent_root, path, extent_key); @@ -8270,6 +8322,13 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) if (block_group->cached == BTRFS_CACHE_STARTED) wait_block_group_cache_done(block_group); + /* + * We haven't cached this block group, which means we could + * possibly have excluded extents on this block group. + */ + if (block_group->cached == BTRFS_CACHE_NO) + free_excluded_extents(info->extent_root, block_group); + btrfs_remove_free_space_cache(block_group); btrfs_put_block_group(block_group); @@ -8385,6 +8444,13 @@ int btrfs_read_block_groups(struct btrfs_root *root) cache->sectorsize = root->sectorsize; /* + * We need to exclude the super stripes now so that the space + * info has super bytes accounted for, otherwise we'll think + * we have more space than we actually do. + */ + exclude_super_stripes(root, cache); + + /* * check for two cases, either we are full, and therefore * don't need to bother with the caching work since we won't * find any space, or we are empty, and we can just add all @@ -8392,12 +8458,10 @@ int btrfs_read_block_groups(struct btrfs_root *root) * time, particularly in the full case. */ if (found_key.offset == btrfs_block_group_used(&cache->item)) { - exclude_super_stripes(root, cache); cache->last_byte_to_unpin = (u64)-1; cache->cached = BTRFS_CACHE_FINISHED; free_excluded_extents(root, cache); } else if (btrfs_block_group_used(&cache->item) == 0) { - exclude_super_stripes(root, cache); cache->last_byte_to_unpin = (u64)-1; cache->cached = BTRFS_CACHE_FINISHED; add_new_free_space(cache, root->fs_info, diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 2e993cf1766..5e76a474cb7 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1865,7 +1865,7 @@ static int submit_one_bio(int rw, struct bio *bio, int mirror_num, bio_get(bio); if (tree->ops && tree->ops->submit_bio_hook) - tree->ops->submit_bio_hook(page->mapping->host, rw, bio, + ret = tree->ops->submit_bio_hook(page->mapping->host, rw, bio, mirror_num, bio_flags, start); else submit_bio(rw, bio); @@ -1920,6 +1920,8 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree, nr = bio_get_nr_vecs(bdev); bio = btrfs_bio_alloc(bdev, sector, nr, GFP_NOFS | __GFP_HIGH); + if (!bio) + return -ENOMEM; bio_add_page(bio, page, page_size, offset); bio->bi_end_io = end_io_func; @@ -2126,7 +2128,7 @@ int extent_read_full_page(struct extent_io_tree *tree, struct page *page, ret = __extent_read_full_page(tree, page, get_extent, &bio, 0, &bio_flags); if (bio) - submit_one_bio(READ, bio, 0, bio_flags); + ret = submit_one_bio(READ, bio, 0, bio_flags); return ret; } diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index a562a250ae7..4f19a3e1bf3 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -536,6 +536,8 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans, root = root->fs_info->csum_root; path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; while (1) { key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; @@ -548,7 +550,10 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans, if (path->slots[0] == 0) goto out; path->slots[0]--; + } else if (ret < 0) { + goto out; } + leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index c800d58f301..c1d3a818731 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -793,8 +793,12 @@ again: for (i = 0; i < num_pages; i++) { pages[i] = grab_cache_page(inode->i_mapping, index + i); if (!pages[i]) { - err = -ENOMEM; - BUG_ON(1); + int c; + for (c = i - 1; c >= 0; c--) { + unlock_page(pages[c]); + page_cache_release(pages[c]); + } + return -ENOMEM; } wait_on_page_writeback(pages[i]); } @@ -946,6 +950,10 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, PAGE_CACHE_SIZE, PAGE_CACHE_SIZE / (sizeof(struct page *))); pages = kmalloc(nrptrs * sizeof(struct page *), GFP_KERNEL); + if (!pages) { + ret = -ENOMEM; + goto out; + } /* generic_write_checks can change our pos */ start_pos = pos; @@ -984,8 +992,8 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, size_t write_bytes = min(iov_iter_count(&i), nrptrs * (size_t)PAGE_CACHE_SIZE - offset); - size_t num_pages = (write_bytes + PAGE_CACHE_SIZE - 1) >> - PAGE_CACHE_SHIFT; + size_t num_pages = (write_bytes + offset + + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; WARN_ON(num_pages > nrptrs); memset(pages, 0, sizeof(struct page *) * nrptrs); @@ -1015,8 +1023,8 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, copied = btrfs_copy_from_user(pos, num_pages, write_bytes, pages, &i); - dirty_pages = (copied + PAGE_CACHE_SIZE - 1) >> - PAGE_CACHE_SHIFT; + dirty_pages = (copied + offset + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT; if (num_pages > dirty_pages) { if (copied > 0) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 60d68426695..a0390657451 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -987,11 +987,18 @@ tree_search_offset(struct btrfs_block_group_cache *block_group, return entry; } -static void unlink_free_space(struct btrfs_block_group_cache *block_group, - struct btrfs_free_space *info) +static inline void +__unlink_free_space(struct btrfs_block_group_cache *block_group, + struct btrfs_free_space *info) { rb_erase(&info->offset_index, &block_group->free_space_offset); block_group->free_extents--; +} + +static void unlink_free_space(struct btrfs_block_group_cache *block_group, + struct btrfs_free_space *info) +{ + __unlink_free_space(block_group, info); block_group->free_space -= info->bytes; } @@ -1016,14 +1023,18 @@ static void recalculate_thresholds(struct btrfs_block_group_cache *block_group) u64 max_bytes; u64 bitmap_bytes; u64 extent_bytes; + u64 size = block_group->key.offset; /* * The goal is to keep the total amount of memory used per 1gb of space * at or below 32k, so we need to adjust how much memory we allow to be * used by extent based free space tracking */ - max_bytes = MAX_CACHE_BYTES_PER_GIG * - (div64_u64(block_group->key.offset, 1024 * 1024 * 1024)); + if (size < 1024 * 1024 * 1024) + max_bytes = MAX_CACHE_BYTES_PER_GIG; + else + max_bytes = MAX_CACHE_BYTES_PER_GIG * + div64_u64(size, 1024 * 1024 * 1024); /* * we want to account for 1 more bitmap than what we have so we can make @@ -1171,6 +1182,16 @@ static void add_new_bitmap(struct btrfs_block_group_cache *block_group, recalculate_thresholds(block_group); } +static void free_bitmap(struct btrfs_block_group_cache *block_group, + struct btrfs_free_space *bitmap_info) +{ + unlink_free_space(block_group, bitmap_info); + kfree(bitmap_info->bitmap); + kfree(bitmap_info); + block_group->total_bitmaps--; + recalculate_thresholds(block_group); +} + static noinline int remove_from_bitmap(struct btrfs_block_group_cache *block_group, struct btrfs_free_space *bitmap_info, u64 *offset, u64 *bytes) @@ -1195,6 +1216,7 @@ again: */ search_start = *offset; search_bytes = *bytes; + search_bytes = min(search_bytes, end - search_start + 1); ret = search_bitmap(block_group, bitmap_info, &search_start, &search_bytes); BUG_ON(ret < 0 || search_start != *offset); @@ -1211,13 +1233,8 @@ again: if (*bytes) { struct rb_node *next = rb_next(&bitmap_info->offset_index); - if (!bitmap_info->bytes) { - unlink_free_space(block_group, bitmap_info); - kfree(bitmap_info->bitmap); - kfree(bitmap_info); - block_group->total_bitmaps--; - recalculate_thresholds(block_group); - } + if (!bitmap_info->bytes) + free_bitmap(block_group, bitmap_info); /* * no entry after this bitmap, but we still have bytes to @@ -1250,13 +1267,8 @@ again: return -EAGAIN; goto again; - } else if (!bitmap_info->bytes) { - unlink_free_space(block_group, bitmap_info); - kfree(bitmap_info->bitmap); - kfree(bitmap_info); - block_group->total_bitmaps--; - recalculate_thresholds(block_group); - } + } else if (!bitmap_info->bytes) + free_bitmap(block_group, bitmap_info); return 0; } @@ -1359,22 +1371,14 @@ out: return ret; } -int btrfs_add_free_space(struct btrfs_block_group_cache *block_group, - u64 offset, u64 bytes) +bool try_merge_free_space(struct btrfs_block_group_cache *block_group, + struct btrfs_free_space *info, bool update_stat) { - struct btrfs_free_space *right_info = NULL; - struct btrfs_free_space *left_info = NULL; - struct btrfs_free_space *info = NULL; - int ret = 0; - - info = kzalloc(sizeof(struct btrfs_free_space), GFP_NOFS); - if (!info) - return -ENOMEM; - - info->offset = offset; - info->bytes = bytes; - - spin_lock(&block_group->tree_lock); + struct btrfs_free_space *left_info; + struct btrfs_free_space *right_info; + bool merged = false; + u64 offset = info->offset; + u64 bytes = info->bytes; /* * first we want to see if there is free space adjacent to the range we @@ -1388,37 +1392,62 @@ int btrfs_add_free_space(struct btrfs_block_group_cache *block_group, else left_info = tree_search_offset(block_group, offset - 1, 0, 0); - /* - * If there was no extent directly to the left or right of this new - * extent then we know we're going to have to allocate a new extent, so - * before we do that see if we need to drop this into a bitmap - */ - if ((!left_info || left_info->bitmap) && - (!right_info || right_info->bitmap)) { - ret = insert_into_bitmap(block_group, info); - - if (ret < 0) { - goto out; - } else if (ret) { - ret = 0; - goto out; - } - } - if (right_info && !right_info->bitmap) { - unlink_free_space(block_group, right_info); + if (update_stat) + unlink_free_space(block_group, right_info); + else + __unlink_free_space(block_group, right_info); info->bytes += right_info->bytes; kfree(right_info); + merged = true; } if (left_info && !left_info->bitmap && left_info->offset + left_info->bytes == offset) { - unlink_free_space(block_group, left_info); + if (update_stat) + unlink_free_space(block_group, left_info); + else + __unlink_free_space(block_group, left_info); info->offset = left_info->offset; info->bytes += left_info->bytes; kfree(left_info); + merged = true; } + return merged; +} + +int btrfs_add_free_space(struct btrfs_block_group_cache *block_group, + u64 offset, u64 bytes) +{ + struct btrfs_free_space *info; + int ret = 0; + + info = kzalloc(sizeof(struct btrfs_free_space), GFP_NOFS); + if (!info) + return -ENOMEM; + + info->offset = offset; + info->bytes = bytes; + + spin_lock(&block_group->tree_lock); + + if (try_merge_free_space(block_group, info, true)) + goto link; + + /* + * There was no extent directly to the left or right of this new + * extent then we know we're going to have to allocate a new extent, so + * before we do that see if we need to drop this into a bitmap + */ + ret = insert_into_bitmap(block_group, info); + if (ret < 0) { + goto out; + } else if (ret) { + ret = 0; + goto out; + } +link: ret = link_free_space(block_group, info); if (ret) kfree(info); @@ -1621,6 +1650,7 @@ __btrfs_return_cluster_to_free_space( node = rb_next(&entry->offset_index); rb_erase(&entry->offset_index, &cluster->root); BUG_ON(entry->bitmap); + try_merge_free_space(block_group, entry, false); tree_insert_offset(&block_group->free_space_offset, entry->offset, &entry->offset_index, 0); } @@ -1685,13 +1715,8 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group, ret = offset; if (entry->bitmap) { bitmap_clear_bits(block_group, entry, offset, bytes); - if (!entry->bytes) { - unlink_free_space(block_group, entry); - kfree(entry->bitmap); - kfree(entry); - block_group->total_bitmaps--; - recalculate_thresholds(block_group); - } + if (!entry->bytes) + free_bitmap(block_group, entry); } else { unlink_free_space(block_group, entry); entry->offset += bytes; @@ -1789,6 +1814,8 @@ static u64 btrfs_alloc_from_bitmap(struct btrfs_block_group_cache *block_group, ret = search_start; bitmap_clear_bits(block_group, entry, ret, bytes); + if (entry->bytes == 0) + free_bitmap(block_group, entry); out: spin_unlock(&cluster->lock); spin_unlock(&block_group->tree_lock); @@ -1842,15 +1869,26 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group, entry->offset += bytes; entry->bytes -= bytes; - if (entry->bytes == 0) { + if (entry->bytes == 0) rb_erase(&entry->offset_index, &cluster->root); - kfree(entry); - } break; } out: spin_unlock(&cluster->lock); + if (!ret) + return 0; + + spin_lock(&block_group->tree_lock); + + block_group->free_space -= bytes; + if (entry->bytes == 0) { + block_group->free_extents--; + kfree(entry); + } + + spin_unlock(&block_group->tree_lock); + return ret; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 160b55b3e13..bcc461a9695 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -416,7 +416,7 @@ again: } if (start == 0) { trans = btrfs_join_transaction(root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); btrfs_set_trans_block_group(trans, inode); trans->block_rsv = &root->fs_info->delalloc_block_rsv; @@ -612,6 +612,7 @@ retry: GFP_NOFS); trans = btrfs_join_transaction(root, 1); + BUG_ON(IS_ERR(trans)); ret = btrfs_reserve_extent(trans, root, async_extent->compressed_size, async_extent->compressed_size, @@ -771,7 +772,7 @@ static noinline int cow_file_range(struct inode *inode, BUG_ON(root == root->fs_info->tree_root); trans = btrfs_join_transaction(root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); btrfs_set_trans_block_group(trans, inode); trans->block_rsv = &root->fs_info->delalloc_block_rsv; @@ -1049,7 +1050,7 @@ static noinline int run_delalloc_nocow(struct inode *inode, } else { trans = btrfs_join_transaction(root, 1); } - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); cow_start = (u64)-1; cur_offset = start; @@ -1557,6 +1558,7 @@ out: out_page: unlock_page(page); page_cache_release(page); + kfree(fixup); } /* @@ -1703,7 +1705,7 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) trans = btrfs_join_transaction_nolock(root, 1); else trans = btrfs_join_transaction(root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); btrfs_set_trans_block_group(trans, inode); trans->block_rsv = &root->fs_info->delalloc_block_rsv; ret = btrfs_update_inode(trans, root, inode); @@ -1720,6 +1722,7 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) trans = btrfs_join_transaction_nolock(root, 1); else trans = btrfs_join_transaction(root, 1); + BUG_ON(IS_ERR(trans)); btrfs_set_trans_block_group(trans, inode); trans->block_rsv = &root->fs_info->delalloc_block_rsv; @@ -2354,6 +2357,7 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) */ if (is_bad_inode(inode)) { trans = btrfs_start_transaction(root, 0); + BUG_ON(IS_ERR(trans)); btrfs_orphan_del(trans, inode); btrfs_end_transaction(trans, root); iput(inode); @@ -2381,6 +2385,7 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) if (root->orphan_block_rsv || root->orphan_item_inserted) { trans = btrfs_join_transaction(root, 1); + BUG_ON(IS_ERR(trans)); btrfs_end_transaction(trans, root); } @@ -2641,7 +2646,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans, path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; - goto err; + goto out; } path->leave_spinning = 1; @@ -2714,9 +2719,10 @@ static int check_path_shared(struct btrfs_root *root, struct extent_buffer *eb; int level; u64 refs = 1; - int uninitialized_var(ret); for (level = 0; level < BTRFS_MAX_LEVEL; level++) { + int ret; + if (!path->nodes[level]) break; eb = path->nodes[level]; @@ -2727,7 +2733,7 @@ static int check_path_shared(struct btrfs_root *root, if (refs > 1) return 1; } - return ret; /* XXX callers? */ + return 0; } /* @@ -4134,7 +4140,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) } srcu_read_unlock(&root->fs_info->subvol_srcu, index); - if (root != sub_root) { + if (!IS_ERR(inode) && root != sub_root) { down_read(&root->fs_info->cleanup_work_sem); if (!(inode->i_sb->s_flags & MS_RDONLY)) btrfs_orphan_cleanup(sub_root); @@ -4347,6 +4353,8 @@ int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc) trans = btrfs_join_transaction_nolock(root, 1); else trans = btrfs_join_transaction(root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); btrfs_set_trans_block_group(trans, inode); if (nolock) ret = btrfs_end_transaction_nolock(trans, root); @@ -4372,6 +4380,7 @@ void btrfs_dirty_inode(struct inode *inode) return; trans = btrfs_join_transaction(root, 1); + BUG_ON(IS_ERR(trans)); btrfs_set_trans_block_group(trans, inode); ret = btrfs_update_inode(trans, root, inode); @@ -5176,6 +5185,8 @@ again: em = NULL; btrfs_release_path(root, path); trans = btrfs_join_transaction(root, 1); + if (IS_ERR(trans)) + return ERR_CAST(trans); goto again; } map = kmap(page); @@ -5280,8 +5291,8 @@ static struct extent_map *btrfs_new_extent_direct(struct inode *inode, btrfs_drop_extent_cache(inode, start, start + len - 1, 0); trans = btrfs_join_transaction(root, 0); - if (!trans) - return ERR_PTR(-ENOMEM); + if (IS_ERR(trans)) + return ERR_CAST(trans); trans->block_rsv = &root->fs_info->delalloc_block_rsv; @@ -5505,7 +5516,7 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, * while we look for nocow cross refs */ trans = btrfs_join_transaction(root, 0); - if (!trans) + if (IS_ERR(trans)) goto must_cow; if (can_nocow_odirect(trans, inode, start, len) == 1) { @@ -5640,7 +5651,7 @@ again: BUG_ON(!ordered); trans = btrfs_join_transaction(root, 1); - if (!trans) { + if (IS_ERR(trans)) { err = -ENOMEM; goto out; } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a506a22b522..02d224e8c83 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -203,7 +203,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) trans = btrfs_join_transaction(root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); ret = btrfs_update_inode(trans, root, inode); BUG_ON(ret); @@ -907,6 +907,10 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, if (new_size > old_size) { trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out_unlock; + } ret = btrfs_grow_device(trans, device, new_size); btrfs_commit_transaction(trans, root); } else { @@ -1898,7 +1902,10 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, memcpy(&new_key, &key, sizeof(new_key)); new_key.objectid = inode->i_ino; - new_key.offset = key.offset + destoff - off; + if (off <= key.offset) + new_key.offset = key.offset + destoff - off; + else + new_key.offset = destoff; trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { @@ -2082,7 +2089,7 @@ static long btrfs_ioctl_trans_start(struct file *file) ret = -ENOMEM; trans = btrfs_start_ioctl_transaction(root, 0); - if (!trans) + if (IS_ERR(trans)) goto out_drop; file->private_data = trans; @@ -2138,9 +2145,9 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp) path->leave_spinning = 1; trans = btrfs_start_transaction(root, 1); - if (!trans) { + if (IS_ERR(trans)) { btrfs_free_path(path); - return -ENOMEM; + return PTR_ERR(trans); } dir_id = btrfs_super_root_dir(&root->fs_info->super_copy); @@ -2334,6 +2341,8 @@ static noinline long btrfs_ioctl_start_sync(struct file *file, void __user *argp u64 transid; trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) + return PTR_ERR(trans); transid = trans->transid; btrfs_commit_transaction_async(trans, root, 0); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 2b61e1ddcd9..083a5547737 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -141,7 +141,7 @@ static inline struct rb_node *tree_search(struct btrfs_ordered_inode_tree *tree, u64 file_offset) { struct rb_root *root = &tree->tree; - struct rb_node *prev; + struct rb_node *prev = NULL; struct rb_node *ret; struct btrfs_ordered_extent *entry; diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index 0d126be22b6..fb2605d998e 100644 --- a/fs/btrfs/print-tree.c +++ b/fs/btrfs/print-tree.c @@ -260,6 +260,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) #else BUG(); #endif + break; case BTRFS_BLOCK_GROUP_ITEM_KEY: bi = btrfs_item_ptr(l, i, struct btrfs_block_group_item); diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 045c9c2b2d7..1f5556acb53 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2028,6 +2028,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, while (1) { trans = btrfs_start_transaction(root, 0); + BUG_ON(IS_ERR(trans)); trans->block_rsv = rc->block_rsv; ret = btrfs_block_rsv_check(trans, root, rc->block_rsv, @@ -2147,6 +2148,12 @@ again: } trans = btrfs_join_transaction(rc->extent_root, 1); + if (IS_ERR(trans)) { + if (!err) + btrfs_block_rsv_release(rc->extent_root, + rc->block_rsv, num_bytes); + return PTR_ERR(trans); + } if (!err) { if (num_bytes != rc->merging_rsv_size) { @@ -3222,6 +3229,7 @@ truncate: trans = btrfs_join_transaction(root, 0); if (IS_ERR(trans)) { btrfs_free_path(path); + ret = PTR_ERR(trans); goto out; } @@ -3628,6 +3636,7 @@ int prepare_to_relocate(struct reloc_control *rc) set_reloc_control(rc); trans = btrfs_join_transaction(rc->extent_root, 1); + BUG_ON(IS_ERR(trans)); btrfs_commit_transaction(trans, rc->extent_root); return 0; } @@ -3657,6 +3666,7 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) while (1) { trans = btrfs_start_transaction(rc->extent_root, 0); + BUG_ON(IS_ERR(trans)); if (update_backref_cache(trans, &rc->backref_cache)) { btrfs_end_transaction(trans, rc->extent_root); @@ -3804,7 +3814,10 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) /* get rid of pinned extents */ trans = btrfs_join_transaction(rc->extent_root, 1); - btrfs_commit_transaction(trans, rc->extent_root); + if (IS_ERR(trans)) + err = PTR_ERR(trans); + else + btrfs_commit_transaction(trans, rc->extent_root); out_free: btrfs_free_block_rsv(rc->extent_root, rc->block_rsv); btrfs_free_path(path); @@ -4022,6 +4035,7 @@ static noinline_for_stack int mark_garbage_root(struct btrfs_root *root) int ret; trans = btrfs_start_transaction(root->fs_info->tree_root, 0); + BUG_ON(IS_ERR(trans)); memset(&root->root_item.drop_progress, 0, sizeof(root->root_item.drop_progress)); @@ -4125,6 +4139,11 @@ int btrfs_recover_relocation(struct btrfs_root *root) set_reloc_control(rc); trans = btrfs_join_transaction(rc->extent_root, 1); + if (IS_ERR(trans)) { + unset_reloc_control(rc); + err = PTR_ERR(trans); + goto out_free; + } rc->merge_reloc_tree = 1; @@ -4154,9 +4173,13 @@ int btrfs_recover_relocation(struct btrfs_root *root) unset_reloc_control(rc); trans = btrfs_join_transaction(rc->extent_root, 1); - btrfs_commit_transaction(trans, rc->extent_root); -out: + if (IS_ERR(trans)) + err = PTR_ERR(trans); + else + btrfs_commit_transaction(trans, rc->extent_root); +out_free: kfree(rc); +out: while (!list_empty(&reloc_roots)) { reloc_root = list_entry(reloc_roots.next, struct btrfs_root, root_list); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index b2130c46fdb..a004008f7d2 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -383,7 +383,7 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, struct btrfs_fs_devices **fs_devices) { substring_t args[MAX_OPT_ARGS]; - char *opts, *p; + char *opts, *orig, *p; int error = 0; int intarg; @@ -397,6 +397,7 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, opts = kstrdup(options, GFP_KERNEL); if (!opts) return -ENOMEM; + orig = opts; while ((p = strsep(&opts, ",")) != NULL) { int token; @@ -432,7 +433,7 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, } out_free_opts: - kfree(opts); + kfree(orig); out: /* * If no subvolume name is specified we use the default one. Allocate @@ -623,6 +624,8 @@ int btrfs_sync_fs(struct super_block *sb, int wait) btrfs_wait_ordered_extents(root, 0, 0); trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) + return PTR_ERR(trans); ret = btrfs_commit_transaction(trans, root); return ret; } @@ -761,6 +764,8 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, } btrfs_close_devices(fs_devices); + kfree(fs_info); + kfree(tree_root); } else { char b[BDEVNAME_SIZE]; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index bae5c7b8bbe..3d73c8d93bb 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1161,6 +1161,11 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans, INIT_DELAYED_WORK(&ac->work, do_async_commit); ac->root = root; ac->newtrans = btrfs_join_transaction(root, 0); + if (IS_ERR(ac->newtrans)) { + int err = PTR_ERR(ac->newtrans); + kfree(ac); + return err; + } /* take transaction reference */ mutex_lock(&root->fs_info->trans_mutex); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 054744ac571..a4bbb854dfd 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -338,6 +338,12 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans, } dst_copy = kmalloc(item_size, GFP_NOFS); src_copy = kmalloc(item_size, GFP_NOFS); + if (!dst_copy || !src_copy) { + btrfs_release_path(root, path); + kfree(dst_copy); + kfree(src_copy); + return -ENOMEM; + } read_extent_buffer(eb, src_copy, src_ptr, item_size); @@ -665,6 +671,9 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans, btrfs_dir_item_key_to_cpu(leaf, di, &location); name_len = btrfs_dir_name_len(leaf, di); name = kmalloc(name_len, GFP_NOFS); + if (!name) + return -ENOMEM; + read_extent_buffer(leaf, name, (unsigned long)(di + 1), name_len); btrfs_release_path(root, path); @@ -744,6 +753,9 @@ static noinline int backref_in_log(struct btrfs_root *log, int match = 0; path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + ret = btrfs_search_slot(NULL, log, key, path, 0, 0); if (ret != 0) goto out; @@ -967,6 +979,8 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans, key.offset = (u64)-1; path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; while (1) { ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); @@ -1178,6 +1192,9 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, name_len = btrfs_dir_name_len(eb, di); name = kmalloc(name_len, GFP_NOFS); + if (!name) + return -ENOMEM; + log_type = btrfs_dir_type(eb, di); read_extent_buffer(eb, name, (unsigned long)(di + 1), name_len); @@ -1692,6 +1709,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, root_owner = btrfs_header_owner(parent); next = btrfs_find_create_tree_block(root, bytenr, blocksize); + if (!next) + return -ENOMEM; if (*level == 1) { wc->process_func(root, next, wc, ptr_gen); @@ -2032,6 +2051,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, wait_log_commit(trans, log_root_tree, log_root_tree->log_transid); mutex_unlock(&log_root_tree->log_mutex); + ret = 0; goto out; } atomic_set(&log_root_tree->log_commit[index2], 1); @@ -2096,7 +2116,7 @@ out: smp_mb(); if (waitqueue_active(&root->log_commit_wait[index1])) wake_up(&root->log_commit_wait[index1]); - return 0; + return ret; } static void free_log_tree(struct btrfs_trans_handle *trans, @@ -2194,6 +2214,9 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans, log = root->log_root; path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + di = btrfs_lookup_dir_item(trans, log, path, dir->i_ino, name, name_len, -1); if (IS_ERR(di)) { @@ -2594,6 +2617,9 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, ins_data = kmalloc(nr * sizeof(struct btrfs_key) + nr * sizeof(u32), GFP_NOFS); + if (!ins_data) + return -ENOMEM; + ins_sizes = (u32 *)ins_data; ins_keys = (struct btrfs_key *)(ins_data + nr * sizeof(u32)); @@ -2725,7 +2751,13 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, log = root->log_root; path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; dst_path = btrfs_alloc_path(); + if (!dst_path) { + btrfs_free_path(path); + return -ENOMEM; + } min_key.objectid = inode->i_ino; min_key.type = BTRFS_INODE_ITEM_KEY; @@ -3080,6 +3112,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) BUG_ON(!path); trans = btrfs_start_transaction(fs_info->tree_root, 0); + BUG_ON(IS_ERR(trans)); wc.trans = trans; wc.pin = 1; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d158530233b..2636a051e4b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1213,6 +1213,10 @@ static int btrfs_rm_dev_item(struct btrfs_root *root, return -ENOMEM; trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + btrfs_free_path(path); + return PTR_ERR(trans); + } key.objectid = BTRFS_DEV_ITEMS_OBJECTID; key.type = BTRFS_DEV_ITEM_KEY; key.offset = device->devid; @@ -1606,6 +1610,12 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) } trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + kfree(device); + ret = PTR_ERR(trans); + goto error; + } + lock_chunks(root); device->writeable = 1; @@ -1873,7 +1883,7 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, return ret; trans = btrfs_start_transaction(root, 0); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); lock_chunks(root); @@ -2047,7 +2057,7 @@ int btrfs_balance(struct btrfs_root *dev_root) BUG_ON(ret); trans = btrfs_start_transaction(dev_root, 0); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); ret = btrfs_grow_device(trans, device, old_size); BUG_ON(ret); @@ -2213,6 +2223,11 @@ again: /* Shrinking succeeded, else we would be at "done". */ trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto done; + } + lock_chunks(root); device->disk_total_bytes = new_size; diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 1e7636b145a..beeebf19423 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -372,6 +372,10 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, ppace = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL); + if (!ppace) { + cERROR(1, "DACL memory allocation error"); + return; + } for (i = 0; i < num_aces; ++i) { ppace[i] = (struct cifs_ace *) (acl_base + acl_size); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 46c66ed01af..904aa47e351 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -136,9 +136,6 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command) } } - if (ses->status == CifsExiting) - return -EIO; - /* * Give demultiplex thread up to 10 seconds to reconnect, should be * greater than cifs socket timeout which is 7 seconds @@ -156,7 +153,7 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command) * retrying until process is killed or server comes * back on-line */ - if (!tcon->retry || ses->status == CifsExiting) { + if (!tcon->retry) { cFYI(1, "gave up waiting on reconnect in smb_init"); return -EHOSTDOWN; } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 47d8ff62368..257b6d895e2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -337,8 +337,12 @@ cifs_echo_request(struct work_struct *work) struct TCP_Server_Info *server = container_of(work, struct TCP_Server_Info, echo.work); - /* no need to ping if we got a response recently */ - if (time_before(jiffies, server->lstrp + SMB_ECHO_INTERVAL - HZ)) + /* + * We cannot send an echo until the NEGOTIATE_PROTOCOL request is done. + * Also, no need to ping if we got a response recently + */ + if (server->tcpStatus != CifsGood || + time_before(jiffies, server->lstrp + SMB_ECHO_INTERVAL - HZ)) goto requeue_echo; rc = CIFSSMBEcho(server); @@ -578,12 +582,12 @@ incomplete_rcv: else if (reconnect == 1) continue; - length += 4; /* account for rfc1002 hdr */ + total_read += 4; /* account for rfc1002 hdr */ - - dump_smb(smb_buffer, length); - if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) { - cifs_dump_mem("Bad SMB: ", smb_buffer, 48); + dump_smb(smb_buffer, total_read); + if (checkSMB(smb_buffer, smb_buffer->Mid, total_read)) { + cifs_dump_mem("Bad SMB: ", smb_buffer, + total_read < 48 ? total_read : 48); continue; } @@ -633,11 +637,11 @@ incomplete_rcv: mid_entry->largeBuf = isLargeBuf; multi_t2_fnd: mid_entry->midState = MID_RESPONSE_RECEIVED; - list_del_init(&mid_entry->qhead); - mid_entry->callback(mid_entry); #ifdef CONFIG_CIFS_STATS2 mid_entry->when_received = jiffies; #endif + list_del_init(&mid_entry->qhead); + mid_entry->callback(mid_entry); break; } mid_entry = NULL; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 74c0a282d01..e964b1cd5dd 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1662,10 +1662,10 @@ static ssize_t cifs_iovec_write(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *poffset) { - size_t total_written = 0; - unsigned int written = 0; - unsigned long num_pages, npages; - size_t copied, len, cur_len, i; + unsigned int written; + unsigned long num_pages, npages, i; + size_t copied, len, cur_len; + ssize_t total_written = 0; struct kvec *to_send; struct page **pages; struct iov_iter it; @@ -1821,7 +1821,8 @@ cifs_iovec_read(struct file *file, const struct iovec *iov, { int rc; int xid; - unsigned int total_read, bytes_read = 0; + ssize_t total_read; + unsigned int bytes_read = 0; size_t len, cur_len; int iov_offset = 0; struct cifs_sb_info *cifs_sb; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index b8c5e2eb43d..fbc5aace54b 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -359,6 +359,10 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_hdr *in_buf, if (rc) return rc; + /* enable signing if server requires it */ + if (server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + in_buf->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + mutex_lock(&server->srv_mutex); mid = AllocMidQEntry(in_buf, server); if (mid == NULL) { diff --git a/fs/file_table.c b/fs/file_table.c index c3e89adf53c..eb36b6b17e2 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -125,13 +125,13 @@ struct file *get_empty_filp(void) goto fail; percpu_counter_inc(&nr_files); + f->f_cred = get_cred(cred); if (security_file_alloc(f)) goto fail_sec; INIT_LIST_HEAD(&f->f_u.fu_list); atomic_long_set(&f->f_count, 1); rwlock_init(&f->f_owner.lock); - f->f_cred = get_cred(cred); spin_lock_init(&f->f_lock); eventpoll_init_file(f); /* f->f_version: 0 */ diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c index 52a0bcaa7b6..b1991a2a08e 100644 --- a/fs/hfsplus/extents.c +++ b/fs/hfsplus/extents.c @@ -397,8 +397,8 @@ int hfsplus_file_extend(struct inode *inode) u32 start, len, goal; int res; - if (sbi->total_blocks - sbi->free_blocks + 8 > - sbi->alloc_file->i_size * 8) { + if (sbi->alloc_file->i_size * 8 < + sbi->total_blocks - sbi->free_blocks + 8) { /* extend alloc file */ printk(KERN_ERR "hfs: extend alloc file! " "(%llu,%u,%u)\n", diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c index d66ad113b1c..40ad88c12c6 100644 --- a/fs/hfsplus/part_tbl.c +++ b/fs/hfsplus/part_tbl.c @@ -134,7 +134,7 @@ int hfs_part_find(struct super_block *sb, res = hfsplus_submit_bio(sb->s_bdev, *part_start + HFS_PMAP_BLK, data, READ); if (res) - return res; + goto out; switch (be16_to_cpu(*((__be16 *)data))) { case HFS_OLD_PMAP_MAGIC: @@ -147,7 +147,7 @@ int hfs_part_find(struct super_block *sb, res = -ENOENT; break; } - +out: kfree(data); return res; } diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 9a3b4795f43..b49b55584c8 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -338,20 +338,22 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) struct inode *root, *inode; struct qstr str; struct nls_table *nls = NULL; - int err = -EINVAL; + int err; + err = -EINVAL; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) - return -ENOMEM; + goto out; sb->s_fs_info = sbi; mutex_init(&sbi->alloc_mutex); mutex_init(&sbi->vh_mutex); hfsplus_fill_defaults(sbi); + + err = -EINVAL; if (!hfsplus_parse_options(data, sbi)) { printk(KERN_ERR "hfs: unable to parse mount options\n"); - err = -EINVAL; - goto cleanup; + goto out_unload_nls; } /* temporarily use utf8 to correctly find the hidden dir below */ @@ -359,16 +361,14 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) sbi->nls = load_nls("utf8"); if (!sbi->nls) { printk(KERN_ERR "hfs: unable to load nls for utf8\n"); - err = -EINVAL; - goto cleanup; + goto out_unload_nls; } /* Grab the volume header */ if (hfsplus_read_wrapper(sb)) { if (!silent) printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n"); - err = -EINVAL; - goto cleanup; + goto out_unload_nls; } vhdr = sbi->s_vhdr; @@ -377,7 +377,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION || be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) { printk(KERN_ERR "hfs: wrong filesystem version\n"); - goto cleanup; + goto out_free_vhdr; } sbi->total_blocks = be32_to_cpu(vhdr->total_blocks); sbi->free_blocks = be32_to_cpu(vhdr->free_blocks); @@ -421,19 +421,19 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); if (!sbi->ext_tree) { printk(KERN_ERR "hfs: failed to load extents file\n"); - goto cleanup; + goto out_free_vhdr; } sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID); if (!sbi->cat_tree) { printk(KERN_ERR "hfs: failed to load catalog file\n"); - goto cleanup; + goto out_close_ext_tree; } inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID); if (IS_ERR(inode)) { printk(KERN_ERR "hfs: failed to load allocation file\n"); err = PTR_ERR(inode); - goto cleanup; + goto out_close_cat_tree; } sbi->alloc_file = inode; @@ -442,14 +442,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) if (IS_ERR(root)) { printk(KERN_ERR "hfs: failed to load root directory\n"); err = PTR_ERR(root); - goto cleanup; - } - sb->s_d_op = &hfsplus_dentry_operations; - sb->s_root = d_alloc_root(root); - if (!sb->s_root) { - iput(root); - err = -ENOMEM; - goto cleanup; + goto out_put_alloc_file; } str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; @@ -459,46 +452,69 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { hfs_find_exit(&fd); if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) - goto cleanup; + goto out_put_root; inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id)); if (IS_ERR(inode)) { err = PTR_ERR(inode); - goto cleanup; + goto out_put_root; } sbi->hidden_dir = inode; } else hfs_find_exit(&fd); - if (sb->s_flags & MS_RDONLY) - goto out; + if (!(sb->s_flags & MS_RDONLY)) { + /* + * H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused + * all three are registered with Apple for our use + */ + vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION); + vhdr->modify_date = hfsp_now2mt(); + be32_add_cpu(&vhdr->write_count, 1); + vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); + vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); + hfsplus_sync_fs(sb, 1); - /* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused - * all three are registered with Apple for our use - */ - vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION); - vhdr->modify_date = hfsp_now2mt(); - be32_add_cpu(&vhdr->write_count, 1); - vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); - vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); - hfsplus_sync_fs(sb, 1); - - if (!sbi->hidden_dir) { - mutex_lock(&sbi->vh_mutex); - sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); - hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, - &str, sbi->hidden_dir); - mutex_unlock(&sbi->vh_mutex); - - hfsplus_mark_inode_dirty(sbi->hidden_dir, HFSPLUS_I_CAT_DIRTY); + if (!sbi->hidden_dir) { + mutex_lock(&sbi->vh_mutex); + sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); + hfsplus_create_cat(sbi->hidden_dir->i_ino, root, &str, + sbi->hidden_dir); + mutex_unlock(&sbi->vh_mutex); + + hfsplus_mark_inode_dirty(sbi->hidden_dir, + HFSPLUS_I_CAT_DIRTY); + } } -out: + + sb->s_d_op = &hfsplus_dentry_operations; + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + err = -ENOMEM; + goto out_put_hidden_dir; + } + unload_nls(sbi->nls); sbi->nls = nls; return 0; -cleanup: - hfsplus_put_super(sb); +out_put_hidden_dir: + iput(sbi->hidden_dir); +out_put_root: + iput(sbi->alloc_file); +out_put_alloc_file: + iput(sbi->alloc_file); +out_close_cat_tree: + hfs_btree_close(sbi->cat_tree); +out_close_ext_tree: + hfs_btree_close(sbi->ext_tree); +out_free_vhdr: + kfree(sbi->s_vhdr); + kfree(sbi->s_backup_vhdr); +out_unload_nls: + unload_nls(sbi->nls); unload_nls(nls); + kfree(sbi); +out: return err; } diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c index 196231794f6..3031d81f5f0 100644 --- a/fs/hfsplus/wrapper.c +++ b/fs/hfsplus/wrapper.c @@ -167,7 +167,7 @@ reread: break; case cpu_to_be16(HFSP_WRAP_MAGIC): if (!hfsplus_read_mdb(sbi->s_vhdr, &wd)) - goto out; + goto out_free_backup_vhdr; wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT; part_start += wd.ablk_start + wd.embed_start * wd.ablk_size; part_size = wd.embed_count * wd.ablk_size; @@ -179,7 +179,7 @@ reread: * (should do this only for cdrom/loop though) */ if (hfs_part_find(sb, &part_start, &part_size)) - goto out; + goto out_free_backup_vhdr; goto reread; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a4694c61033..fe29aadb129 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1367,7 +1367,7 @@ extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); -extern void drm_handle_vblank(struct drm_device *dev, int crtc); +extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index acd7fade160..801be59f4f1 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -275,6 +275,7 @@ struct drm_pending_vblank_event; /** * drm_crtc_funcs - control CRTCs for a given device + * @reset: reset CRTC after state has been invalidate (e.g. resume) * @dpms: control display power levels * @save: save CRTC state * @resore: restore CRTC state @@ -302,6 +303,8 @@ struct drm_crtc_funcs { void (*save)(struct drm_crtc *crtc); /* suspend? */ /* Restore CRTC state */ void (*restore)(struct drm_crtc *crtc); /* resume? */ + /* Reset CRTC state */ + void (*reset)(struct drm_crtc *crtc); /* cursor controls */ int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv, @@ -379,6 +382,7 @@ struct drm_crtc { * @dpms: set power state (see drm_crtc_funcs above) * @save: save connector state * @restore: restore connector state + * @reset: reset connector after state has been invalidate (e.g. resume) * @mode_valid: is this mode valid on the given connector? * @mode_fixup: try to fixup proposed mode for this connector * @mode_set: set this mode @@ -396,6 +400,7 @@ struct drm_connector_funcs { void (*dpms)(struct drm_connector *connector, int mode); void (*save)(struct drm_connector *connector); void (*restore)(struct drm_connector *connector); + void (*reset)(struct drm_connector *connector); /* Check to see if anything is attached to the connector. * @force is set to false whilst polling, true when checking the @@ -413,6 +418,7 @@ struct drm_connector_funcs { }; struct drm_encoder_funcs { + void (*reset)(struct drm_encoder *encoder); void (*destroy)(struct drm_encoder *encoder); }; @@ -656,6 +662,7 @@ extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, struct drm_display_mode *mode); extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode); extern void drm_mode_config_init(struct drm_device *dev); +extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); extern void drm_mode_set_name(struct drm_display_mode *mode); extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index fe29ae328bd..5ff1194dc2e 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -28,7 +28,6 @@ {0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \ {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x4243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 2296d8b1931..b0ada6f37dd 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -1,5 +1,6 @@ header-y += byteorder/ header-y += can/ +header-y += caif/ header-y += dvb/ header-y += hdlc/ header-y += isdn/ diff --git a/include/linux/caif/Kbuild b/include/linux/caif/Kbuild new file mode 100644 index 00000000000..a9cf250689d --- /dev/null +++ b/include/linux/caif/Kbuild @@ -0,0 +1,2 @@ +header-y += caif_socket.h +header-y += if_caif.h diff --git a/include/linux/irq.h b/include/linux/irq.h index abde2527c69..80fcb53057b 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -74,7 +74,8 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQF_MODIFY_MASK \ (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ - IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL) + IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ + IRQ_PER_CPU) #ifdef CONFIG_IRQ_PER_CPU # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 0fa7a3a874c..b21d567692b 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -150,6 +150,7 @@ static inline int ip_mroute_opt(int opt) extern int ip_mroute_setsockopt(struct sock *, int, char __user *, unsigned int); extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg); +extern int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); extern int ip_mr_init(void); #else static inline diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 6091ab77f38..9d2deb200f5 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -136,6 +136,7 @@ extern int ip6_mroute_setsockopt(struct sock *, int, char __user *, unsigned int extern int ip6_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ip6_mr_input(struct sk_buff *skb); extern int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg); +extern int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); extern int ip6_mr_init(void); extern void ip6_mr_cleanup(void); #else diff --git a/include/linux/sched.h b/include/linux/sched.h index d747f948b34..0b40ee3f6d7 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2578,13 +2578,6 @@ static inline void inc_syscw(struct task_struct *tsk) #define TASK_SIZE_OF(tsk) TASK_SIZE #endif -/* - * Call the function if the target task is executing on a CPU right now: - */ -extern void task_oncpu_function_call(struct task_struct *p, - void (*func) (void *info), void *info); - - #ifdef CONFIG_MM_OWNER extern void mm_update_next_owner(struct mm_struct *mm); extern void mm_init_owner(struct mm_struct *mm, struct task_struct *p); diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 8a64b811a39..b4c7c1cbcf4 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -195,7 +195,8 @@ static inline int genlmsg_end(struct sk_buff *skb, void *hdr) */ static inline void genlmsg_cancel(struct sk_buff *skb, void *hdr) { - nlmsg_cancel(skb, hdr - GENL_HDRLEN - NLMSG_HDRLEN); + if (hdr) + nlmsg_cancel(skb, hdr - GENL_HDRLEN - NLMSG_HDRLEN); } /** diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 96ba5f7dcab..349cefedc9f 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -77,9 +77,6 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) if (e == NULL) return; - if (!(e->ctmask & (1 << event))) - return; - set_bit(event, &e->cache); } diff --git a/include/net/sock.h b/include/net/sock.h index d884d268c70..bc1cf7d88cc 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -753,6 +753,8 @@ struct proto { int level, int optname, char __user *optval, int __user *option); + int (*compat_ioctl)(struct sock *sk, + unsigned int cmd, unsigned long arg); #endif int (*sendmsg)(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len); diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 648d2335803..b76d4006e36 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -9,6 +9,7 @@ #define _SCSI_SCSI_H #include <linux/types.h> +#include <linux/scatterlist.h> struct scsi_cmnd; diff --git a/kernel/cred.c b/kernel/cred.c index 6a1aa004e37..3a9d6dd53a6 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -252,13 +252,13 @@ struct cred *cred_alloc_blank(void) #endif atomic_set(&new->usage, 1); +#ifdef CONFIG_DEBUG_CREDENTIALS + new->magic = CRED_MAGIC; +#endif if (security_cred_alloc_blank(new, GFP_KERNEL) < 0) goto error; -#ifdef CONFIG_DEBUG_CREDENTIALS - new->magic = CRED_MAGIC; -#endif return new; error: @@ -657,6 +657,8 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) validate_creds(old); *new = *old; + atomic_set(&new->usage, 1); + set_cred_subscribers(new, 0); get_uid(new->user); get_group_info(new->group_info); @@ -674,8 +676,6 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) if (security_prepare_creds(new, old, GFP_KERNEL) < 0) goto error; - atomic_set(&new->usage, 1); - set_cred_subscribers(new, 0); put_cred(old); validate_creds(new); return new; @@ -748,7 +748,11 @@ bool creds_are_invalid(const struct cred *cred) if (cred->magic != CRED_MAGIC) return true; #ifdef CONFIG_SECURITY_SELINUX - if (selinux_is_enabled()) { + /* + * cred->security == NULL if security_cred_alloc_blank() or + * security_prepare_creds() returned an error. + */ + if (selinux_is_enabled() && cred->security) { if ((unsigned long) cred->security < PAGE_SIZE) return true; if ((*(u32 *)cred->security & 0xffffff00) == diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 999835b6112..a353a4d6d00 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -38,6 +38,79 @@ #include <asm/irq_regs.h> +struct remote_function_call { + struct task_struct *p; + int (*func)(void *info); + void *info; + int ret; +}; + +static void remote_function(void *data) +{ + struct remote_function_call *tfc = data; + struct task_struct *p = tfc->p; + + if (p) { + tfc->ret = -EAGAIN; + if (task_cpu(p) != smp_processor_id() || !task_curr(p)) + return; + } + + tfc->ret = tfc->func(tfc->info); +} + +/** + * task_function_call - call a function on the cpu on which a task runs + * @p: the task to evaluate + * @func: the function to be called + * @info: the function call argument + * + * Calls the function @func when the task is currently running. This might + * be on the current CPU, which just calls the function directly + * + * returns: @func return value, or + * -ESRCH - when the process isn't running + * -EAGAIN - when the process moved away + */ +static int +task_function_call(struct task_struct *p, int (*func) (void *info), void *info) +{ + struct remote_function_call data = { + .p = p, + .func = func, + .info = info, + .ret = -ESRCH, /* No such (running) process */ + }; + + if (task_curr(p)) + smp_call_function_single(task_cpu(p), remote_function, &data, 1); + + return data.ret; +} + +/** + * cpu_function_call - call a function on the cpu + * @func: the function to be called + * @info: the function call argument + * + * Calls the function @func on the remote cpu. + * + * returns: @func return value or -ENXIO when the cpu is offline + */ +static int cpu_function_call(int cpu, int (*func) (void *info), void *info) +{ + struct remote_function_call data = { + .p = NULL, + .func = func, + .info = info, + .ret = -ENXIO, /* No such CPU */ + }; + + smp_call_function_single(cpu, remote_function, &data, 1); + + return data.ret; +} + enum event_type_t { EVENT_FLEXIBLE = 0x1, EVENT_PINNED = 0x2, @@ -254,7 +327,6 @@ static void perf_unpin_context(struct perf_event_context *ctx) raw_spin_lock_irqsave(&ctx->lock, flags); --ctx->pin_count; raw_spin_unlock_irqrestore(&ctx->lock, flags); - put_ctx(ctx); } /* @@ -618,35 +690,24 @@ __get_cpu_context(struct perf_event_context *ctx) * We disable the event on the hardware level first. After that we * remove it from the context list. */ -static void __perf_event_remove_from_context(void *info) +static int __perf_remove_from_context(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); - /* - * If this is a task context, we need to check whether it is - * the current task context of this cpu. If not it has been - * scheduled out before the smp call arrived. - */ - if (ctx->task && cpuctx->task_ctx != ctx) - return; - raw_spin_lock(&ctx->lock); - event_sched_out(event, cpuctx, ctx); - list_del_event(event, ctx); - raw_spin_unlock(&ctx->lock); + + return 0; } /* * Remove the event from a task's (or a CPU's) list of events. * - * Must be called with ctx->mutex held. - * * CPU events are removed with a smp call. For task events we only * call when the task is on a CPU. * @@ -657,49 +718,48 @@ static void __perf_event_remove_from_context(void *info) * When called from perf_event_exit_task, it's OK because the * context has been detached from its task. */ -static void perf_event_remove_from_context(struct perf_event *event) +static void perf_remove_from_context(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; struct task_struct *task = ctx->task; + lockdep_assert_held(&ctx->mutex); + if (!task) { /* * Per cpu events are removed via an smp call and * the removal is always successful. */ - smp_call_function_single(event->cpu, - __perf_event_remove_from_context, - event, 1); + cpu_function_call(event->cpu, __perf_remove_from_context, event); return; } retry: - task_oncpu_function_call(task, __perf_event_remove_from_context, - event); + if (!task_function_call(task, __perf_remove_from_context, event)) + return; raw_spin_lock_irq(&ctx->lock); /* - * If the context is active we need to retry the smp call. + * If we failed to find a running task, but find the context active now + * that we've acquired the ctx->lock, retry. */ - if (ctx->nr_active && !list_empty(&event->group_entry)) { + if (ctx->is_active) { raw_spin_unlock_irq(&ctx->lock); goto retry; } /* - * The lock prevents that this context is scheduled in so we - * can remove the event safely, if the call above did not - * succeed. + * Since the task isn't running, its safe to remove the event, us + * holding the ctx->lock ensures the task won't get scheduled in. */ - if (!list_empty(&event->group_entry)) - list_del_event(event, ctx); + list_del_event(event, ctx); raw_spin_unlock_irq(&ctx->lock); } /* * Cross CPU call to disable a performance event */ -static void __perf_event_disable(void *info) +static int __perf_event_disable(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -708,9 +768,12 @@ static void __perf_event_disable(void *info) /* * If this is a per-task event, need to check whether this * event's task is the current task on this cpu. + * + * Can trigger due to concurrent perf_event_context_sched_out() + * flipping contexts around. */ if (ctx->task && cpuctx->task_ctx != ctx) - return; + return -EINVAL; raw_spin_lock(&ctx->lock); @@ -729,6 +792,8 @@ static void __perf_event_disable(void *info) } raw_spin_unlock(&ctx->lock); + + return 0; } /* @@ -753,13 +818,13 @@ void perf_event_disable(struct perf_event *event) /* * Disable the event on the cpu that it's on */ - smp_call_function_single(event->cpu, __perf_event_disable, - event, 1); + cpu_function_call(event->cpu, __perf_event_disable, event); return; } retry: - task_oncpu_function_call(task, __perf_event_disable, event); + if (!task_function_call(task, __perf_event_disable, event)) + return; raw_spin_lock_irq(&ctx->lock); /* @@ -767,6 +832,11 @@ retry: */ if (event->state == PERF_EVENT_STATE_ACTIVE) { raw_spin_unlock_irq(&ctx->lock); + /* + * Reload the task pointer, it might have been changed by + * a concurrent perf_event_context_sched_out(). + */ + task = ctx->task; goto retry; } @@ -778,7 +848,6 @@ retry: update_group_times(event); event->state = PERF_EVENT_STATE_OFF; } - raw_spin_unlock_irq(&ctx->lock); } @@ -928,12 +997,14 @@ static void add_event_to_ctx(struct perf_event *event, event->tstamp_stopped = tstamp; } +static void perf_event_context_sched_in(struct perf_event_context *ctx); + /* * Cross CPU call to install and enable a performance event * * Must be called with ctx->mutex held */ -static void __perf_install_in_context(void *info) +static int __perf_install_in_context(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -942,17 +1013,12 @@ static void __perf_install_in_context(void *info) int err; /* - * If this is a task context, we need to check whether it is - * the current task context of this cpu. If not it has been - * scheduled out before the smp call arrived. - * Or possibly this is the right context but it isn't - * on this cpu because it had no events. + * In case we're installing a new context to an already running task, + * could also happen before perf_event_task_sched_in() on architectures + * which do context switches with IRQs enabled. */ - if (ctx->task && cpuctx->task_ctx != ctx) { - if (cpuctx->task_ctx || ctx->task != current) - return; - cpuctx->task_ctx = ctx; - } + if (ctx->task && !cpuctx->task_ctx) + perf_event_context_sched_in(ctx); raw_spin_lock(&ctx->lock); ctx->is_active = 1; @@ -997,6 +1063,8 @@ static void __perf_install_in_context(void *info) unlock: raw_spin_unlock(&ctx->lock); + + return 0; } /* @@ -1008,8 +1076,6 @@ unlock: * If the event is attached to a task which is on a CPU we use a smp * call to enable it in the task context. The task might have been * scheduled away, but we check this in the smp call again. - * - * Must be called with ctx->mutex held. */ static void perf_install_in_context(struct perf_event_context *ctx, @@ -1018,6 +1084,8 @@ perf_install_in_context(struct perf_event_context *ctx, { struct task_struct *task = ctx->task; + lockdep_assert_held(&ctx->mutex); + event->ctx = ctx; if (!task) { @@ -1025,31 +1093,29 @@ perf_install_in_context(struct perf_event_context *ctx, * Per cpu events are installed via an smp call and * the install is always successful. */ - smp_call_function_single(cpu, __perf_install_in_context, - event, 1); + cpu_function_call(cpu, __perf_install_in_context, event); return; } retry: - task_oncpu_function_call(task, __perf_install_in_context, - event); + if (!task_function_call(task, __perf_install_in_context, event)) + return; raw_spin_lock_irq(&ctx->lock); /* - * we need to retry the smp call. + * If we failed to find a running task, but find the context active now + * that we've acquired the ctx->lock, retry. */ - if (ctx->is_active && list_empty(&event->group_entry)) { + if (ctx->is_active) { raw_spin_unlock_irq(&ctx->lock); goto retry; } /* - * The lock prevents that this context is scheduled in so we - * can add the event safely, if it the call above did not - * succeed. + * Since the task isn't running, its safe to add the event, us holding + * the ctx->lock ensures the task won't get scheduled in. */ - if (list_empty(&event->group_entry)) - add_event_to_ctx(event, ctx); + add_event_to_ctx(event, ctx); raw_spin_unlock_irq(&ctx->lock); } @@ -1078,7 +1144,7 @@ static void __perf_event_mark_enabled(struct perf_event *event, /* * Cross CPU call to enable a performance event */ -static void __perf_event_enable(void *info) +static int __perf_event_enable(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -1086,18 +1152,10 @@ static void __perf_event_enable(void *info) struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); int err; - /* - * If this is a per-task event, need to check whether this - * event's task is the current task on this cpu. - */ - if (ctx->task && cpuctx->task_ctx != ctx) { - if (cpuctx->task_ctx || ctx->task != current) - return; - cpuctx->task_ctx = ctx; - } + if (WARN_ON_ONCE(!ctx->is_active)) + return -EINVAL; raw_spin_lock(&ctx->lock); - ctx->is_active = 1; update_context_time(ctx); if (event->state >= PERF_EVENT_STATE_INACTIVE) @@ -1138,6 +1196,8 @@ static void __perf_event_enable(void *info) unlock: raw_spin_unlock(&ctx->lock); + + return 0; } /* @@ -1158,8 +1218,7 @@ void perf_event_enable(struct perf_event *event) /* * Enable the event on the cpu that it's on */ - smp_call_function_single(event->cpu, __perf_event_enable, - event, 1); + cpu_function_call(event->cpu, __perf_event_enable, event); return; } @@ -1178,8 +1237,15 @@ void perf_event_enable(struct perf_event *event) event->state = PERF_EVENT_STATE_OFF; retry: + if (!ctx->is_active) { + __perf_event_mark_enabled(event, ctx); + goto out; + } + raw_spin_unlock_irq(&ctx->lock); - task_oncpu_function_call(task, __perf_event_enable, event); + + if (!task_function_call(task, __perf_event_enable, event)) + return; raw_spin_lock_irq(&ctx->lock); @@ -1187,15 +1253,14 @@ retry: * If the context is active and the event is still off, * we need to retry the cross-call. */ - if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) + if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) { + /* + * task could have been flipped by a concurrent + * perf_event_context_sched_out() + */ + task = ctx->task; goto retry; - - /* - * Since we have the lock this context can't be scheduled - * in, so we can change the state safely. - */ - if (event->state == PERF_EVENT_STATE_OFF) - __perf_event_mark_enabled(event, ctx); + } out: raw_spin_unlock_irq(&ctx->lock); @@ -1339,8 +1404,8 @@ static void perf_event_sync_stat(struct perf_event_context *ctx, } } -void perf_event_context_sched_out(struct task_struct *task, int ctxn, - struct task_struct *next) +static void perf_event_context_sched_out(struct task_struct *task, int ctxn, + struct task_struct *next) { struct perf_event_context *ctx = task->perf_event_ctxp[ctxn]; struct perf_event_context *next_ctx; @@ -1533,7 +1598,7 @@ static void task_ctx_sched_in(struct perf_event_context *ctx, { struct perf_cpu_context *cpuctx; - cpuctx = __get_cpu_context(ctx); + cpuctx = __get_cpu_context(ctx); if (cpuctx->task_ctx == ctx) return; @@ -1541,7 +1606,7 @@ static void task_ctx_sched_in(struct perf_event_context *ctx, cpuctx->task_ctx = ctx; } -void perf_event_context_sched_in(struct perf_event_context *ctx) +static void perf_event_context_sched_in(struct perf_event_context *ctx) { struct perf_cpu_context *cpuctx; @@ -1627,7 +1692,7 @@ static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count) * Reduce accuracy by one bit such that @a and @b converge * to a similar magnitude. */ -#define REDUCE_FLS(a, b) \ +#define REDUCE_FLS(a, b) \ do { \ if (a##_fls > b##_fls) { \ a >>= 1; \ @@ -2213,6 +2278,9 @@ errout: } +/* + * Returns a matching context with refcount and pincount. + */ static struct perf_event_context * find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) { @@ -2237,6 +2305,7 @@ find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); ctx = &cpuctx->ctx; get_ctx(ctx); + ++ctx->pin_count; return ctx; } @@ -2250,6 +2319,7 @@ retry: ctx = perf_lock_task_context(task, ctxn, &flags); if (ctx) { unclone_ctx(ctx); + ++ctx->pin_count; raw_spin_unlock_irqrestore(&ctx->lock, flags); } @@ -2271,8 +2341,10 @@ retry: err = -ESRCH; else if (task->perf_event_ctxp[ctxn]) err = -EAGAIN; - else + else { + ++ctx->pin_count; rcu_assign_pointer(task->perf_event_ctxp[ctxn], ctx); + } mutex_unlock(&task->perf_event_mutex); if (unlikely(err)) { @@ -5950,10 +6022,10 @@ SYSCALL_DEFINE5(perf_event_open, struct perf_event_context *gctx = group_leader->ctx; mutex_lock(&gctx->mutex); - perf_event_remove_from_context(group_leader); + perf_remove_from_context(group_leader); list_for_each_entry(sibling, &group_leader->sibling_list, group_entry) { - perf_event_remove_from_context(sibling); + perf_remove_from_context(sibling); put_ctx(gctx); } mutex_unlock(&gctx->mutex); @@ -5976,6 +6048,7 @@ SYSCALL_DEFINE5(perf_event_open, perf_install_in_context(ctx, event, cpu); ++ctx->generation; + perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); event->owner = current; @@ -6001,6 +6074,7 @@ SYSCALL_DEFINE5(perf_event_open, return event_fd; err_context: + perf_unpin_context(ctx); put_ctx(ctx); err_alloc: free_event(event); @@ -6051,6 +6125,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, mutex_lock(&ctx->mutex); perf_install_in_context(ctx, event, cpu); ++ctx->generation; + perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); return event; @@ -6104,7 +6179,7 @@ __perf_event_exit_task(struct perf_event *child_event, { struct perf_event *parent_event; - perf_event_remove_from_context(child_event); + perf_remove_from_context(child_event); parent_event = child_event->parent; /* @@ -6411,7 +6486,7 @@ inherit_task_group(struct perf_event *event, struct task_struct *parent, return 0; } - child_ctx = child->perf_event_ctxp[ctxn]; + child_ctx = child->perf_event_ctxp[ctxn]; if (!child_ctx) { /* * This is executed from the parent task context, so @@ -6526,6 +6601,7 @@ int perf_event_init_context(struct task_struct *child, int ctxn) mutex_unlock(&parent_ctx->mutex); perf_unpin_context(parent_ctx); + put_ctx(parent_ctx); return ret; } @@ -6595,9 +6671,9 @@ static void __perf_event_exit_context(void *__info) perf_pmu_rotate_stop(ctx->pmu); list_for_each_entry_safe(event, tmp, &ctx->pinned_groups, group_entry) - __perf_event_remove_from_context(event); + __perf_remove_from_context(event); list_for_each_entry_safe(event, tmp, &ctx->flexible_groups, group_entry) - __perf_event_remove_from_context(event); + __perf_remove_from_context(event); } static void perf_event_exit_cpu_context(int cpu) diff --git a/kernel/sched.c b/kernel/sched.c index fbe86cb04b6..e142e92f38d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2265,27 +2265,6 @@ void kick_process(struct task_struct *p) EXPORT_SYMBOL_GPL(kick_process); #endif /* CONFIG_SMP */ -/** - * task_oncpu_function_call - call a function on the cpu on which a task runs - * @p: the task to evaluate - * @func: the function to be called - * @info: the function call argument - * - * Calls the function @func when the task is currently running. This might - * be on the current CPU, which just calls the function directly - */ -void task_oncpu_function_call(struct task_struct *p, - void (*func) (void *info), void *info) -{ - int cpu; - - preempt_disable(); - cpu = task_cpu(p); - if (task_curr(p)) - smp_call_function_single(cpu, func, info, 1); - preempt_enable(); -} - #ifdef CONFIG_SMP /* * ->cpus_allowed is protected by either TASK_WAKING or rq->lock held. @@ -2776,9 +2755,12 @@ static inline void prepare_task_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next) { + sched_info_switch(prev, next); + perf_event_task_sched_out(prev, next); fire_sched_out_preempt_notifiers(prev, next); prepare_lock_switch(rq, next); prepare_arch_switch(next); + trace_sched_switch(prev, next); } /** @@ -2911,7 +2893,7 @@ context_switch(struct rq *rq, struct task_struct *prev, struct mm_struct *mm, *oldmm; prepare_task_switch(rq, prev, next); - trace_sched_switch(prev, next); + mm = next->mm; oldmm = prev->active_mm; /* @@ -3989,9 +3971,6 @@ need_resched_nonpreemptible: rq->skip_clock_update = 0; if (likely(prev != next)) { - sched_info_switch(prev, next); - perf_event_task_sched_out(prev, next); - rq->nr_switches++; rq->curr = next; ++*switch_count; diff --git a/kernel/timer.c b/kernel/timer.c index 43ca9936f2d..d53ce66daea 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -969,10 +969,14 @@ EXPORT_SYMBOL(try_to_del_timer_sync); int del_timer_sync(struct timer_list *timer) { #ifdef CONFIG_LOCKDEP + unsigned long flags; + + raw_local_irq_save(flags); local_bh_disable(); lock_map_acquire(&timer->lockdep_map); lock_map_release(&timer->lockdep_map); - local_bh_enable(); + _local_bh_enable(); + raw_local_irq_restore(flags); #endif /* * don't use it in hardirq context, because it diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 2dec9bcde8b..8435b43b178 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -353,6 +353,43 @@ static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) kfree(data); } +/* Bitfield fetch function */ +struct bitfield_fetch_param { + struct fetch_param orig; + unsigned char hi_shift; + unsigned char low_shift; +}; + +#define DEFINE_FETCH_bitfield(type) \ +static __kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,\ + void *data, void *dest) \ +{ \ + struct bitfield_fetch_param *bprm = data; \ + type buf = 0; \ + call_fetch(&bprm->orig, regs, &buf); \ + if (buf) { \ + buf <<= bprm->hi_shift; \ + buf >>= bprm->low_shift; \ + } \ + *(type *)dest = buf; \ +} +DEFINE_BASIC_FETCH_FUNCS(bitfield) +#define fetch_bitfield_string NULL +#define fetch_bitfield_string_size NULL + +static __kprobes void +free_bitfield_fetch_param(struct bitfield_fetch_param *data) +{ + /* + * Don't check the bitfield itself, because this must be the + * last fetch function. + */ + if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) + free_deref_fetch_param(data->orig.data); + else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) + free_symbol_cache(data->orig.data); + kfree(data); +} /* Default (unsigned long) fetch type */ #define __DEFAULT_FETCH_TYPE(t) u##t #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) @@ -367,6 +404,7 @@ enum { FETCH_MTD_memory, FETCH_MTD_symbol, FETCH_MTD_deref, + FETCH_MTD_bitfield, FETCH_MTD_END, }; @@ -387,6 +425,7 @@ ASSIGN_FETCH_FUNC(retval, ftype), \ ASSIGN_FETCH_FUNC(memory, ftype), \ ASSIGN_FETCH_FUNC(symbol, ftype), \ ASSIGN_FETCH_FUNC(deref, ftype), \ +ASSIGN_FETCH_FUNC(bitfield, ftype), \ } \ } @@ -430,9 +469,33 @@ static const struct fetch_type *find_fetch_type(const char *type) if (!type) type = DEFAULT_FETCH_TYPE_STR; + /* Special case: bitfield */ + if (*type == 'b') { + unsigned long bs; + type = strchr(type, '/'); + if (!type) + goto fail; + type++; + if (strict_strtoul(type, 0, &bs)) + goto fail; + switch (bs) { + case 8: + return find_fetch_type("u8"); + case 16: + return find_fetch_type("u16"); + case 32: + return find_fetch_type("u32"); + case 64: + return find_fetch_type("u64"); + default: + goto fail; + } + } + for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) if (strcmp(type, fetch_type_table[i].name) == 0) return &fetch_type_table[i]; +fail: return NULL; } @@ -586,7 +649,9 @@ error: static void free_probe_arg(struct probe_arg *arg) { - if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) + if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn)) + free_bitfield_fetch_param(arg->fetch.data); + else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) free_deref_fetch_param(arg->fetch.data); else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) free_symbol_cache(arg->fetch.data); @@ -767,16 +832,15 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t, } break; case '+': /* deref memory */ + arg++; /* Skip '+', because strict_strtol() rejects it. */ case '-': tmp = strchr(arg, '('); if (!tmp) break; *tmp = '\0'; - ret = strict_strtol(arg + 1, 0, &offset); + ret = strict_strtol(arg, 0, &offset); if (ret) break; - if (arg[0] == '-') - offset = -offset; arg = tmp + 1; tmp = strrchr(arg, ')'); if (tmp) { @@ -807,6 +871,41 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t, return ret; } +#define BYTES_TO_BITS(nb) ((BITS_PER_LONG * (nb)) / sizeof(long)) + +/* Bitfield type needs to be parsed into a fetch function */ +static int __parse_bitfield_probe_arg(const char *bf, + const struct fetch_type *t, + struct fetch_param *f) +{ + struct bitfield_fetch_param *bprm; + unsigned long bw, bo; + char *tail; + + if (*bf != 'b') + return 0; + + bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); + if (!bprm) + return -ENOMEM; + bprm->orig = *f; + f->fn = t->fetch[FETCH_MTD_bitfield]; + f->data = (void *)bprm; + + bw = simple_strtoul(bf + 1, &tail, 0); /* Use simple one */ + if (bw == 0 || *tail != '@') + return -EINVAL; + + bf = tail + 1; + bo = simple_strtoul(bf, &tail, 0); + if (tail == bf || *tail != '/') + return -EINVAL; + + bprm->hi_shift = BYTES_TO_BITS(t->size) - (bw + bo); + bprm->low_shift = bprm->hi_shift + bo; + return (BYTES_TO_BITS(t->size) < (bw + bo)) ? -EINVAL : 0; +} + /* String length checking wrapper */ static int parse_probe_arg(char *arg, struct trace_probe *tp, struct probe_arg *parg, int is_return) @@ -836,6 +935,8 @@ static int parse_probe_arg(char *arg, struct trace_probe *tp, parg->offset = tp->size; tp->size += parg->type->size; ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return); + if (ret >= 0 && t != NULL) + ret = __parse_bitfield_probe_arg(t, parg->type, &parg->fetch); if (ret >= 0) { parg->fetch_size.fn = get_fetch_size_function(parg->type, parg->fetch.fn); @@ -1130,7 +1231,7 @@ static int command_trace_probe(const char *buf) return ret; } -#define WRITE_BUFSIZE 128 +#define WRITE_BUFSIZE 4096 static ssize_t probes_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) diff --git a/kernel/watchdog.c b/kernel/watchdog.c index f37f974aa81..18bb15776c5 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -363,8 +363,14 @@ static int watchdog_nmi_enable(int cpu) goto out_save; } - printk(KERN_ERR "NMI watchdog disabled for cpu%i: unable to create perf event: %ld\n", - cpu, PTR_ERR(event)); + + /* vary the KERN level based on the returned errno */ + if (PTR_ERR(event) == -EOPNOTSUPP) + printk(KERN_INFO "NMI watchdog disabled (cpu%i): not supported (no LAPIC?)\n", cpu); + else if (PTR_ERR(event) == -ENOENT) + printk(KERN_WARNING "NMI watchdog disabled (cpu%i): hardware events not enabled\n", cpu); + else + printk(KERN_ERR "NMI watchdog disabled (cpu%i): unable to create perf event: %ld\n", cpu, PTR_ERR(event)); return PTR_ERR(event); /* success path */ diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index cd4c4231fa4..de1022cacaf 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -64,6 +64,7 @@ static void free_info(struct kref *ref) spin_unlock_bh(&bat_priv->vis_list_lock); kfree_skb(info->skb_packet); + kfree(info); } /* Compare two vis packets, used by the hashing algorithm */ @@ -268,10 +269,10 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) buff_pos += sprintf(buff + buff_pos, "%pM,", entry->addr); - for (i = 0; i < packet->entries; i++) + for (j = 0; j < packet->entries; j++) buff_pos += vis_data_read_entry( buff + buff_pos, - &entries[i], + &entries[j], entry->addr, entry->primary); @@ -444,7 +445,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, info); if (hash_added < 0) { /* did not work (for some reason) */ - kref_put(&old_info->refcount, free_info); + kref_put(&info->refcount, free_info); info = NULL; } @@ -815,7 +816,7 @@ static void send_vis_packets(struct work_struct *work) container_of(work, struct delayed_work, work); struct bat_priv *bat_priv = container_of(delayed_work, struct bat_priv, vis_work); - struct vis_info *info, *temp; + struct vis_info *info; spin_lock_bh(&bat_priv->vis_hash_lock); purge_vis_packets(bat_priv); @@ -825,8 +826,9 @@ static void send_vis_packets(struct work_struct *work) send_list_add(bat_priv, bat_priv->my_vis_info); } - list_for_each_entry_safe(info, temp, &bat_priv->vis_send_list, - send_list) { + while (!list_empty(&bat_priv->vis_send_list)) { + info = list_first_entry(&bat_priv->vis_send_list, + typeof(*info), send_list); kref_get(&info->refcount); spin_unlock_bh(&bat_priv->vis_hash_lock); diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 2872393b293..88485cc74dc 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -328,12 +328,12 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC); if (fdb) { memcpy(fdb->addr.addr, addr, ETH_ALEN); - hlist_add_head_rcu(&fdb->hlist, head); - fdb->dst = source; fdb->is_local = is_local; fdb->is_static = is_local; fdb->ageing_timer = jiffies; + + hlist_add_head_rcu(&fdb->hlist, head); } return fdb; } diff --git a/net/core/dev.c b/net/core/dev.c index 24ea2d71e7e..b6d0bf875a8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2563,7 +2563,8 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, map = rcu_dereference(rxqueue->rps_map); if (map) { - if (map->len == 1) { + if (map->len == 1 && + !rcu_dereference_raw(rxqueue->rps_flow_table)) { tcpu = map->cpus[0]; if (cpu_online(tcpu)) cpu = tcpu; @@ -3424,6 +3425,8 @@ static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb) __skb_pull(skb, skb_headlen(skb)); skb_reserve(skb, NET_IP_ALIGN - skb_headroom(skb)); skb->vlan_tci = 0; + skb->dev = napi->dev; + skb->skb_iif = 0; napi->skb = skb; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 750db57f3bb..2d65c6bb24c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1121,8 +1121,7 @@ static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[]) return -EOPNOTSUPP; if (af_ops->validate_link_af) { - err = af_ops->validate_link_af(dev, - tb[IFLA_AF_SPEC]); + err = af_ops->validate_link_af(dev, af); if (err < 0) return err; } @@ -1672,6 +1671,9 @@ replay: snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind); dest_net = rtnl_link_get_net(net, tb); + if (IS_ERR(dest_net)) + return PTR_ERR(dest_net); + dev = rtnl_create_link(net, dest_net, ifname, ops, tb); if (IS_ERR(dev)) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7cd1bc86d59..d883dcc78b6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -210,6 +210,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, shinfo = skb_shinfo(skb); memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); atomic_set(&shinfo->dataref, 1); + kmemcheck_annotate_variable(shinfo->destructor_arg); if (fclone) { struct sk_buff *child = skb + 1; diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 15dcc1a586b..0c282633791 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -265,13 +265,13 @@ static void ec_tx_done(struct sk_buff *skb, int result) static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { - struct sock *sk = sock->sk; struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name; struct net_device *dev; struct ec_addr addr; int err; unsigned char port, cb; #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE) + struct sock *sk = sock->sk; struct sk_buff *skb; struct ec_cb *eb; #endif @@ -488,10 +488,10 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, error_free_buf: vfree(userbuf); +error: #else err = -EPROTOTYPE; #endif - error: mutex_unlock(&econet_mutex); return err; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f2b61107df6..45b89d7bda5 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -880,6 +880,19 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } EXPORT_SYMBOL(inet_ioctl); +#ifdef CONFIG_COMPAT +int inet_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk = sock->sk; + int err = -ENOIOCTLCMD; + + if (sk->sk_prot->compat_ioctl) + err = sk->sk_prot->compat_ioctl(sk, cmd, arg); + + return err; +} +#endif + const struct proto_ops inet_stream_ops = { .family = PF_INET, .owner = THIS_MODULE, @@ -903,6 +916,7 @@ const struct proto_ops inet_stream_ops = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_sock_common_setsockopt, .compat_getsockopt = compat_sock_common_getsockopt, + .compat_ioctl = inet_compat_ioctl, #endif }; EXPORT_SYMBOL(inet_stream_ops); @@ -929,6 +943,7 @@ const struct proto_ops inet_dgram_ops = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_sock_common_setsockopt, .compat_getsockopt = compat_sock_common_getsockopt, + .compat_ioctl = inet_compat_ioctl, #endif }; EXPORT_SYMBOL(inet_dgram_ops); @@ -959,6 +974,7 @@ static const struct proto_ops inet_sockraw_ops = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_sock_common_setsockopt, .compat_getsockopt = compat_sock_common_getsockopt, + .compat_ioctl = inet_compat_ioctl, #endif }; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 3f3a9afd73e..8b65a12654e 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -60,6 +60,7 @@ #include <linux/notifier.h> #include <linux/if_arp.h> #include <linux/netfilter_ipv4.h> +#include <linux/compat.h> #include <net/ipip.h> #include <net/checksum.h> #include <net/netlink.h> @@ -1434,6 +1435,81 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) } } +#ifdef CONFIG_COMPAT +struct compat_sioc_sg_req { + struct in_addr src; + struct in_addr grp; + compat_ulong_t pktcnt; + compat_ulong_t bytecnt; + compat_ulong_t wrong_if; +}; + +struct compat_sioc_vif_req { + vifi_t vifi; /* Which iface */ + compat_ulong_t icount; + compat_ulong_t ocount; + compat_ulong_t ibytes; + compat_ulong_t obytes; +}; + +int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) +{ + struct compat_sioc_sg_req sr; + struct compat_sioc_vif_req vr; + struct vif_device *vif; + struct mfc_cache *c; + struct net *net = sock_net(sk); + struct mr_table *mrt; + + mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); + if (mrt == NULL) + return -ENOENT; + + switch (cmd) { + case SIOCGETVIFCNT: + if (copy_from_user(&vr, arg, sizeof(vr))) + return -EFAULT; + if (vr.vifi >= mrt->maxvif) + return -EINVAL; + read_lock(&mrt_lock); + vif = &mrt->vif_table[vr.vifi]; + if (VIF_EXISTS(mrt, vr.vifi)) { + vr.icount = vif->pkt_in; + vr.ocount = vif->pkt_out; + vr.ibytes = vif->bytes_in; + vr.obytes = vif->bytes_out; + read_unlock(&mrt_lock); + + if (copy_to_user(arg, &vr, sizeof(vr))) + return -EFAULT; + return 0; + } + read_unlock(&mrt_lock); + return -EADDRNOTAVAIL; + case SIOCGETSGCNT: + if (copy_from_user(&sr, arg, sizeof(sr))) + return -EFAULT; + + rcu_read_lock(); + c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); + if (c) { + sr.pktcnt = c->mfc_un.res.pkt; + sr.bytecnt = c->mfc_un.res.bytes; + sr.wrong_if = c->mfc_un.res.wrong_if; + rcu_read_unlock(); + + if (copy_to_user(arg, &sr, sizeof(sr))) + return -EFAULT; + return 0; + } + rcu_read_unlock(); + return -EADDRNOTAVAIL; + default: + return -ENOIOCTLCMD; + } +} +#endif + static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) { diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c index b8ddcc480ed..a5e52a9f0a1 100644 --- a/net/ipv4/netfilter/arpt_mangle.c +++ b/net/ipv4/netfilter/arpt_mangle.c @@ -60,12 +60,12 @@ static int checkentry(const struct xt_tgchk_param *par) if (mangle->flags & ~ARPT_MANGLE_MASK || !(mangle->flags & ARPT_MANGLE_MASK)) - return false; + return -EINVAL; if (mangle->target != NF_DROP && mangle->target != NF_ACCEPT && mangle->target != XT_CONTINUE) - return false; - return true; + return -EINVAL; + return 0; } static struct xt_target arpt_mangle_reg __read_mostly = { diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index a3d5ab786e8..6390ba299b3 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -76,6 +76,7 @@ #include <linux/seq_file.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> +#include <linux/compat.h> static struct raw_hashinfo raw_v4_hashinfo = { .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), @@ -838,6 +839,23 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) } } +#ifdef CONFIG_COMPAT +static int compat_raw_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCOUTQ: + case SIOCINQ: + return -ENOIOCTLCMD; + default: +#ifdef CONFIG_IP_MROUTE + return ipmr_compat_ioctl(sk, cmd, compat_ptr(arg)); +#else + return -ENOIOCTLCMD; +#endif + } +} +#endif + struct proto raw_prot = { .name = "RAW", .owner = THIS_MODULE, @@ -860,6 +878,7 @@ struct proto raw_prot = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_raw_setsockopt, .compat_getsockopt = compat_raw_getsockopt, + .compat_ioctl = compat_raw_ioctl, #endif }; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 351dc4e8524..788a3e74834 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2707,6 +2707,11 @@ static struct dst_entry *ipv4_blackhole_dst_check(struct dst_entry *dst, u32 coo return NULL; } +static unsigned int ipv4_blackhole_default_mtu(const struct dst_entry *dst) +{ + return 0; +} + static void ipv4_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu) { } @@ -2716,6 +2721,7 @@ static struct dst_ops ipv4_dst_blackhole_ops = { .protocol = cpu_to_be16(ETH_P_IP), .destroy = ipv4_dst_destroy, .check = ipv4_blackhole_dst_check, + .default_mtu = ipv4_blackhole_default_mtu, .update_pmtu = ipv4_rt_blackhole_update_pmtu, }; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 9fab274019c..0e1d53bcf1e 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -34,6 +34,7 @@ #include <linux/seq_file.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/compat.h> #include <net/protocol.h> #include <linux/skbuff.h> #include <net/sock.h> @@ -1804,6 +1805,80 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg) } } +#ifdef CONFIG_COMPAT +struct compat_sioc_sg_req6 { + struct sockaddr_in6 src; + struct sockaddr_in6 grp; + compat_ulong_t pktcnt; + compat_ulong_t bytecnt; + compat_ulong_t wrong_if; +}; + +struct compat_sioc_mif_req6 { + mifi_t mifi; + compat_ulong_t icount; + compat_ulong_t ocount; + compat_ulong_t ibytes; + compat_ulong_t obytes; +}; + +int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) +{ + struct compat_sioc_sg_req6 sr; + struct compat_sioc_mif_req6 vr; + struct mif_device *vif; + struct mfc6_cache *c; + struct net *net = sock_net(sk); + struct mr6_table *mrt; + + mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (mrt == NULL) + return -ENOENT; + + switch (cmd) { + case SIOCGETMIFCNT_IN6: + if (copy_from_user(&vr, arg, sizeof(vr))) + return -EFAULT; + if (vr.mifi >= mrt->maxvif) + return -EINVAL; + read_lock(&mrt_lock); + vif = &mrt->vif6_table[vr.mifi]; + if (MIF_EXISTS(mrt, vr.mifi)) { + vr.icount = vif->pkt_in; + vr.ocount = vif->pkt_out; + vr.ibytes = vif->bytes_in; + vr.obytes = vif->bytes_out; + read_unlock(&mrt_lock); + + if (copy_to_user(arg, &vr, sizeof(vr))) + return -EFAULT; + return 0; + } + read_unlock(&mrt_lock); + return -EADDRNOTAVAIL; + case SIOCGETSGCNT_IN6: + if (copy_from_user(&sr, arg, sizeof(sr))) + return -EFAULT; + + read_lock(&mrt_lock); + c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr); + if (c) { + sr.pktcnt = c->mfc_un.res.pkt; + sr.bytecnt = c->mfc_un.res.bytes; + sr.wrong_if = c->mfc_un.res.wrong_if; + read_unlock(&mrt_lock); + + if (copy_to_user(arg, &sr, sizeof(sr))) + return -EFAULT; + return 0; + } + read_unlock(&mrt_lock); + return -EADDRNOTAVAIL; + default: + return -ENOIOCTLCMD; + } +} +#endif static inline int ip6mr_forward2_finish(struct sk_buff *skb) { diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 86c39526ba5..c5b0915d106 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -31,6 +31,7 @@ #include <linux/netfilter.h> #include <linux/netfilter_ipv6.h> #include <linux/skbuff.h> +#include <linux/compat.h> #include <asm/uaccess.h> #include <asm/ioctls.h> @@ -1157,6 +1158,23 @@ static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg) } } +#ifdef CONFIG_COMPAT +static int compat_rawv6_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCOUTQ: + case SIOCINQ: + return -ENOIOCTLCMD; + default: +#ifdef CONFIG_IPV6_MROUTE + return ip6mr_compat_ioctl(sk, cmd, compat_ptr(arg)); +#else + return -ENOIOCTLCMD; +#endif + } +} +#endif + static void rawv6_close(struct sock *sk, long timeout) { if (inet_sk(sk)->inet_num == IPPROTO_RAW) @@ -1215,6 +1233,7 @@ struct proto rawv6_prot = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_rawv6_setsockopt, .compat_getsockopt = compat_rawv6_getsockopt, + .compat_ioctl = compat_rawv6_ioctl, #endif }; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 1534508f6c6..1c29f95695d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -113,6 +113,11 @@ static struct dst_ops ip6_dst_ops_template = { .local_out = __ip6_local_out, }; +static unsigned int ip6_blackhole_default_mtu(const struct dst_entry *dst) +{ + return 0; +} + static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu) { } @@ -122,6 +127,7 @@ static struct dst_ops ip6_dst_blackhole_ops = { .protocol = cpu_to_be16(ETH_P_IPV6), .destroy = ip6_dst_destroy, .check = ip6_dst_check, + .default_mtu = ip6_blackhole_default_mtu, .update_pmtu = ip6_rt_blackhole_update_pmtu, }; @@ -194,7 +200,6 @@ static void ip6_dst_destroy(struct dst_entry *dst) in6_dev_put(idev); } if (peer) { - BUG_ON(!(rt->rt6i_flags & RTF_CACHE)); rt->rt6i_peer = NULL; inet_putpeer(peer); } @@ -204,9 +209,6 @@ void rt6_bind_peer(struct rt6_info *rt, int create) { struct inet_peer *peer; - if (WARN_ON(!(rt->rt6i_flags & RTF_CACHE))) - return; - peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create); if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL) inet_putpeer(peer); diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index fa1d8f4e005..7cb65ef79f9 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -15,6 +15,8 @@ #include <net/addrconf.h> #include <net/inet_frag.h> +static struct ctl_table empty[1]; + static ctl_table ipv6_table_template[] = { { .procname = "route", @@ -35,6 +37,12 @@ static ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "neigh", + .maxlen = 0, + .mode = 0555, + .child = empty, + }, { } }; @@ -152,7 +160,6 @@ static struct ctl_table_header *ip6_base; int ipv6_static_sysctl_register(void) { - static struct ctl_table empty[1]; ip6_base = register_sysctl_paths(net_ipv6_ctl_path, empty); if (ip6_base == NULL) return -ENOMEM; diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 5702de35e2b..63a1b915a7e 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -63,6 +63,9 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct) * this does not harm and it happens very rarely. */ unsigned long missed = e->missed; + if (!((events | missed) & e->ctmask)) + goto out_unlock; + ret = notify->fcn(events | missed, &item); if (unlikely(ret < 0 || missed)) { spin_lock_bh(&ct->lock); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 93297aaceb2..eead9db6f89 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -667,6 +667,7 @@ restart: if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_CT_NEW, ct) < 0) { + nf_conntrack_get(&ct->ct_general); cb->args[1] = (unsigned long)ct; goto out; } diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c index 88f7c3511c7..73c33a42f87 100644 --- a/net/netfilter/xt_iprange.c +++ b/net/netfilter/xt_iprange.c @@ -53,15 +53,13 @@ iprange_mt4(const struct sk_buff *skb, struct xt_action_param *par) } static inline int -iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b) +iprange_ipv6_lt(const struct in6_addr *a, const struct in6_addr *b) { unsigned int i; - int r; for (i = 0; i < 4; ++i) { - r = ntohl(a->s6_addr32[i]) - ntohl(b->s6_addr32[i]); - if (r != 0) - return r; + if (a->s6_addr32[i] != b->s6_addr32[i]) + return ntohl(a->s6_addr32[i]) < ntohl(b->s6_addr32[i]); } return 0; @@ -75,15 +73,15 @@ iprange_mt6(const struct sk_buff *skb, struct xt_action_param *par) bool m; if (info->flags & IPRANGE_SRC) { - m = iprange_ipv6_sub(&iph->saddr, &info->src_min.in6) < 0; - m |= iprange_ipv6_sub(&iph->saddr, &info->src_max.in6) > 0; + m = iprange_ipv6_lt(&iph->saddr, &info->src_min.in6); + m |= iprange_ipv6_lt(&info->src_max.in6, &iph->saddr); m ^= !!(info->flags & IPRANGE_SRC_INV); if (m) return false; } if (info->flags & IPRANGE_DST) { - m = iprange_ipv6_sub(&iph->daddr, &info->dst_min.in6) < 0; - m |= iprange_ipv6_sub(&iph->daddr, &info->dst_max.in6) > 0; + m = iprange_ipv6_lt(&iph->daddr, &info->dst_min.in6); + m |= iprange_ipv6_lt(&info->dst_max.in6, &iph->daddr); m ^= !!(info->flags & IPRANGE_DST_INV); if (m) return false; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e276eb46853..c8d69927068 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3198,7 +3198,11 @@ static void selinux_cred_free(struct cred *cred) { struct task_security_struct *tsec = cred->security; - BUG_ON((unsigned long) cred->security < PAGE_SIZE); + /* + * cred->security == NULL if security_cred_alloc_blank() or + * security_prepare_creds() returned an error. + */ + BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE); cred->security = (void *) 0x7UL; kfree(tsec); } diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 24d3013c023..7c1fc64cb53 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -50,7 +50,11 @@ static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97) if (v & SLFR_1RXV) readl(aaci->base + AACI_SL1RX); - writel(maincr, aaci->base + AACI_MAINCR); + if (maincr != readl(aaci->base + AACI_MAINCR)) { + writel(maincr, aaci->base + AACI_MAINCR); + readl(aaci->base + AACI_MAINCR); + udelay(1); + } } /* @@ -993,6 +997,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci) * disabling the channel doesn't clear the FIFO. */ writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR); + readl(aaci->base + AACI_MAINCR); + udelay(1); writel(aaci->maincr, aaci->base + AACI_MAINCR); /* diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index da03597fc89..5c426df8767 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -55,14 +55,13 @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/ioport.h> +#include <linux/io.h> #include <linux/moduleparam.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/rawmidi.h> #include <linux/delay.h> -#include <asm/io.h> - /* * globals */ diff --git a/sound/oss/Makefile b/sound/oss/Makefile index 96f14dcd0cd..90ffb99c6b1 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -87,7 +87,7 @@ ifeq ($(CONFIG_PSS_HAVE_BOOT),y) $(obj)/bin2hex pss_synth < $< > $@ else $(obj)/pss_boot.h: - ( \ + $(Q)( \ echo 'static unsigned char * pss_synth = NULL;'; \ echo 'static int pss_synthLen = 0;'; \ ) > $@ @@ -102,7 +102,7 @@ ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) $(obj)/hex2hex -i trix_boot < $< > $@ else $(obj)/trix_boot.h: - ( \ + $(Q)( \ echo 'static unsigned char * trix_boot = NULL;'; \ echo 'static int trix_boot_len = 0;'; \ ) > $@ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 9bb030a469c..fbe97d32140 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -85,6 +85,7 @@ struct conexant_spec { unsigned int auto_mic; int auto_mic_ext; /* autocfg.inputs[] index for ext mic */ unsigned int need_dac_fix; + hda_nid_t slave_dig_outs[2]; /* capture */ unsigned int num_adc_nids; @@ -127,6 +128,7 @@ struct conexant_spec { unsigned int ideapad:1; unsigned int thinkpad:1; unsigned int hp_laptop:1; + unsigned int asus:1; unsigned int ext_mic_present; unsigned int recording; @@ -352,6 +354,8 @@ static int conexant_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; } return 0; @@ -403,10 +407,16 @@ static int conexant_add_jack(struct hda_codec *codec, struct conexant_spec *spec; struct conexant_jack *jack; const char *name; - int err; + int i, err; spec = codec->spec; snd_array_init(&spec->jacks, sizeof(*jack), 32); + + jack = spec->jacks.list; + for (i = 0; i < spec->jacks.used; i++, jack++) + if (jack->nid == nid) + return 0 ; /* already present */ + jack = snd_array_new(&spec->jacks); name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ; @@ -2100,7 +2110,7 @@ static int patch_cxt5051(struct hda_codec *codec) static hda_nid_t cxt5066_dac_nids[1] = { 0x10 }; static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 }; static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 }; -#define CXT5066_SPDIF_OUT 0x21 +static hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 }; /* OLPC's microphone port is DC coupled for use with external sensors, * therefore we use a 50% mic bias in order to center the input signal with @@ -2312,6 +2322,19 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec) } } + +/* toggle input of built-in digital mic and mic jack appropriately */ +static void cxt5066_asus_automic(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_jack_detect(codec, 0x1b); + snd_printdd("CXT5066: external microphone present=%d\n", present); + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, + present ? 1 : 0); +} + + /* toggle input of built-in digital mic and mic jack appropriately */ static void cxt5066_hp_laptop_automic(struct hda_codec *codec) { @@ -2387,79 +2410,55 @@ static void cxt5066_hp_automute(struct hda_codec *codec) cxt5066_update_speaker(codec); } -/* unsolicited event for jack sensing */ -static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) +/* Dispatch the right mic autoswitch function */ +static void cxt5066_automic(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5066_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - /* ignore mic events in DC mode; we're always using the jack */ - if (!spec->dc_enable) - cxt5066_olpc_automic(codec); - break; - } -} -/* unsolicited event for jack sensing */ -static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res) -{ - snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26); - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5066_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: + if (spec->dell_vostro) cxt5066_vostro_automic(codec); - break; - } -} - -/* unsolicited event for jack sensing */ -static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res) -{ - snd_printdd("CXT5066_ideapad: unsol event %x (%x)\n", res, res >> 26); - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5066_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: + else if (spec->ideapad) cxt5066_ideapad_automic(codec); - break; - } + else if (spec->thinkpad) + cxt5066_thinkpad_automic(codec); + else if (spec->hp_laptop) + cxt5066_hp_laptop_automic(codec); + else if (spec->asus) + cxt5066_asus_automic(codec); } /* unsolicited event for jack sensing */ -static void cxt5066_hp_laptop_event(struct hda_codec *codec, unsigned int res) +static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) { - snd_printdd("CXT5066_hp_laptop: unsol event %x (%x)\n", res, res >> 26); + struct conexant_spec *spec = codec->spec; + snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5066_hp_automute(codec); break; case CONEXANT_MIC_EVENT: - cxt5066_hp_laptop_automic(codec); + /* ignore mic events in DC mode; we're always using the jack */ + if (!spec->dc_enable) + cxt5066_olpc_automic(codec); break; } } /* unsolicited event for jack sensing */ -static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res) +static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) { - snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26); + snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5066_hp_automute(codec); break; case CONEXANT_MIC_EVENT: - cxt5066_thinkpad_automic(codec); + cxt5066_automic(codec); break; } } + static const struct hda_input_mux cxt5066_analog_mic_boost = { .num_items = 5, .items = { @@ -2633,6 +2632,27 @@ static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) spec->recording = 0; } +static void conexant_check_dig_outs(struct hda_codec *codec, + hda_nid_t *dig_pins, + int num_pins) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t *nid_loc = &spec->multiout.dig_out_nid; + int i; + + for (i = 0; i < num_pins; i++, dig_pins++) { + unsigned int cfg = snd_hda_codec_get_pincfg(codec, *dig_pins); + if (get_defcfg_connect(cfg) == AC_JACK_PORT_NONE) + continue; + if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1) + continue; + if (spec->slave_dig_outs[0]) + nid_loc++; + else + nid_loc = spec->slave_dig_outs; + } +} + static struct hda_input_mux cxt5066_capture_source = { .num_items = 4, .items = { @@ -3039,20 +3059,11 @@ static struct hda_verb cxt5066_init_verbs_hp_laptop[] = { /* initialize jack-sensing, too */ static int cxt5066_init(struct hda_codec *codec) { - struct conexant_spec *spec = codec->spec; - snd_printdd("CXT5066: init\n"); conexant_init(codec); if (codec->patch_ops.unsol_event) { cxt5066_hp_automute(codec); - if (spec->dell_vostro) - cxt5066_vostro_automic(codec); - else if (spec->ideapad) - cxt5066_ideapad_automic(codec); - else if (spec->thinkpad) - cxt5066_thinkpad_automic(codec); - else if (spec->hp_laptop) - cxt5066_hp_laptop_automic(codec); + cxt5066_automic(codec); } cxt5066_set_mic_boost(codec); return 0; @@ -3080,6 +3091,7 @@ enum { CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */ CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ + CXT5066_ASUS, /* Asus K52JU, Lenovo G560 - Int mic at 0x1a and Ext mic at 0x1b */ CXT5066_HP_LAPTOP, /* HP Laptop */ CXT5066_MODELS }; @@ -3091,6 +3103,7 @@ static const char * const cxt5066_models[CXT5066_MODELS] = { [CXT5066_DELL_VOSTRO] = "dell-vostro", [CXT5066_IDEAPAD] = "ideapad", [CXT5066_THINKPAD] = "thinkpad", + [CXT5066_ASUS] = "asus", [CXT5066_HP_LAPTOP] = "hp-laptop", }; @@ -3102,7 +3115,9 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x13f3, "Asus A52J", CXT5066_HP_LAPTOP), + SND_PCI_QUIRK(0x1043, 0x13f3, "Asus A52J", CXT5066_ASUS), + SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS), + SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS), SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", 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), @@ -3111,7 +3126,9 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ {} }; @@ -3133,7 +3150,8 @@ static int patch_cxt5066(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids); spec->multiout.dac_nids = cxt5066_dac_nids; - spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT; + conexant_check_dig_outs(codec, cxt5066_digout_pin_nids, + ARRAY_SIZE(cxt5066_digout_pin_nids)); spec->num_adc_nids = 1; spec->adc_nids = cxt5066_adc_nids; spec->capsrc_nids = cxt5066_capsrc_nids; @@ -3167,17 +3185,20 @@ static int patch_cxt5066(struct hda_codec *codec) spec->num_init_verbs++; spec->dell_automute = 1; break; + case CXT5066_ASUS: case CXT5066_HP_LAPTOP: codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_hp_laptop_event; + codec->patch_ops.unsol_event = cxt5066_unsol_event; spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_hp_laptop; spec->num_init_verbs++; - spec->hp_laptop = 1; + spec->hp_laptop = board_config == CXT5066_HP_LAPTOP; + spec->asus = board_config == CXT5066_ASUS; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; spec->mixers[spec->num_mixers++] = cxt5066_mixers; /* no S/PDIF out */ - spec->multiout.dig_out_nid = 0; + if (board_config == CXT5066_HP_LAPTOP) + spec->multiout.dig_out_nid = 0; /* input source automatically selected */ spec->input_mux = NULL; spec->port_d_mode = 0; @@ -3207,7 +3228,7 @@ static int patch_cxt5066(struct hda_codec *codec) break; case CXT5066_DELL_VOSTRO: codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_vostro_event; + codec->patch_ops.unsol_event = cxt5066_unsol_event; spec->init_verbs[0] = cxt5066_init_verbs_vostro; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixers; @@ -3224,7 +3245,7 @@ static int patch_cxt5066(struct hda_codec *codec) break; case CXT5066_IDEAPAD: codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_ideapad_event; + codec->patch_ops.unsol_event = cxt5066_unsol_event; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; spec->mixers[spec->num_mixers++] = cxt5066_mixers; spec->init_verbs[0] = cxt5066_init_verbs_ideapad; @@ -3240,7 +3261,7 @@ static int patch_cxt5066(struct hda_codec *codec) break; case CXT5066_THINKPAD: codec->patch_ops.init = cxt5066_init; - codec->patch_ops.unsol_event = cxt5066_thinkpad_event; + codec->patch_ops.unsol_event = cxt5066_unsol_event; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; spec->mixers[spec->num_mixers++] = cxt5066_mixers; spec->init_verbs[0] = cxt5066_init_verbs_thinkpad; diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index c2ae63d17cd..f53897a708b 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -92,6 +92,8 @@ struct oxygen_model { void (*update_dac_volume)(struct oxygen *chip); void (*update_dac_mute)(struct oxygen *chip); void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed); + unsigned int (*adjust_dac_routing)(struct oxygen *chip, + unsigned int play_routing); void (*gpio_changed)(struct oxygen *chip); void (*uart_input)(struct oxygen *chip); void (*ac97_switch)(struct oxygen *chip, diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 9bff14d5895..26c7e8bcb22 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -180,6 +180,8 @@ void oxygen_update_dac_routing(struct oxygen *chip) (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); + if (chip->model.adjust_dac_routing) + reg_value = chip->model.adjust_dac_routing(chip, reg_value); oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value, OXYGEN_PLAY_DAC0_SOURCE_MASK | OXYGEN_PLAY_DAC1_SOURCE_MASK | diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index e1fa602eba7..bc6eb58be38 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -24,6 +24,11 @@ * * SPI 0 -> CS4245 * + * I²S 1 -> CS4245 + * I²S 2 -> CS4361 (center/LFE) + * I²S 3 -> CS4361 (surround) + * I²S 4 -> CS4361 (front) + * * GPIO 3 <- ? * GPIO 4 <- headphone detect * GPIO 5 -> route input jack to line-in (0) or mic-in (1) @@ -36,6 +41,7 @@ * input 1 <- aux * input 2 <- front mic * input 4 <- line/mic + * DAC out -> headphones * aux out -> front panel headphones */ @@ -207,6 +213,35 @@ static void set_cs4245_adc_params(struct oxygen *chip, cs4245_write_cached(chip, CS4245_ADC_CTRL, value); } +static inline unsigned int shift_bits(unsigned int value, + unsigned int shift_from, + unsigned int shift_to, + unsigned int mask) +{ + if (shift_from < shift_to) + return (value << (shift_to - shift_from)) & mask; + else + return (value >> (shift_from - shift_to)) & mask; +} + +static unsigned int adjust_dg_dac_routing(struct oxygen *chip, + unsigned int play_routing) +{ + return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) | + shift_bits(play_routing, + OXYGEN_PLAY_DAC2_SOURCE_SHIFT, + OXYGEN_PLAY_DAC1_SOURCE_SHIFT, + OXYGEN_PLAY_DAC1_SOURCE_MASK) | + shift_bits(play_routing, + OXYGEN_PLAY_DAC1_SOURCE_SHIFT, + OXYGEN_PLAY_DAC2_SOURCE_SHIFT, + OXYGEN_PLAY_DAC2_SOURCE_MASK) | + shift_bits(play_routing, + OXYGEN_PLAY_DAC0_SOURCE_SHIFT, + OXYGEN_PLAY_DAC3_SOURCE_SHIFT, + OXYGEN_PLAY_DAC3_SOURCE_MASK); +} + static int output_switch_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -557,6 +592,7 @@ struct oxygen_model model_xonar_dg = { .resume = dg_resume, .set_dac_params = set_cs4245_dac_params, .set_adc_params = set_cs4245_adc_params, + .adjust_dac_routing = adjust_dg_dac_routing, .dump_registers = dump_cs4245_registers, .model_data_size = sizeof(struct dg), .device_config = PLAYBACK_0_TO_I2S | diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h index bd26e092aea..6ce9ad70029 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.h +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h @@ -22,7 +22,7 @@ #define __PDAUDIOCF_H #include <sound/pcm.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/interrupt.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 989e04abb52..fe33e122e37 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -23,8 +23,8 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> +#include <linux/io.h> #include <sound/core.h> -#include <asm/io.h> #include "vxpocket.h" diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 46dbfd067f7..347a567b01e 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -153,7 +153,7 @@ static int cq93vc_resume(struct snd_soc_codec *codec) static int cq93vc_probe(struct snd_soc_codec *codec) { - struct davinci_vc *davinci_vc = codec->dev->platform_data; + struct davinci_vc *davinci_vc = snd_soc_codec_get_drvdata(codec); davinci_vc->cq93vc.codec = codec; codec->control_data = davinci_vc; diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 03d1e860d22..bb4bf65b9e7 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -367,9 +367,12 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec) return 0; } +static const u8 cx20442_reg = CX20442_TELOUT | CX20442_MIC; + static struct snd_soc_codec_driver cx20442_codec_dev = { .probe = cx20442_codec_probe, .remove = cx20442_codec_remove, + .reg_cache_default = &cx20442_reg, .reg_cache_size = 1, .reg_word_size = sizeof(u8), .read = cx20442_read_reg_cache, diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 2101bdcee21..3167be68962 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -507,8 +507,6 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) /* Set up digital mute if not provided by the codec */ if (!codec_dai->driver->ops) { codec_dai->driver->ops = &ams_delta_dai_ops; - } else if (!codec_dai->driver->ops->digital_mute) { - codec_dai->driver->ops->digital_mute = ams_delta_digital_mute; } else { ams_delta_ops.startup = ams_delta_startup; ams_delta_ops.shutdown = ams_delta_shutdown; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bac7291b6ff..c4b60610beb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1664,9 +1664,6 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) goto out; found: - if (!try_module_get(codec->dev->driver->owner)) - return -ENODEV; - ret = soc_probe_codec(card, codec); if (ret < 0) return ret; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 499730ab563..8194f150bab 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1742,7 +1742,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned int val, val_mask; + unsigned int val; int connect, change; struct snd_soc_dapm_update update; @@ -1750,13 +1750,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, if (invert) val = max - val; - val_mask = mask << shift; + mask = mask << shift; val = val << shift; mutex_lock(&widget->codec->mutex); widget->value = val; - change = snd_soc_test_bits(widget->codec, reg, val_mask, val); + change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { if (val) /* new connection */ diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 86b797a35aa..81c3220e04f 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -73,6 +73,17 @@ OPTIONS (Only for --vars) Show external defined variables in addition to local variables. +-F:: +--funcs:: + Show available functions in given module or kernel. + +--filter=FILTER:: + (Only for --vars and --funcs) Set filter. FILTER is a combination of glob + pattern, see FILTER PATTERN for detail. + Default FILTER is "!__k???tab_* & !__crc_*" for --vars, and "!_*" + for --funcs. + If several filters are specified, only the last filter is used. + -f:: --force:: Forcibly add events with existing name. @@ -135,6 +146,14 @@ e.g. This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.) +FILTER PATTERN +-------------- + The filter pattern is a glob matching pattern(s) to filter variables. + In addition, you can use "!" for specifying filter-out rule. You also can give several rules combined with "&" or "|", and fold those rules as one rule by using "(" ")". + +e.g. + With --filter "foo* | bar*", perf probe -V shows variables which start with "foo" or "bar". + With --filter "!foo* & *bar", perf probe -V shows variables which don't start with "foo" and end with "bar", like "fizzbar". But "foobar" is filtered out. EXAMPLES -------- diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7141c42e146..94f73abdc56 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -315,6 +315,7 @@ COMPAT_CFLAGS = COMPAT_OBJS = LIB_H = LIB_OBJS = +PYRF_OBJS = SCRIPT_PERL = SCRIPT_SH = TEST_PROGRAMS = @@ -324,6 +325,9 @@ SCRIPT_SH += perf-archive.sh grep-libs = $(filter -l%,$(1)) strip-libs = $(filter-out -l%,$(1)) +$(OUTPUT)python/perf.so: $(PYRF_OBJS) + $(QUIET_GEN)python util/setup.py --quiet build_ext --build-lib='$(OUTPUT)python' \ + --build-temp='$(OUTPUT)python/temp' # # No Perl scripts right now: # @@ -344,6 +348,8 @@ PROGRAMS += $(EXTRA_PROGRAMS) # PROGRAMS += $(OUTPUT)perf +LANG_BINDINGS = + # 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. # @@ -395,6 +401,7 @@ LIB_H += util/include/dwarf-regs.h LIB_H += util/include/asm/dwarf2.h LIB_H += util/include/asm/cpufeature.h LIB_H += perf.h +LIB_H += util/annotate.h LIB_H += util/cache.h LIB_H += util/callchain.h LIB_H += util/build-id.h @@ -402,6 +409,7 @@ LIB_H += util/debug.h LIB_H += util/debugfs.h LIB_H += util/event.h LIB_H += util/evsel.h +LIB_H += util/evlist.h LIB_H += util/exec_cmd.h LIB_H += util/types.h LIB_H += util/levenshtein.h @@ -416,6 +424,7 @@ LIB_H += util/help.h LIB_H += util/session.h LIB_H += util/strbuf.h LIB_H += util/strlist.h +LIB_H += util/strfilter.h LIB_H += util/svghelper.h LIB_H += util/run-command.h LIB_H += util/sigchain.h @@ -425,21 +434,25 @@ LIB_H += util/values.h LIB_H += util/sort.h LIB_H += util/hist.h LIB_H += util/thread.h +LIB_H += util/thread_map.h LIB_H += util/trace-event.h LIB_H += util/probe-finder.h LIB_H += util/probe-event.h LIB_H += util/pstack.h LIB_H += util/cpumap.h +LIB_H += util/top.h LIB_H += $(ARCH_INCLUDE) LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o +LIB_OBJS += $(OUTPUT)util/annotate.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/evlist.o LIB_OBJS += $(OUTPUT)util/evsel.o LIB_OBJS += $(OUTPUT)util/exec_cmd.o LIB_OBJS += $(OUTPUT)util/help.o @@ -455,6 +468,8 @@ 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/strfilter.o +LIB_OBJS += $(OUTPUT)util/top.o LIB_OBJS += $(OUTPUT)util/usage.o LIB_OBJS += $(OUTPUT)util/wrapper.o LIB_OBJS += $(OUTPUT)util/sigchain.o @@ -469,6 +484,7 @@ LIB_OBJS += $(OUTPUT)util/map.o LIB_OBJS += $(OUTPUT)util/pstack.o LIB_OBJS += $(OUTPUT)util/session.o LIB_OBJS += $(OUTPUT)util/thread.o +LIB_OBJS += $(OUTPUT)util/thread_map.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 @@ -514,6 +530,20 @@ BUILTIN_OBJS += $(OUTPUT)builtin-inject.o PERFLIBS = $(LIB_FILE) +# Files needed for the python binding, perf.so +# pyrf is just an internal name needed for all those wrappers. +# This has to be in sync with what is in the 'sources' variable in +# tools/perf/util/setup.py + +PYRF_OBJS += $(OUTPUT)util/cpumap.o +PYRF_OBJS += $(OUTPUT)util/ctype.o +PYRF_OBJS += $(OUTPUT)util/evlist.o +PYRF_OBJS += $(OUTPUT)util/evsel.o +PYRF_OBJS += $(OUTPUT)util/python.o +PYRF_OBJS += $(OUTPUT)util/thread_map.o +PYRF_OBJS += $(OUTPUT)util/util.o +PYRF_OBJS += $(OUTPUT)util/xyarray.o + # # Platform specific tweaks # @@ -595,6 +625,7 @@ else LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o + LIB_OBJS += $(OUTPUT)util/ui/browsers/top.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_OBJS += $(OUTPUT)util/ui/util.o @@ -635,12 +666,14 @@ else PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) + msg := $(warning No Python.h found, install python-dev[el] to have python support in 'perf script' and to build the python bindings) BASIC_CFLAGS += -DNO_LIBPYTHON else ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS) EXTLIBS += $(PYTHON_EMBED_LIBADD) LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o + LANG_BINDINGS += $(OUTPUT)python/perf.so endif endif @@ -927,7 +960,7 @@ export TAR INSTALL DESTDIR SHELL_PATH SHELL = $(SHELL_PATH) -all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS +all:: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)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 @@ -1024,6 +1057,9 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browsers/top.o: util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< @@ -1257,17 +1293,17 @@ distclean: clean # $(RM) configure clean: - $(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE) + $(RM) $(OUTPUT){*.o,*/*.o,*/*/*.o,*/*/*/*.o,$(LIB_FILE),perf-archive} $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X $(RM) $(TEST_PROGRAMS) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)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 + @python util/setup.py clean --build-lib='$(OUTPUT)python' \ + --build-temp='$(OUTPUT)python/temp' .PHONY: all install clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index d9ab3ce446a..0c7454f8b8a 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -55,7 +55,7 @@ int bench_sched_pipe(int argc, const char **argv, * discarding returned value of read(), write() * causes error in building environment for perf */ - int ret, wait_stat; + int __used ret, wait_stat; pid_t pid, retpid; argc = parse_options(argc, argv, options, diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 8879463807e..427182953fd 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -9,6 +9,7 @@ #include "util/util.h" +#include "util/util.h" #include "util/color.h" #include <linux/list.h> #include "util/cache.h" @@ -18,6 +19,7 @@ #include "perf.h" #include "util/debug.h" +#include "util/annotate.h" #include "util/event.h" #include "util/parse-options.h" #include "util/parse-events.h" @@ -55,15 +57,29 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) if (he == NULL) return -ENOMEM; - return hist_entry__inc_addr_samples(he, al->addr); + if (he->ms.sym != NULL) { + /* + * All aggregated on the first sym_hist. + */ + struct annotation *notes = symbol__annotation(he->ms.sym); + if (notes->src == NULL && + symbol__alloc_hist(he->ms.sym, 1) < 0) + return -ENOMEM; + + return hist_entry__inc_addr_samples(he, 0, al->addr); + } + + return 0; } -static int process_sample_event(event_t *event, struct sample_data *sample, +static int process_sample_event(union perf_event *event, + struct perf_sample *sample, struct perf_session *session) { struct addr_location al; - if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { + if (perf_event__preprocess_sample(event, session, &al, sample, + symbol__annotate_init) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; @@ -78,245 +94,10 @@ static int process_sample_event(event_t *event, struct sample_data *sample, return 0; } -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; - static const char *prev_line; - static const char *prev_color; - - if (self->offset != -1) { - const char *path = NULL; - unsigned int hits = 0; - double percent = 0.0; - const char *color; - struct sym_priv *priv = symbol__priv(sym); - struct sym_ext *sym_ext = priv->ext; - struct sym_hist *h = priv->hist; - s64 offset = self->offset; - struct objdump_line *next = objdump__get_next_ip_line(head, self); - - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (sym_ext) { - if (path == NULL) - path = sym_ext[offset].path; - percent += sym_ext[offset].percent; - } else - hits += h->ip[offset]; - - ++offset; - } - - if (sym_ext == NULL && h->sum) - percent = 100.0 * hits / h->sum; - - color = get_percent_color(percent); - - /* - * Also color the filename and line if needed, with - * the same color than the percentage. Don't print it - * twice for close colored ip with the same filename:line - */ - if (path) { - if (!prev_line || strcmp(prev_line, path) - || color != prev_color) { - color_fprintf(stdout, color, " %s", path); - prev_line = path; - prev_color = color; - } - } - - color_fprintf(stdout, color, " %7.2f", percent); - printf(" : "); - color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line); - } else { - if (!*self->line) - printf(" :\n"); - else - printf(" : %s\n", self->line); - } - - return 0; -} - -static struct rb_root root_sym_ext; - -static void insert_source_line(struct sym_ext *sym_ext) -{ - struct sym_ext *iter; - struct rb_node **p = &root_sym_ext.rb_node; - struct rb_node *parent = NULL; - - while (*p != NULL) { - parent = *p; - iter = rb_entry(parent, struct sym_ext, node); - - if (sym_ext->percent > iter->percent) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&sym_ext->node, parent, p); - rb_insert_color(&sym_ext->node, &root_sym_ext); -} - -static void free_source_line(struct hist_entry *he, int len) -{ - struct sym_priv *priv = symbol__priv(he->ms.sym); - struct sym_ext *sym_ext = priv->ext; - int i; - - if (!sym_ext) - return; - - for (i = 0; i < len; i++) - free(sym_ext[i].path); - free(sym_ext); - - priv->ext = NULL; - root_sym_ext = RB_ROOT; -} - -/* Get the filename:line for the colored entries */ -static void -get_source_line(struct hist_entry *he, int len, const char *filename) -{ - struct symbol *sym = he->ms.sym; - u64 start; - int i; - char cmd[PATH_MAX * 2]; - struct sym_ext *sym_ext; - struct sym_priv *priv = symbol__priv(sym); - struct sym_hist *h = priv->hist; - - if (!h->sum) - return; - - sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext)); - if (!priv->ext) - return; - - start = he->ms.map->unmap_ip(he->ms.map, sym->start); - - for (i = 0; i < len; i++) { - char *path = NULL; - size_t line_len; - u64 offset; - FILE *fp; - - sym_ext[i].percent = 100.0 * h->ip[i] / h->sum; - if (sym_ext[i].percent <= 0.5) - continue; - - offset = start + i; - sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); - fp = popen(cmd, "r"); - if (!fp) - continue; - - if (getline(&path, &line_len, fp) < 0 || !line_len) - goto next; - - sym_ext[i].path = malloc(sizeof(char) * line_len + 1); - if (!sym_ext[i].path) - goto next; - - strcpy(sym_ext[i].path, path); - insert_source_line(&sym_ext[i]); - - next: - pclose(fp); - } -} - -static void print_summary(const char *filename) +static int hist_entry__tty_annotate(struct hist_entry *he, int evidx) { - struct sym_ext *sym_ext; - struct rb_node *node; - - printf("\nSorted summary for file %s\n", filename); - printf("----------------------------------------------\n\n"); - - if (RB_EMPTY_ROOT(&root_sym_ext)) { - printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); - return; - } - - node = rb_first(&root_sym_ext); - while (node) { - double percent; - const char *color; - char *path; - - sym_ext = rb_entry(node, struct sym_ext, node); - percent = sym_ext->percent; - color = get_percent_color(percent); - path = sym_ext->path; - - color_fprintf(stdout, color, " %7.2f %s", percent, path); - node = rb_next(node); - } -} - -static void hist_entry__print_hits(struct hist_entry *self) -{ - struct symbol *sym = self->ms.sym; - struct sym_priv *priv = symbol__priv(sym); - struct sym_hist *h = priv->hist; - u64 len = sym->end - sym->start, offset; - - for (offset = 0; offset < len; ++offset) - if (h->ip[offset] != 0) - printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, - sym->start + offset, h->ip[offset]); - printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); -} - -static int hist_entry__tty_annotate(struct hist_entry *he) -{ - struct map *map = he->ms.map; - struct dso *dso = map->dso; - struct symbol *sym = he->ms.sym; - const char *filename = dso->long_name, *d_filename; - u64 len; - LIST_HEAD(head); - struct objdump_line *pos, *n; - - if (hist_entry__annotate(he, &head, 0) < 0) - return -1; - - if (full_paths) - d_filename = filename; - else - d_filename = basename(filename); - - len = sym->end - sym->start; - - if (print_line) { - get_source_line(he, len, filename); - print_summary(filename); - } - - printf("\n\n------------------------------------------------\n"); - printf(" Percent | Source code & Disassembly of %s\n", d_filename); - printf("------------------------------------------------\n"); - - if (verbose) - hist_entry__print_hits(he); - - list_for_each_entry_safe(pos, n, &head, node) { - objdump_line__print(pos, &head, he, len); - list_del(&pos->node); - objdump_line__free(pos); - } - - if (print_line) - free_source_line(he, len); - - return 0; + return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx, + print_line, full_paths, 0, 0); } static void hists__find_annotations(struct hists *self) @@ -326,13 +107,13 @@ static void hists__find_annotations(struct hists *self) while (nd) { struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); - struct sym_priv *priv; + struct annotation *notes; if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned) goto find_next; - priv = symbol__priv(he->ms.sym); - if (priv->hist == NULL) { + notes = symbol__annotation(he->ms.sym); + if (notes->src == NULL) { find_next: if (key == KEY_LEFT) nd = rb_prev(nd); @@ -342,7 +123,8 @@ find_next: } if (use_browser > 0) { - key = hist_entry__tui_annotate(he); + /* For now all is aggregated on the first */ + key = hist_entry__tui_annotate(he, 0); switch (key) { case KEY_RIGHT: next = rb_next(nd); @@ -357,24 +139,25 @@ find_next: if (next != NULL) nd = next; } else { - hist_entry__tty_annotate(he); + /* For now all is aggregated on the first */ + hist_entry__tty_annotate(he, 0); nd = rb_next(nd); /* * Since we have a hist_entry per IP for the same - * symbol, free he->ms.sym->hist to signal we already + * symbol, free he->ms.sym->src to signal we already * processed this symbol. */ - free(priv->hist); - priv->hist = NULL; + free(notes->src); + notes->src = NULL; } } } static struct perf_event_ops event_ops = { .sample = process_sample_event, - .mmap = event__process_mmap, - .comm = event__process_comm, - .fork = event__process_task, + .mmap = perf_event__process_mmap, + .comm = perf_event__process_comm, + .fork = perf_event__process_task, .ordered_samples = true, .ordering_requires_timestamps = true, }; @@ -451,9 +234,9 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) else if (use_tui) use_browser = 1; - setup_browser(); + setup_browser(true); - symbol_conf.priv_size = sizeof(struct sym_priv); + symbol_conf.priv_size = sizeof(struct annotation); symbol_conf.try_vmlinux_path = true; if (symbol__init() < 0) diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3153e492dbc..6b7d91160ec 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -30,13 +30,13 @@ static int hists__add_entry(struct hists *self, return -ENOMEM; } -static int diff__process_sample_event(event_t *event, - struct sample_data *sample, +static int diff__process_sample_event(union perf_event *event, + struct perf_sample *sample, struct perf_session *session) { struct addr_location al; - if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { + if (perf_event__preprocess_sample(event, session, &al, sample, NULL) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; @@ -56,11 +56,11 @@ static int diff__process_sample_event(event_t *event, static struct perf_event_ops event_ops = { .sample = diff__process_sample_event, - .mmap = event__process_mmap, - .comm = event__process_comm, - .exit = event__process_task, - .fork = event__process_task, - .lost = event__process_lost, + .mmap = perf_event__process_mmap, + .comm = perf_event__process_comm, + .exit = perf_event__process_task, + .fork = perf_event__process_task, + .lost = perf_event__process_lost, .ordered_samples = true, .ordering_requires_timestamps = true, }; diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 0c78ffa7bf6..e29f04ed339 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -16,8 +16,8 @@ static char const *input_name = "-"; static bool inject_build_ids; -static int event__repipe_synth(event_t *event, - struct perf_session *session __used) +static int perf_event__repipe_synth(union perf_event *event, + struct perf_session *session __used) { uint32_t size; void *buf = event; @@ -36,41 +36,44 @@ static int event__repipe_synth(event_t *event, return 0; } -static int event__repipe(event_t *event, struct sample_data *sample __used, - struct perf_session *session) +static int perf_event__repipe(union perf_event *event, + struct perf_sample *sample __used, + struct perf_session *session) { - return event__repipe_synth(event, session); + return perf_event__repipe_synth(event, session); } -static int event__repipe_mmap(event_t *self, struct sample_data *sample, - struct perf_session *session) +static int perf_event__repipe_mmap(union perf_event *event, + struct perf_sample *sample, + struct perf_session *session) { int err; - err = event__process_mmap(self, sample, session); - event__repipe(self, sample, session); + err = perf_event__process_mmap(event, sample, session); + perf_event__repipe(event, sample, session); return err; } -static int event__repipe_task(event_t *self, struct sample_data *sample, - struct perf_session *session) +static int perf_event__repipe_task(union perf_event *event, + struct perf_sample *sample, + struct perf_session *session) { int err; - err = event__process_task(self, sample, session); - event__repipe(self, sample, session); + err = perf_event__process_task(event, sample, session); + perf_event__repipe(event, sample, session); return err; } -static int event__repipe_tracing_data(event_t *self, - struct perf_session *session) +static int perf_event__repipe_tracing_data(union perf_event *event, + struct perf_session *session) { int err; - event__repipe_synth(self, session); - err = event__process_tracing_data(self, session); + perf_event__repipe_synth(event, session); + err = perf_event__process_tracing_data(event, session); return err; } @@ -109,8 +112,8 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session) if (self->kernel) misc = PERF_RECORD_MISC_KERNEL; - err = event__synthesize_build_id(self, misc, event__repipe, - machine, session); + err = perf_event__synthesize_build_id(self, misc, perf_event__repipe, + machine, session); if (err) { pr_err("Can't synthesize build_id event for %s\n", self->long_name); return -1; @@ -119,8 +122,9 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session) return 0; } -static int event__inject_buildid(event_t *event, struct sample_data *sample, - struct perf_session *session) +static int perf_event__inject_buildid(union perf_event *event, + struct perf_sample *sample, + struct perf_session *session) { struct addr_location al; struct thread *thread; @@ -155,24 +159,24 @@ static int event__inject_buildid(event_t *event, struct sample_data *sample, } repipe: - event__repipe(event, sample, session); + perf_event__repipe(event, sample, 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_synth, - .event_type = event__repipe_synth, - .tracing_data = event__repipe_synth, - .build_id = event__repipe_synth, + .sample = perf_event__repipe, + .mmap = perf_event__repipe, + .comm = perf_event__repipe, + .fork = perf_event__repipe, + .exit = perf_event__repipe, + .lost = perf_event__repipe, + .read = perf_event__repipe, + .throttle = perf_event__repipe, + .unthrottle = perf_event__repipe, + .attr = perf_event__repipe_synth, + .event_type = perf_event__repipe_synth, + .tracing_data = perf_event__repipe_synth, + .build_id = perf_event__repipe_synth, }; extern volatile int session_done; @@ -190,10 +194,10 @@ static int __cmd_inject(void) 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; + inject_ops.sample = perf_event__inject_buildid; + inject_ops.mmap = perf_event__repipe_mmap; + inject_ops.fork = perf_event__repipe_task; + inject_ops.tracing_data = perf_event__repipe_tracing_data; } session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops); diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index d97256d6598..7f618f4e7b7 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -275,9 +275,8 @@ static void process_free_event(void *data, s_alloc->alloc_cpu = -1; } -static void -process_raw_event(event_t *raw_event __used, void *data, - int cpu, u64 timestamp, struct thread *thread) +static void process_raw_event(union perf_event *raw_event __used, void *data, + int cpu, u64 timestamp, struct thread *thread) { struct event *event; int type; @@ -304,7 +303,8 @@ process_raw_event(event_t *raw_event __used, void *data, } } -static int process_sample_event(event_t *event, struct sample_data *sample, +static int process_sample_event(union perf_event *event, + struct perf_sample *sample, struct perf_session *session) { struct thread *thread = perf_session__findnew(session, event->ip.pid); @@ -325,7 +325,7 @@ static int process_sample_event(event_t *event, struct sample_data *sample, static struct perf_event_ops event_ops = { .sample = process_sample_event, - .comm = event__process_comm, + .comm = perf_event__process_comm, .ordered_samples = true, }; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 2b36defc5d7..e00d93847c4 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -834,14 +834,14 @@ static void dump_info(void) die("Unknown type of information\n"); } -static int process_sample_event(event_t *self, struct sample_data *sample, +static int process_sample_event(union perf_event *event, struct perf_sample *sample, struct perf_session *s) { struct thread *thread = perf_session__findnew(s, sample->tid); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", - self->header.type); + event->header.type); return -1; } @@ -852,7 +852,7 @@ static int process_sample_event(event_t *self, struct sample_data *sample, static struct perf_event_ops eops = { .sample = process_sample_event, - .comm = event__process_comm, + .comm = perf_event__process_comm, .ordered_samples = true, }; diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index add163c9f0e..2c0e64d0b4a 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -36,6 +36,7 @@ #include "builtin.h" #include "util/util.h" #include "util/strlist.h" +#include "util/strfilter.h" #include "util/symbol.h" #include "util/debug.h" #include "util/debugfs.h" @@ -43,6 +44,8 @@ #include "util/probe-finder.h" #include "util/probe-event.h" +#define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*" +#define DEFAULT_FUNC_FILTER "!_*" #define MAX_PATH_LEN 256 /* Session management structure */ @@ -52,6 +55,7 @@ static struct { bool show_lines; bool show_vars; bool show_ext_vars; + bool show_funcs; bool mod_events; int nevents; struct perf_probe_event events[MAX_PROBES]; @@ -59,6 +63,7 @@ static struct { struct line_range line_range; const char *target_module; int max_probe_points; + struct strfilter *filter; } params; /* Parse an event definition. Note that any error must die. */ @@ -157,6 +162,27 @@ static int opt_show_vars(const struct option *opt __used, } #endif +static int opt_set_filter(const struct option *opt __used, + const char *str, int unset __used) +{ + const char *err; + + if (str) { + pr_debug2("Set filter: %s\n", str); + if (params.filter) + strfilter__delete(params.filter); + params.filter = strfilter__new(str, &err); + if (!params.filter) { + pr_err("Filter parse error at %td.\n", err - str + 1); + pr_err("Source: \"%s\"\n", str); + pr_err(" %*c\n", (int)(err - str + 1), '^'); + return -EINVAL; + } + } + + return 0; +} + static const char * const probe_usage[] = { "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", @@ -221,6 +247,13 @@ static const struct option options[] = { 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_BOOLEAN('F', "funcs", ¶ms.show_funcs, + "Show potential probe-able functions."), + OPT_CALLBACK('\0', "filter", NULL, + "[!]FILTER", "Set a filter (with --vars/funcs only)\n" + "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n" + "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)", + opt_set_filter), OPT_END() }; @@ -246,7 +279,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) params.max_probe_points = MAX_PROBES; if ((!params.nevents && !params.dellist && !params.list_events && - !params.show_lines)) + !params.show_lines && !params.show_funcs)) usage_with_options(probe_usage, options); /* @@ -267,12 +300,41 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) pr_err(" Error: Don't use --list with --vars.\n"); usage_with_options(probe_usage, options); } + if (params.show_funcs) { + pr_err(" Error: Don't use --list with --funcs.\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; } + if (params.show_funcs) { + if (params.nevents != 0 || params.dellist) { + pr_err(" Error: Don't use --funcs with" + " --add/--del.\n"); + usage_with_options(probe_usage, options); + } + if (params.show_lines) { + pr_err(" Error: Don't use --funcs with --line.\n"); + usage_with_options(probe_usage, options); + } + if (params.show_vars) { + pr_err(" Error: Don't use --funcs with --vars.\n"); + usage_with_options(probe_usage, options); + } + if (!params.filter) + params.filter = strfilter__new(DEFAULT_FUNC_FILTER, + NULL); + ret = show_available_funcs(params.target_module, + params.filter); + strfilter__delete(params.filter); + if (ret < 0) + pr_err(" Error: Failed to show functions." + " (%d)\n", ret); + return ret; + } #ifdef DWARF_SUPPORT if (params.show_lines) { @@ -297,10 +359,16 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) " --add/--del.\n"); usage_with_options(probe_usage, options); } + if (!params.filter) + params.filter = strfilter__new(DEFAULT_VAR_FILTER, + NULL); + ret = show_available_vars(params.events, params.nevents, params.max_probe_points, params.target_module, + params.filter, params.show_ext_vars); + strfilter__delete(params.filter); if (ret < 0) pr_err(" Error: Failed to show vars. (%d)\n", ret); return ret; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b2f729fdb31..12e0e41696d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -18,17 +18,20 @@ #include "util/header.h" #include "util/event.h" +#include "util/evlist.h" #include "util/evsel.h" #include "util/debug.h" #include "util/session.h" #include "util/symbol.h" #include "util/cpumap.h" +#include "util/thread_map.h" #include <unistd.h> #include <sched.h> #include <sys/mman.h> #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) +#define SID(e, x, y) xyarray__entry(e->id, x, y) enum write_mode_t { WRITE_FORCE, @@ -39,14 +42,13 @@ static u64 user_interval = ULLONG_MAX; static u64 default_interval = 0; static u64 sample_type; -static struct cpu_map *cpus; 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 const char *output_name = NULL; static int group = 0; static int realtime_prio = 0; static bool nodelay = false; @@ -55,7 +57,6 @@ static bool sample_id_all_avail = true; static bool system_wide = false; static pid_t target_pid = -1; static pid_t target_tid = -1; -static struct thread_map *threads; static pid_t child_pid = -1; static bool no_inherit = false; static enum write_mode_t write_mode = WRITE_FORCE; @@ -66,51 +67,17 @@ static bool sample_address = false; static bool sample_time = false; static bool no_buildid = false; static bool no_buildid_cache = false; +static struct perf_evlist *evsel_list; static long samples = 0; static u64 bytes_written = 0; -static struct pollfd *event_array; - -static int nr_poll = 0; -static int nr_cpu = 0; - static int file_new = 1; static off_t post_processing_offset; static struct perf_session *session; static const char *cpu_list; -struct mmap_data { - void *base; - unsigned int mask; - unsigned int prev; -}; - -static struct mmap_data mmap_array[MAX_NR_CPUS]; - -static unsigned long mmap_read_head(struct mmap_data *md) -{ - struct perf_event_mmap_page *pc = md->base; - long head; - - head = pc->data_head; - rmb(); - - return head; -} - -static void mmap_write_tail(struct mmap_data *md, unsigned long tail) -{ - struct perf_event_mmap_page *pc = md->base; - - /* - * ensure all reads are done before we write the tail out. - */ - /* mb(); */ - pc->data_tail = tail; -} - static void advance_output(size_t size) { bytes_written += size; @@ -131,42 +98,26 @@ static void write_output(void *buf, size_t size) } } -static int process_synthesized_event(event_t *event, - struct sample_data *sample __used, +static int process_synthesized_event(union perf_event *event, + struct perf_sample *sample __used, struct perf_session *self __used) { write_output(event, event->header.size); return 0; } -static void mmap_read(struct mmap_data *md) +static void mmap_read(struct perf_mmap *md) { - unsigned int head = mmap_read_head(md); + unsigned int head = perf_mmap__read_head(md); unsigned int old = md->prev; unsigned char *data = md->base + page_size; unsigned long size; void *buf; - int diff; - /* - * If we're further behind than half the buffer, there's a chance - * the writer will bite our tail and mess up the samples under us. - * - * If we somehow ended up ahead of the head, we got messed up. - * - * In either case, truncate and restart at head. - */ - diff = head - old; - if (diff < 0) { - fprintf(stderr, "WARNING: failed to keep up with mmap data\n"); - /* - * head points to a known good entry, start there. - */ - old = head; - } + if (old == head) + return; - if (old != head) - samples++; + samples++; size = head - old; @@ -185,7 +136,7 @@ static void mmap_read(struct mmap_data *md) write_output(buf, size); md->prev = old; - mmap_write_tail(md, old); + perf_mmap__write_tail(md, old); } static volatile int done = 0; @@ -209,8 +160,6 @@ static void sig_atexit(void) kill(getpid(), signr); } -static int group_fd; - static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) { struct perf_header_attr *h_attr; @@ -234,28 +183,47 @@ static void create_counter(struct perf_evsel *evsel, int cpu) char *filter = evsel->filter; struct perf_event_attr *attr = &evsel->attr; struct perf_header_attr *h_attr; - int track = !evsel->idx; /* only the first counter needs these */ + struct perf_sample_id *sid; int thread_index; int ret; - struct { - u64 count; - u64 time_enabled; - u64 time_running; - u64 id; - } read_data; - /* - * Check if parse_single_tracepoint_event has already asked for - * PERF_SAMPLE_TIME. - * - * XXX this is kludgy but short term fix for problems introduced by - * eac23d1c that broke 'perf script' by having different sample_types - * when using multiple tracepoint events when we use a perf binary - * that tries to use sample_id_all on an older kernel. - * - * We need to move counter creation to perf_session, support - * different sample_types, etc. - */ - bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; + + for (thread_index = 0; thread_index < evsel_list->threads->nr; thread_index++) { + h_attr = get_header_attr(attr, evsel->idx); + if (h_attr == NULL) + die("nomem\n"); + + if (!file_new) { + if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { + fprintf(stderr, "incompatible append\n"); + exit(-1); + } + } + + sid = SID(evsel, cpu, thread_index); + if (perf_header_attr__add_id(h_attr, sid->id) < 0) { + pr_warning("Not enough memory to add id\n"); + exit(-1); + } + + if (filter != NULL) { + ret = ioctl(FD(evsel, cpu, 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 (!sample_type) + sample_type = attr->sample_type; +} + +static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) +{ + struct perf_event_attr *attr = &evsel->attr; + int track = !evsel->idx; /* only the first counter needs these */ attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | @@ -263,7 +231,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu) attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; - if (nr_counters > 1) + if (evlist->nr_entries > 1) attr->sample_type |= PERF_SAMPLE_ID; /* @@ -315,19 +283,40 @@ static void create_counter(struct perf_evsel *evsel, int cpu) attr->mmap = track; attr->comm = track; - attr->inherit = !no_inherit; + if (target_pid == -1 && target_tid == -1 && !system_wide) { attr->disabled = 1; attr->enable_on_exec = 1; } -retry_sample_id: - attr->sample_id_all = sample_id_all_avail ? 1 : 0; +} - for (thread_index = 0; thread_index < threads->nr; thread_index++) { -try_again: - FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); +static void open_counters(struct perf_evlist *evlist) +{ + struct perf_evsel *pos; + int cpu; + + list_for_each_entry(pos, &evlist->entries, node) { + struct perf_event_attr *attr = &pos->attr; + /* + * Check if parse_single_tracepoint_event has already asked for + * PERF_SAMPLE_TIME. + * + * XXX this is kludgy but short term fix for problems introduced by + * eac23d1c that broke 'perf script' by having different sample_types + * when using multiple tracepoint events when we use a perf binary + * that tries to use sample_id_all on an older kernel. + * + * We need to move counter creation to perf_session, support + * different sample_types, etc. + */ + bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; - if (FD(evsel, nr_cpu, thread_index) < 0) { + config_attr(pos, evlist); +retry_sample_id: + attr->sample_id_all = sample_id_all_avail ? 1 : 0; +try_again: + if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group, + !no_inherit) < 0) { int err = errno; if (err == EPERM || err == EACCES) @@ -364,7 +353,7 @@ try_again: } printf("\n"); error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", - FD(evsel, nr_cpu, thread_index), strerror(err)); + err, strerror(err)); #if defined(__i386__) || defined(__x86_64__) if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) @@ -375,90 +364,16 @@ try_again: #endif die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); - exit(-1); - } - - h_attr = get_header_attr(attr, evsel->idx); - if (h_attr == NULL) - die("nomem\n"); - - if (!file_new) { - if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { - fprintf(stderr, "incompatible append\n"); - exit(-1); - } - } - - if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { - perror("Unable to read perf file descriptor"); - exit(-1); - } - - if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { - pr_warning("Not enough memory to add id\n"); - exit(-1); - } - - assert(FD(evsel, nr_cpu, thread_index) >= 0); - fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK); - - /* - * First counter acts as the group leader: - */ - if (group && group_fd == -1) - group_fd = FD(evsel, nr_cpu, thread_index); - - if (evsel->idx || thread_index) { - struct perf_evsel *first; - first = list_entry(evsel_list.next, struct perf_evsel, node); - ret = ioctl(FD(evsel, nr_cpu, thread_index), - PERF_EVENT_IOC_SET_OUTPUT, - FD(first, nr_cpu, 0)); - if (ret) { - error("failed to set output: %d (%s)\n", errno, - strerror(errno)); - exit(-1); - } - } else { - mmap_array[nr_cpu].prev = 0; - mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; - mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, - PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0); - if (mmap_array[nr_cpu].base == MAP_FAILED) { - error("failed to mmap with %d (%s)\n", errno, strerror(errno)); - exit(-1); - } - - event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index); - event_array[nr_poll].events = POLLIN; - nr_poll++; - } - - if (filter != NULL) { - ret = ioctl(FD(evsel, nr_cpu, 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 (!sample_type) - sample_type = attr->sample_type; -} - -static void open_counters(int cpu) -{ - struct perf_evsel *pos; + if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) + die("failed to mmap with %d (%s)\n", errno, strerror(errno)); - group_fd = -1; - - list_for_each_entry(pos, &evsel_list, node) - create_counter(pos, cpu); - - nr_cpu++; + for (cpu = 0; cpu < evsel_list->cpus->nr; ++cpu) { + list_for_each_entry(pos, &evlist->entries, node) + create_counter(pos, cpu); + } } static int process_buildids(void) @@ -481,14 +396,14 @@ static void atexit_header(void) if (!no_buildid) process_buildids(); - perf_header__write(&session->header, output, true); + perf_header__write(&session->header, evsel_list, output, true); perf_session__delete(session); - perf_evsel_list__delete(); + perf_evlist__delete(evsel_list); symbol__exit(); } } -static void event__synthesize_guest_os(struct machine *machine, void *data) +static void perf_event__synthesize_guest_os(struct machine *machine, void *data) { int err; struct perf_session *psession = data; @@ -504,8 +419,8 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) *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); + err = perf_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); @@ -514,11 +429,12 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) * 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"); + err = perf_event__synthesize_kernel_mmap(process_synthesized_event, + psession, machine, "_text"); if (err < 0) - err = event__synthesize_kernel_mmap(process_synthesized_event, - psession, machine, "_stext"); + err = perf_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); @@ -533,9 +449,9 @@ static void mmap_read_all(void) { int i; - for (i = 0; i < nr_cpu; i++) { - if (mmap_array[i].base) - mmap_read(&mmap_array[i]); + for (i = 0; i < evsel_list->cpus->nr; i++) { + if (evsel_list->mmap[i].base) + mmap_read(&evsel_list->mmap[i]); } if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) @@ -566,18 +482,26 @@ 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) { - char oldname[PATH_MAX]; - snprintf(oldname, sizeof(oldname), "%s.old", - output_name); - unlink(oldname); - rename(output_name, oldname); + if (!output_name) { + if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) + pipe_output = 1; + else + output_name = "perf.data"; + } + if (output_name) { + if (!strcmp(output_name, "-")) + pipe_output = 1; + else if (!stat(output_name, &st) && st.st_size) { + if (write_mode == WRITE_FORCE) { + 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 if (write_mode == WRITE_APPEND) { - write_mode = WRITE_FORCE; } flags = O_CREAT|O_RDWR; @@ -611,7 +535,7 @@ static int __cmd_record(int argc, const char **argv) goto out_delete_session; } - if (have_tracepoints(&evsel_list)) + if (have_tracepoints(&evsel_list->entries)) perf_header__set_feat(&session->header, HEADER_TRACE_INFO); /* @@ -659,7 +583,7 @@ static int __cmd_record(int argc, const char **argv) } if (!system_wide && target_tid == -1 && target_pid == -1) - threads->map[0] = child_pid; + evsel_list->threads->map[0] = child_pid; close(child_ready_pipe[1]); close(go_pipe[0]); @@ -673,12 +597,7 @@ static int __cmd_record(int argc, const char **argv) close(child_ready_pipe[0]); } - if (!system_wide && no_inherit && !cpu_list) { - open_counters(-1); - } else { - for (i = 0; i < cpus->nr; i++) - open_counters(cpus->map[i]); - } + open_counters(evsel_list); perf_session__set_sample_type(session, sample_type); @@ -687,7 +606,8 @@ static int __cmd_record(int argc, const char **argv) if (err < 0) return err; } else if (file_new) { - err = perf_header__write(&session->header, output, false); + err = perf_header__write(&session->header, evsel_list, + output, false); if (err < 0) return err; } @@ -697,22 +617,22 @@ static int __cmd_record(int argc, const char **argv) perf_session__set_sample_id_all(session, sample_id_all_avail); if (pipe_output) { - err = event__synthesize_attrs(&session->header, - process_synthesized_event, - session); + err = perf_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); + err = perf_event__synthesize_event_types(process_synthesized_event, + session); if (err < 0) { pr_err("Couldn't synthesize event_types.\n"); return err; } - if (have_tracepoints(&evsel_list)) { + if (have_tracepoints(&evsel_list->entries)) { /* * FIXME err <= 0 here actually means that * there were no tracepoints so its not really @@ -721,9 +641,9 @@ static int __cmd_record(int argc, const char **argv) * return this more properly and also * propagate errors that now are calling die() */ - err = event__synthesize_tracing_data(output, &evsel_list, - process_synthesized_event, - session); + err = perf_event__synthesize_tracing_data(output, evsel_list, + process_synthesized_event, + session); if (err <= 0) { pr_err("Couldn't record tracing data.\n"); return err; @@ -738,31 +658,34 @@ static int __cmd_record(int argc, const char **argv) return -1; } - err = event__synthesize_kernel_mmap(process_synthesized_event, - session, machine, "_text"); + err = perf_event__synthesize_kernel_mmap(process_synthesized_event, + session, machine, "_text"); if (err < 0) - err = event__synthesize_kernel_mmap(process_synthesized_event, - session, machine, "_stext"); + err = perf_event__synthesize_kernel_mmap(process_synthesized_event, + session, machine, "_stext"); if (err < 0) pr_err("Couldn't record kernel reference relocation symbol\n" "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" "Check /proc/kallsyms permission or run as root.\n"); - err = event__synthesize_modules(process_synthesized_event, - session, machine); + err = perf_event__synthesize_modules(process_synthesized_event, + session, machine); if (err < 0) pr_err("Couldn't record kernel module information.\n" "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" "Check /proc/modules permission or run as root.\n"); if (perf_guest) - perf_session__process_machines(session, event__synthesize_guest_os); + perf_session__process_machines(session, + perf_event__synthesize_guest_os); if (!system_wide) - event__synthesize_thread(target_tid, process_synthesized_event, - session); + perf_event__synthesize_thread_map(evsel_list->threads, + process_synthesized_event, + session); else - event__synthesize_threads(process_synthesized_event, session); + perf_event__synthesize_threads(process_synthesized_event, + session); if (realtime_prio) { struct sched_param param; @@ -789,17 +712,17 @@ static int __cmd_record(int argc, const char **argv) if (hits == samples) { if (done) break; - err = poll(event_array, nr_poll, -1); + err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); waking++; } if (done) { - for (i = 0; i < nr_cpu; i++) { + for (i = 0; i < evsel_list->cpus->nr; i++) { struct perf_evsel *pos; - list_for_each_entry(pos, &evsel_list, node) { + list_for_each_entry(pos, &evsel_list->entries, node) { for (thread = 0; - thread < threads->nr; + thread < evsel_list->threads->nr; thread++) ioctl(FD(pos, i, thread), PERF_EVENT_IOC_DISABLE); @@ -838,10 +761,10 @@ static const char * const record_usage[] = { static bool force, append_file; const struct option record_options[] = { - OPT_CALLBACK('e', "event", NULL, "event", + OPT_CALLBACK('e', "event", &evsel_list, "event", "event selector. use 'perf list' to list available events", parse_events), - OPT_CALLBACK(0, "filter", NULL, "filter", + OPT_CALLBACK(0, "filter", &evsel_list, "filter", "event filter", parse_filter), OPT_INTEGER('p', "pid", &target_pid, "record events on existing process id"), @@ -892,6 +815,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) int err = -ENOMEM; struct perf_evsel *pos; + evsel_list = perf_evlist__new(NULL, NULL); + if (evsel_list == NULL) + return -ENOMEM; + argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (!argc && target_pid == -1 && target_tid == -1 && @@ -913,7 +840,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) if (no_buildid_cache || no_buildid) disable_buildid_cache(); - if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { + if (evsel_list->nr_entries == 0 && + perf_evlist__add_default(evsel_list) < 0) { pr_err("Not enough memory for event selector list\n"); goto out_symbol_exit; } @@ -921,27 +849,19 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) if (target_pid != -1) target_tid = target_pid; - threads = thread_map__new(target_pid, target_tid); - if (threads == NULL) { - pr_err("Problems finding threads of monitor\n"); + if (perf_evlist__create_maps(evsel_list, target_pid, + target_tid, cpu_list) < 0) usage_with_options(record_usage, record_options); - } - - cpus = cpu_map__new(cpu_list); - if (cpus == NULL) { - perror("failed to parse CPUs map"); - return -1; - } - list_for_each_entry(pos, &evsel_list, node) { - if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) + list_for_each_entry(pos, &evsel_list->entries, node) { + if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, + evsel_list->threads->nr) < 0) goto out_free_fd; if (perf_header__push_event(pos->attr.config, event_name(pos))) goto out_free_fd; } - event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS * - MAX_COUNTERS * threads->nr)); - if (!event_array) + + if (perf_evlist__alloc_pollfd(evsel_list) < 0) goto out_free_fd; if (user_interval != ULLONG_MAX) @@ -959,16 +879,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) } else { fprintf(stderr, "frequency and count are zero, aborting\n"); err = -EINVAL; - goto out_free_event_array; + goto out_free_fd; } err = __cmd_record(argc, argv); - -out_free_event_array: - free(event_array); out_free_fd: - thread_map__delete(threads); - threads = NULL; + perf_evlist__delete_maps(evsel_list); out_symbol_exit: symbol__exit(); return err; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c27e31f289e..f9a99a1ce60 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -9,6 +9,7 @@ #include "util/util.h" +#include "util/annotate.h" #include "util/color.h" #include <linux/list.h> #include "util/cache.h" @@ -43,6 +44,7 @@ static const char default_pretty_printing_style[] = "normal"; static const char *pretty_printing_style = default_pretty_printing_style; static char callchain_default_opt[] = "fractal,0.5"; +static symbol_filter_t annotate_init; static struct hists *perf_session__hists_findnew(struct perf_session *self, u64 event_stream, u32 type, @@ -77,86 +79,96 @@ static struct hists *perf_session__hists_findnew(struct perf_session *self, return new; } -static int perf_session__add_hist_entry(struct perf_session *self, +static int perf_session__add_hist_entry(struct perf_session *session, struct addr_location *al, - struct sample_data *data) + struct perf_sample *sample) { - struct map_symbol *syms = NULL; struct symbol *parent = NULL; - int err = -ENOMEM; + int err = 0; struct hist_entry *he; struct hists *hists; struct perf_event_attr *attr; - 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; + if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { + err = perf_session__resolve_callchain(session, al->thread, + sample->callchain, &parent); + if (err) + return err; } - attr = perf_header__find_attr(data->id, &self->header); + attr = perf_header__find_attr(sample->id, &session->header); if (attr) - hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config); + hists = perf_session__hists_findnew(session, sample->id, attr->type, attr->config); else - hists = perf_session__hists_findnew(self, data->id, 0, 0); + hists = perf_session__hists_findnew(session, sample->id, 0, 0); if (hists == NULL) - goto out_free_syms; - he = __hists__add_entry(hists, al, parent, data->period); + return -ENOMEM; + + he = __hists__add_entry(hists, al, parent, sample->period); if (he == NULL) - goto out_free_syms; - err = 0; + return -ENOMEM; + if (symbol_conf.use_callchain) { - err = callchain_append(he->callchain, data->callchain, syms, - data->period); + err = callchain_append(he->callchain, &session->callchain_cursor, + sample->period); if (err) - goto out_free_syms; + return err; } /* * Only in the newt browser we are doing integrated annotation, * so we don't allocated the extra space needed because the stdio * code will not use it. */ - if (use_browser > 0) - err = hist_entry__inc_addr_samples(he, al->addr); -out_free_syms: - free(syms); + if (al->sym != NULL && use_browser > 0) { + /* + * All aggregated on the first sym_hist. + */ + struct annotation *notes = symbol__annotation(he->ms.sym); + if (notes->src == NULL && + symbol__alloc_hist(he->ms.sym, 1) < 0) + err = -ENOMEM; + else + err = hist_entry__inc_addr_samples(he, 0, al->addr); + } + return err; } static int add_event_total(struct perf_session *session, - struct sample_data *data, + struct perf_sample *sample, struct perf_event_attr *attr) { struct hists *hists; if (attr) - hists = perf_session__hists_findnew(session, data->id, + hists = perf_session__hists_findnew(session, sample->id, attr->type, attr->config); else - hists = perf_session__hists_findnew(session, data->id, 0, 0); + hists = perf_session__hists_findnew(session, sample->id, 0, 0); if (!hists) return -ENOMEM; - hists->stats.total_period += data->period; + hists->stats.total_period += sample->period; /* * FIXME: add_event_total should be moved from here to * perf_session__process_event so that the proper hist is passed to * the event_op methods. */ hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); - session->hists.stats.total_period += data->period; + session->hists.stats.total_period += sample->period; return 0; } -static int process_sample_event(event_t *event, struct sample_data *sample, +static int process_sample_event(union perf_event *event, + struct perf_sample *sample, struct perf_session *session) { struct addr_location al; struct perf_event_attr *attr; - if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { + if (perf_event__preprocess_sample(event, session, &al, sample, + annotate_init) < 0) { fprintf(stderr, "problem processing %d event, skipping it.\n", event->header.type); return -1; @@ -180,7 +192,8 @@ static int process_sample_event(event_t *event, struct sample_data *sample, return 0; } -static int process_read_event(event_t *event, struct sample_data *sample __used, +static int process_read_event(union perf_event *event, + struct perf_sample *sample __used, struct perf_session *session __used) { struct perf_event_attr *attr; @@ -222,7 +235,7 @@ static int perf_session__setup_sample_type(struct perf_session *self) } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && !symbol_conf.use_callchain) { symbol_conf.use_callchain = true; - if (register_callchain_param(&callchain_param) < 0) { + if (callchain_register_param(&callchain_param) < 0) { fprintf(stderr, "Can't register callchain" " params\n"); return -EINVAL; @@ -233,17 +246,17 @@ static int perf_session__setup_sample_type(struct perf_session *self) } static struct perf_event_ops event_ops = { - .sample = process_sample_event, - .mmap = event__process_mmap, - .comm = event__process_comm, - .exit = event__process_task, - .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, + .sample = process_sample_event, + .mmap = perf_event__process_mmap, + .comm = perf_event__process_comm, + .exit = perf_event__process_task, + .fork = perf_event__process_task, + .lost = perf_event__process_lost, + .read = process_read_event, + .attr = perf_event__process_attr, + .event_type = perf_event__process_event_type, + .tracing_data = perf_event__process_tracing_data, + .build_id = perf_event__process_build_id, .ordered_samples = true, .ordering_requires_timestamps = true, }; @@ -347,7 +360,7 @@ static int __cmd_report(void) } if (use_browser > 0) - hists__tui_browse_tree(&session->hists_tree, help); + hists__tui_browse_tree(&session->hists_tree, help, 0); else hists__tty_browse_tree(&session->hists_tree, help); @@ -424,7 +437,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, if (tok2) callchain_param.print_limit = strtod(tok2, &endptr); setup: - if (register_callchain_param(&callchain_param) < 0) { + if (callchain_register_param(&callchain_param) < 0) { fprintf(stderr, "Can't register callchain params\n"); return -1; } @@ -498,7 +511,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) use_browser = 1; if (strcmp(input_name, "-") != 0) - setup_browser(); + setup_browser(true); else use_browser = 0; /* @@ -507,7 +520,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) * implementation. */ if (use_browser > 0) { - symbol_conf.priv_size = sizeof(struct sym_priv); + symbol_conf.priv_size = sizeof(struct annotation); + annotate_init = symbol__annotate_init; /* * For searching by name on the "Browse map details". * providing it only in verbose mode not to bloat too diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 29acb894e03..a32f411faea 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -369,11 +369,6 @@ static void process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) { int ret = 0; - u64 now; - long long delta; - - now = get_nsecs(); - delta = start_time + atom->timestamp - now; switch (atom->type) { case SCHED_EVENT_RUN: @@ -562,7 +557,7 @@ static void wait_for_tasks(void) static void run_one_test(void) { - u64 T0, T1, delta, avg_delta, fluct, std_dev; + u64 T0, T1, delta, avg_delta, fluct; T0 = get_nsecs(); wait_for_tasks(); @@ -578,7 +573,6 @@ static void run_one_test(void) else fluct = delta - avg_delta; sum_fluct += fluct; - std_dev = sum_fluct / nr_runs / sqrt(nr_runs); if (!run_avg) run_avg = delta; run_avg = (run_avg*9 + delta)/10; @@ -799,7 +793,7 @@ replay_switch_event(struct trace_switch_event *switch_event, u64 timestamp, struct thread *thread __used) { - struct task_desc *prev, *next; + struct task_desc *prev, __used *next; u64 timestamp0; s64 delta; @@ -1404,7 +1398,7 @@ map_switch_event(struct trace_switch_event *switch_event, u64 timestamp, struct thread *thread __used) { - struct thread *sched_out, *sched_in; + struct thread *sched_out __used, *sched_in; int new_shortname; u64 timestamp0; s64 delta; @@ -1580,9 +1574,9 @@ process_sched_migrate_task_event(void *data, struct perf_session *session, event, cpu, timestamp, thread); } -static void -process_raw_event(event_t *raw_event __used, struct perf_session *session, - void *data, int cpu, u64 timestamp, struct thread *thread) +static void process_raw_event(union perf_event *raw_event __used, + struct perf_session *session, void *data, int cpu, + u64 timestamp, struct thread *thread) { struct event *event; int type; @@ -1607,7 +1601,8 @@ process_raw_event(event_t *raw_event __used, struct perf_session *session, process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); } -static int process_sample_event(event_t *event, struct sample_data *sample, +static int process_sample_event(union perf_event *event, + struct perf_sample *sample, struct perf_session *session) { struct thread *thread; @@ -1635,9 +1630,9 @@ static int process_sample_event(event_t *event, struct sample_data *sample, static struct perf_event_ops event_ops = { .sample = process_sample_event, - .comm = event__process_comm, - .lost = event__process_lost, - .fork = event__process_task, + .comm = perf_event__process_comm, + .lost = perf_event__process_lost, + .fork = perf_event__process_task, .ordered_samples = true, }; diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b766c2a9ac9..5f40df635dc 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -63,7 +63,8 @@ static int cleanup_scripting(void) static char const *input_name = "perf.data"; -static int process_sample_event(event_t *event, struct sample_data *sample, +static int process_sample_event(union perf_event *event, + struct perf_sample *sample, struct perf_session *session) { struct thread *thread = perf_session__findnew(session, event->ip.pid); @@ -100,14 +101,14 @@ static int process_sample_event(event_t *event, struct sample_data *sample, } 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, - .ordering_requires_timestamps = true, + .sample = process_sample_event, + .comm = perf_event__process_comm, + .attr = perf_event__process_attr, + .event_type = perf_event__process_event_type, + .tracing_data = perf_event__process_tracing_data, + .build_id = perf_event__process_build_id, .ordered_samples = true, + .ordering_requires_timestamps = true, }; extern volatile int session_done; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a482a191a0c..806a9998fcd 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -43,11 +43,13 @@ #include "util/parse-options.h" #include "util/parse-events.h" #include "util/event.h" +#include "util/evlist.h" #include "util/evsel.h" #include "util/debug.h" #include "util/header.h" #include "util/cpumap.h" #include "util/thread.h" +#include "util/thread_map.h" #include <sys/prctl.h> #include <math.h> @@ -71,8 +73,9 @@ static struct perf_event_attr default_attrs[] = { }; +struct perf_evlist *evsel_list; + static bool system_wide = false; -static struct cpu_map *cpus; static int run_idx = 0; static int run_count = 1; @@ -81,7 +84,6 @@ static bool scale = true; static bool no_aggr = false; static pid_t target_pid = -1; static pid_t target_tid = -1; -static struct thread_map *threads; static pid_t child_pid = -1; static bool null_run = false; static bool big_num = true; @@ -166,7 +168,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) PERF_FORMAT_TOTAL_TIME_RUNNING; if (system_wide) - return perf_evsel__open_per_cpu(evsel, cpus); + return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, false, false); attr->inherit = !no_inherit; if (target_pid == -1 && target_tid == -1) { @@ -174,7 +176,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) attr->enable_on_exec = 1; } - return perf_evsel__open_per_thread(evsel, threads); + return perf_evsel__open_per_thread(evsel, evsel_list->threads, false, false); } /* @@ -199,7 +201,8 @@ static int read_counter_aggr(struct perf_evsel *counter) u64 *count = counter->counts->aggr.values; int i; - if (__perf_evsel__read(counter, cpus->nr, threads->nr, scale) < 0) + if (__perf_evsel__read(counter, evsel_list->cpus->nr, + evsel_list->threads->nr, scale) < 0) return -1; for (i = 0; i < 3; i++) @@ -232,7 +235,7 @@ static int read_counter(struct perf_evsel *counter) u64 *count; int cpu; - for (cpu = 0; cpu < cpus->nr; cpu++) { + for (cpu = 0; cpu < evsel_list->cpus->nr; cpu++) { if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0) return -1; @@ -297,7 +300,7 @@ static int run_perf_stat(int argc __used, const char **argv) } if (target_tid == -1 && target_pid == -1 && !system_wide) - threads->map[0] = child_pid; + evsel_list->threads->map[0] = child_pid; /* * Wait for the child to be ready to exec. @@ -309,7 +312,7 @@ static int run_perf_stat(int argc __used, const char **argv) close(child_ready_pipe[0]); } - list_for_each_entry(counter, &evsel_list, node) { + list_for_each_entry(counter, &evsel_list->entries, node) { if (create_perf_stat_counter(counter) < 0) { if (errno == -EPERM || errno == -EACCES) { error("You may not have permission to collect %sstats.\n" @@ -347,14 +350,15 @@ static int run_perf_stat(int argc __used, const char **argv) update_stats(&walltime_nsecs_stats, t1 - t0); if (no_aggr) { - list_for_each_entry(counter, &evsel_list, node) { + list_for_each_entry(counter, &evsel_list->entries, node) { read_counter(counter); - perf_evsel__close_fd(counter, cpus->nr, 1); + perf_evsel__close_fd(counter, evsel_list->cpus->nr, 1); } } else { - list_for_each_entry(counter, &evsel_list, node) { + list_for_each_entry(counter, &evsel_list->entries, node) { read_counter_aggr(counter); - perf_evsel__close_fd(counter, cpus->nr, threads->nr); + perf_evsel__close_fd(counter, evsel_list->cpus->nr, + evsel_list->threads->nr); } } @@ -382,7 +386,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) if (no_aggr) sprintf(cpustr, "CPU%*d%s", csv_output ? 0 : -4, - cpus->map[cpu], csv_sep); + evsel_list->cpus->map[cpu], csv_sep); fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel)); @@ -410,7 +414,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) if (no_aggr) sprintf(cpustr, "CPU%*d%s", csv_output ? 0 : -4, - cpus->map[cpu], csv_sep); + evsel_list->cpus->map[cpu], csv_sep); else cpu = 0; @@ -496,14 +500,14 @@ static void print_counter(struct perf_evsel *counter) u64 ena, run, val; int cpu; - for (cpu = 0; cpu < cpus->nr; cpu++) { + for (cpu = 0; cpu < evsel_list->cpus->nr; cpu++) { val = counter->counts->cpu[cpu].val; ena = counter->counts->cpu[cpu].ena; run = counter->counts->cpu[cpu].run; if (run == 0 || ena == 0) { fprintf(stderr, "CPU%*d%s%*s%s%-24s", csv_output ? 0 : -4, - cpus->map[cpu], csv_sep, + evsel_list->cpus->map[cpu], csv_sep, csv_output ? 0 : 18, "<not counted>", csv_sep, event_name(counter)); @@ -555,10 +559,10 @@ static void print_stat(int argc, const char **argv) } if (no_aggr) { - list_for_each_entry(counter, &evsel_list, node) + list_for_each_entry(counter, &evsel_list->entries, node) print_counter(counter); } else { - list_for_each_entry(counter, &evsel_list, node) + list_for_each_entry(counter, &evsel_list->entries, node) print_counter_aggr(counter); } @@ -610,7 +614,7 @@ static int stat__set_big_num(const struct option *opt __used, } static const struct option options[] = { - OPT_CALLBACK('e', "event", NULL, "event", + OPT_CALLBACK('e', "event", &evsel_list, "event", "event selector. use 'perf list' to list available events", parse_events), OPT_BOOLEAN('i', "no-inherit", &no_inherit, @@ -648,6 +652,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) setlocale(LC_ALL, ""); + evsel_list = perf_evlist__new(NULL, NULL); + if (evsel_list == NULL) + return -ENOMEM; + argc = parse_options(argc, argv, options, stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -679,44 +687,41 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) usage_with_options(stat_usage, options); /* Set attrs and nr_counters if no event is selected and !null_run */ - if (!null_run && !nr_counters) { + if (!null_run && !evsel_list->nr_entries) { size_t c; - nr_counters = ARRAY_SIZE(default_attrs); - for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { - pos = perf_evsel__new(&default_attrs[c], - nr_counters); + pos = perf_evsel__new(&default_attrs[c], c); if (pos == NULL) goto out; - list_add(&pos->node, &evsel_list); + perf_evlist__add(evsel_list, pos); } } if (target_pid != -1) target_tid = target_pid; - threads = thread_map__new(target_pid, target_tid); - if (threads == NULL) { + evsel_list->threads = thread_map__new(target_pid, target_tid); + if (evsel_list->threads == NULL) { pr_err("Problems finding threads of monitor\n"); usage_with_options(stat_usage, options); } if (system_wide) - cpus = cpu_map__new(cpu_list); + evsel_list->cpus = cpu_map__new(cpu_list); else - cpus = cpu_map__dummy_new(); + evsel_list->cpus = cpu_map__dummy_new(); - if (cpus == NULL) { + if (evsel_list->cpus == NULL) { perror("failed to parse CPUs map"); usage_with_options(stat_usage, options); return -1; } - list_for_each_entry(pos, &evsel_list, node) { + list_for_each_entry(pos, &evsel_list->entries, node) { if (perf_evsel__alloc_stat_priv(pos) < 0 || - perf_evsel__alloc_counts(pos, cpus->nr) < 0 || - perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) + perf_evsel__alloc_counts(pos, evsel_list->cpus->nr) < 0 || + perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, evsel_list->threads->nr) < 0) goto out_free_fd; } @@ -741,11 +746,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) if (status != -1) print_stat(argc, argv); out_free_fd: - list_for_each_entry(pos, &evsel_list, node) + list_for_each_entry(pos, &evsel_list->entries, node) perf_evsel__free_stat_priv(pos); - perf_evsel_list__delete(); + perf_evlist__delete_maps(evsel_list); out: - thread_map__delete(threads); - threads = NULL; + perf_evlist__delete(evsel_list); return status; } diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 5dcdba653d7..1b2106c58f6 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -7,10 +7,11 @@ #include "util/cache.h" #include "util/debug.h" +#include "util/evlist.h" #include "util/parse-options.h" -#include "util/session.h" +#include "util/parse-events.h" #include "util/symbol.h" -#include "util/thread.h" +#include "util/thread_map.h" static long page_size; @@ -238,14 +239,14 @@ out: #include "util/evsel.h" #include <sys/types.h> -static int trace_event__id(const char *event_name) +static int trace_event__id(const char *evname) { char *filename; int err = -1, fd; if (asprintf(&filename, "/sys/kernel/debug/tracing/events/syscalls/%s/id", - event_name) < 0) + evname) < 0) return -1; fd = open(filename, O_RDONLY); @@ -289,7 +290,7 @@ static int test__open_syscall_event(void) goto out_thread_map_delete; } - if (perf_evsel__open_per_thread(evsel, threads) < 0) { + if (perf_evsel__open_per_thread(evsel, threads, false, false) < 0) { pr_debug("failed to open counter: %s, " "tweak /proc/sys/kernel/perf_event_paranoid?\n", strerror(errno)); @@ -347,9 +348,9 @@ static int test__open_syscall_event_on_all_cpus(void) } cpus = cpu_map__new(NULL); - if (threads == NULL) { - pr_debug("thread_map__new\n"); - return -1; + if (cpus == NULL) { + pr_debug("cpu_map__new\n"); + goto out_thread_map_delete; } @@ -364,7 +365,7 @@ static int test__open_syscall_event_on_all_cpus(void) goto out_thread_map_delete; } - if (perf_evsel__open(evsel, cpus, threads) < 0) { + if (perf_evsel__open(evsel, cpus, threads, false, false) < 0) { pr_debug("failed to open counter: %s, " "tweak /proc/sys/kernel/perf_event_paranoid?\n", strerror(errno)); @@ -408,6 +409,8 @@ static int test__open_syscall_event_on_all_cpus(void) goto out_close_fd; } + err = 0; + for (cpu = 0; cpu < cpus->nr; ++cpu) { unsigned int expected; @@ -416,18 +419,18 @@ static int test__open_syscall_event_on_all_cpus(void) if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { pr_debug("perf_evsel__open_read_on_cpu\n"); - goto out_close_fd; + err = -1; + break; } expected = nr_open_calls + cpu; if (evsel->counts->cpu[cpu].val != expected) { pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); - goto out_close_fd; + err = -1; } } - err = 0; out_close_fd: perf_evsel__close_fd(evsel, 1, threads->nr); out_evsel_delete: @@ -437,6 +440,159 @@ out_thread_map_delete: return err; } +/* + * This test will generate random numbers of calls to some getpid syscalls, + * then establish an mmap for a group of events that are created to monitor + * the syscalls. + * + * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated + * sample.id field to map back to its respective perf_evsel instance. + * + * Then it checks if the number of syscalls reported as perf events by + * the kernel corresponds to the number of syscalls made. + */ +static int test__basic_mmap(void) +{ + int err = -1; + union perf_event *event; + struct thread_map *threads; + struct cpu_map *cpus; + struct perf_evlist *evlist; + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .read_format = PERF_FORMAT_ID, + .sample_type = PERF_SAMPLE_ID, + .watermark = 0, + }; + cpu_set_t cpu_set; + const char *syscall_names[] = { "getsid", "getppid", "getpgrp", + "getpgid", }; + pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, + (void*)getpgid }; +#define nsyscalls ARRAY_SIZE(syscall_names) + int ids[nsyscalls]; + unsigned int nr_events[nsyscalls], + expected_nr_events[nsyscalls], i, j; + struct perf_evsel *evsels[nsyscalls], *evsel; + + for (i = 0; i < nsyscalls; ++i) { + char name[64]; + + snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); + ids[i] = trace_event__id(name); + if (ids[i] < 0) { + pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); + return -1; + } + nr_events[i] = 0; + expected_nr_events[i] = random() % 257; + } + + threads = thread_map__new(-1, getpid()); + if (threads == NULL) { + pr_debug("thread_map__new\n"); + return -1; + } + + cpus = cpu_map__new(NULL); + if (cpus == NULL) { + pr_debug("cpu_map__new\n"); + goto out_free_threads; + } + + CPU_ZERO(&cpu_set); + CPU_SET(cpus->map[0], &cpu_set); + sched_setaffinity(0, sizeof(cpu_set), &cpu_set); + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { + pr_debug("sched_setaffinity() failed on CPU %d: %s ", + cpus->map[0], strerror(errno)); + goto out_free_cpus; + } + + evlist = perf_evlist__new(cpus, threads); + if (evlist == NULL) { + pr_debug("perf_evlist__new\n"); + goto out_free_cpus; + } + + /* anonymous union fields, can't be initialized above */ + attr.wakeup_events = 1; + attr.sample_period = 1; + + for (i = 0; i < nsyscalls; ++i) { + attr.config = ids[i]; + evsels[i] = perf_evsel__new(&attr, i); + if (evsels[i] == NULL) { + pr_debug("perf_evsel__new\n"); + goto out_free_evlist; + } + + perf_evlist__add(evlist, evsels[i]); + + if (perf_evsel__open(evsels[i], cpus, threads, false, false) < 0) { + pr_debug("failed to open counter: %s, " + "tweak /proc/sys/kernel/perf_event_paranoid?\n", + strerror(errno)); + goto out_close_fd; + } + } + + if (perf_evlist__mmap(evlist, 128, true) < 0) { + pr_debug("failed to mmap events: %d (%s)\n", errno, + strerror(errno)); + goto out_close_fd; + } + + for (i = 0; i < nsyscalls; ++i) + for (j = 0; j < expected_nr_events[i]; ++j) { + int foo = syscalls[i](); + ++foo; + } + + while ((event = perf_evlist__read_on_cpu(evlist, 0)) != NULL) { + struct perf_sample sample; + + if (event->header.type != PERF_RECORD_SAMPLE) { + pr_debug("unexpected %s event\n", + perf_event__name(event->header.type)); + goto out_munmap; + } + + perf_event__parse_sample(event, attr.sample_type, false, &sample); + evsel = perf_evlist__id2evsel(evlist, sample.id); + if (evsel == NULL) { + pr_debug("event with id %" PRIu64 + " doesn't map to an evsel\n", sample.id); + goto out_munmap; + } + nr_events[evsel->idx]++; + } + + list_for_each_entry(evsel, &evlist->entries, node) { + if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { + pr_debug("expected %d %s events, got %d\n", + expected_nr_events[evsel->idx], + event_name(evsel), nr_events[evsel->idx]); + goto out_munmap; + } + } + + err = 0; +out_munmap: + perf_evlist__munmap(evlist); +out_close_fd: + for (i = 0; i < nsyscalls; ++i) + perf_evsel__close_fd(evsels[i], 1, threads->nr); +out_free_evlist: + perf_evlist__delete(evlist); +out_free_cpus: + cpu_map__delete(cpus); +out_free_threads: + thread_map__delete(threads); + return err; +#undef nsyscalls +} + static struct test { const char *desc; int (*func)(void); @@ -454,6 +610,10 @@ static struct test { .func = test__open_syscall_event_on_all_cpus, }, { + .desc = "read samples using the mmap interface", + .func = test__basic_mmap, + }, + { .func = NULL, }, }; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 746cf03cb05..0801275c500 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -276,21 +276,24 @@ static int cpus_cstate_state[MAX_CPUS]; static u64 cpus_pstate_start_times[MAX_CPUS]; static u64 cpus_pstate_state[MAX_CPUS]; -static int process_comm_event(event_t *event, struct sample_data *sample __used, +static int process_comm_event(union perf_event *event, + struct perf_sample *sample __used, struct perf_session *session __used) { pid_set_comm(event->comm.tid, event->comm.comm); return 0; } -static int process_fork_event(event_t *event, struct sample_data *sample __used, +static int process_fork_event(union perf_event *event, + struct perf_sample *sample __used, struct perf_session *session __used) { pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); return 0; } -static int process_exit_event(event_t *event, struct sample_data *sample __used, +static int process_exit_event(union perf_event *event, + struct perf_sample *sample __used, struct perf_session *session __used) { pid_exit(event->fork.pid, event->fork.time); @@ -486,8 +489,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) } -static int process_sample_event(event_t *event __used, - struct sample_data *sample, +static int process_sample_event(union perf_event *event __used, + struct perf_sample *sample, struct perf_session *session) { struct trace_entry *te; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b6998e05576..c9fd66d4a08 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -20,11 +20,16 @@ #include "perf.h" +#include "util/annotate.h" +#include "util/cache.h" #include "util/color.h" +#include "util/evlist.h" #include "util/evsel.h" #include "util/session.h" #include "util/symbol.h" #include "util/thread.h" +#include "util/thread_map.h" +#include "util/top.h" #include "util/util.h" #include <linux/rbtree.h> #include "util/parse-options.h" @@ -45,7 +50,6 @@ #include <errno.h> #include <time.h> #include <sched.h> -#include <pthread.h> #include <sys/syscall.h> #include <sys/ioctl.h> @@ -60,85 +64,42 @@ #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) +static struct perf_top top = { + .count_filter = 5, + .delay_secs = 2, + .display_weighted = -1, + .target_pid = -1, + .target_tid = -1, + .active_symbols = LIST_HEAD_INIT(top.active_symbols), + .active_symbols_lock = PTHREAD_MUTEX_INITIALIZER, + .freq = 1000, /* 1 KHz */ +}; + static bool system_wide = false; -static int default_interval = 0; +static bool use_tui, use_stdio; -static int count_filter = 5; -static int print_entries; +static int default_interval = 0; -static int target_pid = -1; -static int target_tid = -1; -static struct thread_map *threads; static bool inherit = false; -static struct cpu_map *cpus; static int realtime_prio = 0; static bool group = false; static unsigned int page_size; -static unsigned int mmap_pages = 16; -static int freq = 1000; /* 1 KHz */ +static unsigned int mmap_pages = 128; -static int delay_secs = 2; -static bool zero = false; static bool dump_symtab = false; -static bool hide_kernel_symbols = false; -static bool hide_user_symbols = false; static struct winsize winsize; -/* - * Source - */ - -struct source_line { - u64 eip; - unsigned long count[MAX_COUNTERS]; - char *line; - struct source_line *next; -}; - static const char *sym_filter = NULL; struct sym_entry *sym_filter_entry = NULL; struct sym_entry *sym_filter_entry_sched = NULL; static int sym_pcnt_filter = 5; -static int sym_counter = 0; -static struct perf_evsel *sym_evsel = NULL; -static int display_weighted = -1; -static const char *cpu_list; - -/* - * Symbols - */ - -struct sym_entry_source { - struct source_line *source; - struct source_line *lines; - struct source_line **lines_tail; - pthread_mutex_t lock; -}; - -struct sym_entry { - struct rb_node rb_node; - struct list_head node; - unsigned long snap_count; - double weight; - int skip; - u16 name_len; - u8 origin; - struct map *map; - struct sym_entry_source *src; - unsigned long count[0]; -}; /* * Source functions */ -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) { char *s = getenv("LINES"); @@ -163,10 +124,10 @@ void get_term_dimensions(struct winsize *ws) static void update_print_entries(struct winsize *ws) { - print_entries = ws->ws_row; + top.print_entries = ws->ws_row; - if (print_entries > 9) - print_entries -= 9; + if (top.print_entries > 9) + top.print_entries -= 9; } static void sig_winch_handler(int sig __used) @@ -178,12 +139,9 @@ static void sig_winch_handler(int sig __used) static int parse_source(struct sym_entry *syme) { struct symbol *sym; - struct sym_entry_source *source; + struct annotation *notes; struct map *map; - FILE *file; - char command[PATH_MAX*2]; - const char *path; - u64 len; + int err = -1; if (!syme) return -1; @@ -194,411 +152,136 @@ static int parse_source(struct sym_entry *syme) /* * We can't annotate with just /proc/kallsyms */ - if (map->dso->origin == DSO__ORIG_KERNEL) + if (map->dso->origin == DSO__ORIG_KERNEL) { + pr_err("Can't annotate %s: No vmlinux file was found in the " + "path\n", sym->name); + sleep(1); return -1; - - if (syme->src == NULL) { - syme->src = zalloc(sizeof(*source)); - if (syme->src == NULL) - return -1; - pthread_mutex_init(&syme->src->lock, NULL); } - source = syme->src; - - if (source->lines) { - pthread_mutex_lock(&source->lock); + notes = symbol__annotation(sym); + if (notes->src != NULL) { + pthread_mutex_lock(¬es->lock); goto out_assign; } - path = map->dso->long_name; - - len = sym->end - sym->start; - - sprintf(command, - "objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s", - BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), - BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); - - file = popen(command, "r"); - if (!file) - return -1; - pthread_mutex_lock(&source->lock); - source->lines_tail = &source->lines; - while (!feof(file)) { - struct source_line *src; - size_t dummy = 0; - char *c, *sep; + pthread_mutex_lock(¬es->lock); - src = malloc(sizeof(struct source_line)); - assert(src != NULL); - memset(src, 0, sizeof(struct source_line)); - - if (getline(&src->line, &dummy, file) < 0) - break; - if (!src->line) - break; - - c = strchr(src->line, '\n'); - if (c) - *c = 0; - - src->next = NULL; - *source->lines_tail = src; - source->lines_tail = &src->next; - - src->eip = strtoull(src->line, &sep, 16); - if (*sep == ':') - src->eip = map__objdump_2ip(map, src->eip); - else /* this line has no ip info (e.g. source line) */ - src->eip = 0; + if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { + pr_err("Not enough memory for annotating '%s' symbol!\n", + sym->name); + sleep(1); + goto out_unlock; } - pclose(file); + + err = symbol__annotate(sym, syme->map, 0); + if (err == 0) { out_assign: sym_filter_entry = syme; - pthread_mutex_unlock(&source->lock); - return 0; + } +out_unlock: + pthread_mutex_unlock(¬es->lock); + return err; } static void __zero_source_counters(struct sym_entry *syme) { - int i; - struct source_line *line; - - line = syme->src->lines; - while (line) { - for (i = 0; i < nr_counters; i++) - line->count[i] = 0; - line = line->next; - } + struct symbol *sym = sym_entry__symbol(syme); + symbol__annotate_zero_histograms(sym); } static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) { - struct source_line *line; + struct annotation *notes; + struct symbol *sym; if (syme != sym_filter_entry) return; - if (pthread_mutex_trylock(&syme->src->lock)) - return; - - if (syme->src == NULL || syme->src->source == NULL) - goto out_unlock; - - for (line = syme->src->lines; line; line = line->next) { - /* skip lines without IP info */ - if (line->eip == 0) - continue; - if (line->eip == ip) { - line->count[counter]++; - break; - } - if (line->eip > ip) - break; - } -out_unlock: - pthread_mutex_unlock(&syme->src->lock); -} - -#define PATTERN_LEN (BITS_PER_LONG / 4 + 2) - -static void lookup_sym_source(struct sym_entry *syme) -{ - struct symbol *symbol = sym_entry__symbol(syme); - struct source_line *line; - char pattern[PATTERN_LEN + 1]; - - sprintf(pattern, "%0*" PRIx64 " <", BITS_PER_LONG / 4, - map__rip_2objdump(syme->map, symbol->start)); - - pthread_mutex_lock(&syme->src->lock); - for (line = syme->src->lines; line; line = line->next) { - if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { - syme->src->source = line; - break; - } - } - pthread_mutex_unlock(&syme->src->lock); -} + sym = sym_entry__symbol(syme); + notes = symbol__annotation(sym); -static void show_lines(struct source_line *queue, int count, int total) -{ - int i; - struct source_line *line; + if (pthread_mutex_trylock(¬es->lock)) + return; - line = queue; - for (i = 0; i < count; i++) { - float pcnt = 100.0*(float)line->count[sym_counter]/(float)total; + ip = syme->map->map_ip(syme->map, ip); + symbol__inc_addr_samples(sym, syme->map, counter, ip); - printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line); - line = line->next; - } + pthread_mutex_unlock(¬es->lock); } -#define TRACE_COUNT 3 - static void show_details(struct sym_entry *syme) { + struct annotation *notes; struct symbol *symbol; - struct source_line *line; - struct source_line *line_queue = NULL; - int displayed = 0; - int line_queue_count = 0, total = 0, more = 0; + int more; if (!syme) return; - if (!syme->src->source) - lookup_sym_source(syme); - - if (!syme->src->source) - return; - symbol = sym_entry__symbol(syme); - printf("Showing %s for %s\n", event_name(sym_evsel), symbol->name); - printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); + notes = symbol__annotation(symbol); - pthread_mutex_lock(&syme->src->lock); - line = syme->src->source; - while (line) { - total += line->count[sym_counter]; - line = line->next; - } - - line = syme->src->source; - while (line) { - float pcnt = 0.0; - - if (!line_queue_count) - line_queue = line; - line_queue_count++; - - if (line->count[sym_counter]) - pcnt = 100.0 * line->count[sym_counter] / (float)total; - if (pcnt >= (float)sym_pcnt_filter) { - if (displayed <= print_entries) - show_lines(line_queue, line_queue_count, total); - else more++; - displayed += line_queue_count; - line_queue_count = 0; - line_queue = NULL; - } else if (line_queue_count > TRACE_COUNT) { - line_queue = line_queue->next; - line_queue_count--; - } + pthread_mutex_lock(¬es->lock); - line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; - line = line->next; - } - pthread_mutex_unlock(&syme->src->lock); - if (more) - printf("%d lines not displayed, maybe increase display entries [e]\n", more); -} - -/* - * Symbols will be added here in event__process_sample and will get out - * after decayed. - */ -static LIST_HEAD(active_symbols); -static pthread_mutex_t active_symbols_lock = PTHREAD_MUTEX_INITIALIZER; - -/* - * Ordering weight: count-1 * count-2 * ... / count-n - */ -static double sym_weight(const struct sym_entry *sym) -{ - double weight = sym->snap_count; - int counter; - - if (!display_weighted) - return weight; - - for (counter = 1; counter < nr_counters-1; counter++) - weight *= sym->count[counter]; + if (notes->src == NULL) + goto out_unlock; - weight /= (sym->count[counter] + 1); + printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); + printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); - return weight; + more = symbol__annotate_printf(symbol, syme->map, top.sym_evsel->idx, + 0, sym_pcnt_filter, top.print_entries, 4); + if (top.zero) + symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); + else + symbol__annotate_decay_histogram(symbol, top.sym_evsel->idx); + if (more != 0) + printf("%d lines not displayed, maybe increase display entries [e]\n", more); +out_unlock: + pthread_mutex_unlock(¬es->lock); } -static long samples; -static long kernel_samples, us_samples; -static long exact_samples; -static long guest_us_samples, guest_kernel_samples; static const char CONSOLE_CLEAR[] = "[H[2J"; static void __list_insert_active_sym(struct sym_entry *syme) { - list_add(&syme->node, &active_symbols); -} - -static void list_remove_active_sym(struct sym_entry *syme) -{ - pthread_mutex_lock(&active_symbols_lock); - list_del_init(&syme->node); - pthread_mutex_unlock(&active_symbols_lock); -} - -static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) -{ - struct rb_node **p = &tree->rb_node; - struct rb_node *parent = NULL; - struct sym_entry *iter; - - while (*p != NULL) { - parent = *p; - iter = rb_entry(parent, struct sym_entry, rb_node); - - if (se->weight > iter->weight) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&se->rb_node, parent, p); - rb_insert_color(&se->rb_node, tree); + list_add(&syme->node, &top.active_symbols); } -static void print_sym_table(void) +static void print_sym_table(struct perf_session *session) { - int printed = 0, j; - struct perf_evsel *counter; - int 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 sum_ksamples = 0.0; - struct sym_entry *syme, *n; - struct rb_root tmp = RB_ROOT; + char bf[160]; + int printed = 0; struct rb_node *nd; - int sym_width = 0, dso_width = 0, dso_short_width = 0; + struct sym_entry *syme; + struct rb_root tmp = RB_ROOT; const int win_width = winsize.ws_col - 1; - - samples = us_samples = kernel_samples = exact_samples = 0; - guest_kernel_samples = guest_us_samples = 0; - - /* Sort the active symbols */ - pthread_mutex_lock(&active_symbols_lock); - syme = list_entry(active_symbols.next, struct sym_entry, node); - pthread_mutex_unlock(&active_symbols_lock); - - list_for_each_entry_safe_from(syme, n, &active_symbols, node) { - syme->snap_count = syme->count[snap]; - if (syme->snap_count != 0) { - - if ((hide_user_symbols && - syme->origin == PERF_RECORD_MISC_USER) || - (hide_kernel_symbols && - syme->origin == PERF_RECORD_MISC_KERNEL)) { - list_remove_active_sym(syme); - continue; - } - syme->weight = sym_weight(syme); - rb_insert_active_sym(&tmp, syme); - sum_ksamples += syme->snap_count; - - for (j = 0; j < nr_counters; j++) - syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; - } else - list_remove_active_sym(syme); - } + int sym_width, dso_width, dso_short_width; + float sum_ksamples = perf_top__decay_samples(&top, &tmp); 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); - } - - if (nr_counters == 1 || !display_weighted) { - struct perf_evsel *first; - first = list_entry(evsel_list.next, struct perf_evsel, node); - printf("%" PRIu64, (uint64_t)first->attr.sample_period); - if (freq) - printf("Hz "); - else - printf(" "); - } + perf_top__header_snprintf(&top, bf, sizeof(bf)); + printf("%s\n", bf); - if (!display_weighted) - printf("%s", event_name(sym_evsel)); - else list_for_each_entry(counter, &evsel_list, node) { - if (counter->idx) - printf("/"); + perf_top__reset_sample_counters(&top); - printf("%s", event_name(counter)); - } - - printf( "], "); - - if (target_pid != -1) - printf(" (target_pid: %d", target_pid); - else if (target_tid != -1) - printf(" (target_tid: %d", target_tid); - else - printf(" (all"); + printf("%-*.*s\n", win_width, win_width, graph_dotted_line); - if (cpu_list) - printf(", CPU%s: %s)\n", cpus->nr > 1 ? "s" : "", cpu_list); - else { - if (target_tid != -1) - printf(")\n"); - else - printf(", %d CPU%s)\n", cpus->nr, cpus->nr > 1 ? "s" : ""); + if (session->hists.stats.total_lost != 0) { + color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); + printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", + session->hists.stats.total_lost); } - printf("%-*.*s\n", win_width, win_width, graph_dotted_line); - if (sym_filter_entry) { show_details(sym_filter_entry); return; } - /* - * Find the longest symbol name that will be displayed - */ - for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { - syme = rb_entry(nd, struct sym_entry, rb_node); - if (++printed > print_entries || - (int)syme->snap_count < count_filter) - continue; - - if (syme->map->dso->long_name_len > dso_width) - dso_width = syme->map->dso->long_name_len; - - if (syme->map->dso->short_name_len > dso_short_width) - dso_short_width = syme->map->dso->short_name_len; - - if (syme->name_len > sym_width) - sym_width = syme->name_len; - } - - printed = 0; + perf_top__find_widths(&top, &tmp, &dso_width, &dso_short_width, + &sym_width); if (sym_width + dso_width > winsize.ws_col - 29) { dso_width = dso_short_width; @@ -606,7 +289,7 @@ static void print_sym_table(void) sym_width = winsize.ws_col - dso_width - 29; } putchar('\n'); - if (nr_counters == 1) + if (top.evlist->nr_entries == 1) printf(" samples pcnt"); else printf(" weight samples pcnt"); @@ -615,7 +298,7 @@ static void print_sym_table(void) printf(" RIP "); printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); printf(" %s _______ _____", - nr_counters == 1 ? " " : "______"); + top.evlist->nr_entries == 1 ? " " : "______"); if (verbose) printf(" ________________"); printf(" %-*.*s", sym_width, sym_width, graph_line); @@ -628,13 +311,14 @@ 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) + if (++printed > top.print_entries || + (int)syme->snap_count < top.count_filter) continue; pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / sum_ksamples)); - if (nr_counters == 1 || !display_weighted) + if (top.evlist->nr_entries == 1 || !top.display_weighted) printf("%20.2f ", syme->weight); else printf("%9.1f %10ld ", syme->weight, syme->snap_count); @@ -693,10 +377,8 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) /* zero counters of active symbol */ if (syme) { - pthread_mutex_lock(&syme->src->lock); __zero_source_counters(syme); *target = NULL; - pthread_mutex_unlock(&syme->src->lock); } fprintf(stdout, "\n%s: ", msg); @@ -707,11 +389,11 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) if (p) *p = 0; - pthread_mutex_lock(&active_symbols_lock); - syme = list_entry(active_symbols.next, struct sym_entry, node); - pthread_mutex_unlock(&active_symbols_lock); + pthread_mutex_lock(&top.active_symbols_lock); + syme = list_entry(top.active_symbols.next, struct sym_entry, node); + pthread_mutex_unlock(&top.active_symbols_lock); - list_for_each_entry_safe_from(syme, n, &active_symbols, node) { + list_for_each_entry_safe_from(syme, n, &top.active_symbols, node) { struct symbol *sym = sym_entry__symbol(syme); if (!strcmp(buf, sym->name)) { @@ -741,28 +423,28 @@ static void print_mapped_keys(void) } fprintf(stdout, "\nMapped keys:\n"); - fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); - fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); + fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", top.delay_secs); + fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top.print_entries); - if (nr_counters > 1) - fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); + if (top.evlist->nr_entries > 1) + fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top.sym_evsel)); - fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); + fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top.count_filter); fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); fprintf(stdout, "\t[S] stop annotation.\n"); - if (nr_counters > 1) - fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); + if (top.evlist->nr_entries > 1) + fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", top.display_weighted ? 1 : 0); fprintf(stdout, "\t[K] hide kernel_symbols symbols. \t(%s)\n", - hide_kernel_symbols ? "yes" : "no"); + top.hide_kernel_symbols ? "yes" : "no"); fprintf(stdout, "\t[U] hide user symbols. \t(%s)\n", - hide_user_symbols ? "yes" : "no"); - fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); + top.hide_user_symbols ? "yes" : "no"); + fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", top.zero ? 1 : 0); fprintf(stdout, "\t[qQ] quit.\n"); } @@ -783,7 +465,7 @@ static int key_mapped(int c) return 1; case 'E': case 'w': - return nr_counters > 1 ? 1 : 0; + return top.evlist->nr_entries > 1 ? 1 : 0; default: break; } @@ -818,47 +500,47 @@ static void handle_keypress(struct perf_session *session, int c) switch (c) { case 'd': - prompt_integer(&delay_secs, "Enter display delay"); - if (delay_secs < 1) - delay_secs = 1; + prompt_integer(&top.delay_secs, "Enter display delay"); + if (top.delay_secs < 1) + top.delay_secs = 1; break; case 'e': - prompt_integer(&print_entries, "Enter display entries (lines)"); - if (print_entries == 0) { + prompt_integer(&top.print_entries, "Enter display entries (lines)"); + if (top.print_entries == 0) { sig_winch_handler(SIGWINCH); signal(SIGWINCH, sig_winch_handler); } else signal(SIGWINCH, SIG_DFL); break; case 'E': - if (nr_counters > 1) { + if (top.evlist->nr_entries > 1) { fprintf(stderr, "\nAvailable events:"); - list_for_each_entry(sym_evsel, &evsel_list, node) - fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); + list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) + fprintf(stderr, "\n\t%d %s", top.sym_evsel->idx, event_name(top.sym_evsel)); - prompt_integer(&sym_counter, "Enter details event counter"); + prompt_integer(&top.sym_counter, "Enter details event counter"); - if (sym_counter >= nr_counters) { - sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); - sym_counter = 0; - fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); + if (top.sym_counter >= top.evlist->nr_entries) { + top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); + top.sym_counter = 0; + fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top.sym_evsel)); sleep(1); break; } - list_for_each_entry(sym_evsel, &evsel_list, node) - if (sym_evsel->idx == sym_counter) + list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) + if (top.sym_evsel->idx == top.sym_counter) break; - } else sym_counter = 0; + } else top.sym_counter = 0; break; case 'f': - prompt_integer(&count_filter, "Enter display event count filter"); + prompt_integer(&top.count_filter, "Enter display event count filter"); break; case 'F': prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); break; case 'K': - hide_kernel_symbols = !hide_kernel_symbols; + top.hide_kernel_symbols = !top.hide_kernel_symbols; break; case 'q': case 'Q': @@ -875,26 +557,32 @@ static void handle_keypress(struct perf_session *session, int c) else { struct sym_entry *syme = sym_filter_entry; - pthread_mutex_lock(&syme->src->lock); sym_filter_entry = NULL; __zero_source_counters(syme); - pthread_mutex_unlock(&syme->src->lock); } break; case 'U': - hide_user_symbols = !hide_user_symbols; + top.hide_user_symbols = !top.hide_user_symbols; break; case 'w': - display_weighted = ~display_weighted; + top.display_weighted = ~top.display_weighted; break; case 'z': - zero = !zero; + top.zero = !top.zero; break; default: break; } } +static void *display_thread_tui(void *arg __used) +{ + perf_top__tui_browser(&top); + exit_browser(0); + exit(0); + return NULL; +} + static void *display_thread(void *arg __used) { struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; @@ -909,13 +597,13 @@ static void *display_thread(void *arg __used) tc.c_cc[VTIME] = 0; repeat: - delay_msecs = delay_secs * 1000; + delay_msecs = top.delay_secs * 1000; tcsetattr(0, TCSANOW, &tc); /* trash return*/ getc(stdin); do { - print_sym_table(); + print_sym_table(session); } while (!poll(&stdin_poll, 1, delay_msecs) == 1); c = getc(stdin); @@ -930,6 +618,7 @@ repeat: /* Tag samples to be skipped. */ static const char *skip_symbols[] = { "default_idle", + "native_safe_halt", "cpu_idle", "enter_idle", "exit_idle", @@ -965,7 +654,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) syme = symbol__priv(sym); syme->map = map; - syme->src = NULL; + symbol__annotate_init(map, sym); if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { /* schedule initial sym_filter_entry setup */ @@ -980,44 +669,40 @@ static int symbol_filter(struct map *map, struct symbol *sym) } } - if (!syme->skip) - syme->name_len = strlen(sym->name); - return 0; } -static void event__process_sample(const event_t *self, - struct sample_data *sample, - struct perf_session *session, - struct perf_evsel *evsel) +static void perf_event__process_sample(const union perf_event *event, + struct perf_sample *sample, + struct perf_session *session) { - u64 ip = self->ip.ip; + u64 ip = event->ip.ip; struct sym_entry *syme; struct addr_location al; struct machine *machine; - u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - ++samples; + ++top.samples; switch (origin) { case PERF_RECORD_MISC_USER: - ++us_samples; - if (hide_user_symbols) + ++top.us_samples; + if (top.hide_user_symbols) return; machine = perf_session__find_host_machine(session); break; case PERF_RECORD_MISC_KERNEL: - ++kernel_samples; - if (hide_kernel_symbols) + ++top.kernel_samples; + if (top.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); + ++top.guest_kernel_samples; + machine = perf_session__find_machine(session, event->ip.pid); break; case PERF_RECORD_MISC_GUEST_USER: - ++guest_us_samples; + ++top.guest_us_samples; /* * TODO: we don't process guest user from host side * except simple counting. @@ -1029,15 +714,15 @@ static void event__process_sample(const event_t *self, if (!machine && perf_guest) { pr_err("Can't find guest [%d]'s kernel information\n", - self->ip.pid); + event->ip.pid); return; } - if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) - exact_samples++; + if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) + top.exact_samples++; - if (event__preprocess_sample(self, session, &al, sample, - symbol_filter) < 0 || + if (perf_event__preprocess_sample(event, session, &al, sample, + symbol_filter) < 0 || al.filtered) return; @@ -1082,166 +767,67 @@ static void event__process_sample(const event_t *self, syme = symbol__priv(al.sym); if (!syme->skip) { - syme->count[evsel->idx]++; + struct perf_evsel *evsel; + syme->origin = origin; + evsel = perf_evlist__id2evsel(top.evlist, sample->id); + assert(evsel != NULL); + syme->count[evsel->idx]++; record_precise_ip(syme, evsel->idx, ip); - pthread_mutex_lock(&active_symbols_lock); + pthread_mutex_lock(&top.active_symbols_lock); if (list_empty(&syme->node) || !syme->node.next) __list_insert_active_sym(syme); - pthread_mutex_unlock(&active_symbols_lock); + pthread_mutex_unlock(&top.active_symbols_lock); } } -struct mmap_data { - void *base; - int mask; - unsigned int prev; -}; - -static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel, - int ncpus, int nthreads) +static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu) { - evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data)); - return evsel->priv != NULL ? 0 : -ENOMEM; -} + struct perf_sample sample; + union perf_event *event; -static void perf_evsel__free_mmap(struct perf_evsel *evsel) -{ - xyarray__delete(evsel->priv); - evsel->priv = NULL; -} - -static unsigned int mmap_read_head(struct mmap_data *md) -{ - struct perf_event_mmap_page *pc = md->base; - int head; - - head = pc->data_head; - rmb(); - - return head; -} + while ((event = perf_evlist__read_on_cpu(top.evlist, cpu)) != NULL) { + perf_session__parse_sample(self, event, &sample); -static void perf_session__mmap_read_counter(struct perf_session *self, - struct perf_evsel *evsel, - int cpu, int thread_idx) -{ - struct xyarray *mmap_array = evsel->priv; - struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx); - unsigned int head = mmap_read_head(md); - unsigned int old = md->prev; - unsigned char *data = md->base + page_size; - struct sample_data sample; - int diff; - - /* - * If we're further behind than half the buffer, there's a chance - * the writer will bite our tail and mess up the samples under us. - * - * If we somehow ended up ahead of the head, we got messed up. - * - * In either case, truncate and restart at head. - */ - diff = head - old; - if (diff > md->mask / 2 || diff < 0) { - fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); - - /* - * head points to a known good entry, start there. - */ - old = head; - } - - for (; old != head;) { - event_t *event = (event_t *)&data[old & md->mask]; - - event_t event_copy; - - size_t size = event->header.size; - - /* - * Event straddles the mmap boundary -- header should always - * be inside due to u64 alignment of output. - */ - if ((old & md->mask) + size != ((old + size) & md->mask)) { - unsigned int offset = old; - unsigned int len = min(sizeof(*event), size), cpy; - void *dst = &event_copy; - - do { - cpy = min(md->mask + 1 - (offset & md->mask), len); - memcpy(dst, &data[offset & md->mask], cpy); - offset += cpy; - dst += cpy; - len -= cpy; - } while (len); - - event = &event_copy; - } - - event__parse_sample(event, self, &sample); if (event->header.type == PERF_RECORD_SAMPLE) - event__process_sample(event, &sample, self, evsel); + perf_event__process_sample(event, &sample, self); else - event__process(event, &sample, self); - old += size; + perf_event__process(event, &sample, self); } - - md->prev = old; } -static struct pollfd *event_array; - static void perf_session__mmap_read(struct perf_session *self) { - struct perf_evsel *counter; - int i, thread_index; - - for (i = 0; i < cpus->nr; i++) { - list_for_each_entry(counter, &evsel_list, node) { - for (thread_index = 0; - thread_index < threads->nr; - thread_index++) { - perf_session__mmap_read_counter(self, - counter, i, thread_index); - } - } - } -} + int i; -int nr_poll; -int group_fd; + for (i = 0; i < top.evlist->cpus->nr; i++) + perf_session__mmap_read_cpu(self, i); +} -static void start_counter(int i, struct perf_evsel *evsel) +static void start_counters(struct perf_evlist *evlist) { - struct xyarray *mmap_array = evsel->priv; - struct mmap_data *mm; - struct perf_event_attr *attr; - int cpu = -1; - int thread_index; - - if (target_tid == -1) - cpu = cpus->map[i]; + struct perf_evsel *counter; - attr = &evsel->attr; + list_for_each_entry(counter, &evlist->entries, node) { + struct perf_event_attr *attr = &counter->attr; - attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; + attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; - if (freq) { - attr->sample_type |= PERF_SAMPLE_PERIOD; - attr->freq = 1; - attr->sample_freq = freq; - } + if (top.freq) { + attr->sample_type |= PERF_SAMPLE_PERIOD; + attr->freq = 1; + attr->sample_freq = top.freq; + } - attr->inherit = (cpu < 0) && inherit; - attr->mmap = 1; + if (evlist->nr_entries > 1) { + attr->sample_type |= PERF_SAMPLE_ID; + attr->read_format |= PERF_FORMAT_ID; + } - for (thread_index = 0; thread_index < threads->nr; thread_index++) { + attr->mmap = 1; try_again: - FD(evsel, i, thread_index) = sys_perf_event_open(attr, - threads->map[thread_index], cpu, group_fd, 0); - - if (FD(evsel, i, thread_index) < 0) { + if (perf_evsel__open(counter, top.evlist->cpus, + top.evlist->threads, group, inherit) < 0) { int err = errno; if (err == EPERM || err == EACCES) @@ -1253,8 +839,8 @@ try_again: * 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 (attr->type == PERF_TYPE_HARDWARE && + attr->config == PERF_COUNT_HW_CPU_CYCLES) { if (verbose) warning(" ... trying to fall back to cpu-clock-ticks\n"); @@ -1264,39 +850,23 @@ try_again: goto try_again; } printf("\n"); - error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", - FD(evsel, i, thread_index), strerror(err)); + error("sys_perf_event_open() syscall returned with %d " + "(%s). /bin/dmesg may provide additional information.\n", + err, strerror(err)); die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); exit(-1); } - assert(FD(evsel, i, thread_index) >= 0); - fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK); - - /* - * First counter acts as the group leader: - */ - if (group && group_fd == -1) - group_fd = FD(evsel, i, thread_index); - - event_array[nr_poll].fd = FD(evsel, i, thread_index); - event_array[nr_poll].events = POLLIN; - nr_poll++; - - mm = xyarray__entry(mmap_array, i, thread_index); - mm->prev = 0; - mm->mask = mmap_pages*page_size - 1; - mm->base = mmap(NULL, (mmap_pages+1)*page_size, - PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0); - if (mm->base == MAP_FAILED) - die("failed to mmap with %d (%s)\n", errno, strerror(errno)); } + + if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) + die("failed to mmap with %d (%s)\n", errno, strerror(errno)); } static int __cmd_top(void) { pthread_t thread; - struct perf_evsel *counter; - int i, ret; + struct perf_evsel *first; + int ret __used; /* * 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. @@ -1305,23 +875,23 @@ static int __cmd_top(void) if (session == NULL) return -ENOMEM; - if (target_tid != -1) - event__synthesize_thread(target_tid, event__process, session); + if (top.target_tid != -1) + perf_event__synthesize_thread_map(top.evlist->threads, + perf_event__process, session); else - event__synthesize_threads(event__process, session); + perf_event__synthesize_threads(perf_event__process, session); - for (i = 0; i < cpus->nr; i++) { - group_fd = -1; - list_for_each_entry(counter, &evsel_list, node) - start_counter(i, counter); - } + start_counters(top.evlist); + first = list_entry(top.evlist->entries.next, struct perf_evsel, node); + perf_session__set_sample_type(session, first->attr.sample_type); /* Wait for a minimal set of events before starting the snapshot */ - poll(&event_array[0], nr_poll, 100); + poll(top.evlist->pollfd, top.evlist->nr_fds, 100); perf_session__mmap_read(session); - if (pthread_create(&thread, NULL, display_thread, session)) { + if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : + display_thread), session)) { printf("Could not create display thread.\n"); exit(-1); } @@ -1337,12 +907,12 @@ static int __cmd_top(void) } while (1) { - int hits = samples; + u64 hits = top.samples; perf_session__mmap_read(session); - if (hits == samples) - ret = poll(event_array, nr_poll, 100); + if (hits == top.samples) + ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); } return 0; @@ -1354,31 +924,31 @@ static const char * const top_usage[] = { }; static const struct option options[] = { - OPT_CALLBACK('e', "event", NULL, "event", + OPT_CALLBACK('e', "event", &top.evlist, "event", "event selector. use 'perf list' to list available events", parse_events), OPT_INTEGER('c', "count", &default_interval, "event period to sample"), - OPT_INTEGER('p', "pid", &target_pid, + OPT_INTEGER('p', "pid", &top.target_pid, "profile events on existing process id"), - OPT_INTEGER('t', "tid", &target_tid, + OPT_INTEGER('t', "tid", &top.target_tid, "profile events on existing thread id"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), - OPT_STRING('C', "cpu", &cpu_list, "cpu", + OPT_STRING('C', "cpu", &top.cpu_list, "cpu", "list of cpus to monitor"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), - OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, + OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, "hide kernel symbols"), OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), OPT_INTEGER('r', "realtime", &realtime_prio, "collect data with this RT SCHED_FIFO priority"), - OPT_INTEGER('d', "delay", &delay_secs, + OPT_INTEGER('d', "delay", &top.delay_secs, "number of seconds to delay between refreshes"), OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, "dump the symbol table used for profiling"), - OPT_INTEGER('f', "count-filter", &count_filter, + OPT_INTEGER('f', "count-filter", &top.count_filter, "only display functions with more events than this"), OPT_BOOLEAN('g', "group", &group, "put the counters into a counter group"), @@ -1386,14 +956,16 @@ static const struct option options[] = { "child tasks inherit counters"), OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", "symbol to annotate"), - OPT_BOOLEAN('z', "zero", &zero, + OPT_BOOLEAN('z', "zero", &top.zero, "zero history across updates"), - OPT_INTEGER('F', "freq", &freq, + OPT_INTEGER('F', "freq", &top.freq, "profile at this frequency"), - OPT_INTEGER('E', "entries", &print_entries, + OPT_INTEGER('E', "entries", &top.print_entries, "display this many functions"), - OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, + OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, "hide user symbols"), + OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"), + OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_END() @@ -1404,64 +976,68 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) struct perf_evsel *pos; int status = -ENOMEM; + top.evlist = perf_evlist__new(NULL, NULL); + if (top.evlist == NULL) + return -ENOMEM; + page_size = sysconf(_SC_PAGE_SIZE); argc = parse_options(argc, argv, options, top_usage, 0); if (argc) usage_with_options(top_usage, options); - if (target_pid != -1) - target_tid = target_pid; + /* + * XXX For now start disabled, only using TUI if explicitely asked for. + * Change that when handle_keys equivalent gets written, live annotation + * done, etc. + */ + use_browser = 0; - threads = thread_map__new(target_pid, target_tid); - if (threads == NULL) { - pr_err("Problems finding threads of monitor\n"); - usage_with_options(top_usage, options); - } + if (use_stdio) + use_browser = 0; + else if (use_tui) + use_browser = 1; - event_array = malloc((sizeof(struct pollfd) * - MAX_NR_CPUS * MAX_COUNTERS * threads->nr)); - if (!event_array) - return -ENOMEM; + setup_browser(false); /* CPU and PID are mutually exclusive */ - if (target_tid > 0 && cpu_list) { + if (top.target_tid > 0 && top.cpu_list) { printf("WARNING: PID switch overriding CPU\n"); sleep(1); - cpu_list = NULL; + top.cpu_list = NULL; } - if (!nr_counters && perf_evsel_list__create_default() < 0) { + if (top.target_pid != -1) + top.target_tid = top.target_pid; + + if (perf_evlist__create_maps(top.evlist, top.target_pid, + top.target_tid, top.cpu_list) < 0) + usage_with_options(top_usage, options); + + if (!top.evlist->nr_entries && + perf_evlist__add_default(top.evlist) < 0) { pr_err("Not enough memory for event selector list\n"); return -ENOMEM; } - if (delay_secs < 1) - delay_secs = 1; + if (top.delay_secs < 1) + top.delay_secs = 1; /* * User specified count overrides default frequency. */ if (default_interval) - freq = 0; - else if (freq) { - default_interval = freq; + top.freq = 0; + else if (top.freq) { + default_interval = top.freq; } else { fprintf(stderr, "frequency and count are zero, aborting\n"); exit(EXIT_FAILURE); } - if (target_tid != -1) - cpus = cpu_map__dummy_new(); - else - cpus = cpu_map__new(cpu_list); - - if (cpus == NULL) - usage_with_options(top_usage, options); - - list_for_each_entry(pos, &evsel_list, node) { - if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 || - perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) + list_for_each_entry(pos, &top.evlist->entries, node) { + if (perf_evsel__alloc_fd(pos, top.evlist->cpus->nr, + top.evlist->threads->nr) < 0) goto out_free_fd; /* * Fill in the ones not specifically initialized via -c: @@ -1472,26 +1048,28 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) pos->attr.sample_period = default_interval; } - sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); + if (perf_evlist__alloc_pollfd(top.evlist) < 0 || + perf_evlist__alloc_mmap(top.evlist) < 0) + goto out_free_fd; + + top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); - symbol_conf.priv_size = (sizeof(struct sym_entry) + - (nr_counters + 1) * sizeof(unsigned long)); + symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) + + (top.evlist->nr_entries + 1) * sizeof(unsigned long)); symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); if (symbol__init() < 0) return -1; get_term_dimensions(&winsize); - if (print_entries == 0) { + if (top.print_entries == 0) { update_print_entries(&winsize); signal(SIGWINCH, sig_winch_handler); } status = __cmd_top(); out_free_fd: - list_for_each_entry(pos, &evsel_list, node) - perf_evsel__free_mmap(pos); - perf_evsel_list__delete(); + perf_evlist__delete(top.evlist); return status; } diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 95aaf565c70..a5fc660c1f1 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -94,6 +94,32 @@ void get_term_dimensions(struct winsize *ws); #include "util/types.h" #include <stdbool.h> +struct perf_mmap { + void *base; + int mask; + unsigned int prev; +}; + +static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) +{ + struct perf_event_mmap_page *pc = mm->base; + int head = pc->data_head; + rmb(); + return head; +} + +static inline void perf_mmap__write_tail(struct perf_mmap *md, + unsigned long tail) +{ + struct perf_event_mmap_page *pc = md->base; + + /* + * ensure all reads are done before we write the tail out. + */ + /* mb(); */ + pc->data_tail = tail; +} + /* * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all * counters in the current task. diff --git a/tools/perf/python/twatch.py b/tools/perf/python/twatch.py new file mode 100755 index 00000000000..df638c438a9 --- /dev/null +++ b/tools/perf/python/twatch.py @@ -0,0 +1,41 @@ +#! /usr/bin/python +# -*- python -*- +# -*- coding: utf-8 -*- +# twatch - Experimental use of the perf python interface +# Copyright (C) 2011 Arnaldo Carvalho de Melo <acme@redhat.com> +# +# This application 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; version 2. +# +# This application 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. + +import perf + +def main(): + cpus = perf.cpu_map() + threads = perf.thread_map() + evsel = perf.evsel(task = 1, comm = 1, mmap = 0, + wakeup_events = 1, sample_period = 1, + sample_id_all = 1, + sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU | perf.SAMPLE_TID) + evsel.open(cpus = cpus, threads = threads); + evlist = perf.evlist(cpus, threads) + evlist.add(evsel) + evlist.mmap() + while True: + evlist.poll(timeout = -1) + for cpu in cpus: + event = evlist.read_on_cpu(cpu) + if not event: + continue + print "cpu: %2d, pid: %4d, tid: %4d" % (event.sample_cpu, + event.sample_pid, + event.sample_tid), + print event + +if __name__ == '__main__': + main() diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c new file mode 100644 index 00000000000..02976b895f2 --- /dev/null +++ b/tools/perf/util/annotate.c @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + * + * Parts came from builtin-annotate.c, see those files for further + * copyright notes. + * + * Released under the GPL v2. (and only v2, not any later version) + */ + +#include "util.h" +#include "build-id.h" +#include "color.h" +#include "cache.h" +#include "symbol.h" +#include "debug.h" +#include "annotate.h" +#include <pthread.h> + +int symbol__annotate_init(struct map *map __used, struct symbol *sym) +{ + struct annotation *notes = symbol__annotation(sym); + pthread_mutex_init(¬es->lock, NULL); + return 0; +} + +int symbol__alloc_hist(struct symbol *sym, int nevents) +{ + struct annotation *notes = symbol__annotation(sym); + size_t sizeof_sym_hist = (sizeof(struct sym_hist) + + (sym->end - sym->start) * sizeof(u64)); + + notes->src = zalloc(sizeof(*notes->src) + nevents * sizeof_sym_hist); + if (notes->src == NULL) + return -1; + notes->src->sizeof_sym_hist = sizeof_sym_hist; + notes->src->nr_histograms = nevents; + INIT_LIST_HEAD(¬es->src->source); + return 0; +} + +void symbol__annotate_zero_histograms(struct symbol *sym) +{ + struct annotation *notes = symbol__annotation(sym); + + pthread_mutex_lock(¬es->lock); + if (notes->src != NULL) + memset(notes->src->histograms, 0, + notes->src->nr_histograms * notes->src->sizeof_sym_hist); + pthread_mutex_unlock(¬es->lock); +} + +int symbol__inc_addr_samples(struct symbol *sym, struct map *map, + int evidx, u64 addr) +{ + unsigned offset; + struct annotation *notes; + struct sym_hist *h; + + notes = symbol__annotation(sym); + if (notes->src == NULL) + return -ENOMEM; + + pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); + + if (addr >= sym->end) + return 0; + + offset = addr - sym->start; + h = annotation__histogram(notes, evidx); + h->sum++; + h->addr[offset]++; + + pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64 + ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name, + addr, addr - sym->start, evidx, h->addr[offset]); + return 0; +} + +static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) +{ + struct objdump_line *self = malloc(sizeof(*self) + privsize); + + if (self != NULL) { + self->offset = offset; + self->line = line; + } + + return self; +} + +void objdump_line__free(struct objdump_line *self) +{ + free(self->line); + free(self); +} + +static void objdump__add_line(struct list_head *head, struct objdump_line *line) +{ + list_add_tail(&line->node, head); +} + +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos) +{ + list_for_each_entry_continue(pos, head, node) + if (pos->offset >= 0) + return pos; + + return NULL; +} + +static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, + int evidx, u64 len, int min_pcnt, + int printed, int max_lines, + struct objdump_line *queue) +{ + static const char *prev_line; + static const char *prev_color; + + if (oline->offset != -1) { + const char *path = NULL; + unsigned int hits = 0; + double percent = 0.0; + const char *color; + struct annotation *notes = symbol__annotation(sym); + struct source_line *src_line = notes->src->lines; + struct sym_hist *h = annotation__histogram(notes, evidx); + s64 offset = oline->offset; + struct objdump_line *next; + + next = objdump__get_next_ip_line(¬es->src->source, oline); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (src_line) { + if (path == NULL) + path = src_line[offset].path; + percent += src_line[offset].percent; + } else + hits += h->addr[offset]; + + ++offset; + } + + if (src_line == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + if (percent < min_pcnt) + return -1; + + if (max_lines && printed >= max_lines) + return 1; + + if (queue != NULL) { + list_for_each_entry_from(queue, ¬es->src->source, node) { + if (queue == oline) + break; + objdump_line__print(queue, sym, evidx, len, + 0, 0, 1, NULL); + } + } + + color = get_percent_color(percent); + + /* + * Also color the filename and line if needed, with + * the same color than the percentage. Don't print it + * twice for close colored addr with the same filename:line + */ + if (path) { + if (!prev_line || strcmp(prev_line, path) + || color != prev_color) { + color_fprintf(stdout, color, " %s", path); + prev_line = path; + prev_color = color; + } + } + + color_fprintf(stdout, color, " %7.2f", percent); + printf(" : "); + color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); + } else if (max_lines && printed >= max_lines) + return 1; + else { + if (queue) + return -1; + + if (!*oline->line) + printf(" :\n"); + else + printf(" : %s\n", oline->line); + } + + return 0; +} + +static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, + FILE *file, size_t privsize) +{ + struct annotation *notes = symbol__annotation(sym); + struct objdump_line *objdump_line; + char *line = NULL, *tmp, *tmp2, *c; + size_t line_len; + s64 line_ip, offset = -1; + + if (getline(&line, &line_len, file) < 0) + return -1; + + if (!line) + return -1; + + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + c = strchr(line, '\n'); + if (c) + *c = 0; + + line_ip = -1; + + /* + * Strip leading spaces: + */ + tmp = line; + while (*tmp) { + if (*tmp != ' ') + break; + tmp++; + } + + if (*tmp) { + /* + * Parse hexa addresses followed by ':' + */ + line_ip = strtoull(tmp, &tmp2, 16); + if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') + line_ip = -1; + } + + if (line_ip != -1) { + u64 start = map__rip_2objdump(map, sym->start), + end = map__rip_2objdump(map, sym->end); + + offset = line_ip - start; + if (offset < 0 || (u64)line_ip > end) + offset = -1; + } + + objdump_line = objdump_line__new(offset, line, privsize); + if (objdump_line == NULL) { + free(line); + return -1; + } + objdump__add_line(¬es->src->source, objdump_line); + + return 0; +} + +int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) +{ + struct dso *dso = map->dso; + char *filename = dso__build_id_filename(dso, NULL, 0); + bool free_filename = true; + char command[PATH_MAX * 2]; + FILE *file; + int err = 0; + char symfs_filename[PATH_MAX]; + + if (filename) { + snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", + symbol_conf.symfs, filename); + } + + if (filename == NULL) { + if (dso->has_build_id) { + pr_err("Can't annotate %s: not enough memory\n", + sym->name); + return -ENOMEM; + } + goto fallback; + } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || + strstr(command, "[kernel.kallsyms]") || + access(symfs_filename, R_OK)) { + free(filename); +fallback: + /* + * If we don't have build-ids or the build-id file isn't in the + * cache, or is just a kallsyms file, well, lets hope that this + * DSO is the same as when 'perf record' ran. + */ + filename = dso->long_name; + snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", + symbol_conf.symfs, filename); + free_filename = false; + } + + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + goto out_free_filename; + err = -ENOENT; + dso->annotate_warned = 1; + pr_err("Can't annotate %s: No vmlinux file was found in the " + "path\n", sym->name); + goto out_free_filename; + } + + pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, + filename, sym->name, map->unmap_ip(map, sym->start), + map->unmap_ip(map, sym->end)); + + pr_debug("annotating [%p] %30s : [%p] %30s\n", + dso, dso->long_name, sym, sym->name); + + snprintf(command, sizeof(command), + "objdump --start-address=0x%016" PRIx64 + " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", + map__rip_2objdump(map, sym->start), + map__rip_2objdump(map, sym->end), + symfs_filename, filename); + + pr_debug("Executing: %s\n", command); + + file = popen(command, "r"); + if (!file) + goto out_free_filename; + + while (!feof(file)) + if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) + break; + + pclose(file); +out_free_filename: + if (free_filename) + free(filename); + return err; +} + +static void insert_source_line(struct rb_root *root, struct source_line *src_line) +{ + struct source_line *iter; + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + + while (*p != NULL) { + parent = *p; + iter = rb_entry(parent, struct source_line, node); + + if (src_line->percent > iter->percent) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&src_line->node, parent, p); + rb_insert_color(&src_line->node, root); +} + +static void symbol__free_source_line(struct symbol *sym, int len) +{ + struct annotation *notes = symbol__annotation(sym); + struct source_line *src_line = notes->src->lines; + int i; + + for (i = 0; i < len; i++) + free(src_line[i].path); + + free(src_line); + notes->src->lines = NULL; +} + +/* Get the filename:line for the colored entries */ +static int symbol__get_source_line(struct symbol *sym, struct map *map, + int evidx, struct rb_root *root, int len, + const char *filename) +{ + u64 start; + int i; + char cmd[PATH_MAX * 2]; + struct source_line *src_line; + struct annotation *notes = symbol__annotation(sym); + struct sym_hist *h = annotation__histogram(notes, evidx); + + if (!h->sum) + return 0; + + src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); + if (!notes->src->lines) + return -1; + + start = map->unmap_ip(map, sym->start); + + for (i = 0; i < len; i++) { + char *path = NULL; + size_t line_len; + u64 offset; + FILE *fp; + + src_line[i].percent = 100.0 * h->addr[i] / h->sum; + if (src_line[i].percent <= 0.5) + continue; + + offset = start + i; + sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); + fp = popen(cmd, "r"); + if (!fp) + continue; + + if (getline(&path, &line_len, fp) < 0 || !line_len) + goto next; + + src_line[i].path = malloc(sizeof(char) * line_len + 1); + if (!src_line[i].path) + goto next; + + strcpy(src_line[i].path, path); + insert_source_line(root, &src_line[i]); + + next: + pclose(fp); + } + + return 0; +} + +static void print_summary(struct rb_root *root, const char *filename) +{ + struct source_line *src_line; + struct rb_node *node; + + printf("\nSorted summary for file %s\n", filename); + printf("----------------------------------------------\n\n"); + + if (RB_EMPTY_ROOT(root)) { + printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); + return; + } + + node = rb_first(root); + while (node) { + double percent; + const char *color; + char *path; + + src_line = rb_entry(node, struct source_line, node); + percent = src_line->percent; + color = get_percent_color(percent); + path = src_line->path; + + color_fprintf(stdout, color, " %7.2f %s", percent, path); + node = rb_next(node); + } +} + +static void symbol__annotate_hits(struct symbol *sym, int evidx) +{ + struct annotation *notes = symbol__annotation(sym); + struct sym_hist *h = annotation__histogram(notes, evidx); + u64 len = sym->end - sym->start, offset; + + for (offset = 0; offset < len; ++offset) + if (h->addr[offset] != 0) + printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, + sym->start + offset, h->addr[offset]); + printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); +} + +int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, + bool full_paths, int min_pcnt, int max_lines, + int context) +{ + struct dso *dso = map->dso; + const char *filename = dso->long_name, *d_filename; + struct annotation *notes = symbol__annotation(sym); + struct objdump_line *pos, *queue = NULL; + int printed = 2, queue_len = 0; + int more = 0; + u64 len; + + if (full_paths) + d_filename = filename; + else + d_filename = basename(filename); + + len = sym->end - sym->start; + + printf(" Percent | Source code & Disassembly of %s\n", d_filename); + printf("------------------------------------------------\n"); + + if (verbose) + symbol__annotate_hits(sym, evidx); + + list_for_each_entry(pos, ¬es->src->source, node) { + if (context && queue == NULL) { + queue = pos; + queue_len = 0; + } + + switch (objdump_line__print(pos, sym, evidx, len, min_pcnt, + printed, max_lines, queue)) { + case 0: + ++printed; + if (context) { + printed += queue_len; + queue = NULL; + queue_len = 0; + } + break; + case 1: + /* filtered by max_lines */ + ++more; + break; + case -1: + default: + /* + * Filtered by min_pcnt or non IP lines when + * context != 0 + */ + if (!context) + break; + if (queue_len == context) + queue = list_entry(queue->node.next, typeof(*queue), node); + else + ++queue_len; + break; + } + } + + return more; +} + +void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) +{ + struct annotation *notes = symbol__annotation(sym); + struct sym_hist *h = annotation__histogram(notes, evidx); + + memset(h, 0, notes->src->sizeof_sym_hist); +} + +void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) +{ + struct annotation *notes = symbol__annotation(sym); + struct sym_hist *h = annotation__histogram(notes, evidx); + struct objdump_line *pos; + + h->sum = 0; + + list_for_each_entry(pos, ¬es->src->source, node) { + if (pos->offset != -1) { + h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8; + h->sum += h->addr[pos->offset]; + } + } +} + +void objdump_line_list__purge(struct list_head *head) +{ + struct objdump_line *pos, *n; + + list_for_each_entry_safe(pos, n, head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } +} + +int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, + bool print_lines, bool full_paths, int min_pcnt, + int max_lines) +{ + struct dso *dso = map->dso; + const char *filename = dso->long_name; + struct rb_root source_line = RB_ROOT; + u64 len; + + if (symbol__annotate(sym, map, 0) < 0) + return -1; + + len = sym->end - sym->start; + + if (print_lines) { + symbol__get_source_line(sym, map, evidx, &source_line, + len, filename); + print_summary(&source_line, filename); + } + + symbol__annotate_printf(sym, map, evidx, full_paths, + min_pcnt, max_lines, 0); + if (print_lines) + symbol__free_source_line(sym, len); + + objdump_line_list__purge(&symbol__annotation(sym)->src->source); + + return 0; +} diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h new file mode 100644 index 00000000000..e848803fcd4 --- /dev/null +++ b/tools/perf/util/annotate.h @@ -0,0 +1,101 @@ +#ifndef __PERF_ANNOTATE_H +#define __PERF_ANNOTATE_H + +#include <stdbool.h> +#include "types.h" +#include "symbol.h" +#include <linux/list.h> +#include <linux/rbtree.h> + +struct objdump_line { + struct list_head node; + s64 offset; + char *line; +}; + +void objdump_line__free(struct objdump_line *self); +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos); + +struct sym_hist { + u64 sum; + u64 addr[0]; +}; + +struct source_line { + struct rb_node node; + double percent; + char *path; +}; + +/** struct annotated_source - symbols with hits have this attached as in sannotation + * + * @histogram: Array of addr hit histograms per event being monitored + * @lines: If 'print_lines' is specified, per source code line percentages + * @source: source parsed from objdump -dS + * + * lines is allocated, percentages calculated and all sorted by percentage + * when the annotation is about to be presented, so the percentages are for + * one of the entries in the histogram array, i.e. for the event/counter being + * presented. It is deallocated right after symbol__{tui,tty,etc}_annotate + * returns. + */ +struct annotated_source { + struct list_head source; + struct source_line *lines; + int nr_histograms; + int sizeof_sym_hist; + struct sym_hist histograms[0]; +}; + +struct annotation { + pthread_mutex_t lock; + struct annotated_source *src; +}; + +struct sannotation { + struct annotation annotation; + struct symbol symbol; +}; + +static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx) +{ + return (((void *)¬es->src->histograms) + + (notes->src->sizeof_sym_hist * idx)); +} + +static inline struct annotation *symbol__annotation(struct symbol *sym) +{ + struct sannotation *a = container_of(sym, struct sannotation, symbol); + return &a->annotation; +} + +int symbol__inc_addr_samples(struct symbol *sym, struct map *map, + int evidx, u64 addr); +int symbol__alloc_hist(struct symbol *sym, int nevents); +void symbol__annotate_zero_histograms(struct symbol *sym); + +int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); +int symbol__annotate_init(struct map *map __used, struct symbol *sym); +int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, + bool full_paths, int min_pcnt, int max_lines, + int context); +void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); +void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); +void objdump_line_list__purge(struct list_head *head); + +int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, + bool print_lines, bool full_paths, int min_pcnt, + int max_lines); + +#ifdef NO_NEWT_SUPPORT +static inline int symbol__tui_annotate(struct symbol *sym __used, + struct map *map __used, int evidx __used) +{ + return 0; +} +#else +int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx); +#endif + +#endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index deffb8c9607..31f934af986 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -14,8 +14,8 @@ #include <linux/kernel.h> #include "debug.h" -static int build_id__mark_dso_hit(event_t *event, - struct sample_data *sample __used, +static int build_id__mark_dso_hit(union perf_event *event, + struct perf_sample *sample __used, struct perf_session *session) { struct addr_location al; @@ -37,13 +37,14 @@ static int build_id__mark_dso_hit(event_t *event, return 0; } -static int event__exit_del_thread(event_t *self, struct sample_data *sample __used, - struct perf_session *session) +static int perf_event__exit_del_thread(union perf_event *event, + struct perf_sample *sample __used, + struct perf_session *session) { - struct thread *thread = perf_session__findnew(session, self->fork.tid); + struct thread *thread = perf_session__findnew(session, event->fork.tid); - dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, - self->fork.ppid, self->fork.ptid); + dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, + event->fork.ppid, event->fork.ptid); if (thread) { rb_erase(&thread->rb_node, &session->threads); @@ -56,9 +57,9 @@ static int event__exit_del_thread(event_t *self, struct sample_data *sample __us struct perf_event_ops build_id__mark_dso_hit_ops = { .sample = build_id__mark_dso_hit, - .mmap = event__process_mmap, - .fork = event__process_task, - .exit = event__exit_del_thread, + .mmap = perf_event__process_mmap, + .fork = perf_event__process_task, + .exit = perf_event__exit_del_thread, }; char *dso__build_id_filename(struct dso *self, char *bf, size_t size) diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index a7729797fd9..fc5e5a09d5b 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -34,13 +34,14 @@ extern int pager_use_color; extern int use_browser; #ifdef NO_NEWT_SUPPORT -static inline void setup_browser(void) +static inline void setup_browser(bool fallback_to_pager) { - setup_pager(); + if (fallback_to_pager) + setup_pager(); } static inline void exit_browser(bool wait_for_ok __used) {} #else -void setup_browser(void); +void setup_browser(bool fallback_to_pager); void exit_browser(bool wait_for_ok); #endif diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e12d539417b..9f7106a8d9a 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> + * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com> * * Handle the callchains from the stream in an ad-hoc radix tree and then * sort them in an rbtree. @@ -18,7 +18,8 @@ #include "util.h" #include "callchain.h" -bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) +bool ip_callchain__valid(struct ip_callchain *chain, + const union perf_event *event) { unsigned int chain_size = event->header.size; chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; @@ -26,10 +27,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) } #define chain_for_each_child(child, parent) \ - list_for_each_entry(child, &parent->children, brothers) + list_for_each_entry(child, &parent->children, siblings) #define chain_for_each_child_safe(child, next, parent) \ - list_for_each_entry_safe(child, next, &parent->children, brothers) + list_for_each_entry_safe(child, next, &parent->children, siblings) static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, @@ -38,14 +39,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct callchain_node *rnode; - u64 chain_cumul = cumul_hits(chain); + u64 chain_cumul = callchain_cumul_hits(chain); while (*p) { u64 rnode_cumul; parent = *p; rnode = rb_entry(parent, struct callchain_node, rb_node); - rnode_cumul = cumul_hits(rnode); + rnode_cumul = callchain_cumul_hits(rnode); switch (mode) { case CHAIN_FLAT: @@ -104,7 +105,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, chain_for_each_child(child, node) { __sort_chain_graph_abs(child, min_hit); - if (cumul_hits(child) >= min_hit) + if (callchain_cumul_hits(child) >= min_hit) rb_insert_callchain(&node->rb_root, child, CHAIN_GRAPH_ABS); } @@ -129,7 +130,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, chain_for_each_child(child, node) { __sort_chain_graph_rel(child, min_percent); - if (cumul_hits(child) >= min_hit) + if (callchain_cumul_hits(child) >= min_hit) rb_insert_callchain(&node->rb_root, child, CHAIN_GRAPH_REL); } @@ -143,7 +144,7 @@ sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, rb_root->rb_node = chain_root->node.rb_root.rb_node; } -int register_callchain_param(struct callchain_param *param) +int callchain_register_param(struct callchain_param *param) { switch (param->mode) { case CHAIN_GRAPH_ABS: @@ -189,32 +190,27 @@ create_child(struct callchain_node *parent, bool inherit_children) chain_for_each_child(next, new) next->parent = new; } - list_add_tail(&new->brothers, &parent->children); + list_add_tail(&new->siblings, &parent->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 callchain_cursor *cursor) { - unsigned int i; + struct callchain_cursor_node *cursor_node; + + node->val_nr = cursor->nr - cursor->pos; + if (!node->val_nr) + pr_warning("Warning: empty node in callchain tree\n"); - for (i = start; i < chain->nr; i++) { + cursor_node = callchain_cursor_current(cursor); + + while (cursor_node) { struct callchain_list *call; call = zalloc(sizeof(*call)); @@ -222,23 +218,25 @@ 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 = cursor_node->ip; + call->ms.sym = cursor_node->sym; + call->ms.map = cursor_node->map; list_add_tail(&call->list, &node->val); + + callchain_cursor_advance(cursor); + cursor_node = callchain_cursor_current(cursor); } - node->val_nr = chain->nr - start; - if (!node->val_nr) - pr_warning("Warning: empty node in callchain tree\n"); } static void -add_child(struct callchain_node *parent, struct resolved_chain *chain, - int start, u64 period) +add_child(struct callchain_node *parent, + struct callchain_cursor *cursor, + u64 period) { struct callchain_node *new; new = create_child(parent, false); - fill_node(new, chain, start); + fill_node(new, cursor); new->children_hit = 0; new->hit = period; @@ -250,9 +248,10 @@ 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, - u64 period) +split_add_child(struct callchain_node *parent, + struct callchain_cursor *cursor, + struct callchain_list *to_split, + u64 idx_parents, u64 idx_local, u64 period) { struct callchain_node *new; struct list_head *old_tail; @@ -272,14 +271,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, /* split the hits */ new->hit = parent->hit; new->children_hit = parent->children_hit; - parent->children_hit = cumul_hits(new); + parent->children_hit = callchain_cumul_hits(new); new->val_nr = parent->val_nr - idx_local; parent->val_nr = idx_local; /* create a new child for the new branch if any */ - if (idx_total < chain->nr) { + if (idx_total < cursor->nr) { parent->hit = 0; - add_child(parent, chain, idx_total, period); + add_child(parent, cursor, period); parent->children_hit += period; } else { parent->hit = period; @@ -287,36 +286,41 @@ 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, u64 period); +append_chain(struct callchain_node *root, + struct callchain_cursor *cursor, + u64 period); static void -append_chain_children(struct callchain_node *root, struct resolved_chain *chain, - unsigned int start, u64 period) +append_chain_children(struct callchain_node *root, + struct callchain_cursor *cursor, + u64 period) { struct callchain_node *rnode; /* lookup in childrens */ chain_for_each_child(rnode, root) { - unsigned int ret = append_chain(rnode, chain, start, period); + unsigned int ret = append_chain(rnode, cursor, period); if (!ret) goto inc_children_hit; } /* nothing in children, add to the current node */ - add_child(root, chain, start, period); + add_child(root, cursor, period); inc_children_hit: root->children_hit += period; } static int -append_chain(struct callchain_node *root, struct resolved_chain *chain, - unsigned int start, u64 period) +append_chain(struct callchain_node *root, + struct callchain_cursor *cursor, + u64 period) { + struct callchain_cursor_node *curr_snap = cursor->curr; struct callchain_list *cnode; - unsigned int i = start; + u64 start = cursor->pos; bool found = false; + u64 matches; /* * Lookup in the current node @@ -324,141 +328,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain, * anywhere inside a function. */ list_for_each_entry(cnode, &root->val, list) { + struct callchain_cursor_node *node; struct symbol *sym; - if (i == chain->nr) + node = callchain_cursor_current(cursor); + if (!node) break; - sym = chain->ips[i].ms.sym; + sym = node->sym; if (cnode->ms.sym && sym) { if (cnode->ms.sym->start != sym->start) break; - } else if (cnode->ip != chain->ips[i].ip) + } else if (cnode->ip != node->ip) break; if (!found) found = true; - i++; + + callchain_cursor_advance(cursor); } /* matches not, relay on the parent */ - if (!found) + if (!found) { + cursor->curr = curr_snap; + cursor->pos = start; return -1; + } + + matches = cursor->pos - start; /* 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, period); + if (matches < root->val_nr) { + split_add_child(root, cursor, cnode, start, matches, period); return 0; } /* we match 100% of the path, increment the hit */ - if (i - start == root->val_nr && i == chain->nr) { + if (matches == root->val_nr && cursor->pos == cursor->nr) { root->hit += period; return 0; } /* We match the node and still have a part remaining */ - append_chain_children(root, chain, i, period); + append_chain_children(root, cursor, period); 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 callchain_append(struct callchain_root *root, struct ip_callchain *chain, - struct map_symbol *syms, u64 period) +int callchain_append(struct callchain_root *root, + struct callchain_cursor *cursor, + u64 period) { - struct resolved_chain *filtered; - - if (!chain->nr) + if (!cursor->nr) return 0; - filtered = zalloc(sizeof(*filtered) + - chain->nr * sizeof(struct resolved_ip)); - if (!filtered) - return -ENOMEM; - - filter_context(chain, filtered, syms); - - if (!filtered->nr) - goto end; + callchain_cursor_commit(cursor); - append_chain_children(&root->node, filtered, 0, period); + append_chain_children(&root->node, cursor, period); - if (filtered->nr > root->max_depth) - root->max_depth = filtered->nr; -end: - free(filtered); + if (cursor->nr > root->max_depth) + root->max_depth = cursor->nr; return 0; } static int -merge_chain_branch(struct callchain_node *dst, struct callchain_node *src, - struct resolved_chain *chain) +merge_chain_branch(struct callchain_cursor *cursor, + struct callchain_node *dst, struct callchain_node *src) { + struct callchain_cursor_node **old_last = cursor->last; struct callchain_node *child, *next_child; struct callchain_list *list, *next_list; - int old_pos = chain->nr; + int old_pos = cursor->nr; int err = 0; list_for_each_entry_safe(list, next_list, &src->val, list) { - chain->ips[chain->nr].ip = list->ip; - chain->ips[chain->nr].ms = list->ms; - chain->nr++; + callchain_cursor_append(cursor, list->ip, + list->ms.map, list->ms.sym); list_del(&list->list); free(list); } - if (src->hit) - append_chain_children(dst, chain, 0, src->hit); + if (src->hit) { + callchain_cursor_commit(cursor); + append_chain_children(dst, cursor, src->hit); + } chain_for_each_child_safe(child, next_child, src) { - err = merge_chain_branch(dst, child, chain); + err = merge_chain_branch(cursor, dst, child); if (err) break; - list_del(&child->brothers); + list_del(&child->siblings); free(child); } - chain->nr = old_pos; + cursor->nr = old_pos; + cursor->last = old_last; return err; } -int callchain_merge(struct callchain_root *dst, struct callchain_root *src) +int callchain_merge(struct callchain_cursor *cursor, + struct callchain_root *dst, struct callchain_root *src) +{ + return merge_chain_branch(cursor, &dst->node, &src->node); +} + +int callchain_cursor_append(struct callchain_cursor *cursor, + u64 ip, struct map *map, struct symbol *sym) { - struct resolved_chain *chain; - int err; + struct callchain_cursor_node *node = *cursor->last; - chain = malloc(sizeof(*chain) + - src->max_depth * sizeof(struct resolved_ip)); - if (!chain) - return -ENOMEM; + if (!node) { + node = calloc(sizeof(*node), 1); + if (!node) + return -ENOMEM; - chain->nr = 0; + *cursor->last = node; + } - err = merge_chain_branch(&dst->node, &src->node, chain); + node->ip = ip; + node->map = map; + node->sym = sym; - free(chain); + cursor->nr++; - return err; + cursor->last = &node->next; + + return 0; } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index c15fb8c24ad..1a79df9f739 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -16,7 +16,7 @@ enum chain_mode { struct callchain_node { struct callchain_node *parent; - struct list_head brothers; + struct list_head siblings; struct list_head children; struct list_head val; struct rb_node rb_node; /* to sort nodes in an rbtree */ @@ -49,9 +49,30 @@ struct callchain_list { struct list_head list; }; +/* + * A callchain cursor is a single linked list that + * let one feed a callchain progressively. + * It keeps persitent allocated entries to minimize + * allocations. + */ +struct callchain_cursor_node { + u64 ip; + struct map *map; + struct symbol *sym; + struct callchain_cursor_node *next; +}; + +struct callchain_cursor { + u64 nr; + struct callchain_cursor_node *first; + struct callchain_cursor_node **last; + u64 pos; + struct callchain_cursor_node *curr; +}; + static inline void callchain_init(struct callchain_root *root) { - INIT_LIST_HEAD(&root->node.brothers); + INIT_LIST_HEAD(&root->node.siblings); INIT_LIST_HEAD(&root->node.children); INIT_LIST_HEAD(&root->node.val); @@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root) root->max_depth = 0; } -static inline u64 cumul_hits(struct callchain_node *node) +static inline u64 callchain_cumul_hits(struct callchain_node *node) { return node->hit + node->children_hit; } -int register_callchain_param(struct callchain_param *param); -int callchain_append(struct callchain_root *root, struct ip_callchain *chain, - struct map_symbol *syms, u64 period); -int callchain_merge(struct callchain_root *dst, struct callchain_root *src); +int callchain_register_param(struct callchain_param *param); +int callchain_append(struct callchain_root *root, + struct callchain_cursor *cursor, + u64 period); + +int callchain_merge(struct callchain_cursor *cursor, + struct callchain_root *dst, struct callchain_root *src); + +bool ip_callchain__valid(struct ip_callchain *chain, + const union perf_event *event); +/* + * Initialize a cursor before adding entries inside, but keep + * the previously allocated entries as a cache. + */ +static inline void callchain_cursor_reset(struct callchain_cursor *cursor) +{ + cursor->nr = 0; + cursor->last = &cursor->first; +} + +int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, + struct map *map, struct symbol *sym); -bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); +/* Close a cursor writing session. Initialize for the reader */ +static inline void callchain_cursor_commit(struct callchain_cursor *cursor) +{ + cursor->curr = cursor->first; + cursor->pos = 0; +} + +/* Cursor reading iteration helpers */ +static inline struct callchain_cursor_node * +callchain_cursor_current(struct callchain_cursor *cursor) +{ + if (cursor->pos == cursor->nr) + return NULL; + + return cursor->curr; +} + +static inline void callchain_cursor_advance(struct callchain_cursor *cursor) +{ + cursor->curr = cursor->curr->next; + cursor->pos++; +} #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 3ccaa104338..6893eec693a 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void) return cpus; } + +void cpu_map__delete(struct cpu_map *map) +{ + free(map); +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index f7a4f42f630..072c0a37479 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -8,6 +8,6 @@ struct cpu_map { struct cpu_map *cpu_map__new(const char *cpu_list); struct cpu_map *cpu_map__dummy_new(void); -void *cpu_map__delete(struct cpu_map *map); +void cpu_map__delete(struct cpu_map *map); #endif /* __PERF_CPUMAP_H */ diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 01bbe8ecec3..d4536a9e0d8 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -57,7 +57,7 @@ void ui__warning(const char *format, ...) } #endif -void trace_event(event_t *event) +void trace_event(union perf_event *event) { unsigned char *raw_event = (void *)event; const char *color = PERF_COLOR_BLUE; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index ca35fd66b5d..93516cf4682 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -9,7 +9,7 @@ extern int verbose; extern bool quiet, dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); -void trace_event(event_t *event); +void trace_event(union perf_event *event); struct ui_progress; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1478ab4ee22..fbf5754c886 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -6,8 +6,9 @@ #include "string.h" #include "strlist.h" #include "thread.h" +#include "thread_map.h" -static const char *event__name[] = { +static const char *perf_event__names[] = { [0] = "TOTAL", [PERF_RECORD_MMAP] = "MMAP", [PERF_RECORD_LOST] = "LOST", @@ -25,16 +26,16 @@ static const char *event__name[] = { [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", }; -const char *event__get_event_name(unsigned int id) +const char *perf_event__name(unsigned int id) { - if (id >= ARRAY_SIZE(event__name)) + if (id >= ARRAY_SIZE(perf_event__names)) return "INVALID"; - if (!event__name[id]) + if (!perf_event__names[id]) return "UNKNOWN"; - return event__name[id]; + return perf_event__names[id]; } -static struct sample_data synth_sample = { +static struct perf_sample synth_sample = { .pid = -1, .tid = -1, .time = -1, @@ -43,9 +44,9 @@ static struct sample_data synth_sample = { .period = 1, }; -static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full, - event__handler_t process, - struct perf_session *session) +static pid_t perf_event__synthesize_comm(union perf_event *event, pid_t pid, + int full, perf_event__handler_t process, + struct perf_session *session) { char filename[PATH_MAX]; char bf[BUFSIZ]; @@ -126,9 +127,10 @@ out: return tgid; } -static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, - event__handler_t process, - struct perf_session *session) +static int perf_event__synthesize_mmap_events(union perf_event *event, + pid_t pid, pid_t tgid, + perf_event__handler_t process, + struct perf_session *session) { char filename[PATH_MAX]; FILE *fp; @@ -199,14 +201,14 @@ static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, return 0; } -int event__synthesize_modules(event__handler_t process, - struct perf_session *session, - struct machine *machine) +int perf_event__synthesize_modules(perf_event__handler_t process, + struct perf_session *session, + struct machine *machine) { struct rb_node *nd; struct map_groups *kmaps = &machine->kmaps; - event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); - + union perf_event *event = zalloc((sizeof(event->mmap) + + session->id_hdr_size)); if (event == NULL) { pr_debug("Not enough memory synthesizing mmap event " "for kernel modules\n"); @@ -251,23 +253,25 @@ int event__synthesize_modules(event__handler_t process, return 0; } -static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event, - pid_t pid, event__handler_t process, +static int __event__synthesize_thread(union perf_event *comm_event, + union perf_event *mmap_event, + pid_t pid, perf_event__handler_t process, struct perf_session *session) { - pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process, + pid_t tgid = perf_event__synthesize_comm(comm_event, pid, 1, process, session); if (tgid == -1) return -1; - return event__synthesize_mmap_events(mmap_event, pid, tgid, + return perf_event__synthesize_mmap_events(mmap_event, pid, tgid, process, session); } -int event__synthesize_thread(pid_t pid, event__handler_t process, - struct perf_session *session) +int perf_event__synthesize_thread_map(struct thread_map *threads, + perf_event__handler_t process, + struct perf_session *session) { - event_t *comm_event, *mmap_event; - int err = -1; + union perf_event *comm_event, *mmap_event; + int err = -1, thread; comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); if (comm_event == NULL) @@ -277,8 +281,15 @@ int event__synthesize_thread(pid_t pid, event__handler_t process, if (mmap_event == NULL) goto out_free_comm; - err = __event__synthesize_thread(comm_event, mmap_event, pid, - process, session); + err = 0; + for (thread = 0; thread < threads->nr; ++thread) { + if (__event__synthesize_thread(comm_event, mmap_event, + threads->map[thread], + process, session)) { + err = -1; + break; + } + } free(mmap_event); out_free_comm: free(comm_event); @@ -286,12 +297,12 @@ out: return err; } -int event__synthesize_threads(event__handler_t process, - struct perf_session *session) +int perf_event__synthesize_threads(perf_event__handler_t process, + struct perf_session *session) { DIR *proc; struct dirent dirent, *next; - event_t *comm_event, *mmap_event; + union perf_event *comm_event, *mmap_event; int err = -1; comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); @@ -349,10 +360,10 @@ static int find_symbol_cb(void *arg, const char *name, char type, return 1; } -int event__synthesize_kernel_mmap(event__handler_t process, - struct perf_session *session, - struct machine *machine, - const char *symbol_name) +int perf_event__synthesize_kernel_mmap(perf_event__handler_t process, + struct perf_session *session, + struct machine *machine, + const char *symbol_name) { size_t size; const char *filename, *mmap_name; @@ -366,8 +377,8 @@ int event__synthesize_kernel_mmap(event__handler_t process, * kernels. */ struct process_symbol_args args = { .name = symbol_name, }; - event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); - + union perf_event *event = zalloc((sizeof(event->mmap) + + session->id_hdr_size)); if (event == NULL) { pr_debug("Not enough memory synthesizing mmap event " "for kernel modules\n"); @@ -440,14 +451,15 @@ static int thread__set_comm_adjust(struct thread *self, const char *comm, return 0; } -int event__process_comm(event_t *self, struct sample_data *sample __used, - struct perf_session *session) +int perf_event__process_comm(union perf_event *event, + struct perf_sample *sample __used, + struct perf_session *session) { - struct thread *thread = perf_session__findnew(session, self->comm.tid); + struct thread *thread = perf_session__findnew(session, event->comm.tid); - dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); + dump_printf(": %s:%d\n", event->comm.comm, event->comm.tid); - if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, + if (thread == NULL || thread__set_comm_adjust(thread, event->comm.comm, &session->hists)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; @@ -456,19 +468,21 @@ int event__process_comm(event_t *self, struct sample_data *sample __used, return 0; } -int event__process_lost(event_t *self, struct sample_data *sample __used, - struct perf_session *session) +int perf_event__process_lost(union perf_event *event, + struct perf_sample *sample __used, + struct perf_session *session) { dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", - self->lost.id, self->lost.lost); - session->hists.stats.total_lost += self->lost.lost; + event->lost.id, event->lost.lost); + session->hists.stats.total_lost += event->lost.lost; return 0; } -static void event_set_kernel_mmap_len(struct map **maps, event_t *self) +static void perf_event__set_kernel_mmap_len(union perf_event *event, + struct map **maps) { - maps[MAP__FUNCTION]->start = self->mmap.start; - maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; + maps[MAP__FUNCTION]->start = event->mmap.start; + maps[MAP__FUNCTION]->end = event->mmap.start + event->mmap.len; /* * Be a bit paranoid here, some perf.data file came with * a zero sized synthesized MMAP event for the kernel. @@ -477,8 +491,8 @@ static void event_set_kernel_mmap_len(struct map **maps, event_t *self) maps[MAP__FUNCTION]->end = ~0ULL; } -static int event__process_kernel_mmap(event_t *self, - struct perf_session *session) +static int perf_event__process_kernel_mmap(union perf_event *event, + struct perf_session *session) { struct map *map; char kmmap_prefix[PATH_MAX]; @@ -486,9 +500,9 @@ static int event__process_kernel_mmap(event_t *self, enum dso_kernel_type kernel_type; bool is_kernel_mmap; - machine = perf_session__findnew_machine(session, self->mmap.pid); + machine = perf_session__findnew_machine(session, event->mmap.pid); if (!machine) { - pr_err("Can't find id %d's machine\n", self->mmap.pid); + pr_err("Can't find id %d's machine\n", event->mmap.pid); goto out_problem; } @@ -498,17 +512,17 @@ static int event__process_kernel_mmap(event_t *self, else kernel_type = DSO_TYPE_GUEST_KERNEL; - is_kernel_mmap = memcmp(self->mmap.filename, + is_kernel_mmap = memcmp(event->mmap.filename, kmmap_prefix, strlen(kmmap_prefix)) == 0; - if (self->mmap.filename[0] == '/' || - (!is_kernel_mmap && self->mmap.filename[0] == '[')) { + if (event->mmap.filename[0] == '/' || + (!is_kernel_mmap && event->mmap.filename[0] == '[')) { char short_module_name[1024]; char *name, *dot; - if (self->mmap.filename[0] == '/') { - name = strrchr(self->mmap.filename, '/'); + if (event->mmap.filename[0] == '/') { + name = strrchr(event->mmap.filename, '/'); if (name == NULL) goto out_problem; @@ -520,10 +534,10 @@ static int event__process_kernel_mmap(event_t *self, "[%.*s]", (int)(dot - name), name); strxfrchar(short_module_name, '-', '_'); } else - strcpy(short_module_name, self->mmap.filename); + strcpy(short_module_name, event->mmap.filename); - map = machine__new_module(machine, self->mmap.start, - self->mmap.filename); + map = machine__new_module(machine, event->mmap.start, + event->mmap.filename); if (map == NULL) goto out_problem; @@ -533,9 +547,9 @@ static int event__process_kernel_mmap(event_t *self, map->dso->short_name = name; map->dso->sname_alloc = 1; - map->end = map->start + self->mmap.len; + map->end = map->start + event->mmap.len; } else if (is_kernel_mmap) { - const char *symbol_name = (self->mmap.filename + + const char *symbol_name = (event->mmap.filename + strlen(kmmap_prefix)); /* * Should be there already, from the build-id table in @@ -550,10 +564,10 @@ static int event__process_kernel_mmap(event_t *self, if (__machine__create_kernel_maps(machine, kernel) < 0) goto out_problem; - event_set_kernel_mmap_len(machine->vmlinux_maps, self); + perf_event__set_kernel_mmap_len(event, machine->vmlinux_maps); perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, symbol_name, - self->mmap.pgoff); + event->mmap.pgoff); if (machine__is_default_guest(machine)) { /* * preload dso of guest kernel and modules @@ -567,22 +581,23 @@ out_problem: return -1; } -int event__process_mmap(event_t *self, struct sample_data *sample __used, - struct perf_session *session) +int perf_event__process_mmap(union perf_event *event, + struct perf_sample *sample __used, + struct perf_session *session) { struct machine *machine; struct thread *thread; struct map *map; - u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; int ret = 0; dump_printf(" %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", - self->mmap.pid, self->mmap.tid, self->mmap.start, - self->mmap.len, self->mmap.pgoff, self->mmap.filename); + event->mmap.pid, event->mmap.tid, event->mmap.start, + event->mmap.len, event->mmap.pgoff, event->mmap.filename); if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || cpumode == PERF_RECORD_MISC_KERNEL) { - ret = event__process_kernel_mmap(self, session); + ret = perf_event__process_kernel_mmap(event, session); if (ret < 0) goto out_problem; return 0; @@ -591,12 +606,12 @@ int event__process_mmap(event_t *self, struct sample_data *sample __used, machine = perf_session__find_host_machine(session); if (machine == NULL) goto out_problem; - thread = perf_session__findnew(session, self->mmap.pid); + thread = perf_session__findnew(session, event->mmap.pid); if (thread == NULL) goto out_problem; - map = map__new(&machine->user_dsos, self->mmap.start, - self->mmap.len, self->mmap.pgoff, - self->mmap.pid, self->mmap.filename, + map = map__new(&machine->user_dsos, event->mmap.start, + event->mmap.len, event->mmap.pgoff, + event->mmap.pid, event->mmap.filename, MAP__FUNCTION); if (map == NULL) goto out_problem; @@ -609,16 +624,17 @@ out_problem: return 0; } -int event__process_task(event_t *self, struct sample_data *sample __used, - struct perf_session *session) +int perf_event__process_task(union perf_event *event, + struct perf_sample *sample __used, + struct perf_session *session) { - struct thread *thread = perf_session__findnew(session, self->fork.tid); - struct thread *parent = perf_session__findnew(session, self->fork.ptid); + struct thread *thread = perf_session__findnew(session, event->fork.tid); + struct thread *parent = perf_session__findnew(session, event->fork.ptid); - dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, - self->fork.ppid, self->fork.ptid); + dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, + event->fork.ppid, event->fork.ptid); - if (self->header.type == PERF_RECORD_EXIT) { + if (event->header.type == PERF_RECORD_EXIT) { perf_session__remove_thread(session, thread); return 0; } @@ -632,20 +648,22 @@ int event__process_task(event_t *self, struct sample_data *sample __used, return 0; } -int event__process(event_t *event, struct sample_data *sample, - struct perf_session *session) +int perf_event__process(union perf_event *event, struct perf_sample *sample, + struct perf_session *session) { switch (event->header.type) { case PERF_RECORD_COMM: - event__process_comm(event, sample, session); + perf_event__process_comm(event, sample, session); break; case PERF_RECORD_MMAP: - event__process_mmap(event, sample, session); + perf_event__process_mmap(event, sample, session); break; case PERF_RECORD_FORK: case PERF_RECORD_EXIT: - event__process_task(event, sample, session); + perf_event__process_task(event, sample, session); break; + case PERF_RECORD_LOST: + perf_event__process_lost(event, sample, session); default: break; } @@ -754,12 +772,14 @@ static void dso__calc_col_width(struct dso *self, struct hists *hists) self->slen_calculated = 1; } -int event__preprocess_sample(const event_t *self, struct perf_session *session, - struct addr_location *al, struct sample_data *data, - symbol_filter_t filter) +int perf_event__preprocess_sample(const union perf_event *event, + struct perf_session *session, + struct addr_location *al, + struct perf_sample *sample, + symbol_filter_t filter) { - u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - struct thread *thread = perf_session__findnew(session, self->ip.pid); + u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + struct thread *thread = perf_session__findnew(session, event->ip.pid); if (thread == NULL) return -1; @@ -781,12 +801,12 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, machine__create_kernel_maps(&session->host_machine); thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - self->ip.pid, self->ip.ip, al); + event->ip.pid, event->ip.ip, al); dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : "<not found>"); al->sym = NULL; - al->cpu = data->cpu; + al->cpu = sample->cpu; if (al->map) { if (symbol_conf.dso_list && @@ -826,128 +846,3 @@ out_filtered: al->filtered = true; return 0; } - -static int event__parse_id_sample(const event_t *event, - struct perf_session *session, - struct sample_data *sample) -{ - const u64 *array; - u64 type; - - sample->cpu = sample->pid = sample->tid = -1; - sample->stream_id = sample->id = sample->time = -1ULL; - - if (!session->sample_id_all) - return 0; - - array = event->sample.array; - array += ((event->header.size - - sizeof(event->header)) / sizeof(u64)) - 1; - type = session->sample_type; - - if (type & PERF_SAMPLE_CPU) { - u32 *p = (u32 *)array; - sample->cpu = *p; - array--; - } - - if (type & PERF_SAMPLE_STREAM_ID) { - sample->stream_id = *array; - array--; - } - - if (type & PERF_SAMPLE_ID) { - sample->id = *array; - array--; - } - - if (type & PERF_SAMPLE_TIME) { - sample->time = *array; - array--; - } - - if (type & PERF_SAMPLE_TID) { - u32 *p = (u32 *)array; - sample->pid = p[0]; - sample->tid = p[1]; - } - - return 0; -} - -int event__parse_sample(const event_t *event, struct perf_session *session, - struct sample_data *data) -{ - const u64 *array; - u64 type; - - if (event->header.type != PERF_RECORD_SAMPLE) - return event__parse_id_sample(event, session, data); - - array = event->sample.array; - type = session->sample_type; - - if (type & PERF_SAMPLE_IP) { - data->ip = event->ip.ip; - array++; - } - - if (type & PERF_SAMPLE_TID) { - u32 *p = (u32 *)array; - data->pid = p[0]; - data->tid = p[1]; - array++; - } - - if (type & PERF_SAMPLE_TIME) { - data->time = *array; - array++; - } - - if (type & PERF_SAMPLE_ADDR) { - data->addr = *array; - array++; - } - - data->id = -1ULL; - if (type & PERF_SAMPLE_ID) { - data->id = *array; - array++; - } - - if (type & PERF_SAMPLE_STREAM_ID) { - data->stream_id = *array; - array++; - } - - if (type & PERF_SAMPLE_CPU) { - u32 *p = (u32 *)array; - data->cpu = *p; - array++; - } else - data->cpu = -1; - - if (type & PERF_SAMPLE_PERIOD) { - data->period = *array; - array++; - } - - if (type & PERF_SAMPLE_READ) { - pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); - return -1; - } - - if (type & PERF_SAMPLE_CALLCHAIN) { - data->callchain = (struct ip_callchain *)array; - array += 1 + data->callchain->nr; - } - - if (type & PERF_SAMPLE_RAW) { - u32 *p = (u32 *)array; - data->raw_size = *p; - p++; - data->raw_data = p; - } - - return 0; -} diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2b7e91902f1..9c35170fb37 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -61,7 +61,7 @@ struct sample_event { u64 array[]; }; -struct sample_data { +struct perf_sample { u64 ip; u32 pid, tid; u64 time; @@ -117,7 +117,7 @@ struct tracing_data_event { u32 size; }; -typedef union event_union { +union perf_event { struct perf_event_header header; struct ip_event ip; struct mmap_event mmap; @@ -130,48 +130,54 @@ typedef union event_union { struct event_type_event event_type; struct tracing_data_event tracing_data; struct build_id_event build_id; -} event_t; +}; -void event__print_totals(void); +void perf_event__print_totals(void); struct perf_session; +struct thread_map; -typedef int (*event__handler_synth_t)(event_t *event, +typedef int (*perf_event__handler_synth_t)(union perf_event *event, + struct perf_session *session); +typedef int (*perf_event__handler_t)(union perf_event *event, + struct perf_sample *sample, struct perf_session *session); -typedef int (*event__handler_t)(event_t *event, struct sample_data *sample, - struct perf_session *session); -int event__synthesize_thread(pid_t pid, event__handler_t process, +int perf_event__synthesize_thread_map(struct thread_map *threads, + perf_event__handler_t process, + struct perf_session *session); +int perf_event__synthesize_threads(perf_event__handler_t process, + struct perf_session *session); +int perf_event__synthesize_kernel_mmap(perf_event__handler_t process, + struct perf_session *session, + struct machine *machine, + const char *symbol_name); + +int perf_event__synthesize_modules(perf_event__handler_t process, + struct perf_session *session, + struct machine *machine); + +int perf_event__process_comm(union perf_event *event, struct perf_sample *sample, struct perf_session *session); -int 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); - -int event__synthesize_modules(event__handler_t process, - struct perf_session *session, - struct machine *machine); - -int event__process_comm(event_t *self, struct sample_data *sample, - struct perf_session *session); -int event__process_lost(event_t *self, struct sample_data *sample, - struct perf_session *session); -int event__process_mmap(event_t *self, struct sample_data *sample, - struct perf_session *session); -int event__process_task(event_t *self, struct sample_data *sample, +int perf_event__process_lost(union perf_event *event, struct perf_sample *sample, + struct perf_session *session); +int perf_event__process_mmap(union perf_event *event, struct perf_sample *sample, + struct perf_session *session); +int perf_event__process_task(union perf_event *event, struct perf_sample *sample, + struct perf_session *session); +int perf_event__process(union perf_event *event, struct perf_sample *sample, struct perf_session *session); -int event__process(event_t *event, struct sample_data *sample, - struct perf_session *session); struct addr_location; -int event__preprocess_sample(const event_t *self, struct perf_session *session, - struct addr_location *al, struct sample_data *data, - symbol_filter_t filter); -int event__parse_sample(const event_t *event, struct perf_session *session, - struct sample_data *sample); +int perf_event__preprocess_sample(const union perf_event *self, + struct perf_session *session, + struct addr_location *al, + struct perf_sample *sample, + symbol_filter_t filter); + +const char *perf_event__name(unsigned int id); -const char *event__get_event_name(unsigned int id); +int perf_event__parse_sample(const union perf_event *event, u64 type, + bool sample_id_all, struct perf_sample *sample); #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c new file mode 100644 index 00000000000..95b21fece2c --- /dev/null +++ b/tools/perf/util/evlist.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + * + * Parts came from builtin-{top,stat,record}.c, see those files for further + * copyright notes. + * + * Released under the GPL v2. (and only v2, not any later version) + */ +#include <poll.h> +#include "cpumap.h" +#include "thread_map.h" +#include "evlist.h" +#include "evsel.h" +#include "util.h" + +#include <sys/mman.h> + +#include <linux/bitops.h> +#include <linux/hash.h> + +#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) +#define SID(e, x, y) xyarray__entry(e->id, x, y) + +void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, + struct thread_map *threads) +{ + int i; + + for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) + INIT_HLIST_HEAD(&evlist->heads[i]); + INIT_LIST_HEAD(&evlist->entries); + perf_evlist__set_maps(evlist, cpus, threads); +} + +struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, + struct thread_map *threads) +{ + struct perf_evlist *evlist = zalloc(sizeof(*evlist)); + + if (evlist != NULL) + perf_evlist__init(evlist, cpus, threads); + + return evlist; +} + +static void perf_evlist__purge(struct perf_evlist *evlist) +{ + struct perf_evsel *pos, *n; + + list_for_each_entry_safe(pos, n, &evlist->entries, node) { + list_del_init(&pos->node); + perf_evsel__delete(pos); + } + + evlist->nr_entries = 0; +} + +void perf_evlist__exit(struct perf_evlist *evlist) +{ + free(evlist->mmap); + free(evlist->pollfd); + evlist->mmap = NULL; + evlist->pollfd = NULL; +} + +void perf_evlist__delete(struct perf_evlist *evlist) +{ + perf_evlist__purge(evlist); + perf_evlist__exit(evlist); + free(evlist); +} + +void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) +{ + list_add_tail(&entry->node, &evlist->entries); + ++evlist->nr_entries; +} + +int perf_evlist__add_default(struct perf_evlist *evlist) +{ + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + }; + struct perf_evsel *evsel = perf_evsel__new(&attr, 0); + + if (evsel == NULL) + return -ENOMEM; + + perf_evlist__add(evlist, evsel); + return 0; +} + +int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) +{ + int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; + evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); + return evlist->pollfd != NULL ? 0 : -ENOMEM; +} + +void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) +{ + fcntl(fd, F_SETFL, O_NONBLOCK); + evlist->pollfd[evlist->nr_fds].fd = fd; + evlist->pollfd[evlist->nr_fds].events = POLLIN; + evlist->nr_fds++; +} + +static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel, + int cpu, int thread, int fd) +{ + struct perf_sample_id *sid; + u64 read_data[4] = { 0, }; + int hash, id_idx = 1; /* The first entry is the counter value */ + + if (!(evsel->attr.read_format & PERF_FORMAT_ID) || + read(fd, &read_data, sizeof(read_data)) == -1) + return -1; + + if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + ++id_idx; + if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + ++id_idx; + + sid = SID(evsel, cpu, thread); + sid->id = read_data[id_idx]; + sid->evsel = evsel; + hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); + hlist_add_head(&sid->node, &evlist->heads[hash]); + return 0; +} + +struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) +{ + struct hlist_head *head; + struct hlist_node *pos; + struct perf_sample_id *sid; + int hash; + + if (evlist->nr_entries == 1) + return list_entry(evlist->entries.next, struct perf_evsel, node); + + hash = hash_64(id, PERF_EVLIST__HLIST_BITS); + head = &evlist->heads[hash]; + + hlist_for_each_entry(sid, pos, head, node) + if (sid->id == id) + return sid->evsel; + return NULL; +} + +union perf_event *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu) +{ + /* XXX Move this to perf.c, making it generally available */ + unsigned int page_size = sysconf(_SC_PAGE_SIZE); + struct perf_mmap *md = &evlist->mmap[cpu]; + unsigned int head = perf_mmap__read_head(md); + unsigned int old = md->prev; + unsigned char *data = md->base + page_size; + union perf_event *event = NULL; + + if (evlist->overwrite) { + /* + * If we're further behind than half the buffer, there's a chance + * the writer will bite our tail and mess up the samples under us. + * + * If we somehow ended up ahead of the head, we got messed up. + * + * In either case, truncate and restart at head. + */ + int diff = head - old; + if (diff > md->mask / 2 || diff < 0) { + fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); + + /* + * head points to a known good entry, start there. + */ + old = head; + } + } + + if (old != head) { + size_t size; + + event = (union perf_event *)&data[old & md->mask]; + size = event->header.size; + + /* + * Event straddles the mmap boundary -- header should always + * be inside due to u64 alignment of output. + */ + if ((old & md->mask) + size != ((old + size) & md->mask)) { + unsigned int offset = old; + unsigned int len = min(sizeof(*event), size), cpy; + void *dst = &evlist->event_copy; + + do { + cpy = min(md->mask + 1 - (offset & md->mask), len); + memcpy(dst, &data[offset & md->mask], cpy); + offset += cpy; + dst += cpy; + len -= cpy; + } while (len); + + event = &evlist->event_copy; + } + + old += size; + } + + md->prev = old; + + if (!evlist->overwrite) + perf_mmap__write_tail(md, old); + + return event; +} + +void perf_evlist__munmap(struct perf_evlist *evlist) +{ + int cpu; + + for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { + if (evlist->mmap[cpu].base != NULL) { + munmap(evlist->mmap[cpu].base, evlist->mmap_len); + evlist->mmap[cpu].base = NULL; + } + } +} + +int perf_evlist__alloc_mmap(struct perf_evlist *evlist) +{ + evlist->mmap = zalloc(evlist->cpus->nr * sizeof(struct perf_mmap)); + return evlist->mmap != NULL ? 0 : -ENOMEM; +} + +static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, + int mask, int fd) +{ + evlist->mmap[cpu].prev = 0; + evlist->mmap[cpu].mask = mask; + evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, + MAP_SHARED, fd, 0); + if (evlist->mmap[cpu].base == MAP_FAILED) + return -1; + + perf_evlist__add_pollfd(evlist, fd); + return 0; +} + +/** perf_evlist__mmap - Create per cpu maps to receive events + * + * @evlist - list of events + * @pages - map length in pages + * @overwrite - overwrite older events? + * + * If overwrite is false the user needs to signal event consuption using: + * + * struct perf_mmap *m = &evlist->mmap[cpu]; + * unsigned int head = perf_mmap__read_head(m); + * + * perf_mmap__write_tail(m, head) + * + * Using perf_evlist__read_on_cpu does this automatically. + */ +int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite) +{ + unsigned int page_size = sysconf(_SC_PAGE_SIZE); + int mask = pages * page_size - 1, cpu; + struct perf_evsel *first_evsel, *evsel; + const struct cpu_map *cpus = evlist->cpus; + const struct thread_map *threads = evlist->threads; + int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); + + if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) + return -ENOMEM; + + if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) + return -ENOMEM; + + evlist->overwrite = overwrite; + evlist->mmap_len = (pages + 1) * page_size; + first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); + + list_for_each_entry(evsel, &evlist->entries, node) { + if ((evsel->attr.read_format & PERF_FORMAT_ID) && + evsel->id == NULL && + perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) + return -ENOMEM; + + for (cpu = 0; cpu < cpus->nr; cpu++) { + for (thread = 0; thread < threads->nr; thread++) { + int fd = FD(evsel, cpu, thread); + + if (evsel->idx || thread) { + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, + FD(first_evsel, cpu, 0)) != 0) + goto out_unmap; + } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) + goto out_unmap; + + if ((evsel->attr.read_format & PERF_FORMAT_ID) && + perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0) + goto out_unmap; + } + } + } + + return 0; + +out_unmap: + for (cpu = 0; cpu < cpus->nr; cpu++) { + if (evlist->mmap[cpu].base != NULL) { + munmap(evlist->mmap[cpu].base, evlist->mmap_len); + evlist->mmap[cpu].base = NULL; + } + } + return -1; +} + +int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, + pid_t target_tid, const char *cpu_list) +{ + evlist->threads = thread_map__new(target_pid, target_tid); + + if (evlist->threads == NULL) + return -1; + + if (target_tid != -1) + evlist->cpus = cpu_map__dummy_new(); + else + evlist->cpus = cpu_map__new(cpu_list); + + if (evlist->cpus == NULL) + goto out_delete_threads; + + return 0; + +out_delete_threads: + thread_map__delete(evlist->threads); + return -1; +} + +void perf_evlist__delete_maps(struct perf_evlist *evlist) +{ + cpu_map__delete(evlist->cpus); + thread_map__delete(evlist->threads); + evlist->cpus = NULL; + evlist->threads = NULL; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h new file mode 100644 index 00000000000..c9884056097 --- /dev/null +++ b/tools/perf/util/evlist.h @@ -0,0 +1,64 @@ +#ifndef __PERF_EVLIST_H +#define __PERF_EVLIST_H 1 + +#include <linux/list.h> +#include "../perf.h" +#include "event.h" + +struct pollfd; +struct thread_map; +struct cpu_map; + +#define PERF_EVLIST__HLIST_BITS 8 +#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) + +struct perf_evlist { + struct list_head entries; + struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; + int nr_entries; + int nr_fds; + int mmap_len; + bool overwrite; + union perf_event event_copy; + struct perf_mmap *mmap; + struct pollfd *pollfd; + struct thread_map *threads; + struct cpu_map *cpus; +}; + +struct perf_evsel; + +struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, + struct thread_map *threads); +void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, + struct thread_map *threads); +void perf_evlist__exit(struct perf_evlist *evlist); +void perf_evlist__delete(struct perf_evlist *evlist); + +void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); +int perf_evlist__add_default(struct perf_evlist *evlist); + +int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); +void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); + +struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); + +union perf_event *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu); + +int perf_evlist__alloc_mmap(struct perf_evlist *evlist); +int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); +void perf_evlist__munmap(struct perf_evlist *evlist); + +static inline void perf_evlist__set_maps(struct perf_evlist *evlist, + struct cpu_map *cpus, + struct thread_map *threads) +{ + evlist->cpus = cpus; + evlist->threads = threads; +} + +int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, + pid_t target_tid, const char *cpu_list); +void perf_evlist__delete_maps(struct perf_evlist *evlist); + +#endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d8575d31ee6..211063eed47 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1,20 +1,34 @@ +/* + * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + * + * Parts came from builtin-{top,stat,record}.c, see those files for further + * copyright notes. + * + * Released under the GPL v2. (and only v2, not any later version) + */ + #include "evsel.h" -#include "../perf.h" +#include "evlist.h" #include "util.h" #include "cpumap.h" -#include "thread.h" +#include "thread_map.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) +void perf_evsel__init(struct perf_evsel *evsel, + struct perf_event_attr *attr, int idx) +{ + evsel->idx = idx; + evsel->attr = *attr; + INIT_LIST_HEAD(&evsel->node); +} + struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); - if (evsel != NULL) { - evsel->idx = idx; - evsel->attr = *attr; - INIT_LIST_HEAD(&evsel->node); - } + if (evsel != NULL) + perf_evsel__init(evsel, attr, idx); return evsel; } @@ -25,6 +39,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) return evsel->fd != NULL ? 0 : -ENOMEM; } +int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); + return evsel->id != NULL ? 0 : -ENOMEM; +} + int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) { evsel->counts = zalloc((sizeof(*evsel->counts) + @@ -38,6 +58,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) evsel->fd = NULL; } +void perf_evsel__free_id(struct perf_evsel *evsel) +{ + xyarray__delete(evsel->id); + evsel->id = NULL; +} + void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) { int cpu, thread; @@ -49,10 +75,16 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) } } -void perf_evsel__delete(struct perf_evsel *evsel) +void perf_evsel__exit(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); xyarray__delete(evsel->fd); + xyarray__delete(evsel->id); +} + +void perf_evsel__delete(struct perf_evsel *evsel) +{ + perf_evsel__exit(evsel); free(evsel); } @@ -128,7 +160,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, } static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads) + struct thread_map *threads, bool group, bool inherit) { int cpu, thread; @@ -137,12 +169,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, return -1; for (cpu = 0; cpu < cpus->nr; cpu++) { + int group_fd = -1; + + evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; + for (thread = 0; thread < threads->nr; thread++) { FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, threads->map[thread], - cpus->map[cpu], -1, 0); + cpus->map[cpu], + group_fd, 0); if (FD(evsel, cpu, thread) < 0) goto out_close; + + if (group && group_fd == -1) + group_fd = FD(evsel, cpu, thread); } } @@ -175,10 +215,9 @@ static struct { .threads = { -1, }, }; -int perf_evsel__open(struct perf_evsel *evsel, - struct cpu_map *cpus, struct thread_map *threads) +int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, + struct thread_map *threads, bool group, bool inherit) { - if (cpus == NULL) { /* Work around old compiler warnings about strict aliasing */ cpus = &empty_cpu_map.map; @@ -187,15 +226,135 @@ int perf_evsel__open(struct perf_evsel *evsel, if (threads == NULL) threads = &empty_thread_map.map; - return __perf_evsel__open(evsel, cpus, threads); + return __perf_evsel__open(evsel, cpus, threads, group, inherit); +} + +int perf_evsel__open_per_cpu(struct perf_evsel *evsel, + struct cpu_map *cpus, bool group, bool inherit) +{ + return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); } -int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) +int perf_evsel__open_per_thread(struct perf_evsel *evsel, + struct thread_map *threads, bool group, bool inherit) { - return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); + return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); } -int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) +static int perf_event__parse_id_sample(const union perf_event *event, u64 type, + struct perf_sample *sample) { - return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); + const u64 *array = event->sample.array; + + array += ((event->header.size - + sizeof(event->header)) / sizeof(u64)) - 1; + + if (type & PERF_SAMPLE_CPU) { + u32 *p = (u32 *)array; + sample->cpu = *p; + array--; + } + + if (type & PERF_SAMPLE_STREAM_ID) { + sample->stream_id = *array; + array--; + } + + if (type & PERF_SAMPLE_ID) { + sample->id = *array; + array--; + } + + if (type & PERF_SAMPLE_TIME) { + sample->time = *array; + array--; + } + + if (type & PERF_SAMPLE_TID) { + u32 *p = (u32 *)array; + sample->pid = p[0]; + sample->tid = p[1]; + } + + return 0; +} + +int perf_event__parse_sample(const union perf_event *event, u64 type, + bool sample_id_all, struct perf_sample *data) +{ + const u64 *array; + + data->cpu = data->pid = data->tid = -1; + data->stream_id = data->id = data->time = -1ULL; + + if (event->header.type != PERF_RECORD_SAMPLE) { + if (!sample_id_all) + return 0; + return perf_event__parse_id_sample(event, type, data); + } + + array = event->sample.array; + + if (type & PERF_SAMPLE_IP) { + data->ip = event->ip.ip; + array++; + } + + if (type & PERF_SAMPLE_TID) { + u32 *p = (u32 *)array; + data->pid = p[0]; + data->tid = p[1]; + array++; + } + + if (type & PERF_SAMPLE_TIME) { + data->time = *array; + array++; + } + + if (type & PERF_SAMPLE_ADDR) { + data->addr = *array; + array++; + } + + data->id = -1ULL; + if (type & PERF_SAMPLE_ID) { + data->id = *array; + array++; + } + + if (type & PERF_SAMPLE_STREAM_ID) { + data->stream_id = *array; + array++; + } + + if (type & PERF_SAMPLE_CPU) { + u32 *p = (u32 *)array; + data->cpu = *p; + array++; + } + + if (type & PERF_SAMPLE_PERIOD) { + data->period = *array; + array++; + } + + if (type & PERF_SAMPLE_READ) { + fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); + return -1; + } + + if (type & PERF_SAMPLE_CALLCHAIN) { + data->callchain = (struct ip_callchain *)array; + array += 1 + data->callchain->nr; + } + + if (type & PERF_SAMPLE_RAW) { + u32 *p = (u32 *)array; + data->raw_size = *p; + p++; + data->raw_data = p; + } + + return 0; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b2d755fe88a..eecdc3aabc1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -24,11 +24,24 @@ struct perf_counts { struct perf_counts_values cpu[]; }; +struct perf_evsel; + +/* + * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are + * more than one entry in the evlist. + */ +struct perf_sample_id { + struct hlist_node node; + u64 id; + struct perf_evsel *evsel; +}; + struct perf_evsel { struct list_head node; struct perf_event_attr attr; char *filter; struct xyarray *fd; + struct xyarray *id; struct perf_counts *counts; int idx; void *priv; @@ -36,19 +49,27 @@ struct perf_evsel { struct cpu_map; struct thread_map; +struct perf_evlist; struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); +void perf_evsel__init(struct perf_evsel *evsel, + struct perf_event_attr *attr, int idx); +void perf_evsel__exit(struct perf_evsel *evsel); void perf_evsel__delete(struct perf_evsel *evsel); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); +int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); void perf_evsel__free_fd(struct perf_evsel *evsel); +void perf_evsel__free_id(struct perf_evsel *evsel); void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); -int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); -int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads); -int perf_evsel__open(struct perf_evsel *evsel, - struct cpu_map *cpus, struct thread_map *threads); +int perf_evsel__open_per_cpu(struct perf_evsel *evsel, + struct cpu_map *cpus, bool group, bool inherit); +int perf_evsel__open_per_thread(struct perf_evsel *evsel, + struct thread_map *threads, bool group, bool inherit); +int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, + struct thread_map *threads, bool group, bool inherit); #define perf_evsel__match(evsel, t, c) \ (evsel->attr.type == PERF_TYPE_##t && \ diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f6a929e7498..72c124dc578 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -8,6 +8,7 @@ #include <linux/list.h> #include <linux/kernel.h> +#include "evlist.h" #include "util.h" #include "header.h" #include "../perf.h" @@ -428,7 +429,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi return ret; } -static int perf_header__adds_write(struct perf_header *self, int fd) +static int perf_header__adds_write(struct perf_header *self, + struct perf_evlist *evlist, int fd) { int nr_sections; struct perf_session *session; @@ -463,7 +465,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) /* Write trace info */ trace_sec->offset = lseek(fd, 0, SEEK_CUR); - read_tracing_data(fd, &evsel_list); + read_tracing_data(fd, &evlist->entries); trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; } @@ -513,7 +515,8 @@ int perf_header__write_pipe(int fd) return 0; } -int perf_header__write(struct perf_header *self, int fd, bool at_exit) +int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, + int fd, bool at_exit) { struct perf_file_header f_header; struct perf_file_attr f_attr; @@ -566,7 +569,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) self->data_offset = lseek(fd, 0, SEEK_CUR); if (at_exit) { - err = perf_header__adds_write(self, fd); + err = perf_header__adds_write(self, evlist, fd); if (err < 0) return err; } @@ -997,11 +1000,11 @@ 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) +int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, + perf_event__handler_t process, + struct perf_session *session) { - event_t *ev; + union perf_event *ev; size_t size; int err; @@ -1028,8 +1031,9 @@ int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, return err; } -int event__synthesize_attrs(struct perf_header *self, event__handler_t process, - struct perf_session *session) +int perf_event__synthesize_attrs(struct perf_header *self, + perf_event__handler_t process, + struct perf_session *session) { struct perf_header_attr *attr; int i, err = 0; @@ -1037,8 +1041,8 @@ int event__synthesize_attrs(struct perf_header *self, event__handler_t process, for (i = 0; i < self->attrs; i++) { attr = self->attr[i]; - err = event__synthesize_attr(&attr->attr, attr->ids, attr->id, - process, session); + err = perf_event__synthesize_attr(&attr->attr, attr->ids, + attr->id, process, session); if (err) { pr_debug("failed to create perf header attribute\n"); return err; @@ -1048,21 +1052,22 @@ int event__synthesize_attrs(struct perf_header *self, event__handler_t process, return err; } -int event__process_attr(event_t *self, struct perf_session *session) +int perf_event__process_attr(union perf_event *event, + struct perf_session *session) { struct perf_header_attr *attr; unsigned int i, ids, n_ids; - attr = perf_header_attr__new(&self->attr.attr); + attr = perf_header_attr__new(&event->attr.attr); if (attr == NULL) return -ENOMEM; - ids = self->header.size; - ids -= (void *)&self->attr.id - (void *)self; + ids = event->header.size; + ids -= (void *)&event->attr.id - (void *)event; n_ids = ids / sizeof(u64); for (i = 0; i < n_ids; i++) { - if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) { + if (perf_header_attr__add_id(attr, event->attr.id[i]) < 0) { perf_header_attr__delete(attr); return -ENOMEM; } @@ -1078,11 +1083,11 @@ int event__process_attr(event_t *self, struct perf_session *session) return 0; } -int event__synthesize_event_type(u64 event_id, char *name, - event__handler_t process, - struct perf_session *session) +int perf_event__synthesize_event_type(u64 event_id, char *name, + perf_event__handler_t process, + struct perf_session *session) { - event_t ev; + union perf_event ev; size_t size = 0; int err = 0; @@ -1103,8 +1108,8 @@ int event__synthesize_event_type(u64 event_id, char *name, return err; } -int event__synthesize_event_types(event__handler_t process, - struct perf_session *session) +int perf_event__synthesize_event_types(perf_event__handler_t process, + struct perf_session *session) { struct perf_trace_event_type *type; int i, err = 0; @@ -1112,8 +1117,9 @@ int event__synthesize_event_types(event__handler_t process, for (i = 0; i < event_count; i++) { type = &events[i]; - err = event__synthesize_event_type(type->event_id, type->name, - process, session); + err = perf_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; @@ -1123,28 +1129,28 @@ int event__synthesize_event_types(event__handler_t process, return err; } -int event__process_event_type(event_t *self, - struct perf_session *session __unused) +int perf_event__process_event_type(union perf_event *event, + struct perf_session *session __unused) { - if (perf_header__push_event(self->event_type.event_type.event_id, - self->event_type.event_type.name) < 0) + if (perf_header__push_event(event->event_type.event_type.event_id, + event->event_type.event_type.name) < 0) return -ENOMEM; return 0; } -int event__synthesize_tracing_data(int fd, struct list_head *pattrs, - event__handler_t process, +int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, + perf_event__handler_t process, struct perf_session *session __unused) { - event_t ev; + union perf_event ev; ssize_t size = 0, aligned_size = 0, padding; - int err = 0; + int err __used = 0; memset(&ev, 0, sizeof(ev)); ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; - size = read_tracing_data_size(fd, pattrs); + size = read_tracing_data_size(fd, &evlist->entries); if (size <= 0) return size; aligned_size = ALIGN(size, sizeof(u64)); @@ -1154,16 +1160,16 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, process(&ev, NULL, session); - err = read_tracing_data(fd, pattrs); + err = read_tracing_data(fd, &evlist->entries); write_padded(fd, NULL, 0, padding); return aligned_size; } -int event__process_tracing_data(event_t *self, - struct perf_session *session) +int perf_event__process_tracing_data(union perf_event *event, + struct perf_session *session) { - ssize_t size_read, padding, size = self->tracing_data.size; + ssize_t size_read, padding, size = event->tracing_data.size; off_t offset = lseek(session->fd, 0, SEEK_CUR); char buf[BUFSIZ]; @@ -1189,12 +1195,12 @@ int event__process_tracing_data(event_t *self, 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) +int perf_event__synthesize_build_id(struct dso *pos, u16 misc, + perf_event__handler_t process, + struct machine *machine, + struct perf_session *session) { - event_t ev; + union perf_event ev; size_t len; int err = 0; @@ -1217,11 +1223,11 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, return err; } -int event__process_build_id(event_t *self, - struct perf_session *session) +int perf_event__process_build_id(union perf_event *event, + struct perf_session *session) { - __event_process_build_id(&self->build_id, - self->build_id.filename, + __event_process_build_id(&event->build_id, + event->build_id.filename, session); return 0; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 33f16be7b72..f042cebcec1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -65,8 +65,11 @@ struct perf_header { int perf_header__init(struct perf_header *self); void perf_header__exit(struct perf_header *self); +struct perf_evlist; + int perf_header__read(struct perf_session *session, int fd); -int perf_header__write(struct perf_header *self, int fd, bool at_exit); +int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, + int fd, bool at_exit); int perf_header__write_pipe(int fd); int perf_header__add_attr(struct perf_header *self, @@ -97,32 +100,32 @@ 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, +int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, + perf_event__handler_t process, + struct perf_session *session); +int perf_event__synthesize_attrs(struct perf_header *self, + perf_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 list_head *pattrs, - event__handler_t process, +int perf_event__process_attr(union perf_event *event, struct perf_session *session); + +int perf_event__synthesize_event_type(u64 event_id, char *name, + perf_event__handler_t process, + struct perf_session *session); +int perf_event__synthesize_event_types(perf_event__handler_t process, + struct perf_session *session); +int perf_event__process_event_type(union perf_event *event, 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); +int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, + perf_event__handler_t process, + struct perf_session *session); +int perf_event__process_tracing_data(union perf_event *event, + struct perf_session *session); + +int perf_event__synthesize_build_id(struct dso *pos, u16 misc, + perf_event__handler_t process, + struct machine *machine, + struct perf_session *session); +int perf_event__process_build_id(union perf_event *event, + struct perf_session *session); #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 32f4f1f2f6e..3f437236f19 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,3 +1,4 @@ +#include "annotate.h" #include "util.h" #include "build-id.h" #include "hist.h" @@ -211,7 +212,9 @@ void hist_entry__free(struct hist_entry *he) * collapse the histogram */ -static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) +static bool hists__collapse_insert_entry(struct hists *self, + struct rb_root *root, + struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -226,8 +229,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) if (!cmp) { iter->period += he->period; - if (symbol_conf.use_callchain) - callchain_merge(iter->callchain, he->callchain); + if (symbol_conf.use_callchain) { + callchain_cursor_reset(&self->callchain_cursor); + callchain_merge(&self->callchain_cursor, iter->callchain, + he->callchain); + } hist_entry__free(he); return false; } @@ -262,7 +268,7 @@ void hists__collapse_resort(struct hists *self) next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &self->entries); - if (collapse__insert_entry(&tmp, n)) + if (hists__collapse_insert_entry(self, &tmp, n)) hists__inc_nr_entries(self, n); } @@ -425,7 +431,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, u64 cumul; child = rb_entry(node, struct callchain_node, rb_node); - cumul = cumul_hits(child); + cumul = callchain_cumul_hits(child); remaining -= cumul; /* @@ -944,225 +950,14 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) } } -static int symbol__alloc_hist(struct symbol *self) -{ - struct sym_priv *priv = symbol__priv(self); - const int size = (sizeof(*priv->hist) + - (self->end - self->start) * sizeof(u64)); - - priv->hist = zalloc(size); - return priv->hist == NULL ? -1 : 0; -} - -int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) -{ - unsigned int sym_size, offset; - struct symbol *sym = self->ms.sym; - struct sym_priv *priv; - struct sym_hist *h; - - if (!sym || !self->ms.map) - return 0; - - priv = symbol__priv(sym); - if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) - return -ENOMEM; - - sym_size = sym->end - sym->start; - offset = ip - sym->start; - - pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); - - if (offset >= sym_size) - return 0; - - h = priv->hist; - h->sum++; - h->ip[offset]++; - - pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64 - "] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name, - ip, ip - self->ms.sym->start, h->ip[offset]); - return 0; -} - -static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) -{ - struct objdump_line *self = malloc(sizeof(*self) + privsize); - - if (self != NULL) { - self->offset = offset; - self->line = line; - } - - return self; -} - -void objdump_line__free(struct objdump_line *self) -{ - free(self->line); - free(self); -} - -static void objdump__add_line(struct list_head *head, struct objdump_line *line) -{ - list_add_tail(&line->node, head); -} - -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) -{ - list_for_each_entry_continue(pos, head, node) - if (pos->offset >= 0) - return pos; - - return NULL; -} - -static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, - struct list_head *head, size_t privsize) +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) { - struct symbol *sym = self->ms.sym; - struct objdump_line *objdump_line; - char *line = NULL, *tmp, *tmp2, *c; - size_t line_len; - s64 line_ip, offset = -1; - - if (getline(&line, &line_len, file) < 0) - return -1; - - if (!line) - return -1; - - while (line_len != 0 && isspace(line[line_len - 1])) - line[--line_len] = '\0'; - - c = strchr(line, '\n'); - if (c) - *c = 0; - - line_ip = -1; - - /* - * Strip leading spaces: - */ - tmp = line; - while (*tmp) { - if (*tmp != ' ') - break; - tmp++; - } - - if (*tmp) { - /* - * Parse hexa addresses followed by ':' - */ - line_ip = strtoull(tmp, &tmp2, 16); - if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') - line_ip = -1; - } - - if (line_ip != -1) { - u64 start = map__rip_2objdump(self->ms.map, sym->start), - end = map__rip_2objdump(self->ms.map, sym->end); - - offset = line_ip - start; - if (offset < 0 || (u64)line_ip > end) - offset = -1; - } - - objdump_line = objdump_line__new(offset, line, privsize); - if (objdump_line == NULL) { - free(line); - return -1; - } - objdump__add_line(head, objdump_line); - - return 0; + return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); } -int hist_entry__annotate(struct hist_entry *self, struct list_head *head, - size_t privsize) +int hist_entry__annotate(struct hist_entry *he, size_t privsize) { - struct symbol *sym = self->ms.sym; - struct map *map = self->ms.map; - struct dso *dso = map->dso; - char *filename = dso__build_id_filename(dso, NULL, 0); - bool free_filename = true; - char command[PATH_MAX * 2]; - FILE *file; - int err = 0; - u64 len; - char symfs_filename[PATH_MAX]; - - if (filename) { - snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", - symbol_conf.symfs, filename); - } - - if (filename == NULL) { - if (dso->has_build_id) { - pr_err("Can't annotate %s: not enough memory\n", - sym->name); - return -ENOMEM; - } - goto fallback; - } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || - strstr(command, "[kernel.kallsyms]") || - access(symfs_filename, R_OK)) { - free(filename); -fallback: - /* - * If we don't have build-ids or the build-id file isn't in the - * cache, or is just a kallsyms file, well, lets hope that this - * DSO is the same as when 'perf record' ran. - */ - filename = dso->long_name; - snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", - symbol_conf.symfs, filename); - free_filename = false; - } - - if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) - goto out_free_filename; - err = -ENOENT; - dso->annotate_warned = 1; - pr_err("Can't annotate %s: No vmlinux file was found in the " - "path\n", sym->name); - goto out_free_filename; - } - - pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, - filename, sym->name, map->unmap_ip(map, sym->start), - map->unmap_ip(map, sym->end)); - - len = sym->end - sym->start; - - pr_debug("annotating [%p] %30s : [%p] %30s\n", - dso, dso->long_name, sym, sym->name); - - snprintf(command, sizeof(command), - "objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", - map__rip_2objdump(map, sym->start), - map__rip_2objdump(map, sym->end), - symfs_filename, filename); - - pr_debug("Executing: %s\n", command); - - file = popen(command, "r"); - if (!file) - goto out_free_filename; - - while (!feof(file)) - if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) - break; - - pclose(file); -out_free_filename: - if (free_filename) - free(filename); - return err; + return symbol__annotate(he->ms.sym, he->ms.map, privsize); } void hists__inc_nr_events(struct hists *self, u32 type) @@ -1177,7 +972,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) size_t ret = 0; for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { - const char *name = event__get_event_name(i); + const char *name = perf_event__name(i); if (!strcmp(name, "UNKNOWN")) continue; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ee789856a8c..37c79089de0 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -9,33 +9,6 @@ extern struct callchain_param callchain_param; struct hist_entry; struct addr_location; struct symbol; -struct rb_root; - -struct objdump_line { - struct list_head node; - s64 offset; - char *line; -}; - -void objdump_line__free(struct objdump_line *self); -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos); - -struct sym_hist { - u64 sum; - u64 ip[0]; -}; - -struct sym_ext { - struct rb_node node; - double percent; - char *path; -}; - -struct sym_priv { - struct sym_hist *hist; - struct sym_ext *ext; -}; /* * The kernel collects the number of events it couldn't send in a stretch and @@ -77,6 +50,8 @@ struct hists { u64 event_stream; u32 type; u16 col_len[HISTC_NR_COLS]; + /* Best would be to reuse the session callchain cursor */ + struct callchain_cursor callchain_cursor; }; struct hist_entry *__hists__add_entry(struct hists *self, @@ -102,9 +77,8 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); -int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); -int hist_entry__annotate(struct hist_entry *self, struct list_head *head, - size_t privsize); +int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); +int hist_entry__annotate(struct hist_entry *self, size_t privsize); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); @@ -116,18 +90,20 @@ bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); #ifdef NO_NEWT_SUPPORT static inline int hists__browse(struct hists *self __used, const char *helpline __used, - const char *ev_name __used) + const char *ev_name __used, int evidx __used) { return 0; } static inline int hists__tui_browse_tree(struct rb_root *self __used, - const char *help __used) + const char *help __used, + int evidx __used) { return 0; } -static inline int hist_entry__tui_annotate(struct hist_entry *self __used) +static inline int hist_entry__tui_annotate(struct hist_entry *self __used, + int evidx __used) { return 0; } @@ -136,13 +112,13 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used) #else #include <newt.h> int hists__browse(struct hists *self, const char *helpline, - const char *ev_name); -int hist_entry__tui_annotate(struct hist_entry *self); + const char *ev_name, int evidx); +int hist_entry__tui_annotate(struct hist_entry *self, int evidx); #define KEY_LEFT NEWT_KEY_LEFT #define KEY_RIGHT NEWT_KEY_RIGHT -int hists__tui_browse_tree(struct rb_root *self, const char *help); +int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx); #endif unsigned int hists__sort_list_width(struct hists *self); diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index f5ca26e53fb..356c7e467b8 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h @@ -1,3 +1,4 @@ +#include <linux/kernel.h> #include "../../../../include/linux/list.h" #ifndef PERF_LIST_H diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 135f69baf96..cf082daa43e 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1,6 +1,7 @@ #include "../../../include/linux/hw_breakpoint.h" #include "util.h" #include "../perf.h" +#include "evlist.h" #include "evsel.h" #include "parse-options.h" #include "parse-events.h" @@ -11,10 +12,6 @@ #include "header.h" #include "debugfs.h" -int nr_counters; - -LIST_HEAD(evsel_list); - struct event_symbol { u8 type; u64 config; @@ -449,8 +446,8 @@ parse_single_tracepoint_event(char *sys_name, /* sys + ':' + event + ':' + flags*/ #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) static enum event_result -parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, - char *flags) +parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, + const char *evt_exp, char *flags) { char evt_path[MAXPATHLEN]; struct dirent *evt_ent; @@ -483,15 +480,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, if (len < 0) return EVT_FAILED; - if (parse_events(NULL, event_opt, 0)) + if (parse_events(opt, event_opt, 0)) return EVT_FAILED; } return EVT_HANDLED_ALL; } -static enum event_result parse_tracepoint_event(const char **strp, - struct perf_event_attr *attr) +static enum event_result +parse_tracepoint_event(const struct option *opt, const char **strp, + struct perf_event_attr *attr) { const char *evt_name; char *flags = NULL, *comma_loc; @@ -530,7 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp, return EVT_FAILED; if (strpbrk(evt_name, "*?")) { *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ - return parse_multiple_tracepoint_event(sys_name, evt_name, + return parse_multiple_tracepoint_event(opt, sys_name, evt_name, flags); } else { return parse_single_tracepoint_event(sys_name, evt_name, @@ -740,11 +738,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) * Symbolic names are (almost) exactly matched. */ static enum event_result -parse_event_symbols(const char **str, struct perf_event_attr *attr) +parse_event_symbols(const struct option *opt, const char **str, + struct perf_event_attr *attr) { enum event_result ret; - ret = parse_tracepoint_event(str, attr); + ret = parse_tracepoint_event(opt, str, attr); if (ret != EVT_FAILED) goto modifier; @@ -778,14 +777,15 @@ modifier: return ret; } -int parse_events(const struct option *opt __used, const char *str, int unset __used) +int parse_events(const struct option *opt, const char *str, int unset __used) { + struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; struct perf_event_attr attr; enum event_result ret; for (;;) { memset(&attr, 0, sizeof(attr)); - ret = parse_event_symbols(&str, &attr); + ret = parse_event_symbols(opt, &str, &attr); if (ret == EVT_FAILED) return -1; @@ -794,12 +794,10 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u if (ret != EVT_HANDLED_ALL) { struct perf_evsel *evsel; - evsel = perf_evsel__new(&attr, - nr_counters); + evsel = perf_evsel__new(&attr, evlist->nr_entries); if (evsel == NULL) return -1; - list_add_tail(&evsel->node, &evsel_list); - ++nr_counters; + perf_evlist__add(evlist, evsel); } if (*str == 0) @@ -813,13 +811,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u return 0; } -int parse_filter(const struct option *opt __used, const char *str, +int parse_filter(const struct option *opt, const char *str, int unset __used) { + struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; struct perf_evsel *last = NULL; - if (!list_empty(&evsel_list)) - last = list_entry(evsel_list.prev, struct perf_evsel, node); + if (evlist->nr_entries > 0) + last = list_entry(evlist->entries.prev, struct perf_evsel, node); if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { fprintf(stderr, @@ -981,33 +980,3 @@ void print_events(void) exit(129); } - -int perf_evsel_list__create_default(void) -{ - struct perf_evsel *evsel; - struct perf_event_attr attr; - - memset(&attr, 0, sizeof(attr)); - attr.type = PERF_TYPE_HARDWARE; - attr.config = PERF_COUNT_HW_CPU_CYCLES; - - evsel = perf_evsel__new(&attr, 0); - - if (evsel == NULL) - return -ENOMEM; - - list_add(&evsel->node, &evsel_list); - ++nr_counters; - return 0; -} - -void perf_evsel_list__delete(void) -{ - struct perf_evsel *pos, *n; - - list_for_each_entry_safe(pos, n, &evsel_list, node) { - list_del_init(&pos->node); - perf_evsel__delete(pos); - } - nr_counters = 0; -} diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 458e3ecf17a..cf7e94abb67 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -9,11 +9,6 @@ struct list_head; struct perf_evsel; -extern struct list_head evsel_list; - -int perf_evsel_list__create_default(void); -void perf_evsel_list__delete(void); - struct option; struct tracepoint_path { @@ -25,8 +20,6 @@ struct tracepoint_path { extern struct tracepoint_path *tracepoint_id_to_path(u64 config); extern bool have_tracepoints(struct list_head *evlist); -extern int nr_counters; - const char *event_name(struct perf_evsel *event); extern const char *__event_name(int type, u64 config); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6e29d9c9dcc..9d237e3cff5 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -31,6 +31,7 @@ #include <string.h> #include <stdarg.h> #include <limits.h> +#include <elf.h> #undef _GNU_SOURCE #include "util.h" @@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name, NULL); } -const char *kernel_get_module_path(const char *module) +static struct map *kernel_get_module_map(const char *module) +{ + struct rb_node *nd; + struct map_groups *grp = &machine.kmaps; + + if (!module) + module = "kernel"; + + for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node); + if (strncmp(pos->dso->short_name + 1, module, + pos->dso->short_name_len - 2) == 0) { + return pos; + } + } + return NULL; +} + +static struct dso *kernel_get_module_dso(const char *module) { struct dso *dso; struct map *map; @@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module) } } found: - return dso->long_name; + return dso; +} + +const char *kernel_get_module_path(const char *module) +{ + struct dso *dso = kernel_get_module_dso(module); + return (dso) ? dso->long_name : NULL; } #ifdef DWARF_SUPPORT @@ -426,12 +451,14 @@ end: } static int show_available_vars_at(int fd, struct perf_probe_event *pev, - int max_vls, bool externs) + int max_vls, struct strfilter *_filter, + bool externs) { char *buf; - int ret, i; + int ret, i, nvars; struct str_node *node; struct variable_list *vls = NULL, *vl; + const char *var; buf = synthesize_perf_probe_point(&pev->point); if (!buf) @@ -439,36 +466,45 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, pr_debug("Searching variables at %s\n", buf); ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); - if (ret > 0) { - /* Some variables were found */ - fprintf(stdout, "Available variables at %s\n", buf); - for (i = 0; i < ret; i++) { - vl = &vls[i]; - /* - * A probe point might be converted to - * several trace points. - */ - fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, - vl->point.offset); - free(vl->point.symbol); - if (vl->vars) { - strlist__for_each(node, vl->vars) + if (ret <= 0) { + pr_err("Failed to find variables at %s (%d)\n", buf, ret); + goto end; + } + /* Some variables are found */ + fprintf(stdout, "Available variables at %s\n", buf); + for (i = 0; i < ret; i++) { + vl = &vls[i]; + /* + * A probe point might be converted to + * several trace points. + */ + fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, + vl->point.offset); + free(vl->point.symbol); + nvars = 0; + if (vl->vars) { + strlist__for_each(node, vl->vars) { + var = strchr(node->s, '\t') + 1; + if (strfilter__compare(_filter, var)) { fprintf(stdout, "\t\t%s\n", node->s); - strlist__delete(vl->vars); - } else - fprintf(stdout, "(No variables)\n"); + nvars++; + } + } + strlist__delete(vl->vars); } - free(vls); - } else - pr_err("Failed to find variables at %s (%d)\n", buf, ret); - + if (nvars == 0) + fprintf(stdout, "\t\t(No matched variables)\n"); + } + free(vls); +end: free(buf); return ret; } /* Show available variables on given probe point */ int show_available_vars(struct perf_probe_event *pevs, int npevs, - int max_vls, const char *module, bool externs) + int max_vls, const char *module, + struct strfilter *_filter, bool externs) { int i, fd, ret = 0; @@ -485,7 +521,8 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, setup_pager(); for (i = 0; i < npevs && ret >= 0; i++) - ret = show_available_vars_at(fd, &pevs[i], max_vls, externs); + ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, + externs); close(fd); return ret; @@ -531,7 +568,9 @@ int show_line_range(struct line_range *lr __unused, const char *module __unused) int show_available_vars(struct perf_probe_event *pevs __unused, int npevs __unused, int max_vls __unused, - const char *module __unused, bool externs __unused) + const char *module __unused, + struct strfilter *filter __unused, + bool externs __unused) { pr_warning("Debuginfo-analysis is not supported.\n"); return -ENOSYS; @@ -1912,4 +1951,46 @@ int del_perf_probe_events(struct strlist *dellist) return ret; } +/* TODO: don't use a global variable for filter ... */ +static struct strfilter *available_func_filter; +/* + * If a symbol corresponds to a function with global binding and + * matches filter return 0. For all others return 1. + */ +static int filter_available_functions(struct map *map __unused, + struct symbol *sym) +{ + if (sym->binding == STB_GLOBAL && + strfilter__compare(available_func_filter, sym->name)) + return 0; + return 1; +} + +int show_available_funcs(const char *module, struct strfilter *_filter) +{ + struct map *map; + int ret; + + setup_pager(); + + ret = init_vmlinux(); + if (ret < 0) + return ret; + + map = kernel_get_module_map(module); + if (!map) { + pr_err("Failed to find %s map.\n", (module) ? : "kernel"); + return -EINVAL; + } + available_func_filter = _filter; + if (map__load(map, filter_available_functions)) { + pr_err("Failed to load map.\n"); + return -EINVAL; + } + if (!dso__sorted_by_name(map->dso, map->type)) + dso__sort_by_name(map->dso, map->type); + + dso__fprintf_symbols_by_name(map->dso, map->type, stdout); + return 0; +} diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 5accbedfea3..3434fc9d79d 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -3,6 +3,7 @@ #include <stdbool.h> #include "strlist.h" +#include "strfilter.h" extern bool probe_event_dry_run; @@ -126,7 +127,8 @@ extern int show_perf_probe_events(void); extern int show_line_range(struct line_range *lr, const char *module); extern int show_available_vars(struct perf_probe_event *pevs, int npevs, int max_probe_points, const char *module, - bool externs); + struct strfilter *filter, bool externs); +extern int show_available_funcs(const char *module, struct strfilter *filter); /* Maximum index number of event-name postfix */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ab83b6ac5d6..fe461f6559f 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -33,6 +33,7 @@ #include <ctype.h> #include <dwarf-regs.h> +#include <linux/bitops.h> #include "event.h" #include "debug.h" #include "util.h" @@ -280,6 +281,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) return name ? (strcmp(tname, name) == 0) : false; } +/* Get callsite line number of inline-function instance */ +static int die_get_call_lineno(Dwarf_Die *in_die) +{ + Dwarf_Attribute attr; + Dwarf_Word ret; + + if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) + return -ENOENT; + + dwarf_formudata(&attr, &ret); + return (int)ret; +} + /* Get type die */ static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) { @@ -320,13 +334,23 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) return vr_die; } -static bool die_is_signed_type(Dwarf_Die *tp_die) +static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, + Dwarf_Word *result) { Dwarf_Attribute attr; + + if (dwarf_attr(tp_die, attr_name, &attr) == NULL || + dwarf_formudata(&attr, result) != 0) + return -ENOENT; + + return 0; +} + +static bool die_is_signed_type(Dwarf_Die *tp_die) +{ Dwarf_Word ret; - if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || - dwarf_formudata(&attr, &ret) != 0) + if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) return false; return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || @@ -335,11 +359,29 @@ static bool die_is_signed_type(Dwarf_Die *tp_die) 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) + if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret)) + return 0; + + return (int)ret; +} + +static int die_get_bit_size(Dwarf_Die *tp_die) +{ + Dwarf_Word ret; + + if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret)) + return 0; + + return (int)ret; +} + +static int die_get_bit_offset(Dwarf_Die *tp_die) +{ + Dwarf_Word ret; + + if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret)) return 0; return (int)ret; @@ -458,6 +500,151 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); } +/* Walker on lines (Note: line number will not be sorted) */ +typedef int (* line_walk_handler_t) (const char *fname, int lineno, + Dwarf_Addr addr, void *data); + +struct __line_walk_param { + const char *fname; + line_walk_handler_t handler; + void *data; + int retval; +}; + +static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) +{ + struct __line_walk_param *lw = data; + Dwarf_Addr addr; + int lineno; + + if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { + lineno = die_get_call_lineno(in_die); + if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { + lw->retval = lw->handler(lw->fname, lineno, addr, + lw->data); + if (lw->retval != 0) + return DIE_FIND_CB_FOUND; + } + } + return DIE_FIND_CB_SIBLING; +} + +/* Walk on lines of blocks included in given DIE */ +static int __die_walk_funclines(Dwarf_Die *sp_die, + line_walk_handler_t handler, void *data) +{ + struct __line_walk_param lw = { + .handler = handler, + .data = data, + .retval = 0, + }; + Dwarf_Die die_mem; + Dwarf_Addr addr; + int lineno; + + /* Handle function declaration line */ + lw.fname = dwarf_decl_file(sp_die); + if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && + dwarf_entrypc(sp_die, &addr) == 0) { + lw.retval = handler(lw.fname, lineno, addr, data); + if (lw.retval != 0) + goto done; + } + die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); +done: + return lw.retval; +} + +static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) +{ + struct __line_walk_param *lw = data; + + lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); + if (lw->retval != 0) + return DWARF_CB_ABORT; + + return DWARF_CB_OK; +} + +/* + * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on + * the lines inside the subprogram, otherwise PDIE must be a CU DIE. + */ +static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, + void *data) +{ + Dwarf_Lines *lines; + Dwarf_Line *line; + Dwarf_Addr addr; + const char *fname; + int lineno, ret = 0; + Dwarf_Die die_mem, *cu_die; + size_t nlines, i; + + /* Get the CU die */ + if (dwarf_tag(pdie) == DW_TAG_subprogram) + cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); + else + cu_die = pdie; + if (!cu_die) { + pr_debug2("Failed to get CU from subprogram\n"); + return -EINVAL; + } + + /* Get lines list in the CU */ + if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { + pr_debug2("Failed to get source lines on this CU.\n"); + return -ENOENT; + } + pr_debug2("Get %zd lines from this CU\n", nlines); + + /* Walk on the lines on lines list */ + for (i = 0; i < nlines; i++) { + line = dwarf_onesrcline(lines, i); + if (line == NULL || + dwarf_lineno(line, &lineno) != 0 || + dwarf_lineaddr(line, &addr) != 0) { + pr_debug2("Failed to get line info. " + "Possible error in debuginfo.\n"); + continue; + } + /* Filter lines based on address */ + if (pdie != cu_die) + /* + * Address filtering + * The line is included in given function, and + * no inline block includes it. + */ + if (!dwarf_haspc(pdie, addr) || + die_find_inlinefunc(pdie, addr, &die_mem)) + continue; + /* Get source line */ + fname = dwarf_linesrc(line, NULL, NULL); + + ret = handler(fname, lineno, addr, data); + if (ret != 0) + return ret; + } + + /* + * Dwarf lines doesn't include function declarations and inlined + * subroutines. We have to check functions list or given function. + */ + if (pdie != cu_die) + ret = __die_walk_funclines(pdie, handler, data); + else { + struct __line_walk_param param = { + .handler = handler, + .data = data, + .retval = 0, + }; + dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); + ret = param.retval; + } + + return ret; +} + struct __find_variable_param { const char *name; Dwarf_Addr addr; @@ -669,6 +856,8 @@ static_var: return 0; } +#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long)) + static int convert_variable_type(Dwarf_Die *vr_die, struct probe_trace_arg *tvar, const char *cast) @@ -685,6 +874,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, return (tvar->type == NULL) ? -ENOMEM : 0; } + if (die_get_bit_size(vr_die) != 0) { + /* This is a bitfield */ + ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die), + die_get_bit_offset(vr_die), + BYTES_TO_BITS(die_get_byte_size(vr_die))); + goto formatted; + } + if (die_get_real_type(vr_die, &type) == NULL) { pr_warning("Failed to get a type information of %s.\n", dwarf_diename(vr_die)); @@ -729,29 +926,31 @@ static int convert_variable_type(Dwarf_Die *vr_die, return (tvar->type == NULL) ? -ENOMEM : 0; } - 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 = BYTES_TO_BITS(die_get_byte_size(&type)); + if (!ret) + /* No size ... try to use default type */ + return 0; - 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; - } - tvar->type = strdup(buf); - if (tvar->type == NULL) - return -ENOMEM; + /* 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); + +formatted: + if (ret < 0 || ret >= 16) { + if (ret >= 16) + ret = -E2BIG; + pr_warning("Failed to convert variable type: %s\n", + strerror(-ret)); + return ret; } + tvar->type = strdup(buf); + if (tvar->type == NULL) + return -ENOMEM; return 0; } @@ -1050,157 +1249,102 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) return ret; } -/* Find probe point from its line number */ -static int find_probe_point_by_line(struct probe_finder *pf) +static int probe_point_line_walker(const char *fname, int lineno, + Dwarf_Addr addr, void *data) { - Dwarf_Lines *lines; - Dwarf_Line *line; - size_t nlines, i; - Dwarf_Addr addr; - int lineno; - int ret = 0; - - if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { - pr_warning("No source lines found.\n"); - return -ENOENT; - } + struct probe_finder *pf = data; + int ret; - for (i = 0; i < nlines && ret == 0; i++) { - line = dwarf_onesrcline(lines, i); - if (dwarf_lineno(line, &lineno) != 0 || - lineno != pf->lno) - continue; + if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) + return 0; - /* TODO: Get fileno from line, but how? */ - if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) - continue; + pf->addr = addr; + ret = call_probe_finder(NULL, pf); - if (dwarf_lineaddr(line, &addr) != 0) { - pr_warning("Failed to get the address of the line.\n"); - return -ENOENT; - } - pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", - (int)i, lineno, (uintmax_t)addr); - pf->addr = addr; + /* Continue if no error, because the line will be in inline function */ + return ret < 0 ?: 0; +} - ret = call_probe_finder(NULL, pf); - /* Continuing, because target line might be inlined. */ - } - return ret; +/* Find probe point from its line number */ +static int find_probe_point_by_line(struct probe_finder *pf) +{ + return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); } /* Find lines which match lazy pattern */ static int find_lazy_match_lines(struct list_head *head, const char *fname, const char *pat) { - char *fbuf, *p1, *p2; - int fd, line, nlines = -1; - struct stat st; - - fd = open(fname, O_RDONLY); - if (fd < 0) { - pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); + FILE *fp; + char *line = NULL; + size_t line_len; + ssize_t len; + int count = 0, linenum = 1; + + fp = fopen(fname, "r"); + if (!fp) { + pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); return -errno; } - if (fstat(fd, &st) < 0) { - pr_warning("Failed to get the size of %s: %s\n", - fname, strerror(errno)); - nlines = -errno; - goto out_close; - } - - nlines = -ENOMEM; - fbuf = malloc(st.st_size + 2); - if (fbuf == NULL) - goto out_close; - if (read(fd, fbuf, st.st_size) < 0) { - pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); - nlines = -errno; - goto out_free_fbuf; - } - fbuf[st.st_size] = '\n'; /* Dummy line */ - fbuf[st.st_size + 1] = '\0'; - p1 = fbuf; - line = 1; - nlines = 0; - while ((p2 = strchr(p1, '\n')) != NULL) { - *p2 = '\0'; - if (strlazymatch(p1, pat)) { - line_list__add_line(head, line); - nlines++; + while ((len = getline(&line, &line_len, fp)) > 0) { + + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + + if (strlazymatch(line, pat)) { + line_list__add_line(head, linenum); + count++; } - line++; - p1 = p2 + 1; + linenum++; } -out_free_fbuf: - free(fbuf); -out_close: - close(fd); - return nlines; + + if (ferror(fp)) + count = -errno; + free(line); + fclose(fp); + + if (count == 0) + pr_debug("No matched lines found in %s.\n", fname); + return count; +} + +static int probe_point_lazy_walker(const char *fname, int lineno, + Dwarf_Addr addr, void *data) +{ + struct probe_finder *pf = data; + int ret; + + if (!line_list__has_line(&pf->lcache, lineno) || + strtailcmp(fname, pf->fname) != 0) + return 0; + + pr_debug("Probe line found: line:%d addr:0x%llx\n", + lineno, (unsigned long long)addr); + pf->addr = addr; + ret = call_probe_finder(NULL, pf); + + /* + * Continue if no error, because the lazy pattern will match + * to other lines + */ + return ret < 0 ?: 0; } /* Find probe points from lazy pattern */ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) { - Dwarf_Lines *lines; - Dwarf_Line *line; - size_t nlines, i; - Dwarf_Addr addr; - Dwarf_Die die_mem; - int lineno; int ret = 0; 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) + if (ret <= 0) return ret; } - if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { - pr_warning("No source lines found.\n"); - return -ENOENT; - } - - for (i = 0; i < nlines && ret >= 0; i++) { - line = dwarf_onesrcline(lines, i); - - if (dwarf_lineno(line, &lineno) != 0 || - !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; - } - 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)) - continue; - } - - pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", - (int)i, lineno, (unsigned long long)addr); - pf->addr = addr; - - ret = call_probe_finder(sp_die, pf); - /* Continuing, because target line might be inlined. */ - } - /* TODO: deallocate lines, but how? */ - return ret; + return die_walk_lines(sp_die, probe_point_lazy_walker, pf); } /* Callback parameter with return value */ @@ -1644,91 +1788,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, 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) +static int line_range_walk_cb(const char *fname, int lineno, + Dwarf_Addr addr __used, + void *data) { - struct dwarf_callback_param *param = data; - struct line_finder *lf = param->data; - const char *src; - int lineno; + struct line_finder *lf = data; - 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 || + if ((strtailcmp(fname, lf->fname) != 0) || (lf->lno_s > lineno || lf->lno_e < lineno)) - return DWARF_CB_OK; + return 0; - param->retval = line_range_add_line(src, lineno, lf->lr); - if (param->retval < 0) - return DWARF_CB_ABORT; - return DWARF_CB_OK; -} + if (line_range_add_line(fname, lineno, lf->lr) < 0) + return -EINVAL; -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 0; } /* Find line range from its line number */ static int 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; - 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.\n"); - return -ENOENT; - } - - /* 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)) - continue; - - if (sp_die) { - /* Address filtering 1: does sp_die include addr? */ - if (dwarf_lineaddr(line, &addr) != 0 || - !dwarf_haspc(sp_die, addr)) - continue; - - /* Address filtering 2: No child include addr? */ - if (die_find_inlinefunc(sp_die, addr, &die_mem)) - continue; - } - - /* TODO: Get fileno from line, but how? */ - src = dwarf_linesrc(line, NULL, NULL); - if (strtailcmp(src, lf->fname) != 0) - continue; - - ret = line_range_add_line(src, lineno, lf->lr); - if (ret < 0) - return ret; - } + int ret; - /* - * 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); + ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); /* Update status */ if (ret >= 0) @@ -1758,9 +1839,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) struct line_finder *lf = param->data; struct line_range *lr = lf->lr; - pr_debug("find (%llx) %s\n", - (unsigned long long)dwarf_dieoffset(sp_die), - dwarf_diename(sp_die)); if (dwarf_tag(sp_die) == DW_TAG_subprogram && die_compare_name(sp_die, lr->function)) { lf->fname = dwarf_decl_file(sp_die); diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c new file mode 100644 index 00000000000..5317ef22904 --- /dev/null +++ b/tools/perf/util/python.c @@ -0,0 +1,891 @@ +#include <Python.h> +#include <structmember.h> +#include <inttypes.h> +#include <poll.h> +#include "evlist.h" +#include "evsel.h" +#include "event.h" +#include "cpumap.h" +#include "thread_map.h" + +struct throttle_event { + struct perf_event_header header; + u64 time; + u64 id; + u64 stream_id; +}; + +PyMODINIT_FUNC initperf(void); + +#define member_def(type, member, ptype, help) \ + { #member, ptype, \ + offsetof(struct pyrf_event, event) + offsetof(struct type, member), \ + 0, help } + +#define sample_member_def(name, member, ptype, help) \ + { #name, ptype, \ + offsetof(struct pyrf_event, sample) + offsetof(struct perf_sample, member), \ + 0, help } + +struct pyrf_event { + PyObject_HEAD + struct perf_sample sample; + union perf_event event; +}; + +#define sample_members \ + sample_member_def(sample_ip, ip, T_ULONGLONG, "event type"), \ + sample_member_def(sample_pid, pid, T_INT, "event pid"), \ + sample_member_def(sample_tid, tid, T_INT, "event tid"), \ + sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ + sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"), \ + sample_member_def(sample_id, id, T_ULONGLONG, "event id"), \ + sample_member_def(sample_stream_id, stream_id, T_ULONGLONG, "event stream id"), \ + sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ + sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), + +static char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object."); + +static PyMemberDef pyrf_mmap_event__members[] = { + sample_members + member_def(perf_event_header, type, T_UINT, "event type"), + member_def(mmap_event, pid, T_UINT, "event pid"), + member_def(mmap_event, tid, T_UINT, "event tid"), + member_def(mmap_event, start, T_ULONGLONG, "start of the map"), + member_def(mmap_event, len, T_ULONGLONG, "map length"), + member_def(mmap_event, pgoff, T_ULONGLONG, "page offset"), + member_def(mmap_event, filename, T_STRING_INPLACE, "backing store"), + { .name = NULL, }, +}; + +static PyObject *pyrf_mmap_event__repr(struct pyrf_event *pevent) +{ + PyObject *ret; + char *s; + + if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRIx64 ", " + "length: %#" PRIx64 ", offset: %#" PRIx64 ", " + "filename: %s }", + pevent->event.mmap.pid, pevent->event.mmap.tid, + pevent->event.mmap.start, pevent->event.mmap.len, + pevent->event.mmap.pgoff, pevent->event.mmap.filename) < 0) { + ret = PyErr_NoMemory(); + } else { + ret = PyString_FromString(s); + free(s); + } + return ret; +} + +static PyTypeObject pyrf_mmap_event__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.mmap_event", + .tp_basicsize = sizeof(struct pyrf_event), + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_mmap_event__doc, + .tp_members = pyrf_mmap_event__members, + .tp_repr = (reprfunc)pyrf_mmap_event__repr, +}; + +static char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object."); + +static PyMemberDef pyrf_task_event__members[] = { + sample_members + member_def(perf_event_header, type, T_UINT, "event type"), + member_def(fork_event, pid, T_UINT, "event pid"), + member_def(fork_event, ppid, T_UINT, "event ppid"), + member_def(fork_event, tid, T_UINT, "event tid"), + member_def(fork_event, ptid, T_UINT, "event ptid"), + member_def(fork_event, time, T_ULONGLONG, "timestamp"), + { .name = NULL, }, +}; + +static PyObject *pyrf_task_event__repr(struct pyrf_event *pevent) +{ + return PyString_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, " + "ptid: %u, time: %" PRIu64 "}", + pevent->event.header.type == PERF_RECORD_FORK ? "fork" : "exit", + pevent->event.fork.pid, + pevent->event.fork.ppid, + pevent->event.fork.tid, + pevent->event.fork.ptid, + pevent->event.fork.time); +} + +static PyTypeObject pyrf_task_event__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.task_event", + .tp_basicsize = sizeof(struct pyrf_event), + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_task_event__doc, + .tp_members = pyrf_task_event__members, + .tp_repr = (reprfunc)pyrf_task_event__repr, +}; + +static char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object."); + +static PyMemberDef pyrf_comm_event__members[] = { + sample_members + member_def(perf_event_header, type, T_UINT, "event type"), + member_def(comm_event, pid, T_UINT, "event pid"), + member_def(comm_event, tid, T_UINT, "event tid"), + member_def(comm_event, comm, T_STRING_INPLACE, "process name"), + { .name = NULL, }, +}; + +static PyObject *pyrf_comm_event__repr(struct pyrf_event *pevent) +{ + return PyString_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }", + pevent->event.comm.pid, + pevent->event.comm.tid, + pevent->event.comm.comm); +} + +static PyTypeObject pyrf_comm_event__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.comm_event", + .tp_basicsize = sizeof(struct pyrf_event), + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_comm_event__doc, + .tp_members = pyrf_comm_event__members, + .tp_repr = (reprfunc)pyrf_comm_event__repr, +}; + +static char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."); + +static PyMemberDef pyrf_throttle_event__members[] = { + sample_members + member_def(perf_event_header, type, T_UINT, "event type"), + member_def(throttle_event, time, T_ULONGLONG, "timestamp"), + member_def(throttle_event, id, T_ULONGLONG, "event id"), + member_def(throttle_event, stream_id, T_ULONGLONG, "event stream id"), + { .name = NULL, }, +}; + +static PyObject *pyrf_throttle_event__repr(struct pyrf_event *pevent) +{ + struct throttle_event *te = (struct throttle_event *)(&pevent->event.header + 1); + + return PyString_FromFormat("{ type: %sthrottle, time: %" PRIu64 ", id: %" PRIu64 + ", stream_id: %" PRIu64 " }", + pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un", + te->time, te->id, te->stream_id); +} + +static PyTypeObject pyrf_throttle_event__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.throttle_event", + .tp_basicsize = sizeof(struct pyrf_event), + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_throttle_event__doc, + .tp_members = pyrf_throttle_event__members, + .tp_repr = (reprfunc)pyrf_throttle_event__repr, +}; + +static int pyrf_event__setup_types(void) +{ + int err; + pyrf_mmap_event__type.tp_new = + pyrf_task_event__type.tp_new = + pyrf_comm_event__type.tp_new = + pyrf_throttle_event__type.tp_new = PyType_GenericNew; + err = PyType_Ready(&pyrf_mmap_event__type); + if (err < 0) + goto out; + err = PyType_Ready(&pyrf_task_event__type); + if (err < 0) + goto out; + err = PyType_Ready(&pyrf_comm_event__type); + if (err < 0) + goto out; + err = PyType_Ready(&pyrf_throttle_event__type); + if (err < 0) + goto out; +out: + return err; +} + +static PyTypeObject *pyrf_event__type[] = { + [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, + [PERF_RECORD_LOST] = &pyrf_mmap_event__type, + [PERF_RECORD_COMM] = &pyrf_comm_event__type, + [PERF_RECORD_EXIT] = &pyrf_task_event__type, + [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, + [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, + [PERF_RECORD_FORK] = &pyrf_task_event__type, + [PERF_RECORD_READ] = &pyrf_mmap_event__type, + [PERF_RECORD_SAMPLE] = &pyrf_mmap_event__type, +}; + +static PyObject *pyrf_event__new(union perf_event *event) +{ + struct pyrf_event *pevent; + PyTypeObject *ptype; + + if (event->header.type < PERF_RECORD_MMAP || + event->header.type > PERF_RECORD_SAMPLE) + return NULL; + + ptype = pyrf_event__type[event->header.type]; + pevent = PyObject_New(struct pyrf_event, ptype); + if (pevent != NULL) + memcpy(&pevent->event, event, event->header.size); + return (PyObject *)pevent; +} + +struct pyrf_cpu_map { + PyObject_HEAD + + struct cpu_map *cpus; +}; + +static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "cpustr", NULL, NULL, }; + char *cpustr = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", + kwlist, &cpustr)) + return -1; + + pcpus->cpus = cpu_map__new(cpustr); + if (pcpus->cpus == NULL) + return -1; + return 0; +} + +static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus) +{ + cpu_map__delete(pcpus->cpus); + pcpus->ob_type->tp_free((PyObject*)pcpus); +} + +static Py_ssize_t pyrf_cpu_map__length(PyObject *obj) +{ + struct pyrf_cpu_map *pcpus = (void *)obj; + + return pcpus->cpus->nr; +} + +static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) +{ + struct pyrf_cpu_map *pcpus = (void *)obj; + + if (i >= pcpus->cpus->nr) + return NULL; + + return Py_BuildValue("i", pcpus->cpus->map[i]); +} + +static PySequenceMethods pyrf_cpu_map__sequence_methods = { + .sq_length = pyrf_cpu_map__length, + .sq_item = pyrf_cpu_map__item, +}; + +static char pyrf_cpu_map__doc[] = PyDoc_STR("cpu map object."); + +static PyTypeObject pyrf_cpu_map__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.cpu_map", + .tp_basicsize = sizeof(struct pyrf_cpu_map), + .tp_dealloc = (destructor)pyrf_cpu_map__delete, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_cpu_map__doc, + .tp_as_sequence = &pyrf_cpu_map__sequence_methods, + .tp_init = (initproc)pyrf_cpu_map__init, +}; + +static int pyrf_cpu_map__setup_types(void) +{ + pyrf_cpu_map__type.tp_new = PyType_GenericNew; + return PyType_Ready(&pyrf_cpu_map__type); +} + +struct pyrf_thread_map { + PyObject_HEAD + + struct thread_map *threads; +}; + +static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "pid", "tid", NULL, NULL, }; + int pid = -1, tid = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", + kwlist, &pid, &tid)) + return -1; + + pthreads->threads = thread_map__new(pid, tid); + if (pthreads->threads == NULL) + return -1; + return 0; +} + +static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads) +{ + thread_map__delete(pthreads->threads); + pthreads->ob_type->tp_free((PyObject*)pthreads); +} + +static Py_ssize_t pyrf_thread_map__length(PyObject *obj) +{ + struct pyrf_thread_map *pthreads = (void *)obj; + + return pthreads->threads->nr; +} + +static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) +{ + struct pyrf_thread_map *pthreads = (void *)obj; + + if (i >= pthreads->threads->nr) + return NULL; + + return Py_BuildValue("i", pthreads->threads->map[i]); +} + +static PySequenceMethods pyrf_thread_map__sequence_methods = { + .sq_length = pyrf_thread_map__length, + .sq_item = pyrf_thread_map__item, +}; + +static char pyrf_thread_map__doc[] = PyDoc_STR("thread map object."); + +static PyTypeObject pyrf_thread_map__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.thread_map", + .tp_basicsize = sizeof(struct pyrf_thread_map), + .tp_dealloc = (destructor)pyrf_thread_map__delete, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_thread_map__doc, + .tp_as_sequence = &pyrf_thread_map__sequence_methods, + .tp_init = (initproc)pyrf_thread_map__init, +}; + +static int pyrf_thread_map__setup_types(void) +{ + pyrf_thread_map__type.tp_new = PyType_GenericNew; + return PyType_Ready(&pyrf_thread_map__type); +} + +struct pyrf_evsel { + PyObject_HEAD + + struct perf_evsel evsel; +}; + +static int pyrf_evsel__init(struct pyrf_evsel *pevsel, + PyObject *args, PyObject *kwargs) +{ + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + .sample_type = PERF_SAMPLE_PERIOD | PERF_SAMPLE_TID, + }; + static char *kwlist[] = { + "type", + "config", + "sample_freq", + "sample_period", + "sample_type", + "read_format", + "disabled", + "inherit", + "pinned", + "exclusive", + "exclude_user", + "exclude_kernel", + "exclude_hv", + "exclude_idle", + "mmap", + "comm", + "freq", + "inherit_stat", + "enable_on_exec", + "task", + "watermark", + "precise_ip", + "mmap_data", + "sample_id_all", + "wakeup_events", + "bp_type", + "bp_addr", + "bp_len", NULL, NULL, }; + u64 sample_period = 0; + u32 disabled = 0, + inherit = 0, + pinned = 0, + exclusive = 0, + exclude_user = 0, + exclude_kernel = 0, + exclude_hv = 0, + exclude_idle = 0, + mmap = 0, + comm = 0, + freq = 1, + inherit_stat = 0, + enable_on_exec = 0, + task = 0, + watermark = 0, + precise_ip = 0, + mmap_data = 0, + sample_id_all = 1; + int idx = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|iKiKKiiiiiiiiiiiiiiiiiiiiiKK", kwlist, + &attr.type, &attr.config, &attr.sample_freq, + &sample_period, &attr.sample_type, + &attr.read_format, &disabled, &inherit, + &pinned, &exclusive, &exclude_user, + &exclude_kernel, &exclude_hv, &exclude_idle, + &mmap, &comm, &freq, &inherit_stat, + &enable_on_exec, &task, &watermark, + &precise_ip, &mmap_data, &sample_id_all, + &attr.wakeup_events, &attr.bp_type, + &attr.bp_addr, &attr.bp_len, &idx)) + return -1; + + /* union... */ + if (sample_period != 0) { + if (attr.sample_freq != 0) + return -1; /* FIXME: throw right exception */ + attr.sample_period = sample_period; + } + + /* Bitfields */ + attr.disabled = disabled; + attr.inherit = inherit; + attr.pinned = pinned; + attr.exclusive = exclusive; + attr.exclude_user = exclude_user; + attr.exclude_kernel = exclude_kernel; + attr.exclude_hv = exclude_hv; + attr.exclude_idle = exclude_idle; + attr.mmap = mmap; + attr.comm = comm; + attr.freq = freq; + attr.inherit_stat = inherit_stat; + attr.enable_on_exec = enable_on_exec; + attr.task = task; + attr.watermark = watermark; + attr.precise_ip = precise_ip; + attr.mmap_data = mmap_data; + attr.sample_id_all = sample_id_all; + + perf_evsel__init(&pevsel->evsel, &attr, idx); + return 0; +} + +static void pyrf_evsel__delete(struct pyrf_evsel *pevsel) +{ + perf_evsel__exit(&pevsel->evsel); + pevsel->ob_type->tp_free((PyObject*)pevsel); +} + +static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, + PyObject *args, PyObject *kwargs) +{ + struct perf_evsel *evsel = &pevsel->evsel; + struct cpu_map *cpus = NULL; + struct thread_map *threads = NULL; + PyObject *pcpus = NULL, *pthreads = NULL; + int group = 0, overwrite = 0; + static char *kwlist[] = {"cpus", "threads", "group", "overwrite", NULL, NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, + &pcpus, &pthreads, &group, &overwrite)) + return NULL; + + if (pthreads != NULL) + threads = ((struct pyrf_thread_map *)pthreads)->threads; + + if (pcpus != NULL) + cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; + + if (perf_evsel__open(evsel, cpus, threads, group, overwrite) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef pyrf_evsel__methods[] = { + { + .ml_name = "open", + .ml_meth = (PyCFunction)pyrf_evsel__open, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("open the event selector file descriptor table.") + }, + { .ml_name = NULL, } +}; + +static char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object."); + +static PyTypeObject pyrf_evsel__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.evsel", + .tp_basicsize = sizeof(struct pyrf_evsel), + .tp_dealloc = (destructor)pyrf_evsel__delete, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_evsel__doc, + .tp_methods = pyrf_evsel__methods, + .tp_init = (initproc)pyrf_evsel__init, +}; + +static int pyrf_evsel__setup_types(void) +{ + pyrf_evsel__type.tp_new = PyType_GenericNew; + return PyType_Ready(&pyrf_evsel__type); +} + +struct pyrf_evlist { + PyObject_HEAD + + struct perf_evlist evlist; +}; + +static int pyrf_evlist__init(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs __used) +{ + PyObject *pcpus = NULL, *pthreads = NULL; + struct cpu_map *cpus; + struct thread_map *threads; + + if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads)) + return -1; + + threads = ((struct pyrf_thread_map *)pthreads)->threads; + cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; + perf_evlist__init(&pevlist->evlist, cpus, threads); + return 0; +} + +static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) +{ + perf_evlist__exit(&pevlist->evlist); + pevlist->ob_type->tp_free((PyObject*)pevlist); +} + +static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs) +{ + struct perf_evlist *evlist = &pevlist->evlist; + static char *kwlist[] = {"pages", "overwrite", + NULL, NULL}; + int pages = 128, overwrite = false; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, + &pages, &overwrite)) + return NULL; + + if (perf_evlist__mmap(evlist, pages, overwrite) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs) +{ + struct perf_evlist *evlist = &pevlist->evlist; + static char *kwlist[] = {"timeout", NULL, NULL}; + int timeout = -1, n; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) + return NULL; + + n = poll(evlist->pollfd, evlist->nr_fds, timeout); + if (n < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return Py_BuildValue("i", n); +} + +static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, + PyObject *args __used, PyObject *kwargs __used) +{ + struct perf_evlist *evlist = &pevlist->evlist; + PyObject *list = PyList_New(0); + int i; + + for (i = 0; i < evlist->nr_fds; ++i) { + PyObject *file; + FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); + + if (fp == NULL) + goto free_list; + + file = PyFile_FromFile(fp, "perf", "r", NULL); + if (file == NULL) + goto free_list; + + if (PyList_Append(list, file) != 0) { + Py_DECREF(file); + goto free_list; + } + + Py_DECREF(file); + } + + return list; +free_list: + return PyErr_NoMemory(); +} + + +static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs __used) +{ + struct perf_evlist *evlist = &pevlist->evlist; + PyObject *pevsel; + struct perf_evsel *evsel; + + if (!PyArg_ParseTuple(args, "O", &pevsel)) + return NULL; + + Py_INCREF(pevsel); + evsel = &((struct pyrf_evsel *)pevsel)->evsel; + evsel->idx = evlist->nr_entries; + perf_evlist__add(evlist, evsel); + + return Py_BuildValue("i", evlist->nr_entries); +} + +static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs) +{ + struct perf_evlist *evlist = &pevlist->evlist; + union perf_event *event; + int sample_id_all = 1, cpu; + static char *kwlist[] = {"sample_id_all", NULL, NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, + &cpu, &sample_id_all)) + return NULL; + + event = perf_evlist__read_on_cpu(evlist, cpu); + if (event != NULL) { + struct perf_evsel *first; + PyObject *pyevent = pyrf_event__new(event); + struct pyrf_event *pevent = (struct pyrf_event *)pyevent; + + if (pyevent == NULL) + return PyErr_NoMemory(); + + first = list_entry(evlist->entries.next, struct perf_evsel, node); + perf_event__parse_sample(event, first->attr.sample_type, sample_id_all, + &pevent->sample); + return pyevent; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef pyrf_evlist__methods[] = { + { + .ml_name = "mmap", + .ml_meth = (PyCFunction)pyrf_evlist__mmap, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("mmap the file descriptor table.") + }, + { + .ml_name = "poll", + .ml_meth = (PyCFunction)pyrf_evlist__poll, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("poll the file descriptor table.") + }, + { + .ml_name = "get_pollfd", + .ml_meth = (PyCFunction)pyrf_evlist__get_pollfd, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("get the poll file descriptor table.") + }, + { + .ml_name = "add", + .ml_meth = (PyCFunction)pyrf_evlist__add, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("adds an event selector to the list.") + }, + { + .ml_name = "read_on_cpu", + .ml_meth = (PyCFunction)pyrf_evlist__read_on_cpu, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("reads an event.") + }, + { .ml_name = NULL, } +}; + +static Py_ssize_t pyrf_evlist__length(PyObject *obj) +{ + struct pyrf_evlist *pevlist = (void *)obj; + + return pevlist->evlist.nr_entries; +} + +static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) +{ + struct pyrf_evlist *pevlist = (void *)obj; + struct perf_evsel *pos; + + if (i >= pevlist->evlist.nr_entries) + return NULL; + + list_for_each_entry(pos, &pevlist->evlist.entries, node) + if (i-- == 0) + break; + + return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); +} + +static PySequenceMethods pyrf_evlist__sequence_methods = { + .sq_length = pyrf_evlist__length, + .sq_item = pyrf_evlist__item, +}; + +static char pyrf_evlist__doc[] = PyDoc_STR("perf event selector list object."); + +static PyTypeObject pyrf_evlist__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.evlist", + .tp_basicsize = sizeof(struct pyrf_evlist), + .tp_dealloc = (destructor)pyrf_evlist__delete, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_as_sequence = &pyrf_evlist__sequence_methods, + .tp_doc = pyrf_evlist__doc, + .tp_methods = pyrf_evlist__methods, + .tp_init = (initproc)pyrf_evlist__init, +}; + +static int pyrf_evlist__setup_types(void) +{ + pyrf_evlist__type.tp_new = PyType_GenericNew; + return PyType_Ready(&pyrf_evlist__type); +} + +static struct { + const char *name; + int value; +} perf__constants[] = { + { "TYPE_HARDWARE", PERF_TYPE_HARDWARE }, + { "TYPE_SOFTWARE", PERF_TYPE_SOFTWARE }, + { "TYPE_TRACEPOINT", PERF_TYPE_TRACEPOINT }, + { "TYPE_HW_CACHE", PERF_TYPE_HW_CACHE }, + { "TYPE_RAW", PERF_TYPE_RAW }, + { "TYPE_BREAKPOINT", PERF_TYPE_BREAKPOINT }, + + { "COUNT_HW_CPU_CYCLES", PERF_COUNT_HW_CPU_CYCLES }, + { "COUNT_HW_INSTRUCTIONS", PERF_COUNT_HW_INSTRUCTIONS }, + { "COUNT_HW_CACHE_REFERENCES", PERF_COUNT_HW_CACHE_REFERENCES }, + { "COUNT_HW_CACHE_MISSES", PERF_COUNT_HW_CACHE_MISSES }, + { "COUNT_HW_BRANCH_INSTRUCTIONS", PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, + { "COUNT_HW_BRANCH_MISSES", PERF_COUNT_HW_BRANCH_MISSES }, + { "COUNT_HW_BUS_CYCLES", PERF_COUNT_HW_BUS_CYCLES }, + { "COUNT_HW_CACHE_L1D", PERF_COUNT_HW_CACHE_L1D }, + { "COUNT_HW_CACHE_L1I", PERF_COUNT_HW_CACHE_L1I }, + { "COUNT_HW_CACHE_LL", PERF_COUNT_HW_CACHE_LL }, + { "COUNT_HW_CACHE_DTLB", PERF_COUNT_HW_CACHE_DTLB }, + { "COUNT_HW_CACHE_ITLB", PERF_COUNT_HW_CACHE_ITLB }, + { "COUNT_HW_CACHE_BPU", PERF_COUNT_HW_CACHE_BPU }, + { "COUNT_HW_CACHE_OP_READ", PERF_COUNT_HW_CACHE_OP_READ }, + { "COUNT_HW_CACHE_OP_WRITE", PERF_COUNT_HW_CACHE_OP_WRITE }, + { "COUNT_HW_CACHE_OP_PREFETCH", PERF_COUNT_HW_CACHE_OP_PREFETCH }, + { "COUNT_HW_CACHE_RESULT_ACCESS", PERF_COUNT_HW_CACHE_RESULT_ACCESS }, + { "COUNT_HW_CACHE_RESULT_MISS", PERF_COUNT_HW_CACHE_RESULT_MISS }, + + { "COUNT_SW_CPU_CLOCK", PERF_COUNT_SW_CPU_CLOCK }, + { "COUNT_SW_TASK_CLOCK", PERF_COUNT_SW_TASK_CLOCK }, + { "COUNT_SW_PAGE_FAULTS", PERF_COUNT_SW_PAGE_FAULTS }, + { "COUNT_SW_CONTEXT_SWITCHES", PERF_COUNT_SW_CONTEXT_SWITCHES }, + { "COUNT_SW_CPU_MIGRATIONS", PERF_COUNT_SW_CPU_MIGRATIONS }, + { "COUNT_SW_PAGE_FAULTS_MIN", PERF_COUNT_SW_PAGE_FAULTS_MIN }, + { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, + { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, + { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, + + { "SAMPLE_IP", PERF_SAMPLE_IP }, + { "SAMPLE_TID", PERF_SAMPLE_TID }, + { "SAMPLE_TIME", PERF_SAMPLE_TIME }, + { "SAMPLE_ADDR", PERF_SAMPLE_ADDR }, + { "SAMPLE_READ", PERF_SAMPLE_READ }, + { "SAMPLE_CALLCHAIN", PERF_SAMPLE_CALLCHAIN }, + { "SAMPLE_ID", PERF_SAMPLE_ID }, + { "SAMPLE_CPU", PERF_SAMPLE_CPU }, + { "SAMPLE_PERIOD", PERF_SAMPLE_PERIOD }, + { "SAMPLE_STREAM_ID", PERF_SAMPLE_STREAM_ID }, + { "SAMPLE_RAW", PERF_SAMPLE_RAW }, + + { "FORMAT_TOTAL_TIME_ENABLED", PERF_FORMAT_TOTAL_TIME_ENABLED }, + { "FORMAT_TOTAL_TIME_RUNNING", PERF_FORMAT_TOTAL_TIME_RUNNING }, + { "FORMAT_ID", PERF_FORMAT_ID }, + { "FORMAT_GROUP", PERF_FORMAT_GROUP }, + + { "RECORD_MMAP", PERF_RECORD_MMAP }, + { "RECORD_LOST", PERF_RECORD_LOST }, + { "RECORD_COMM", PERF_RECORD_COMM }, + { "RECORD_EXIT", PERF_RECORD_EXIT }, + { "RECORD_THROTTLE", PERF_RECORD_THROTTLE }, + { "RECORD_UNTHROTTLE", PERF_RECORD_UNTHROTTLE }, + { "RECORD_FORK", PERF_RECORD_FORK }, + { "RECORD_READ", PERF_RECORD_READ }, + { "RECORD_SAMPLE", PERF_RECORD_SAMPLE }, + { .name = NULL, }, +}; + +static PyMethodDef perf__methods[] = { + { .ml_name = NULL, } +}; + +PyMODINIT_FUNC initperf(void) +{ + PyObject *obj; + int i; + PyObject *dict, *module = Py_InitModule("perf", perf__methods); + + if (module == NULL || + pyrf_event__setup_types() < 0 || + pyrf_evlist__setup_types() < 0 || + pyrf_evsel__setup_types() < 0 || + pyrf_thread_map__setup_types() < 0 || + pyrf_cpu_map__setup_types() < 0) + return; + + Py_INCREF(&pyrf_evlist__type); + PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); + + Py_INCREF(&pyrf_evsel__type); + PyModule_AddObject(module, "evsel", (PyObject*)&pyrf_evsel__type); + + Py_INCREF(&pyrf_thread_map__type); + PyModule_AddObject(module, "thread_map", (PyObject*)&pyrf_thread_map__type); + + Py_INCREF(&pyrf_cpu_map__type); + PyModule_AddObject(module, "cpu_map", (PyObject*)&pyrf_cpu_map__type); + + dict = PyModule_GetDict(module); + if (dict == NULL) + goto error; + + for (i = 0; perf__constants[i].name != NULL; i++) { + obj = PyInt_FromLong(perf__constants[i].value); + if (obj == NULL) + goto error; + PyDict_SetItemString(dict, perf__constants[i].name, obj); + Py_DECREF(obj); + } + +error: + if (PyErr_Occurred()) + PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); +} diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c6d99334bdf..2040b853852 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -248,8 +248,7 @@ static void python_process_event(int cpu, void *data, context = PyCObject_FromVoidPtr(scripting_context, NULL); PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); - PyTuple_SetItem(t, n++, - PyCObject_FromVoidPtr(scripting_context, NULL)); + PyTuple_SetItem(t, n++, context); if (handler) { PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 105f00bfd55..a3a871f7bda 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -67,7 +67,7 @@ out_close: static void perf_session__id_header_size(struct perf_session *session) { - struct sample_data *data; + struct perf_sample *data; u64 sample_type = session->sample_type; u16 size = 0; @@ -165,7 +165,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, } else if (mode == O_WRONLY) { /* * In O_RDONLY mode this will be performed when reading the - * kernel MMAP event, in event__process_mmap(). + * kernel MMAP event, in perf_event__process_mmap(). */ if (perf_session__create_kernel_maps(self) < 0) goto out_delete; @@ -242,17 +242,16 @@ 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) +int perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent) { u8 cpumode = PERF_RECORD_MISC_USER; unsigned int i; - struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); + int err; - if (!syms) - return NULL; + callchain_cursor_reset(&self->callchain_cursor); for (i = 0; i < chain->nr; i++) { u64 ip = chain->ips[i]; @@ -281,30 +280,33 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, *parent = al.sym; if (!symbol_conf.use_callchain) break; - syms[i].map = al.map; - syms[i].sym = al.sym; } + + err = callchain_cursor_append(&self->callchain_cursor, + ip, al.map, al.sym); + if (err) + return err; } - return syms; + return 0; } -static int process_event_synth_stub(event_t *event __used, +static int process_event_synth_stub(union perf_event *event __used, struct perf_session *session __used) { dump_printf(": unhandled!\n"); return 0; } -static int process_event_stub(event_t *event __used, - struct sample_data *sample __used, +static int process_event_stub(union perf_event *event __used, + struct perf_sample *sample __used, struct perf_session *session __used) { dump_printf(": unhandled!\n"); return 0; } -static int process_finished_round_stub(event_t *event __used, +static int process_finished_round_stub(union perf_event *event __used, struct perf_session *session __used, struct perf_event_ops *ops __used) { @@ -312,7 +314,7 @@ static int process_finished_round_stub(event_t *event __used, return 0; } -static int process_finished_round(event_t *event, +static int process_finished_round(union perf_event *event, struct perf_session *session, struct perf_event_ops *ops); @@ -329,7 +331,7 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) if (handler->exit == NULL) handler->exit = process_event_stub; if (handler->lost == NULL) - handler->lost = event__process_lost; + handler->lost = perf_event__process_lost; if (handler->read == NULL) handler->read = process_event_stub; if (handler->throttle == NULL) @@ -363,98 +365,98 @@ void mem_bswap_64(void *src, int byte_size) } } -static void event__all64_swap(event_t *self) +static void perf_event__all64_swap(union perf_event *event) { - struct perf_event_header *hdr = &self->header; - mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); + struct perf_event_header *hdr = &event->header; + mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr)); } -static void event__comm_swap(event_t *self) +static void perf_event__comm_swap(union perf_event *event) { - self->comm.pid = bswap_32(self->comm.pid); - self->comm.tid = bswap_32(self->comm.tid); + event->comm.pid = bswap_32(event->comm.pid); + event->comm.tid = bswap_32(event->comm.tid); } -static void event__mmap_swap(event_t *self) +static void perf_event__mmap_swap(union perf_event *event) { - self->mmap.pid = bswap_32(self->mmap.pid); - self->mmap.tid = bswap_32(self->mmap.tid); - self->mmap.start = bswap_64(self->mmap.start); - self->mmap.len = bswap_64(self->mmap.len); - self->mmap.pgoff = bswap_64(self->mmap.pgoff); + event->mmap.pid = bswap_32(event->mmap.pid); + event->mmap.tid = bswap_32(event->mmap.tid); + event->mmap.start = bswap_64(event->mmap.start); + event->mmap.len = bswap_64(event->mmap.len); + event->mmap.pgoff = bswap_64(event->mmap.pgoff); } -static void event__task_swap(event_t *self) +static void perf_event__task_swap(union perf_event *event) { - self->fork.pid = bswap_32(self->fork.pid); - self->fork.tid = bswap_32(self->fork.tid); - self->fork.ppid = bswap_32(self->fork.ppid); - self->fork.ptid = bswap_32(self->fork.ptid); - self->fork.time = bswap_64(self->fork.time); + event->fork.pid = bswap_32(event->fork.pid); + event->fork.tid = bswap_32(event->fork.tid); + event->fork.ppid = bswap_32(event->fork.ppid); + event->fork.ptid = bswap_32(event->fork.ptid); + event->fork.time = bswap_64(event->fork.time); } -static void event__read_swap(event_t *self) +static void perf_event__read_swap(union perf_event *event) { - self->read.pid = bswap_32(self->read.pid); - self->read.tid = bswap_32(self->read.tid); - self->read.value = bswap_64(self->read.value); - self->read.time_enabled = bswap_64(self->read.time_enabled); - self->read.time_running = bswap_64(self->read.time_running); - self->read.id = bswap_64(self->read.id); + event->read.pid = bswap_32(event->read.pid); + event->read.tid = bswap_32(event->read.tid); + event->read.value = bswap_64(event->read.value); + event->read.time_enabled = bswap_64(event->read.time_enabled); + event->read.time_running = bswap_64(event->read.time_running); + event->read.id = bswap_64(event->read.id); } -static void event__attr_swap(event_t *self) +static void perf_event__attr_swap(union perf_event *event) { 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); + event->attr.attr.type = bswap_32(event->attr.attr.type); + event->attr.attr.size = bswap_32(event->attr.attr.size); + event->attr.attr.config = bswap_64(event->attr.attr.config); + event->attr.attr.sample_period = bswap_64(event->attr.attr.sample_period); + event->attr.attr.sample_type = bswap_64(event->attr.attr.sample_type); + event->attr.attr.read_format = bswap_64(event->attr.attr.read_format); + event->attr.attr.wakeup_events = bswap_32(event->attr.attr.wakeup_events); + event->attr.attr.bp_type = bswap_32(event->attr.attr.bp_type); + event->attr.attr.bp_addr = bswap_64(event->attr.attr.bp_addr); + event->attr.attr.bp_len = bswap_64(event->attr.attr.bp_len); + + size = event->header.size; + size -= (void *)&event->attr.id - (void *)event; + mem_bswap_64(event->attr.id, size); } -static void event__event_type_swap(event_t *self) +static void perf_event__event_type_swap(union perf_event *event) { - self->event_type.event_type.event_id = - bswap_64(self->event_type.event_type.event_id); + event->event_type.event_type.event_id = + bswap_64(event->event_type.event_type.event_id); } -static void event__tracing_data_swap(event_t *self) +static void perf_event__tracing_data_swap(union perf_event *event) { - self->tracing_data.size = bswap_32(self->tracing_data.size); + event->tracing_data.size = bswap_32(event->tracing_data.size); } -typedef void (*event__swap_op)(event_t *self); - -static event__swap_op event__swap_ops[] = { - [PERF_RECORD_MMAP] = event__mmap_swap, - [PERF_RECORD_COMM] = event__comm_swap, - [PERF_RECORD_FORK] = event__task_swap, - [PERF_RECORD_EXIT] = event__task_swap, - [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, +typedef void (*perf_event__swap_op)(union perf_event *event); + +static perf_event__swap_op perf_event__swap_ops[] = { + [PERF_RECORD_MMAP] = perf_event__mmap_swap, + [PERF_RECORD_COMM] = perf_event__comm_swap, + [PERF_RECORD_FORK] = perf_event__task_swap, + [PERF_RECORD_EXIT] = perf_event__task_swap, + [PERF_RECORD_LOST] = perf_event__all64_swap, + [PERF_RECORD_READ] = perf_event__read_swap, + [PERF_RECORD_SAMPLE] = perf_event__all64_swap, + [PERF_RECORD_HEADER_ATTR] = perf_event__attr_swap, + [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, + [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, + [PERF_RECORD_HEADER_BUILD_ID] = NULL, + [PERF_RECORD_HEADER_MAX] = NULL, }; struct sample_queue { u64 timestamp; u64 file_offset; - event_t *event; + union perf_event *event; struct list_head list; }; @@ -472,8 +474,8 @@ static void perf_session_free_sample_buffers(struct perf_session *session) } static int perf_session_deliver_event(struct perf_session *session, - event_t *event, - struct sample_data *sample, + union perf_event *event, + struct perf_sample *sample, struct perf_event_ops *ops, u64 file_offset); @@ -483,7 +485,7 @@ static void flush_sample_queue(struct perf_session *s, struct ordered_samples *os = &s->ordered_samples; struct list_head *head = &os->samples; struct sample_queue *tmp, *iter; - struct sample_data sample; + struct perf_sample sample; u64 limit = os->next_flush; u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; @@ -494,7 +496,7 @@ static void flush_sample_queue(struct perf_session *s, if (iter->timestamp > limit) break; - event__parse_sample(iter->event, s, &sample); + perf_session__parse_sample(s, iter->event, &sample); perf_session_deliver_event(s, iter->event, &sample, ops, iter->file_offset); @@ -550,7 +552,7 @@ static void flush_sample_queue(struct perf_session *s, * Flush every events below timestamp 7 * etc... */ -static int process_finished_round(event_t *event __used, +static int process_finished_round(union perf_event *event __used, struct perf_session *session, struct perf_event_ops *ops) { @@ -607,12 +609,12 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) -static int perf_session_queue_event(struct perf_session *s, event_t *event, - struct sample_data *data, u64 file_offset) +static int perf_session_queue_event(struct perf_session *s, union perf_event *event, + struct perf_sample *sample, u64 file_offset) { struct ordered_samples *os = &s->ordered_samples; struct list_head *sc = &os->sample_cache; - u64 timestamp = data->time; + u64 timestamp = sample->time; struct sample_queue *new; if (!timestamp || timestamp == ~0ULL) @@ -648,7 +650,7 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event, return 0; } -static void callchain__printf(struct sample_data *sample) +static void callchain__printf(struct perf_sample *sample) { unsigned int i; @@ -660,8 +662,8 @@ static void callchain__printf(struct sample_data *sample) } static void perf_session__print_tstamp(struct perf_session *session, - event_t *event, - struct sample_data *sample) + union perf_event *event, + struct perf_sample *sample) { if (event->header.type != PERF_RECORD_SAMPLE && !session->sample_id_all) { @@ -676,8 +678,8 @@ static void perf_session__print_tstamp(struct perf_session *session, printf("%" PRIu64 " ", sample->time); } -static void dump_event(struct perf_session *session, event_t *event, - u64 file_offset, struct sample_data *sample) +static void dump_event(struct perf_session *session, union perf_event *event, + u64 file_offset, struct perf_sample *sample) { if (!dump_trace) return; @@ -691,11 +693,11 @@ static void dump_event(struct perf_session *session, event_t *event, perf_session__print_tstamp(session, event, sample); printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset, - event->header.size, event__get_event_name(event->header.type)); + event->header.size, perf_event__name(event->header.type)); } -static void dump_sample(struct perf_session *session, event_t *event, - struct sample_data *sample) +static void dump_sample(struct perf_session *session, union perf_event *event, + struct perf_sample *sample) { if (!dump_trace) return; @@ -709,8 +711,8 @@ static void dump_sample(struct perf_session *session, event_t *event, } static int perf_session_deliver_event(struct perf_session *session, - event_t *event, - struct sample_data *sample, + union perf_event *event, + struct perf_sample *sample, struct perf_event_ops *ops, u64 file_offset) { @@ -743,7 +745,7 @@ static int perf_session_deliver_event(struct perf_session *session, } static int perf_session__preprocess_sample(struct perf_session *session, - event_t *event, struct sample_data *sample) + union perf_event *event, struct perf_sample *sample) { if (event->header.type != PERF_RECORD_SAMPLE || !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) @@ -758,7 +760,7 @@ static int perf_session__preprocess_sample(struct perf_session *session, return 0; } -static int perf_session__process_user_event(struct perf_session *session, event_t *event, +static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, struct perf_event_ops *ops, u64 file_offset) { dump_event(session, event, file_offset, NULL); @@ -783,15 +785,16 @@ static int perf_session__process_user_event(struct perf_session *session, event_ } static int perf_session__process_event(struct perf_session *session, - event_t *event, + union perf_event *event, struct perf_event_ops *ops, u64 file_offset) { - struct sample_data sample; + struct perf_sample sample; int ret; - if (session->header.needs_swap && event__swap_ops[event->header.type]) - event__swap_ops[event->header.type](event); + if (session->header.needs_swap && + perf_event__swap_ops[event->header.type]) + perf_event__swap_ops[event->header.type](event); if (event->header.type >= PERF_RECORD_HEADER_MAX) return -EINVAL; @@ -804,7 +807,7 @@ static int perf_session__process_event(struct perf_session *session, /* * For all kernel events we get the sample data */ - event__parse_sample(event, session, &sample); + perf_session__parse_sample(session, event, &sample); /* Preprocess sample records - precheck callchains */ if (perf_session__preprocess_sample(session, event, &sample)) @@ -843,7 +846,7 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se static void perf_session__warn_about_errors(const struct perf_session *session, const struct perf_event_ops *ops) { - if (ops->lost == event__process_lost && + if (ops->lost == perf_event__process_lost && session->hists.stats.total_lost != 0) { ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 "!\n\nCheck IO/CPU overload!\n\n", @@ -875,7 +878,7 @@ volatile int session_done; static int __perf_session__process_pipe_events(struct perf_session *self, struct perf_event_ops *ops) { - event_t event; + union perf_event event; uint32_t size; int skip = 0; u64 head; @@ -956,7 +959,7 @@ int __perf_session__process_events(struct perf_session *session, struct ui_progress *progress; size_t page_size, mmap_size; char *buf, *mmaps[8]; - event_t *event; + union perf_event *event; uint32_t size; perf_event_ops__fill_defaults(ops); @@ -1001,7 +1004,7 @@ remap: file_pos = file_offset + head; more: - event = (event_t *)(buf + head); + event = (union perf_event *)(buf + head); if (session->header.needs_swap) perf_event_header__bswap(&event->header); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index decd83f274f..977b3a1b14a 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -51,15 +51,17 @@ struct perf_session { int cwdlen; char *cwd; struct ordered_samples ordered_samples; - char filename[0]; + struct callchain_cursor callchain_cursor; + char filename[0]; }; struct perf_event_ops; -typedef int (*event_op)(event_t *self, struct sample_data *sample, +typedef int (*event_op)(union perf_event *self, struct perf_sample *sample, struct perf_session *session); -typedef int (*event_synth_op)(event_t *self, struct perf_session *session); -typedef int (*event_op2)(event_t *self, struct perf_session *session, +typedef int (*event_synth_op)(union perf_event *self, + struct perf_session *session); +typedef int (*event_op2)(union perf_event *self, struct perf_session *session, struct perf_event_ops *ops); struct perf_event_ops { @@ -94,10 +96,10 @@ 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); +int 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); @@ -154,4 +156,13 @@ size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) { return hists__fprintf_nr_events(&self->hists, fp); } + +static inline int perf_session__parse_sample(struct perf_session *session, + const union perf_event *event, + struct perf_sample *sample) +{ + return perf_event__parse_sample(event, session->sample_type, + session->sample_id_all, sample); +} + #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py new file mode 100644 index 00000000000..1947b0430c9 --- /dev/null +++ b/tools/perf/util/setup.py @@ -0,0 +1,19 @@ +#!/usr/bin/python2 + +from distutils.core import setup, Extension + +perf = Extension('perf', + sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', + 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', + 'util/util.c', 'util/xyarray.c'], + include_dirs = ['util/include'], + extra_compile_args = ['-fno-strict-aliasing', '-Wno-write-strings']) + +setup(name='perf', + version='0.1', + description='Interface with the Linux profiling infrastructure', + author='Arnaldo Carvalho de Melo', + author_email='acme@redhat.com', + license='GPLv2', + url='http://perf.wiki.kernel.org', + ext_modules=[perf]) diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c new file mode 100644 index 00000000000..4064b7d708b --- /dev/null +++ b/tools/perf/util/strfilter.c @@ -0,0 +1,200 @@ +#include <ctype.h> +#include "util.h" +#include "string.h" +#include "strfilter.h" + +/* Operators */ +static const char *OP_and = "&"; /* Logical AND */ +static const char *OP_or = "|"; /* Logical OR */ +static const char *OP_not = "!"; /* Logical NOT */ + +#define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!') +#define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')') + +static void strfilter_node__delete(struct strfilter_node *self) +{ + if (self) { + if (self->p && !is_operator(*self->p)) + free((char *)self->p); + strfilter_node__delete(self->l); + strfilter_node__delete(self->r); + free(self); + } +} + +void strfilter__delete(struct strfilter *self) +{ + if (self) { + strfilter_node__delete(self->root); + free(self); + } +} + +static const char *get_token(const char *s, const char **e) +{ + const char *p; + + while (isspace(*s)) /* Skip spaces */ + s++; + + if (*s == '\0') { + p = s; + goto end; + } + + p = s + 1; + if (!is_separator(*s)) { + /* End search */ +retry: + while (*p && !is_separator(*p) && !isspace(*p)) + p++; + /* Escape and special case: '!' is also used in glob pattern */ + if (*(p - 1) == '\\' || (*p == '!' && *(p - 1) == '[')) { + p++; + goto retry; + } + } +end: + *e = p; + return s; +} + +static struct strfilter_node *strfilter_node__alloc(const char *op, + struct strfilter_node *l, + struct strfilter_node *r) +{ + struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); + + if (ret) { + ret->p = op; + ret->l = l; + ret->r = r; + } + + return ret; +} + +static struct strfilter_node *strfilter_node__new(const char *s, + const char **ep) +{ + struct strfilter_node root, *cur, *last_op; + const char *e; + + if (!s) + return NULL; + + memset(&root, 0, sizeof(root)); + last_op = cur = &root; + + s = get_token(s, &e); + while (*s != '\0' && *s != ')') { + switch (*s) { + case '&': /* Exchg last OP->r with AND */ + if (!cur->r || !last_op->r) + goto error; + cur = strfilter_node__alloc(OP_and, last_op->r, NULL); + if (!cur) + goto nomem; + last_op->r = cur; + last_op = cur; + break; + case '|': /* Exchg the root with OR */ + if (!cur->r || !root.r) + goto error; + cur = strfilter_node__alloc(OP_or, root.r, NULL); + if (!cur) + goto nomem; + root.r = cur; + last_op = cur; + break; + case '!': /* Add NOT as a leaf node */ + if (cur->r) + goto error; + cur->r = strfilter_node__alloc(OP_not, NULL, NULL); + if (!cur->r) + goto nomem; + cur = cur->r; + break; + case '(': /* Recursively parses inside the parenthesis */ + if (cur->r) + goto error; + cur->r = strfilter_node__new(s + 1, &s); + if (!s) + goto nomem; + if (!cur->r || *s != ')') + goto error; + e = s + 1; + break; + default: + if (cur->r) + goto error; + cur->r = strfilter_node__alloc(NULL, NULL, NULL); + if (!cur->r) + goto nomem; + cur->r->p = strndup(s, e - s); + if (!cur->r->p) + goto nomem; + } + s = get_token(e, &e); + } + if (!cur->r) + goto error; + *ep = s; + return root.r; +nomem: + s = NULL; +error: + *ep = s; + strfilter_node__delete(root.r); + return NULL; +} + +/* + * Parse filter rule and return new strfilter. + * Return NULL if fail, and *ep == NULL if memory allocation failed. + */ +struct strfilter *strfilter__new(const char *rules, const char **err) +{ + struct strfilter *ret = zalloc(sizeof(struct strfilter)); + const char *ep = NULL; + + if (ret) + ret->root = strfilter_node__new(rules, &ep); + + if (!ret || !ret->root || *ep != '\0') { + if (err) + *err = ep; + strfilter__delete(ret); + ret = NULL; + } + + return ret; +} + +static bool strfilter_node__compare(struct strfilter_node *self, + const char *str) +{ + if (!self || !self->p) + return false; + + switch (*self->p) { + case '|': /* OR */ + return strfilter_node__compare(self->l, str) || + strfilter_node__compare(self->r, str); + case '&': /* AND */ + return strfilter_node__compare(self->l, str) && + strfilter_node__compare(self->r, str); + case '!': /* NOT */ + return !strfilter_node__compare(self->r, str); + default: + return strglobmatch(str, self->p); + } +} + +/* Return true if STR matches the filter rules */ +bool strfilter__compare(struct strfilter *self, const char *str) +{ + if (!self) + return false; + return strfilter_node__compare(self->root, str); +} diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h new file mode 100644 index 00000000000..00f58a7506d --- /dev/null +++ b/tools/perf/util/strfilter.h @@ -0,0 +1,48 @@ +#ifndef __PERF_STRFILTER_H +#define __PERF_STRFILTER_H +/* General purpose glob matching filter */ + +#include <linux/list.h> +#include <stdbool.h> + +/* A node of string filter */ +struct strfilter_node { + struct strfilter_node *l; /* Tree left branche (for &,|) */ + struct strfilter_node *r; /* Tree right branche (for !,&,|) */ + const char *p; /* Operator or rule */ +}; + +/* String filter */ +struct strfilter { + struct strfilter_node *root; +}; + +/** + * strfilter__new - Create a new string filter + * @rules: Filter rule, which is a combination of glob expressions. + * @err: Pointer which points an error detected on @rules + * + * Parse @rules and return new strfilter. Return NULL if an error detected. + * In that case, *@err will indicate where it is detected, and *@err is NULL + * if a memory allocation is failed. + */ +struct strfilter *strfilter__new(const char *rules, const char **err); + +/** + * strfilter__compare - compare given string and a string filter + * @self: String filter + * @str: target string + * + * Compare @str and @self. Return true if the str match the rule + */ +bool strfilter__compare(struct strfilter *self, const char *str); + +/** + * strfilter__delete - delete a string filter + * @self: String filter to delete + * + * Delete @self. + */ +void strfilter__delete(struct strfilter *self); + +#endif diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7821d0e6866..3e193f8e306 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1525,8 +1525,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) symbol_conf.symfs, self->long_name); break; case DSO__ORIG_GUEST_KMODULE: - if (map->groups && map->groups->machine) - root_dir = map->groups->machine->root_dir; + if (map->groups && machine) + root_dir = machine->root_dir; else root_dir = ""; snprintf(name, size, "%s%s%s", symbol_conf.symfs, diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 00f4eade2e3..d5d3b22250f 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -7,61 +7,6 @@ #include "util.h" #include "debug.h" -/* Skip "." and ".." directories */ -static int filter(const struct dirent *dir) -{ - if (dir->d_name[0] == '.') - return 0; - else - return 1; -} - -struct thread_map *thread_map__new_by_pid(pid_t pid) -{ - struct thread_map *threads; - char name[256]; - int items; - struct dirent **namelist = NULL; - int i; - - sprintf(name, "/proc/%d/task", pid); - items = scandir(name, &namelist, filter, NULL); - if (items <= 0) - return NULL; - - threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); - if (threads != NULL) { - for (i = 0; i < items; i++) - threads->map[i] = atoi(namelist[i]->d_name); - threads->nr = items; - } - - for (i=0; i<items; i++) - free(namelist[i]); - free(namelist); - - return threads; -} - -struct thread_map *thread_map__new_by_tid(pid_t tid) -{ - struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); - - if (threads != NULL) { - threads->map[0] = tid; - threads->nr = 1; - } - - return threads; -} - -struct thread_map *thread_map__new(pid_t pid, pid_t tid) -{ - if (pid != -1) - return thread_map__new_by_pid(pid); - return thread_map__new_by_tid(tid); -} - static struct thread *thread__new(pid_t pid) { struct thread *self = zalloc(sizeof(*self)); diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index d7574101054..e5f2401c1b5 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -18,24 +18,10 @@ struct thread { int comm_len; }; -struct thread_map { - int nr; - int map[]; -}; - struct perf_session; void thread__delete(struct thread *self); -struct thread_map *thread_map__new_by_pid(pid_t pid); -struct thread_map *thread_map__new_by_tid(pid_t tid); -struct thread_map *thread_map__new(pid_t pid, pid_t tid); - -static inline void thread_map__delete(struct thread_map *threads) -{ - free(threads); -} - 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); diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c new file mode 100644 index 00000000000..a5df131b77c --- /dev/null +++ b/tools/perf/util/thread_map.c @@ -0,0 +1,64 @@ +#include <dirent.h> +#include <stdlib.h> +#include <stdio.h> +#include "thread_map.h" + +/* Skip "." and ".." directories */ +static int filter(const struct dirent *dir) +{ + if (dir->d_name[0] == '.') + return 0; + else + return 1; +} + +struct thread_map *thread_map__new_by_pid(pid_t pid) +{ + struct thread_map *threads; + char name[256]; + int items; + struct dirent **namelist = NULL; + int i; + + sprintf(name, "/proc/%d/task", pid); + items = scandir(name, &namelist, filter, NULL); + if (items <= 0) + return NULL; + + threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); + if (threads != NULL) { + for (i = 0; i < items; i++) + threads->map[i] = atoi(namelist[i]->d_name); + threads->nr = items; + } + + for (i=0; i<items; i++) + free(namelist[i]); + free(namelist); + + return threads; +} + +struct thread_map *thread_map__new_by_tid(pid_t tid) +{ + struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); + + if (threads != NULL) { + threads->map[0] = tid; + threads->nr = 1; + } + + return threads; +} + +struct thread_map *thread_map__new(pid_t pid, pid_t tid) +{ + if (pid != -1) + return thread_map__new_by_pid(pid); + return thread_map__new_by_tid(tid); +} + +void thread_map__delete(struct thread_map *threads) +{ + free(threads); +} diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h new file mode 100644 index 00000000000..3cb90731140 --- /dev/null +++ b/tools/perf/util/thread_map.h @@ -0,0 +1,15 @@ +#ifndef __PERF_THREAD_MAP_H +#define __PERF_THREAD_MAP_H + +#include <sys/types.h> + +struct thread_map { + int nr; + int map[]; +}; + +struct thread_map *thread_map__new_by_pid(pid_t pid); +struct thread_map *thread_map__new_by_tid(pid_t tid); +struct thread_map *thread_map__new(pid_t pid, pid_t tid); +void thread_map__delete(struct thread_map *threads); +#endif /* __PERF_THREAD_MAP_H */ diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c new file mode 100644 index 00000000000..70a9c13f4ad --- /dev/null +++ b/tools/perf/util/top.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + * + * Refactored from builtin-top.c, see that files for further copyright notes. + * + * Released under the GPL v2. (and only v2, not any later version) + */ + +#include "cpumap.h" +#include "event.h" +#include "evlist.h" +#include "evsel.h" +#include "parse-events.h" +#include "symbol.h" +#include "top.h" +#include <inttypes.h> + +/* + * Ordering weight: count-1 * count-2 * ... / count-n + */ +static double sym_weight(const struct sym_entry *sym, struct perf_top *top) +{ + double weight = sym->snap_count; + int counter; + + if (!top->display_weighted) + return weight; + + for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) + weight *= sym->count[counter]; + + weight /= (sym->count[counter] + 1); + + return weight; +} + +static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) +{ + pthread_mutex_lock(&top->active_symbols_lock); + list_del_init(&syme->node); + pthread_mutex_unlock(&top->active_symbols_lock); +} + +static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) +{ + struct rb_node **p = &tree->rb_node; + struct rb_node *parent = NULL; + struct sym_entry *iter; + + while (*p != NULL) { + parent = *p; + iter = rb_entry(parent, struct sym_entry, rb_node); + + if (se->weight > iter->weight) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&se->rb_node, parent, p); + rb_insert_color(&se->rb_node, tree); +} + +size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) +{ + struct perf_evsel *counter; + float samples_per_sec = top->samples / top->delay_secs; + float ksamples_per_sec = top->kernel_samples / top->delay_secs; + float esamples_percent = (100.0 * top->exact_samples) / top->samples; + size_t ret = 0; + + if (!perf_guest) { + ret = snprintf(bf, size, + " 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 { + float us_samples_per_sec = top->us_samples / top->delay_secs; + float guest_kernel_samples_per_sec = top->guest_kernel_samples / top->delay_secs; + float guest_us_samples_per_sec = top->guest_us_samples / top->delay_secs; + + ret = snprintf(bf, size, + " 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); + } + + if (top->evlist->nr_entries == 1 || !top->display_weighted) { + struct perf_evsel *first; + first = list_entry(top->evlist->entries.next, struct perf_evsel, node); + ret += snprintf(bf + ret, size - ret, "%" PRIu64 "%s ", + (uint64_t)first->attr.sample_period, + top->freq ? "Hz" : ""); + } + + if (!top->display_weighted) { + ret += snprintf(bf + ret, size - ret, "%s", + event_name(top->sym_evsel)); + } else list_for_each_entry(counter, &top->evlist->entries, node) { + ret += snprintf(bf + ret, size - ret, "%s%s", + counter->idx ? "/" : "", event_name(counter)); + } + + ret += snprintf(bf + ret, size - ret, "], "); + + if (top->target_pid != -1) + ret += snprintf(bf + ret, size - ret, " (target_pid: %d", + top->target_pid); + else if (top->target_tid != -1) + ret += snprintf(bf + ret, size - ret, " (target_tid: %d", + top->target_tid); + else + ret += snprintf(bf + ret, size - ret, " (all"); + + if (top->cpu_list) + ret += snprintf(bf + ret, size - ret, ", CPU%s: %s)", + top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); + else { + if (top->target_tid != -1) + ret += snprintf(bf + ret, size - ret, ")"); + else + ret += snprintf(bf + ret, size - ret, ", %d CPU%s)", + top->evlist->cpus->nr, + top->evlist->cpus->nr > 1 ? "s" : ""); + } + + return ret; +} + +void perf_top__reset_sample_counters(struct perf_top *top) +{ + top->samples = top->us_samples = top->kernel_samples = + top->exact_samples = top->guest_kernel_samples = + top->guest_us_samples = 0; +} + +float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) +{ + struct sym_entry *syme, *n; + float sum_ksamples = 0.0; + int snap = !top->display_weighted ? top->sym_counter : 0, j; + + /* Sort the active symbols */ + pthread_mutex_lock(&top->active_symbols_lock); + syme = list_entry(top->active_symbols.next, struct sym_entry, node); + pthread_mutex_unlock(&top->active_symbols_lock); + + top->rb_entries = 0; + list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { + syme->snap_count = syme->count[snap]; + if (syme->snap_count != 0) { + + if ((top->hide_user_symbols && + syme->origin == PERF_RECORD_MISC_USER) || + (top->hide_kernel_symbols && + syme->origin == PERF_RECORD_MISC_KERNEL)) { + perf_top__remove_active_sym(top, syme); + continue; + } + syme->weight = sym_weight(syme, top); + + if ((int)syme->snap_count >= top->count_filter) { + rb_insert_active_sym(root, syme); + ++top->rb_entries; + } + sum_ksamples += syme->snap_count; + + for (j = 0; j < top->evlist->nr_entries; j++) + syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; + } else + perf_top__remove_active_sym(top, syme); + } + + return sum_ksamples; +} + +/* + * Find the longest symbol name that will be displayed + */ +void perf_top__find_widths(struct perf_top *top, struct rb_root *root, + int *dso_width, int *dso_short_width, int *sym_width) +{ + struct rb_node *nd; + int printed = 0; + + *sym_width = *dso_width = *dso_short_width = 0; + + for (nd = rb_first(root); nd; nd = rb_next(nd)) { + struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); + struct symbol *sym = sym_entry__symbol(syme); + + if (++printed > top->print_entries || + (int)syme->snap_count < top->count_filter) + continue; + + if (syme->map->dso->long_name_len > *dso_width) + *dso_width = syme->map->dso->long_name_len; + + if (syme->map->dso->short_name_len > *dso_short_width) + *dso_short_width = syme->map->dso->short_name_len; + + if (sym->namelen > *sym_width) + *sym_width = sym->namelen; + } +} diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h new file mode 100644 index 00000000000..4f769f47e19 --- /dev/null +++ b/tools/perf/util/top.h @@ -0,0 +1,64 @@ +#ifndef __PERF_TOP_H +#define __PERF_TOP_H 1 + +#include "types.h" +#include "../perf.h" +#include <stddef.h> +#include <pthread.h> +#include <linux/list.h> +#include <linux/rbtree.h> + +struct perf_evlist; +struct perf_evsel; + +struct sym_entry { + struct rb_node rb_node; + struct list_head node; + unsigned long snap_count; + double weight; + int skip; + u8 origin; + struct map *map; + unsigned long count[0]; +}; + +static inline struct symbol *sym_entry__symbol(struct sym_entry *self) +{ + return ((void *)self) + symbol_conf.priv_size; +} + +struct perf_top { + struct perf_evlist *evlist; + /* + * Symbols will be added here in perf_event__process_sample and will + * get out after decayed. + */ + struct list_head active_symbols; + pthread_mutex_t active_symbols_lock; + u64 samples; + u64 kernel_samples, us_samples; + u64 exact_samples; + u64 guest_us_samples, guest_kernel_samples; + int print_entries, count_filter, delay_secs; + int display_weighted, freq, rb_entries, sym_counter; + pid_t target_pid, target_tid; + bool hide_kernel_symbols, hide_user_symbols, zero; + const char *cpu_list; + struct perf_evsel *sym_evsel; +}; + +size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); +void perf_top__reset_sample_counters(struct perf_top *top); +float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); +void perf_top__find_widths(struct perf_top *top, struct rb_root *root, + int *dso_width, int *dso_short_width, int *sym_width); + +#ifdef NO_NEWT_SUPPORT +static inline int perf_top__tui_browser(struct perf_top *top __used) +{ + return 0; +} +#else +int perf_top__tui_browser(struct perf_top *top); +#endif +#endif /* __PERF_TOP_H */ diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 73a02223c62..d8e622dd738 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -153,7 +153,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) char *next = NULL; char *addr_str; char ch; - int ret; + int ret __used; int i; line = strtok_r(file, "\n", &next); diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 82b78f99251..1aa39658539 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -1,9 +1,11 @@ #include "../browser.h" #include "../helpline.h" #include "../libslang.h" +#include "../../annotate.h" #include "../../hist.h" #include "../../sort.h" #include "../../symbol.h" +#include "../../annotate.h" static void ui__error_window(const char *fmt, ...) { @@ -58,32 +60,34 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro } static double objdump_line__calc_percent(struct objdump_line *self, - struct list_head *head, - struct symbol *sym) + struct symbol *sym, int evidx) { double percent = 0.0; if (self->offset != -1) { int len = sym->end - sym->start; unsigned int hits = 0; - struct sym_priv *priv = symbol__priv(sym); - struct sym_ext *sym_ext = priv->ext; - struct sym_hist *h = priv->hist; + struct annotation *notes = symbol__annotation(sym); + struct source_line *src_line = notes->src->lines; + struct sym_hist *h = annotation__histogram(notes, evidx); s64 offset = self->offset; - struct objdump_line *next = objdump__get_next_ip_line(head, self); - + struct objdump_line *next; + next = objdump__get_next_ip_line(¬es->src->source, self); while (offset < (s64)len && (next == NULL || offset < next->offset)) { - if (sym_ext) { - percent += sym_ext[offset].percent; + if (src_line) { + percent += src_line[offset].percent; } else - hits += h->ip[offset]; + hits += h->addr[offset]; ++offset; } - - if (sym_ext == NULL && h->sum) + /* + * If the percentage wasn't already calculated in + * symbol__get_source_line, do it now: + */ + if (src_line == NULL && h->sum) percent = 100.0 * hits / h->sum; } @@ -136,10 +140,10 @@ static void annotate_browser__set_top(struct annotate_browser *self, static int annotate_browser__run(struct annotate_browser *self) { struct rb_node *nd; - struct hist_entry *he = self->b.priv; + struct symbol *sym = self->b.priv; int key; - if (ui_browser__show(&self->b, he->ms.sym->name, + if (ui_browser__show(&self->b, sym->name, "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) return -1; /* @@ -179,42 +183,47 @@ out: return key; } -int hist_entry__tui_annotate(struct hist_entry *self) +int hist_entry__tui_annotate(struct hist_entry *he, int evidx) +{ + return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx); +} + +int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx) { struct objdump_line *pos, *n; struct objdump_line_rb_node *rbpos; - LIST_HEAD(head); + struct annotation *notes = symbol__annotation(sym); struct annotate_browser browser = { .b = { - .entries = &head, + .entries = ¬es->src->source, .refresh = ui_browser__list_head_refresh, .seek = ui_browser__list_head_seek, .write = annotate_browser__write, - .priv = self, + .priv = sym, }, }; int ret; - if (self->ms.sym == NULL) + if (sym == NULL) return -1; - if (self->ms.map->dso->annotate_warned) + if (map->dso->annotate_warned) return -1; - if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { + if (symbol__annotate(sym, map, sizeof(*rbpos)) < 0) { ui__error_window(ui_helpline__last_msg); return -1; } ui_helpline__push("Press <- or ESC to exit"); - list_for_each_entry(pos, &head, node) { + list_for_each_entry(pos, ¬es->src->source, node) { size_t line_len = strlen(pos->line); if (browser.b.width < line_len) browser.b.width = line_len; rbpos = objdump_line__rb(pos); rbpos->idx = browser.b.nr_entries++; - rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); + rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); if (rbpos->percent < 0.01) continue; objdump__insert_line(&browser.entries, rbpos); @@ -229,7 +238,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) browser.b.width += 18; /* Percentage */ ret = annotate_browser__run(&browser); - list_for_each_entry_safe(pos, n, &head, node) { + list_for_each_entry_safe(pos, n, ¬es->src->source, node) { list_del(&pos->node); objdump_line__free(pos); } diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 60c463c1602..294b4953852 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -377,7 +377,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, 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); + u64 cumul = callchain_cumul_hits(child); struct callchain_list *chain; char folded_sign = ' '; int first = true; @@ -797,7 +797,8 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, return printed; } -int hists__browse(struct hists *self, const char *helpline, const char *ev_name) +int hists__browse(struct hists *self, const char *helpline, + const char *ev_name, int evidx) { struct hist_browser *browser = hist_browser__new(self); struct pstack *fstack; @@ -935,7 +936,7 @@ do_annotate: if (he == NULL) continue; - hist_entry__tui_annotate(he); + hist_entry__tui_annotate(he, evidx); } else if (choice == browse_map) map__browse(browser->selection->map); else if (choice == zoom_dso) { @@ -984,7 +985,7 @@ out: return key; } -int hists__tui_browse_tree(struct rb_root *self, const char *help) +int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx) { struct rb_node *first = rb_first(self), *nd = first, *next; int key = 0; @@ -993,7 +994,7 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help) struct hists *hists = rb_entry(nd, struct hists, rb_node); const char *ev_name = __event_name(hists->type, hists->config); - key = hists__browse(hists, help, ev_name); + key = hists__browse(hists, help, ev_name, evidx); switch (key) { case NEWT_KEY_TAB: next = rb_next(nd); diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index e5158369106..8462bffe20b 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c @@ -41,7 +41,7 @@ static int ui_entry__read(const char *title, char *bf, size_t size, int width) out_free_form: newtPopWindow(); newtFormDestroy(form); - return 0; + return err; } struct map_browser { diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c new file mode 100644 index 00000000000..ca6062483a8 --- /dev/null +++ b/tools/perf/util/ui/browsers/top.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + * + * Parts came from builtin-{top,stat,record}.c, see those files for further + * copyright notes. + * + * Released under the GPL v2. (and only v2, not any later version) + */ +#include "../browser.h" +#include "../helpline.h" +#include "../libslang.h" +#include "../../evlist.h" +#include "../../hist.h" +#include "../../sort.h" +#include "../../symbol.h" +#include "../../top.h" + +struct perf_top_browser { + struct ui_browser b; + struct rb_root root; + float sum_ksamples; + int dso_width; + int dso_short_width; + int sym_width; +}; + +static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) +{ + struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); + struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); + bool current_entry = ui_browser__is_current_entry(browser, row); + struct symbol *symbol = sym_entry__symbol(syme); + struct perf_top *top = browser->priv; + int width = browser->width; + double pcnt; + + pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / + top_browser->sum_ksamples)); + ui_browser__set_percent_color(browser, pcnt, current_entry); + + if (top->evlist->nr_entries == 1 || !top->display_weighted) { + slsmg_printf("%20.2f ", syme->weight); + width -= 24; + } else { + slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); + width -= 23; + } + + slsmg_printf("%4.1f%%", pcnt); + width -= 7; + + if (verbose) { + slsmg_printf(" %016" PRIx64, symbol->start); + width -= 17; + } + + slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, + symbol->name); + width -= top_browser->sym_width; + slsmg_write_nstring(width >= syme->map->dso->long_name_len ? + syme->map->dso->long_name : + syme->map->dso->short_name, width); +} + +static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) +{ + struct perf_top *top = browser->b.priv; + + browser->root = RB_ROOT; + browser->b.top = NULL; + browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); + perf_top__find_widths(top, &browser->root, &browser->dso_width, + &browser->dso_short_width, + &browser->sym_width); + if (browser->sym_width + browser->dso_width > browser->b.width - 29) { + browser->dso_width = browser->dso_short_width; + if (browser->sym_width + browser->dso_width > browser->b.width - 29) + browser->sym_width = browser->b.width - browser->dso_width - 29; + } + browser->b.nr_entries = top->rb_entries; +} + +static int perf_top_browser__run(struct perf_top_browser *browser) +{ + int key; + char title[160]; + struct perf_top *top = browser->b.priv; + int delay_msecs = top->delay_secs * 1000; + + perf_top_browser__update_rb_tree(browser); + perf_top__header_snprintf(top, title, sizeof(title)); + perf_top__reset_sample_counters(top); + + if (ui_browser__show(&browser->b, title, "ESC: exit") < 0) + return -1; + + newtFormSetTimer(browser->b.form, delay_msecs); + + while (1) { + key = ui_browser__run(&browser->b); + + switch (key) { + case -1: + /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ + perf_top_browser__update_rb_tree(browser); + perf_top__header_snprintf(top, title, sizeof(title)); + perf_top__reset_sample_counters(top); + ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); + SLsmg_gotorc(0, 0); + slsmg_write_nstring(title, browser->b.width); + break; + case NEWT_KEY_TAB: + default: + goto out; + } + } +out: + ui_browser__hide(&browser->b); + return key; +} + +int perf_top__tui_browser(struct perf_top *top) +{ + struct perf_top_browser browser = { + .b = { + .entries = &browser.root, + .refresh = ui_browser__rb_tree_refresh, + .seek = ui_browser__rb_tree_seek, + .write = perf_top_browser__write, + .priv = top, + }, + }; + + ui_helpline__push("Press <- or ESC to exit"); + return perf_top_browser__run(&browser); +} diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 5623da8e808..2b63e1c9b18 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h @@ -13,11 +13,11 @@ #if SLANG_VERSION < 20104 #define slsmg_printf(msg, args...) \ - SLsmg_printf((char *)msg, ##args) + SLsmg_printf((char *)(msg), ##args) #define slsmg_write_nstring(msg, len) \ - SLsmg_write_nstring((char *)msg, len) + SLsmg_write_nstring((char *)(msg), len) #define sltt_set_color(obj, name, fg, bg) \ - SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) + SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg)) #else #define slsmg_printf SLsmg_printf #define slsmg_write_nstring SLsmg_write_nstring diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index 662085032eb..fbf1a145492 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c @@ -14,11 +14,12 @@ static void newt_suspend(void *d __used) newtResume(); } -void setup_browser(void) +void setup_browser(bool fallback_to_pager) { if (!isatty(1) || !use_browser || dump_trace) { use_browser = 0; - setup_pager(); + if (fallback_to_pager) + setup_pager(); return; } |