diff --git a/0001-drm-radeon-don-t-mess-with-hot-plug-detect-for-eDP-o.patch b/0001-drm-radeon-don-t-mess-with-hot-plug-detect-for-eDP-o.patch deleted file mode 100644 index 5389095b9..000000000 --- a/0001-drm-radeon-don-t-mess-with-hot-plug-detect-for-eDP-o.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0971d87045f5052de9f16f3b1aa0dce59f59a060 Mon Sep 17 00:00:00 2001 -From: Jerome Glisse -Date: Thu, 3 May 2012 19:05:40 -0400 -Subject: [PATCH] drm/radeon: don't mess with hot plug detect for eDP or LVDS - connector - -It seems imac pannel doesn't like whe we change the hot plug setup -and then refuse to work. This should fix : -https://bugzilla.redhat.com/show_bug.cgi?id=726143 - -Signed-off-by: Matthew Garrett -Signed-off-by: Jerome Glisse ---- - drivers/gpu/drm/radeon/r600.c | 8 ++++++++ - 1 files changed, 8 insertions(+), 0 deletions(-) - -diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c -index 4fbc590..5fd0490 100644 ---- a/drivers/gpu/drm/radeon/r600.c -+++ b/drivers/gpu/drm/radeon/r600.c -@@ -713,6 +713,14 @@ void r600_hpd_init(struct radeon_device *rdev) - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - -+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || -+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { -+ /* don't try to enable HDP on eDP or LVDS help to avoid -+ * issue such as: -+ * https://bugzilla.redhat.com/show_bug.cgi?id=726143 -+ */ -+ continue; -+ } - if (ASIC_IS_DCE3(rdev)) { - u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa); - if (ASIC_IS_DCE32(rdev)) --- -1.7.7.6 - diff --git a/CPU-hotplug-cpusets-suspend-Dont-modify-cpusets-during.patch b/CPU-hotplug-cpusets-suspend-Dont-modify-cpusets-during.patch index f57eceed9..99e05f5e5 100644 --- a/CPU-hotplug-cpusets-suspend-Dont-modify-cpusets-during.patch +++ b/CPU-hotplug-cpusets-suspend-Dont-modify-cpusets-during.patch @@ -52,9 +52,11 @@ Signed-off-by: Ingo Molnar kernel/sched/core.c | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) ---- linux-3.4.6-3.1.fc17.noarch.orig/kernel/cpuset.c -+++ linux-3.4.6-3.1.fc17.noarch/kernel/cpuset.c -@@ -2065,6 +2065,9 @@ static void scan_for_empty_cpusets(struc +diff --git a/kernel/cpuset.c b/kernel/cpuset.c +index 47de450..77abe1a 100644 +--- a/kernel/cpuset.c ++++ b/kernel/cpuset.c +@@ -2054,6 +2054,9 @@ static void scan_for_empty_cpusets(struct cpuset *root) * (of no affect) on systems that are actively using CPU hotplug * but making no active use of cpusets. * @@ -64,11 +66,13 @@ Signed-off-by: Ingo Molnar * This routine ensures that top_cpuset.cpus_allowed tracks * cpu_active_mask on each CPU hotplug (cpuhp) event. * ---- linux-3.4.6-3.1.fc17.noarch.orig/kernel/sched/core.c -+++ linux-3.4.6-3.1.fc17.noarch/kernel/sched/core.c -@@ -6931,34 +6931,66 @@ int __init sched_create_sysfs_power_savi +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index ca07ee0..e368731 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -7014,34 +7014,66 @@ match2: + mutex_unlock(&sched_domains_mutex); } - #endif /* CONFIG_SCHED_MC || CONFIG_SCHED_SMT */ +static int num_cpus_frozen; /* used to mark begin/end of suspend/resume */ + @@ -137,3 +141,6 @@ Signed-off-by: Ingo Molnar } void __init sched_init_smp(void) +-- +1.7.7.6 + diff --git a/atl1c_net_next_update-3.4.patch b/atl1c_net_next_update-3.4.patch deleted file mode 100644 index 1ec9771bc..000000000 --- a/atl1c_net_next_update-3.4.patch +++ /dev/null @@ -1,3658 +0,0 @@ -diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c.h b/drivers/net/ethernet/atheros/atl1c/atl1c.h -index ca70e16..b2bf324 100644 ---- a/drivers/net/ethernet/atheros/atl1c/atl1c.h -+++ b/drivers/net/ethernet/atheros/atl1c/atl1c.h -@@ -74,8 +74,6 @@ - - #define AT_RX_BUF_SIZE (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN) - #define MAX_JUMBO_FRAME_SIZE (6*1024) --#define MAX_TSO_FRAME_SIZE (7*1024) --#define MAX_TX_OFFLOAD_THRESH (9*1024) - - #define AT_MAX_RECEIVE_QUEUE 4 - #define AT_DEF_RECEIVE_QUEUE 1 -@@ -100,7 +98,7 @@ - #define ATL1C_ASPM_L0s_ENABLE 0x0001 - #define ATL1C_ASPM_L1_ENABLE 0x0002 - --#define AT_REGS_LEN (75 * sizeof(u32)) -+#define AT_REGS_LEN (74 * sizeof(u32)) - #define AT_EEPROM_LEN 512 - - #define ATL1C_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i])) -@@ -297,20 +295,6 @@ enum atl1c_dma_req_block { - atl1c_dma_req_4096 = 5 - }; - --enum atl1c_rss_mode { -- atl1c_rss_mode_disable = 0, -- atl1c_rss_sig_que = 1, -- atl1c_rss_mul_que_sig_int = 2, -- atl1c_rss_mul_que_mul_int = 4, --}; -- --enum atl1c_rss_type { -- atl1c_rss_disable = 0, -- atl1c_rss_ipv4 = 1, -- atl1c_rss_ipv4_tcp = 2, -- atl1c_rss_ipv6 = 4, -- atl1c_rss_ipv6_tcp = 8 --}; - - enum atl1c_nic_type { - athr_l1c = 0, -@@ -388,7 +372,6 @@ struct atl1c_hw { - enum atl1c_dma_order dma_order; - enum atl1c_dma_rcb rcb_value; - enum atl1c_dma_req_block dmar_block; -- enum atl1c_dma_req_block dmaw_block; - - u16 device_id; - u16 vendor_id; -@@ -399,8 +382,6 @@ struct atl1c_hw { - u16 phy_id2; - - u32 intr_mask; -- u8 dmaw_dly_cnt; -- u8 dmar_dly_cnt; - - u8 preamble_len; - u16 max_frame_size; -@@ -440,10 +421,6 @@ struct atl1c_hw { - #define ATL1C_FPGA_VERSION 0x8000 - u16 link_cap_flags; - #define ATL1C_LINK_CAP_1000M 0x0001 -- u16 cmb_tpd; -- u16 cmb_rrd; -- u16 cmb_rx_timer; /* 2us resolution */ -- u16 cmb_tx_timer; - u32 smb_timer; - - u16 rrd_thresh; /* Threshold of number of RRD produced to trigger -@@ -451,9 +428,6 @@ struct atl1c_hw { - u16 tpd_thresh; - u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */ - u8 rfd_burst; -- enum atl1c_rss_type rss_type; -- enum atl1c_rss_mode rss_mode; -- u8 rss_hash_bits; - u32 base_cpu; - u32 indirect_tab; - u8 mac_addr[ETH_ALEN]; -@@ -462,12 +436,12 @@ struct atl1c_hw { - bool phy_configured; - bool re_autoneg; - bool emi_ca; -+ bool msi_lnkpatch; /* link patch for specific platforms */ - }; - - /* - * atl1c_ring_header represents a single, contiguous block of DMA space -- * mapped for the three descriptor rings (tpd, rfd, rrd) and the two -- * message blocks (cmb, smb) described below -+ * mapped for the three descriptor rings (tpd, rfd, rrd) described below - */ - struct atl1c_ring_header { - void *desc; /* virtual address */ -@@ -541,16 +515,6 @@ struct atl1c_rrd_ring { - u16 next_to_clean; - }; - --struct atl1c_cmb { -- void *cmb; -- dma_addr_t dma; --}; -- --struct atl1c_smb { -- void *smb; -- dma_addr_t dma; --}; -- - /* board specific private data structure */ - struct atl1c_adapter { - struct net_device *netdev; -@@ -586,11 +550,8 @@ struct atl1c_adapter { - /* All Descriptor memory */ - struct atl1c_ring_header ring_header; - struct atl1c_tpd_ring tpd_ring[AT_MAX_TRANSMIT_QUEUE]; -- struct atl1c_rfd_ring rfd_ring[AT_MAX_RECEIVE_QUEUE]; -- struct atl1c_rrd_ring rrd_ring[AT_MAX_RECEIVE_QUEUE]; -- struct atl1c_cmb cmb; -- struct atl1c_smb smb; -- int num_rx_queues; -+ struct atl1c_rfd_ring rfd_ring; -+ struct atl1c_rrd_ring rrd_ring; - u32 bd_number; /* board number;*/ - }; - -@@ -618,8 +579,14 @@ struct atl1c_adapter { - #define AT_WRITE_REGW(a, reg, value) (\ - writew((value), ((a)->hw_addr + reg))) - --#define AT_READ_REGW(a, reg) (\ -- readw((a)->hw_addr + reg)) -+#define AT_READ_REGW(a, reg, pdata) do { \ -+ if (unlikely((a)->hibernate)) { \ -+ readw((a)->hw_addr + reg); \ -+ *(u16 *)pdata = readw((a)->hw_addr + reg); \ -+ } else { \ -+ *(u16 *)pdata = readw((a)->hw_addr + reg); \ -+ } \ -+ } while (0) - - #define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \ - writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))) -diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c -index 0a9326a..859ea84 100644 ---- a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c -+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c -@@ -141,8 +141,7 @@ static void atl1c_get_regs(struct net_device *netdev, - - memset(p, 0, AT_REGS_LEN); - -- regs->version = 0; -- AT_READ_REG(hw, REG_VPD_CAP, p++); -+ regs->version = 1; - AT_READ_REG(hw, REG_PM_CTRL, p++); - AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++); - AT_READ_REG(hw, REG_TWSI_CTRL, p++); -@@ -154,7 +153,7 @@ static void atl1c_get_regs(struct net_device *netdev, - AT_READ_REG(hw, REG_LINK_CTRL, p++); - AT_READ_REG(hw, REG_IDLE_STATUS, p++); - AT_READ_REG(hw, REG_MDIO_CTRL, p++); -- AT_READ_REG(hw, REG_SERDES_LOCK, p++); -+ AT_READ_REG(hw, REG_SERDES, p++); - AT_READ_REG(hw, REG_MAC_CTRL, p++); - AT_READ_REG(hw, REG_MAC_IPG_IFG, p++); - AT_READ_REG(hw, REG_MAC_STA_ADDR, p++); -@@ -167,9 +166,9 @@ static void atl1c_get_regs(struct net_device *netdev, - AT_READ_REG(hw, REG_WOL_CTRL, p++); - - atl1c_read_phy_reg(hw, MII_BMCR, &phy_data); -- regs_buff[73] = (u32) phy_data; -+ regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data; - atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); -- regs_buff[74] = (u32) phy_data; -+ regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data; - } - - static int atl1c_get_eeprom_len(struct net_device *netdev) -diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c -index bd1667c..ff9c738 100644 ---- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c -+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c -@@ -43,7 +43,7 @@ int atl1c_check_eeprom_exist(struct atl1c_hw *hw) - return 0; - } - --void atl1c_hw_set_mac_addr(struct atl1c_hw *hw) -+void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr) - { - u32 value; - /* -@@ -51,35 +51,48 @@ void atl1c_hw_set_mac_addr(struct atl1c_hw *hw) - * 0: 6AF600DC 1: 000B - * low dword - */ -- value = (((u32)hw->mac_addr[2]) << 24) | -- (((u32)hw->mac_addr[3]) << 16) | -- (((u32)hw->mac_addr[4]) << 8) | -- (((u32)hw->mac_addr[5])) ; -+ value = mac_addr[2] << 24 | -+ mac_addr[3] << 16 | -+ mac_addr[4] << 8 | -+ mac_addr[5]; - AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); - /* hight dword */ -- value = (((u32)hw->mac_addr[0]) << 8) | -- (((u32)hw->mac_addr[1])) ; -+ value = mac_addr[0] << 8 | -+ mac_addr[1]; - AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); - } - -+/* read mac address from hardware register */ -+static bool atl1c_read_current_addr(struct atl1c_hw *hw, u8 *eth_addr) -+{ -+ u32 addr[2]; -+ -+ AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]); -+ AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]); -+ -+ *(u32 *) ð_addr[2] = htonl(addr[0]); -+ *(u16 *) ð_addr[0] = htons((u16)addr[1]); -+ -+ return is_valid_ether_addr(eth_addr); -+} -+ - /* - * atl1c_get_permanent_address - * return 0 if get valid mac address, - */ - static int atl1c_get_permanent_address(struct atl1c_hw *hw) - { -- u32 addr[2]; - u32 i; - u32 otp_ctrl_data; - u32 twsi_ctrl_data; -- u32 ltssm_ctrl_data; -- u32 wol_data; -- u8 eth_addr[ETH_ALEN]; - u16 phy_data; - bool raise_vol = false; - -+ /* MAC-address from BIOS is the 1st priority */ -+ if (atl1c_read_current_addr(hw, hw->perm_mac_addr)) -+ return 0; -+ - /* init */ -- addr[0] = addr[1] = 0; - AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); - if (atl1c_check_eeprom_exist(hw)) { - if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) { -@@ -91,33 +104,17 @@ static int atl1c_get_permanent_address(struct atl1c_hw *hw) - msleep(1); - } - } -- -- if (hw->nic_type == athr_l2c_b || -- hw->nic_type == athr_l2c_b2 || -- hw->nic_type == athr_l1d) { -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x00); -- if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data)) -- goto out; -- phy_data &= 0xFF7F; -- atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data); -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x3B); -- if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data)) -- goto out; -- phy_data |= 0x8; -- atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data); -+ /* raise voltage temporally for l2cb */ -+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { -+ atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data); -+ phy_data &= ~ANACTRL_HB_EN; -+ atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data); -+ atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); -+ phy_data |= VOLT_CTRL_SWLOWEST; -+ atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); - udelay(20); - raise_vol = true; - } -- /* close open bit of ReadOnly*/ -- AT_READ_REG(hw, REG_LTSSM_ID_CTRL, <ssm_ctrl_data); -- ltssm_ctrl_data &= ~LTSSM_ID_EN_WRO; -- AT_WRITE_REG(hw, REG_LTSSM_ID_CTRL, ltssm_ctrl_data); -- -- /* clear any WOL settings */ -- AT_WRITE_REG(hw, REG_WOL_CTRL, 0); -- AT_READ_REG(hw, REG_WOL_CTRL, &wol_data); -- - - AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); - twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; -@@ -138,37 +135,18 @@ static int atl1c_get_permanent_address(struct atl1c_hw *hw) - msleep(1); - } - if (raise_vol) { -- if (hw->nic_type == athr_l2c_b || -- hw->nic_type == athr_l2c_b2 || -- hw->nic_type == athr_l1d || -- hw->nic_type == athr_l1d_2) { -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x00); -- if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data)) -- goto out; -- phy_data |= 0x80; -- atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data); -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x3B); -- if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data)) -- goto out; -- phy_data &= 0xFFF7; -- atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data); -- udelay(20); -- } -+ atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data); -+ phy_data |= ANACTRL_HB_EN; -+ atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data); -+ atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); -+ phy_data &= ~VOLT_CTRL_SWLOWEST; -+ atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); -+ udelay(20); - } - -- /* maybe MAC-address is from BIOS */ -- AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]); -- AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]); -- *(u32 *) ð_addr[2] = swab32(addr[0]); -- *(u16 *) ð_addr[0] = swab16(*(u16 *)&addr[1]); -- -- if (is_valid_ether_addr(eth_addr)) { -- memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); -+ if (atl1c_read_current_addr(hw, hw->perm_mac_addr)) - return 0; -- } - --out: - return -1; - } - -@@ -278,33 +256,158 @@ void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value) - } - - /* -- * Reads the value from a PHY register -- * hw - Struct containing variables accessed by shared code -- * reg_addr - address of the PHY register to read -+ * wait mdio module be idle -+ * return true: idle -+ * false: still busy - */ --int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) -+bool atl1c_wait_mdio_idle(struct atl1c_hw *hw) - { - u32 val; - int i; - -- val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | -- MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | -- MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; -+ for (i = 0; i < MDIO_MAX_AC_TO; i++) { -+ AT_READ_REG(hw, REG_MDIO_CTRL, &val); -+ if (!(val & (MDIO_CTRL_BUSY | MDIO_CTRL_START))) -+ break; -+ udelay(10); -+ } -+ -+ return i != MDIO_MAX_AC_TO; -+} -+ -+void atl1c_stop_phy_polling(struct atl1c_hw *hw) -+{ -+ if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) -+ return; -+ -+ AT_WRITE_REG(hw, REG_MDIO_CTRL, 0); -+ atl1c_wait_mdio_idle(hw); -+} -+ -+void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel) -+{ -+ u32 val; -+ -+ if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) -+ return; - -+ val = MDIO_CTRL_SPRES_PRMBL | -+ FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | -+ FIELDX(MDIO_CTRL_REG, 1) | -+ MDIO_CTRL_START | -+ MDIO_CTRL_OP_READ; -+ AT_WRITE_REG(hw, REG_MDIO_CTRL, val); -+ atl1c_wait_mdio_idle(hw); -+ val |= MDIO_CTRL_AP_EN; -+ val &= ~MDIO_CTRL_START; - AT_WRITE_REG(hw, REG_MDIO_CTRL, val); -+ udelay(30); -+} - -- for (i = 0; i < MDIO_WAIT_TIMES; i++) { -- udelay(2); -- AT_READ_REG(hw, REG_MDIO_CTRL, &val); -- if (!(val & (MDIO_START | MDIO_BUSY))) -- break; -+ -+/* -+ * atl1c_read_phy_core -+ * core funtion to read register in PHY via MDIO control regsiter. -+ * ext: extension register (see IEEE 802.3) -+ * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0) -+ * reg: reg to read -+ */ -+int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, -+ u16 reg, u16 *phy_data) -+{ -+ u32 val; -+ u16 clk_sel = MDIO_CTRL_CLK_25_4; -+ -+ atl1c_stop_phy_polling(hw); -+ -+ *phy_data = 0; -+ -+ /* only l2c_b2 & l1d_2 could use slow clock */ -+ if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) && -+ hw->hibernate) -+ clk_sel = MDIO_CTRL_CLK_25_128; -+ if (ext) { -+ val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg); -+ AT_WRITE_REG(hw, REG_MDIO_EXTN, val); -+ val = MDIO_CTRL_SPRES_PRMBL | -+ FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | -+ MDIO_CTRL_START | -+ MDIO_CTRL_MODE_EXT | -+ MDIO_CTRL_OP_READ; -+ } else { -+ val = MDIO_CTRL_SPRES_PRMBL | -+ FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | -+ FIELDX(MDIO_CTRL_REG, reg) | -+ MDIO_CTRL_START | -+ MDIO_CTRL_OP_READ; - } -- if (!(val & (MDIO_START | MDIO_BUSY))) { -- *phy_data = (u16)val; -- return 0; -+ AT_WRITE_REG(hw, REG_MDIO_CTRL, val); -+ -+ if (!atl1c_wait_mdio_idle(hw)) -+ return -1; -+ -+ AT_READ_REG(hw, REG_MDIO_CTRL, &val); -+ *phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA); -+ -+ atl1c_start_phy_polling(hw, clk_sel); -+ -+ return 0; -+} -+ -+/* -+ * atl1c_write_phy_core -+ * core funtion to write to register in PHY via MDIO control regsiter. -+ * ext: extension register (see IEEE 802.3) -+ * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0) -+ * reg: reg to write -+ */ -+int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, -+ u16 reg, u16 phy_data) -+{ -+ u32 val; -+ u16 clk_sel = MDIO_CTRL_CLK_25_4; -+ -+ atl1c_stop_phy_polling(hw); -+ -+ -+ /* only l2c_b2 & l1d_2 could use slow clock */ -+ if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) && -+ hw->hibernate) -+ clk_sel = MDIO_CTRL_CLK_25_128; -+ -+ if (ext) { -+ val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg); -+ AT_WRITE_REG(hw, REG_MDIO_EXTN, val); -+ val = MDIO_CTRL_SPRES_PRMBL | -+ FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | -+ FIELDX(MDIO_CTRL_DATA, phy_data) | -+ MDIO_CTRL_START | -+ MDIO_CTRL_MODE_EXT; -+ } else { -+ val = MDIO_CTRL_SPRES_PRMBL | -+ FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | -+ FIELDX(MDIO_CTRL_DATA, phy_data) | -+ FIELDX(MDIO_CTRL_REG, reg) | -+ MDIO_CTRL_START; - } -+ AT_WRITE_REG(hw, REG_MDIO_CTRL, val); - -- return -1; -+ if (!atl1c_wait_mdio_idle(hw)) -+ return -1; -+ -+ atl1c_start_phy_polling(hw, clk_sel); -+ -+ return 0; -+} -+ -+/* -+ * Reads the value from a PHY register -+ * hw - Struct containing variables accessed by shared code -+ * reg_addr - address of the PHY register to read -+ */ -+int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) -+{ -+ return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data); - } - - /* -@@ -315,27 +418,47 @@ int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) - */ - int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data) - { -- int i; -- u32 val; -+ return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data); -+} - -- val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | -- (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | -- MDIO_SUP_PREAMBLE | MDIO_START | -- MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; -+/* read from PHY extension register */ -+int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr, -+ u16 reg_addr, u16 *phy_data) -+{ -+ return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data); -+} - -- AT_WRITE_REG(hw, REG_MDIO_CTRL, val); -+/* write to PHY extension register */ -+int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr, -+ u16 reg_addr, u16 phy_data) -+{ -+ return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data); -+} - -- for (i = 0; i < MDIO_WAIT_TIMES; i++) { -- udelay(2); -- AT_READ_REG(hw, REG_MDIO_CTRL, &val); -- if (!(val & (MDIO_START | MDIO_BUSY))) -- break; -- } -+int atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) -+{ -+ int err; - -- if (!(val & (MDIO_START | MDIO_BUSY))) -- return 0; -+ err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr); -+ if (unlikely(err)) -+ return err; -+ else -+ err = atl1c_read_phy_reg(hw, MII_DBG_DATA, phy_data); - -- return -1; -+ return err; -+} -+ -+int atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data) -+{ -+ int err; -+ -+ err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr); -+ if (unlikely(err)) -+ return err; -+ else -+ err = atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data); -+ -+ return err; - } - - /* -@@ -380,119 +503,100 @@ static int atl1c_phy_setup_adv(struct atl1c_hw *hw) - - void atl1c_phy_disable(struct atl1c_hw *hw) - { -- AT_WRITE_REGW(hw, REG_GPHY_CTRL, -- GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET); -+ atl1c_power_saving(hw, 0); - } - --static void atl1c_phy_magic_data(struct atl1c_hw *hw) --{ -- u16 data; -- -- data = ANA_LOOP_SEL_10BT | ANA_EN_MASK_TB | ANA_EN_10BT_IDLE | -- ((1 & ANA_INTERVAL_SEL_TIMER_MASK) << -- ANA_INTERVAL_SEL_TIMER_SHIFT); -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_18); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, data); -- -- data = (2 & ANA_SERDES_CDR_BW_MASK) | ANA_MS_PAD_DBG | -- ANA_SERDES_EN_DEEM | ANA_SERDES_SEL_HSP | ANA_SERDES_EN_PLL | -- ANA_SERDES_EN_LCKDT; -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_5); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, data); -- -- data = (44 & ANA_LONG_CABLE_TH_100_MASK) | -- ((33 & ANA_SHORT_CABLE_TH_100_MASK) << -- ANA_SHORT_CABLE_TH_100_SHIFT) | ANA_BP_BAD_LINK_ACCUM | -- ANA_BP_SMALL_BW; -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_54); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, data); -- -- data = (11 & ANA_IECHO_ADJ_MASK) | ((11 & ANA_IECHO_ADJ_MASK) << -- ANA_IECHO_ADJ_2_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) << -- ANA_IECHO_ADJ_1_SHIFT) | ((8 & ANA_IECHO_ADJ_MASK) << -- ANA_IECHO_ADJ_0_SHIFT); -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_4); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, data); -- -- data = ANA_RESTART_CAL | ((7 & ANA_MANUL_SWICH_ON_MASK) << -- ANA_MANUL_SWICH_ON_SHIFT) | ANA_MAN_ENABLE | -- ANA_SEL_HSP | ANA_EN_HB | ANA_OEN_125M; -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_0); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, data); -- -- if (hw->ctrl_flags & ATL1C_HIB_DISABLE) { -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_41); -- if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0) -- return; -- data &= ~ANA_TOP_PS_EN; -- atl1c_write_phy_reg(hw, MII_DBG_DATA, data); -- -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, MII_ANA_CTRL_11); -- if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &data) != 0) -- return; -- data &= ~ANA_PS_HIB_EN; -- atl1c_write_phy_reg(hw, MII_DBG_DATA, data); -- } --} - - int atl1c_phy_reset(struct atl1c_hw *hw) - { - struct atl1c_adapter *adapter = hw->adapter; - struct pci_dev *pdev = adapter->pdev; - u16 phy_data; -- u32 phy_ctrl_data = GPHY_CTRL_DEFAULT; -- u32 mii_ier_data = IER_LINK_UP | IER_LINK_DOWN; -+ u32 phy_ctrl_data, lpi_ctrl; - int err; - -- if (hw->ctrl_flags & ATL1C_HIB_DISABLE) -- phy_ctrl_data &= ~GPHY_CTRL_HIB_EN; -- -+ /* reset PHY core */ -+ AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl_data); -+ phy_ctrl_data &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_PHY_IDDQ | -+ GPHY_CTRL_GATE_25M_EN | GPHY_CTRL_PWDOWN_HW | GPHY_CTRL_CLS); -+ phy_ctrl_data |= GPHY_CTRL_SEL_ANA_RST; -+ if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) -+ phy_ctrl_data |= (GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE); -+ else -+ phy_ctrl_data &= ~(GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE); - AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); - AT_WRITE_FLUSH(hw); -- msleep(40); -- phy_ctrl_data |= GPHY_CTRL_EXT_RESET; -- AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); -+ udelay(10); -+ AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data | GPHY_CTRL_EXT_RESET); - AT_WRITE_FLUSH(hw); -- msleep(10); -+ udelay(10 * GPHY_CTRL_EXT_RST_TO); /* delay 800us */ - -+ /* switch clock */ - if (hw->nic_type == athr_l2c_b) { -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x0A); -- atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data & 0xDFFF); -+ atl1c_read_phy_dbg(hw, MIIDBG_CFGLPSPD, &phy_data); -+ atl1c_write_phy_dbg(hw, MIIDBG_CFGLPSPD, -+ phy_data & ~CFGLPSPD_RSTCNT_CLK125SW); - } - -- if (hw->nic_type == athr_l2c_b || -- hw->nic_type == athr_l2c_b2 || -- hw->nic_type == athr_l1d || -- hw->nic_type == athr_l1d_2) { -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x3B); -- atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data & 0xFFF7); -- msleep(20); -+ /* tx-half amplitude issue fix */ -+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { -+ atl1c_read_phy_dbg(hw, MIIDBG_CABLE1TH_DET, &phy_data); -+ phy_data |= CABLE1TH_DET_EN; -+ atl1c_write_phy_dbg(hw, MIIDBG_CABLE1TH_DET, phy_data); - } -- if (hw->nic_type == athr_l1d) { -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x29); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, 0x929D); -+ -+ /* clear bit3 of dbgport 3B to lower voltage */ -+ if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) { -+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { -+ atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); -+ phy_data &= ~VOLT_CTRL_SWLOWEST; -+ atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); -+ } -+ /* power saving config */ -+ phy_data = -+ hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ? -+ L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF; -+ atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data); -+ /* hib */ -+ atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, -+ SYSMODCTRL_IECHOADJ_DEF); -+ } else { -+ /* disable pws */ -+ atl1c_read_phy_dbg(hw, MIIDBG_LEGCYPS, &phy_data); -+ atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, -+ phy_data & ~LEGCYPS_EN); -+ /* disable hibernate */ -+ atl1c_read_phy_dbg(hw, MIIDBG_HIBNEG, &phy_data); -+ atl1c_write_phy_dbg(hw, MIIDBG_HIBNEG, -+ phy_data & HIBNEG_PSHIB_EN); - } -- if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c_b2 -- || hw->nic_type == athr_l2c) { -- atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x29); -- atl1c_write_phy_reg(hw, MII_DBG_DATA, 0xB6DD); -+ /* disable AZ(EEE) by default */ -+ if (hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 || -+ hw->nic_type == athr_l2c_b2) { -+ AT_READ_REG(hw, REG_LPI_CTRL, &lpi_ctrl); -+ AT_WRITE_REG(hw, REG_LPI_CTRL, lpi_ctrl & ~LPI_CTRL_EN); -+ atl1c_write_phy_ext(hw, MIIEXT_ANEG, MIIEXT_LOCAL_EEEADV, 0); -+ atl1c_write_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL3, -+ L2CB_CLDCTRL3); - } -- err = atl1c_write_phy_reg(hw, MII_IER, mii_ier_data); -+ -+ /* other debug port to set */ -+ atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, ANACTRL_DEF); -+ atl1c_write_phy_dbg(hw, MIIDBG_SRDSYSMOD, SRDSYSMOD_DEF); -+ atl1c_write_phy_dbg(hw, MIIDBG_TST10BTCFG, TST10BTCFG_DEF); -+ /* UNH-IOL test issue, set bit7 */ -+ atl1c_write_phy_dbg(hw, MIIDBG_TST100BTCFG, -+ TST100BTCFG_DEF | TST100BTCFG_LITCH_EN); -+ -+ /* set phy interrupt mask */ -+ phy_data = IER_LINK_UP | IER_LINK_DOWN; -+ err = atl1c_write_phy_reg(hw, MII_IER, phy_data); - if (err) { - if (netif_msg_hw(adapter)) - dev_err(&pdev->dev, - "Error enable PHY linkChange Interrupt\n"); - return err; - } -- if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) -- atl1c_phy_magic_data(hw); - return 0; - } - -@@ -589,7 +693,8 @@ int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex) - return 0; - } - --int atl1c_phy_power_saving(struct atl1c_hw *hw) -+/* select one link mode to get lower power consumption */ -+int atl1c_phy_to_ps_link(struct atl1c_hw *hw) - { - struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; - struct pci_dev *pdev = adapter->pdev; -@@ -660,3 +765,101 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw) - - return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); - } -+ -+int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc) -+{ -+ struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; -+ struct pci_dev *pdev = adapter->pdev; -+ u32 master_ctrl, mac_ctrl, phy_ctrl; -+ u32 wol_ctrl, speed; -+ u16 phy_data; -+ -+ wol_ctrl = 0; -+ speed = adapter->link_speed == SPEED_1000 ? -+ MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100; -+ -+ AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl); -+ AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl); -+ AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl); -+ -+ master_ctrl &= ~MASTER_CTRL_CLK_SEL_DIS; -+ mac_ctrl = FIELD_SETX(mac_ctrl, MAC_CTRL_SPEED, speed); -+ mac_ctrl &= ~(MAC_CTRL_DUPLX | MAC_CTRL_RX_EN | MAC_CTRL_TX_EN); -+ if (adapter->link_duplex == FULL_DUPLEX) -+ mac_ctrl |= MAC_CTRL_DUPLX; -+ phy_ctrl &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_CLS); -+ phy_ctrl |= GPHY_CTRL_SEL_ANA_RST | GPHY_CTRL_HIB_PULSE | -+ GPHY_CTRL_HIB_EN; -+ if (!wufc) { /* without WoL */ -+ master_ctrl |= MASTER_CTRL_CLK_SEL_DIS; -+ phy_ctrl |= GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_PWDOWN_HW; -+ AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl); -+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl); -+ AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl); -+ AT_WRITE_REG(hw, REG_WOL_CTRL, 0); -+ hw->phy_configured = false; /* re-init PHY when resume */ -+ return 0; -+ } -+ phy_ctrl |= GPHY_CTRL_EXT_RESET; -+ if (wufc & AT_WUFC_MAG) { -+ mac_ctrl |= MAC_CTRL_RX_EN | MAC_CTRL_BC_EN; -+ wol_ctrl |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN; -+ if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V11) -+ wol_ctrl |= WOL_PATTERN_EN | WOL_PATTERN_PME_EN; -+ } -+ if (wufc & AT_WUFC_LNKC) { -+ wol_ctrl |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; -+ if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) { -+ dev_dbg(&pdev->dev, "%s: write phy MII_IER faild.\n", -+ atl1c_driver_name); -+ } -+ } -+ /* clear PHY interrupt */ -+ atl1c_read_phy_reg(hw, MII_ISR, &phy_data); -+ -+ dev_dbg(&pdev->dev, "%s: suspend MAC=%x,MASTER=%x,PHY=0x%x,WOL=%x\n", -+ atl1c_driver_name, mac_ctrl, master_ctrl, phy_ctrl, wol_ctrl); -+ AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl); -+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl); -+ AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl); -+ AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl); -+ -+ return 0; -+} -+ -+ -+/* configure phy after Link change Event */ -+void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed) -+{ -+ u16 phy_val; -+ bool adj_thresh = false; -+ -+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 || -+ hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2) -+ adj_thresh = true; -+ -+ if (link_speed != SPEED_0) { /* link up */ -+ /* az with brcm, half-amp */ -+ if (hw->nic_type == athr_l1d_2) { -+ atl1c_read_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL6, -+ &phy_val); -+ phy_val = FIELD_GETX(phy_val, CLDCTRL6_CAB_LEN); -+ phy_val = phy_val > CLDCTRL6_CAB_LEN_SHORT ? -+ AZ_ANADECT_LONG : AZ_ANADECT_DEF; -+ atl1c_write_phy_dbg(hw, MIIDBG_AZ_ANADECT, phy_val); -+ } -+ /* threshold adjust */ -+ if (adj_thresh && link_speed == SPEED_100 && hw->msi_lnkpatch) { -+ atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_UP); -+ atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, -+ L1D_SYSMODCTRL_IECHOADJ_DEF); -+ } -+ } else { /* link down */ -+ if (adj_thresh && hw->msi_lnkpatch) { -+ atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, -+ SYSMODCTRL_IECHOADJ_DEF); -+ atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, -+ L1D_MSE16DB_DOWN); -+ } -+ } -+} -diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h -index 655fc6c..17d935b 100644 ---- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h -+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h -@@ -25,12 +25,18 @@ - #include - #include - -+#define FIELD_GETX(_x, _name) ((_x) >> (_name##_SHIFT) & (_name##_MASK)) -+#define FIELD_SETX(_x, _name, _v) \ -+(((_x) & ~((_name##_MASK) << (_name##_SHIFT))) |\ -+(((_v) & (_name##_MASK)) << (_name##_SHIFT))) -+#define FIELDX(_name, _v) (((_v) & (_name##_MASK)) << (_name##_SHIFT)) -+ - struct atl1c_adapter; - struct atl1c_hw; - - /* function prototype */ - void atl1c_phy_disable(struct atl1c_hw *hw); --void atl1c_hw_set_mac_addr(struct atl1c_hw *hw); -+void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr); - int atl1c_phy_reset(struct atl1c_hw *hw); - int atl1c_read_mac_addr(struct atl1c_hw *hw); - int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex); -@@ -42,47 +48,45 @@ bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value); - int atl1c_phy_init(struct atl1c_hw *hw); - int atl1c_check_eeprom_exist(struct atl1c_hw *hw); - int atl1c_restart_autoneg(struct atl1c_hw *hw); --int atl1c_phy_power_saving(struct atl1c_hw *hw); -+int atl1c_phy_to_ps_link(struct atl1c_hw *hw); -+int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc); -+bool atl1c_wait_mdio_idle(struct atl1c_hw *hw); -+void atl1c_stop_phy_polling(struct atl1c_hw *hw); -+void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel); -+int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, -+ u16 reg, u16 *phy_data); -+int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, -+ u16 reg, u16 phy_data); -+int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr, -+ u16 reg_addr, u16 *phy_data); -+int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr, -+ u16 reg_addr, u16 phy_data); -+int atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data); -+int atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data); -+void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed); -+ -+/* hw-ids */ -+#define PCI_DEVICE_ID_ATTANSIC_L2C 0x1062 -+#define PCI_DEVICE_ID_ATTANSIC_L1C 0x1063 -+#define PCI_DEVICE_ID_ATHEROS_L2C_B 0x2060 /* AR8152 v1.1 Fast 10/100 */ -+#define PCI_DEVICE_ID_ATHEROS_L2C_B2 0x2062 /* AR8152 v2.0 Fast 10/100 */ -+#define PCI_DEVICE_ID_ATHEROS_L1D 0x1073 /* AR8151 v1.0 Gigabit 1000 */ -+#define PCI_DEVICE_ID_ATHEROS_L1D_2_0 0x1083 /* AR8151 v2.0 Gigabit 1000 */ -+#define L2CB_V10 0xc0 -+#define L2CB_V11 0xc1 -+ - /* register definition */ - #define REG_DEVICE_CAP 0x5C - #define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7 - #define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0 - --#define REG_DEVICE_CTRL 0x60 --#define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7 --#define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5 --#define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7 --#define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12 -+#define DEVICE_CTRL_MAXRRS_MIN 2 - - #define REG_LINK_CTRL 0x68 - #define LINK_CTRL_L0S_EN 0x01 - #define LINK_CTRL_L1_EN 0x02 - #define LINK_CTRL_EXT_SYNC 0x80 - --#define REG_VPD_CAP 0x6C --#define VPD_CAP_ID_MASK 0xff --#define VPD_CAP_ID_SHIFT 0 --#define VPD_CAP_NEXT_PTR_MASK 0xFF --#define VPD_CAP_NEXT_PTR_SHIFT 8 --#define VPD_CAP_VPD_ADDR_MASK 0x7FFF --#define VPD_CAP_VPD_ADDR_SHIFT 16 --#define VPD_CAP_VPD_FLAG 0x80000000 -- --#define REG_VPD_DATA 0x70 -- --#define REG_PCIE_UC_SEVERITY 0x10C --#define PCIE_UC_SERVRITY_TRN 0x00000001 --#define PCIE_UC_SERVRITY_DLP 0x00000010 --#define PCIE_UC_SERVRITY_PSN_TLP 0x00001000 --#define PCIE_UC_SERVRITY_FCP 0x00002000 --#define PCIE_UC_SERVRITY_CPL_TO 0x00004000 --#define PCIE_UC_SERVRITY_CA 0x00008000 --#define PCIE_UC_SERVRITY_UC 0x00010000 --#define PCIE_UC_SERVRITY_ROV 0x00020000 --#define PCIE_UC_SERVRITY_MLFP 0x00040000 --#define PCIE_UC_SERVRITY_ECRC 0x00080000 --#define PCIE_UC_SERVRITY_UR 0x00100000 -- - #define REG_DEV_SERIALNUM_CTRL 0x200 - #define REG_DEV_MAC_SEL_MASK 0x0 /* 0:EUI; 1:MAC */ - #define REG_DEV_MAC_SEL_SHIFT 0 -@@ -90,25 +94,17 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define REG_DEV_SERIAL_NUM_EN_SHIFT 1 - - #define REG_TWSI_CTRL 0x218 -+#define TWSI_CTLR_FREQ_MASK 0x3UL -+#define TWSI_CTRL_FREQ_SHIFT 24 -+#define TWSI_CTRL_FREQ_100K 0 -+#define TWSI_CTRL_FREQ_200K 1 -+#define TWSI_CTRL_FREQ_300K 2 -+#define TWSI_CTRL_FREQ_400K 3 -+#define TWSI_CTRL_LD_EXIST BIT(23) -+#define TWSI_CTRL_HW_LDSTAT BIT(12) /* 0:finish,1:in progress */ -+#define TWSI_CTRL_SW_LDSTART BIT(11) - #define TWSI_CTRL_LD_OFFSET_MASK 0xFF - #define TWSI_CTRL_LD_OFFSET_SHIFT 0 --#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 --#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 --#define TWSI_CTRL_SW_LDSTART 0x800 --#define TWSI_CTRL_HW_LDSTART 0x1000 --#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F --#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 --#define TWSI_CTRL_LD_EXIST 0x400000 --#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 --#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 --#define TWSI_CTRL_FREQ_SEL_100K 0 --#define TWSI_CTRL_FREQ_SEL_200K 1 --#define TWSI_CTRL_FREQ_SEL_300K 2 --#define TWSI_CTRL_FREQ_SEL_400K 3 --#define TWSI_CTRL_SMB_SLV_ADDR --#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 --#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 -- - - #define REG_PCIE_DEV_MISC_CTRL 0x21C - #define PCIE_DEV_MISC_EXT_PIPE 0x2 -@@ -118,16 +114,23 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define PCIE_DEV_MISC_SERDES_SEL_DIN 0x10 - - #define REG_PCIE_PHYMISC 0x1000 --#define PCIE_PHYMISC_FORCE_RCV_DET 0x4 -+#define PCIE_PHYMISC_FORCE_RCV_DET BIT(2) -+#define PCIE_PHYMISC_NFTS_MASK 0xFFUL -+#define PCIE_PHYMISC_NFTS_SHIFT 16 - - #define REG_PCIE_PHYMISC2 0x1004 --#define PCIE_PHYMISC2_SERDES_CDR_MASK 0x3 --#define PCIE_PHYMISC2_SERDES_CDR_SHIFT 16 --#define PCIE_PHYMISC2_SERDES_TH_MASK 0x3 --#define PCIE_PHYMISC2_SERDES_TH_SHIFT 18 -+#define PCIE_PHYMISC2_L0S_TH_MASK 0x3UL -+#define PCIE_PHYMISC2_L0S_TH_SHIFT 18 -+#define L2CB1_PCIE_PHYMISC2_L0S_TH 3 -+#define PCIE_PHYMISC2_CDR_BW_MASK 0x3UL -+#define PCIE_PHYMISC2_CDR_BW_SHIFT 16 -+#define L2CB1_PCIE_PHYMISC2_CDR_BW 3 - - #define REG_TWSI_DEBUG 0x1108 --#define TWSI_DEBUG_DEV_EXIST 0x20000000 -+#define TWSI_DEBUG_DEV_EXIST BIT(29) -+ -+#define REG_DMA_DBG 0x1114 -+#define DMA_DBG_VENDOR_MSG BIT(0) - - #define REG_EEPROM_CTRL 0x12C0 - #define EEPROM_CTRL_DATA_HI_MASK 0xFFFF -@@ -140,56 +143,81 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define REG_EEPROM_DATA_LO 0x12C4 - - #define REG_OTP_CTRL 0x12F0 --#define OTP_CTRL_CLK_EN 0x0002 -+#define OTP_CTRL_CLK_EN BIT(1) - - #define REG_PM_CTRL 0x12F8 --#define PM_CTRL_SDES_EN 0x00000001 --#define PM_CTRL_RBER_EN 0x00000002 --#define PM_CTRL_CLK_REQ_EN 0x00000004 --#define PM_CTRL_ASPM_L1_EN 0x00000008 --#define PM_CTRL_SERDES_L1_EN 0x00000010 --#define PM_CTRL_SERDES_PLL_L1_EN 0x00000020 --#define PM_CTRL_SERDES_PD_EX_L1 0x00000040 --#define PM_CTRL_SERDES_BUDS_RX_L1_EN 0x00000080 --#define PM_CTRL_L0S_ENTRY_TIMER_MASK 0xF --#define PM_CTRL_L0S_ENTRY_TIMER_SHIFT 8 --#define PM_CTRL_ASPM_L0S_EN 0x00001000 --#define PM_CTRL_CLK_SWH_L1 0x00002000 --#define PM_CTRL_CLK_PWM_VER1_1 0x00004000 --#define PM_CTRL_RCVR_WT_TIMER 0x00008000 --#define PM_CTRL_L1_ENTRY_TIMER_MASK 0xF --#define PM_CTRL_L1_ENTRY_TIMER_SHIFT 16 --#define PM_CTRL_PM_REQ_TIMER_MASK 0xF --#define PM_CTRL_PM_REQ_TIMER_SHIFT 20 --#define PM_CTRL_LCKDET_TIMER_MASK 0xF -+#define PM_CTRL_HOTRST BIT(31) -+#define PM_CTRL_MAC_ASPM_CHK BIT(30) /* L0s/L1 dis by MAC based on -+ * thrghput(setting in 15A0) */ -+#define PM_CTRL_SA_DLY_EN BIT(29) -+#define PM_CTRL_L0S_BUFSRX_EN BIT(28) -+#define PM_CTRL_LCKDET_TIMER_MASK 0xFUL - #define PM_CTRL_LCKDET_TIMER_SHIFT 24 --#define PM_CTRL_EN_BUFS_RX_L0S 0x10000000 --#define PM_CTRL_SA_DLY_EN 0x20000000 --#define PM_CTRL_MAC_ASPM_CHK 0x40000000 --#define PM_CTRL_HOTRST 0x80000000 -+#define PM_CTRL_LCKDET_TIMER_DEF 0xC -+#define PM_CTRL_PM_REQ_TIMER_MASK 0xFUL -+#define PM_CTRL_PM_REQ_TIMER_SHIFT 20 /* pm_request_l1 time > @ -+ * ->L0s not L1 */ -+#define PM_CTRL_PM_REQ_TO_DEF 0xF -+#define PMCTRL_TXL1_AFTER_L0S BIT(19) /* l1dv2.0+ */ -+#define L1D_PMCTRL_L1_ENTRY_TM_MASK 7UL /* l1dv2.0+, 3bits */ -+#define L1D_PMCTRL_L1_ENTRY_TM_SHIFT 16 -+#define L1D_PMCTRL_L1_ENTRY_TM_DIS 0 -+#define L1D_PMCTRL_L1_ENTRY_TM_2US 1 -+#define L1D_PMCTRL_L1_ENTRY_TM_4US 2 -+#define L1D_PMCTRL_L1_ENTRY_TM_8US 3 -+#define L1D_PMCTRL_L1_ENTRY_TM_16US 4 -+#define L1D_PMCTRL_L1_ENTRY_TM_24US 5 -+#define L1D_PMCTRL_L1_ENTRY_TM_32US 6 -+#define L1D_PMCTRL_L1_ENTRY_TM_63US 7 -+#define PM_CTRL_L1_ENTRY_TIMER_MASK 0xFUL /* l1C 4bits */ -+#define PM_CTRL_L1_ENTRY_TIMER_SHIFT 16 -+#define L2CB1_PM_CTRL_L1_ENTRY_TM 7 -+#define L1C_PM_CTRL_L1_ENTRY_TM 0xF -+#define PM_CTRL_RCVR_WT_TIMER BIT(15) /* 1:1us, 0:2ms */ -+#define PM_CTRL_CLK_PWM_VER1_1 BIT(14) /* 0:1.0a,1:1.1 */ -+#define PM_CTRL_CLK_SWH_L1 BIT(13) /* en pcie clk sw in L1 */ -+#define PM_CTRL_ASPM_L0S_EN BIT(12) -+#define PM_CTRL_RXL1_AFTER_L0S BIT(11) /* l1dv2.0+ */ -+#define L1D_PMCTRL_L0S_TIMER_MASK 7UL /* l1d2.0+, 3bits*/ -+#define L1D_PMCTRL_L0S_TIMER_SHIFT 8 -+#define PM_CTRL_L0S_ENTRY_TIMER_MASK 0xFUL /* l1c, 4bits */ -+#define PM_CTRL_L0S_ENTRY_TIMER_SHIFT 8 -+#define PM_CTRL_SERDES_BUFS_RX_L1_EN BIT(7) -+#define PM_CTRL_SERDES_PD_EX_L1 BIT(6) /* power down serdes rx */ -+#define PM_CTRL_SERDES_PLL_L1_EN BIT(5) -+#define PM_CTRL_SERDES_L1_EN BIT(4) -+#define PM_CTRL_ASPM_L1_EN BIT(3) -+#define PM_CTRL_CLK_REQ_EN BIT(2) -+#define PM_CTRL_RBER_EN BIT(1) -+#define PM_CTRL_SPRSDWER_EN BIT(0) - - #define REG_LTSSM_ID_CTRL 0x12FC - #define LTSSM_ID_EN_WRO 0x1000 -+ -+ - /* Selene Master Control Register */ - #define REG_MASTER_CTRL 0x1400 --#define MASTER_CTRL_SOFT_RST 0x1 --#define MASTER_CTRL_TEST_MODE_MASK 0x3 --#define MASTER_CTRL_TEST_MODE_SHIFT 2 --#define MASTER_CTRL_BERT_START 0x10 --#define MASTER_CTRL_OOB_DIS_OFF 0x40 --#define MASTER_CTRL_SA_TIMER_EN 0x80 --#define MASTER_CTRL_MTIMER_EN 0x100 --#define MASTER_CTRL_MANUAL_INT 0x200 --#define MASTER_CTRL_TX_ITIMER_EN 0x400 --#define MASTER_CTRL_RX_ITIMER_EN 0x800 --#define MASTER_CTRL_CLK_SEL_DIS 0x1000 --#define MASTER_CTRL_CLK_SWH_MODE 0x2000 --#define MASTER_CTRL_INT_RDCLR 0x4000 --#define MASTER_CTRL_REV_NUM_SHIFT 16 --#define MASTER_CTRL_REV_NUM_MASK 0xff --#define MASTER_CTRL_DEV_ID_SHIFT 24 --#define MASTER_CTRL_DEV_ID_MASK 0x7f --#define MASTER_CTRL_OTP_SEL 0x80000000 -+#define MASTER_CTRL_OTP_SEL BIT(31) -+#define MASTER_DEV_NUM_MASK 0x7FUL -+#define MASTER_DEV_NUM_SHIFT 24 -+#define MASTER_REV_NUM_MASK 0xFFUL -+#define MASTER_REV_NUM_SHIFT 16 -+#define MASTER_CTRL_INT_RDCLR BIT(14) -+#define MASTER_CTRL_CLK_SEL_DIS BIT(12) /* 1:alwys sel pclk from -+ * serdes, not sw to 25M */ -+#define MASTER_CTRL_RX_ITIMER_EN BIT(11) /* IRQ MODURATION FOR RX */ -+#define MASTER_CTRL_TX_ITIMER_EN BIT(10) /* MODURATION FOR TX/RX */ -+#define MASTER_CTRL_MANU_INT BIT(9) /* SOFT MANUAL INT */ -+#define MASTER_CTRL_MANUTIMER_EN BIT(8) -+#define MASTER_CTRL_SA_TIMER_EN BIT(7) /* SYS ALIVE TIMER EN */ -+#define MASTER_CTRL_OOB_DIS BIT(6) /* OUT OF BOX DIS */ -+#define MASTER_CTRL_WAKEN_25M BIT(5) /* WAKE WO. PCIE CLK */ -+#define MASTER_CTRL_BERT_START BIT(4) -+#define MASTER_PCIE_TSTMOD_MASK 3UL -+#define MASTER_PCIE_TSTMOD_SHIFT 2 -+#define MASTER_PCIE_RST BIT(1) -+#define MASTER_CTRL_SOFT_RST BIT(0) /* RST MAC & DMA */ -+#define DMA_MAC_RST_TO 50 - - /* Timer Initial Value Register */ - #define REG_MANUAL_TIMER_INIT 0x1404 -@@ -201,87 +229,85 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define IRQ_MODRT_RX_TIMER_SHIFT 16 - - #define REG_GPHY_CTRL 0x140C --#define GPHY_CTRL_EXT_RESET 0x1 --#define GPHY_CTRL_RTL_MODE 0x2 --#define GPHY_CTRL_LED_MODE 0x4 --#define GPHY_CTRL_ANEG_NOW 0x8 --#define GPHY_CTRL_REV_ANEG 0x10 --#define GPHY_CTRL_GATE_25M_EN 0x20 --#define GPHY_CTRL_LPW_EXIT 0x40 --#define GPHY_CTRL_PHY_IDDQ 0x80 --#define GPHY_CTRL_PHY_IDDQ_DIS 0x100 --#define GPHY_CTRL_GIGA_DIS 0x200 --#define GPHY_CTRL_HIB_EN 0x400 --#define GPHY_CTRL_HIB_PULSE 0x800 --#define GPHY_CTRL_SEL_ANA_RST 0x1000 --#define GPHY_CTRL_PHY_PLL_ON 0x2000 --#define GPHY_CTRL_PWDOWN_HW 0x4000 --#define GPHY_CTRL_PHY_PLL_BYPASS 0x8000 -- --#define GPHY_CTRL_DEFAULT ( \ -- GPHY_CTRL_SEL_ANA_RST |\ -- GPHY_CTRL_HIB_PULSE |\ -- GPHY_CTRL_HIB_EN) -- --#define GPHY_CTRL_PW_WOL_DIS ( \ -- GPHY_CTRL_SEL_ANA_RST |\ -- GPHY_CTRL_HIB_PULSE |\ -- GPHY_CTRL_HIB_EN |\ -- GPHY_CTRL_PWDOWN_HW |\ -- GPHY_CTRL_PHY_IDDQ) -- --#define GPHY_CTRL_POWER_SAVING ( \ -- GPHY_CTRL_SEL_ANA_RST |\ -- GPHY_CTRL_HIB_EN |\ -- GPHY_CTRL_HIB_PULSE |\ -- GPHY_CTRL_PWDOWN_HW |\ -- GPHY_CTRL_PHY_IDDQ) -+#define GPHY_CTRL_ADDR_MASK 0x1FUL -+#define GPHY_CTRL_ADDR_SHIFT 19 -+#define GPHY_CTRL_BP_VLTGSW BIT(18) -+#define GPHY_CTRL_100AB_EN BIT(17) -+#define GPHY_CTRL_10AB_EN BIT(16) -+#define GPHY_CTRL_PHY_PLL_BYPASS BIT(15) -+#define GPHY_CTRL_PWDOWN_HW BIT(14) /* affect MAC&PHY, to low pw */ -+#define GPHY_CTRL_PHY_PLL_ON BIT(13) /* 1:pll always on, 0:can sw */ -+#define GPHY_CTRL_SEL_ANA_RST BIT(12) -+#define GPHY_CTRL_HIB_PULSE BIT(11) -+#define GPHY_CTRL_HIB_EN BIT(10) -+#define GPHY_CTRL_GIGA_DIS BIT(9) -+#define GPHY_CTRL_PHY_IDDQ_DIS BIT(8) /* pw on RST */ -+#define GPHY_CTRL_PHY_IDDQ BIT(7) /* bit8 affect bit7 while rb */ -+#define GPHY_CTRL_LPW_EXIT BIT(6) -+#define GPHY_CTRL_GATE_25M_EN BIT(5) -+#define GPHY_CTRL_REV_ANEG BIT(4) -+#define GPHY_CTRL_ANEG_NOW BIT(3) -+#define GPHY_CTRL_LED_MODE BIT(2) -+#define GPHY_CTRL_RTL_MODE BIT(1) -+#define GPHY_CTRL_EXT_RESET BIT(0) /* 1:out of DSP RST status */ -+#define GPHY_CTRL_EXT_RST_TO 80 /* 800us atmost */ -+#define GPHY_CTRL_CLS (\ -+ GPHY_CTRL_LED_MODE |\ -+ GPHY_CTRL_100AB_EN |\ -+ GPHY_CTRL_PHY_PLL_ON) -+ - /* Block IDLE Status Register */ --#define REG_IDLE_STATUS 0x1410 --#define IDLE_STATUS_MASK 0x00FF --#define IDLE_STATUS_RXMAC_NO_IDLE 0x1 --#define IDLE_STATUS_TXMAC_NO_IDLE 0x2 --#define IDLE_STATUS_RXQ_NO_IDLE 0x4 --#define IDLE_STATUS_TXQ_NO_IDLE 0x8 --#define IDLE_STATUS_DMAR_NO_IDLE 0x10 --#define IDLE_STATUS_DMAW_NO_IDLE 0x20 --#define IDLE_STATUS_SMB_NO_IDLE 0x40 --#define IDLE_STATUS_CMB_NO_IDLE 0x80 -+#define REG_IDLE_STATUS 0x1410 -+#define IDLE_STATUS_SFORCE_MASK 0xFUL -+#define IDLE_STATUS_SFORCE_SHIFT 14 -+#define IDLE_STATUS_CALIB_DONE BIT(13) -+#define IDLE_STATUS_CALIB_RES_MASK 0x1FUL -+#define IDLE_STATUS_CALIB_RES_SHIFT 8 -+#define IDLE_STATUS_CALIBERR_MASK 0xFUL -+#define IDLE_STATUS_CALIBERR_SHIFT 4 -+#define IDLE_STATUS_TXQ_BUSY BIT(3) -+#define IDLE_STATUS_RXQ_BUSY BIT(2) -+#define IDLE_STATUS_TXMAC_BUSY BIT(1) -+#define IDLE_STATUS_RXMAC_BUSY BIT(0) -+#define IDLE_STATUS_MASK (\ -+ IDLE_STATUS_TXQ_BUSY |\ -+ IDLE_STATUS_RXQ_BUSY |\ -+ IDLE_STATUS_TXMAC_BUSY |\ -+ IDLE_STATUS_RXMAC_BUSY) - - /* MDIO Control Register */ - #define REG_MDIO_CTRL 0x1414 --#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit -- * control data to write to PHY -- * MII management register */ --#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit -- * status data that was read -- * from the PHY MII management register */ --#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */ --#define MDIO_REG_ADDR_SHIFT 16 --#define MDIO_RW 0x200000 /* 1: read, 0: write */ --#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */ --#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO -- * master. And this bit is self -- * cleared after one cycle */ --#define MDIO_CLK_SEL_SHIFT 24 --#define MDIO_CLK_25_4 0 --#define MDIO_CLK_25_6 2 --#define MDIO_CLK_25_8 3 --#define MDIO_CLK_25_10 4 --#define MDIO_CLK_25_14 5 --#define MDIO_CLK_25_20 6 --#define MDIO_CLK_25_28 7 --#define MDIO_BUSY 0x8000000 --#define MDIO_AP_EN 0x10000000 --#define MDIO_WAIT_TIMES 10 -- --/* MII PHY Status Register */ --#define REG_PHY_STATUS 0x1418 --#define PHY_GENERAL_STATUS_MASK 0xFFFF --#define PHY_STATUS_RECV_ENABLE 0x0001 --#define PHY_OE_PWSP_STATUS_MASK 0x07FF --#define PHY_OE_PWSP_STATUS_SHIFT 16 --#define PHY_STATUS_LPW_STATE 0x80000000 -+#define MDIO_CTRL_MODE_EXT BIT(30) -+#define MDIO_CTRL_POST_READ BIT(29) -+#define MDIO_CTRL_AP_EN BIT(28) -+#define MDIO_CTRL_BUSY BIT(27) -+#define MDIO_CTRL_CLK_SEL_MASK 0x7UL -+#define MDIO_CTRL_CLK_SEL_SHIFT 24 -+#define MDIO_CTRL_CLK_25_4 0 /* 25MHz divide 4 */ -+#define MDIO_CTRL_CLK_25_6 2 -+#define MDIO_CTRL_CLK_25_8 3 -+#define MDIO_CTRL_CLK_25_10 4 -+#define MDIO_CTRL_CLK_25_32 5 -+#define MDIO_CTRL_CLK_25_64 6 -+#define MDIO_CTRL_CLK_25_128 7 -+#define MDIO_CTRL_START BIT(23) -+#define MDIO_CTRL_SPRES_PRMBL BIT(22) -+#define MDIO_CTRL_OP_READ BIT(21) /* 1:read, 0:write */ -+#define MDIO_CTRL_REG_MASK 0x1FUL -+#define MDIO_CTRL_REG_SHIFT 16 -+#define MDIO_CTRL_DATA_MASK 0xFFFFUL -+#define MDIO_CTRL_DATA_SHIFT 0 -+#define MDIO_MAX_AC_TO 120 /* 1.2ms timeout for slow clk */ -+ -+/* for extension reg access */ -+#define REG_MDIO_EXTN 0x1448 -+#define MDIO_EXTN_PORTAD_MASK 0x1FUL -+#define MDIO_EXTN_PORTAD_SHIFT 21 -+#define MDIO_EXTN_DEVAD_MASK 0x1FUL -+#define MDIO_EXTN_DEVAD_SHIFT 16 -+#define MDIO_EXTN_REG_MASK 0xFFFFUL -+#define MDIO_EXTN_REG_SHIFT 0 -+ - /* BIST Control and Status Register0 (for the Packet Memory) */ - #define REG_BIST0_CTRL 0x141c - #define BIST0_NOW 0x1 -@@ -299,50 +325,81 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define BIST1_FUSE_FLAG 0x4 - - /* SerDes Lock Detect Control and Status Register */ --#define REG_SERDES_LOCK 0x1424 --#define SERDES_LOCK_DETECT 0x1 /* SerDes lock detected. This signal -- * comes from Analog SerDes */ --#define SERDES_LOCK_DETECT_EN 0x2 /* 1: Enable SerDes Lock detect function */ --#define SERDES_LOCK_STS_SELFB_PLL_SHIFT 0xE --#define SERDES_LOCK_STS_SELFB_PLL_MASK 0x3 --#define SERDES_OVCLK_18_25 0x0 --#define SERDES_OVCLK_12_18 0x1 --#define SERDES_OVCLK_0_4 0x2 --#define SERDES_OVCLK_4_12 0x3 --#define SERDES_MAC_CLK_SLOWDOWN 0x20000 --#define SERDES_PYH_CLK_SLOWDOWN 0x40000 -+#define REG_SERDES 0x1424 -+#define SERDES_PHY_CLK_SLOWDOWN BIT(18) -+#define SERDES_MAC_CLK_SLOWDOWN BIT(17) -+#define SERDES_SELFB_PLL_MASK 0x3UL -+#define SERDES_SELFB_PLL_SHIFT 14 -+#define SERDES_PHYCLK_SEL_GTX BIT(13) /* 1:gtx_clk, 0:25M */ -+#define SERDES_PCIECLK_SEL_SRDS BIT(12) /* 1:serdes,0:25M */ -+#define SERDES_BUFS_RX_EN BIT(11) -+#define SERDES_PD_RX BIT(10) -+#define SERDES_PLL_EN BIT(9) -+#define SERDES_EN BIT(8) -+#define SERDES_SELFB_PLL_SEL_CSR BIT(6) /* 0:state-machine,1:csr */ -+#define SERDES_SELFB_PLL_CSR_MASK 0x3UL -+#define SERDES_SELFB_PLL_CSR_SHIFT 4 -+#define SERDES_SELFB_PLL_CSR_4 3 /* 4-12% OV-CLK */ -+#define SERDES_SELFB_PLL_CSR_0 2 /* 0-4% OV-CLK */ -+#define SERDES_SELFB_PLL_CSR_12 1 /* 12-18% OV-CLK */ -+#define SERDES_SELFB_PLL_CSR_18 0 /* 18-25% OV-CLK */ -+#define SERDES_VCO_SLOW BIT(3) -+#define SERDES_VCO_FAST BIT(2) -+#define SERDES_LOCK_DETECT_EN BIT(1) -+#define SERDES_LOCK_DETECT BIT(0) -+ -+#define REG_LPI_DECISN_TIMER 0x143C -+#define L2CB_LPI_DESISN_TIMER 0x7D00 -+ -+#define REG_LPI_CTRL 0x1440 -+#define LPI_CTRL_CHK_DA BIT(31) -+#define LPI_CTRL_ENH_TO_MASK 0x1FFFUL -+#define LPI_CTRL_ENH_TO_SHIFT 12 -+#define LPI_CTRL_ENH_TH_MASK 0x1FUL -+#define LPI_CTRL_ENH_TH_SHIFT 6 -+#define LPI_CTRL_ENH_EN BIT(5) -+#define LPI_CTRL_CHK_RX BIT(4) -+#define LPI_CTRL_CHK_STATE BIT(3) -+#define LPI_CTRL_GMII BIT(2) -+#define LPI_CTRL_TO_PHY BIT(1) -+#define LPI_CTRL_EN BIT(0) -+ -+#define REG_LPI_WAIT 0x1444 -+#define LPI_WAIT_TIMER_MASK 0xFFFFUL -+#define LPI_WAIT_TIMER_SHIFT 0 - - /* MAC Control Register */ - #define REG_MAC_CTRL 0x1480 --#define MAC_CTRL_TX_EN 0x1 --#define MAC_CTRL_RX_EN 0x2 --#define MAC_CTRL_TX_FLOW 0x4 --#define MAC_CTRL_RX_FLOW 0x8 --#define MAC_CTRL_LOOPBACK 0x10 --#define MAC_CTRL_DUPLX 0x20 --#define MAC_CTRL_ADD_CRC 0x40 --#define MAC_CTRL_PAD 0x80 --#define MAC_CTRL_LENCHK 0x100 --#define MAC_CTRL_HUGE_EN 0x200 --#define MAC_CTRL_PRMLEN_SHIFT 10 --#define MAC_CTRL_PRMLEN_MASK 0xf --#define MAC_CTRL_RMV_VLAN 0x4000 --#define MAC_CTRL_PROMIS_EN 0x8000 --#define MAC_CTRL_TX_PAUSE 0x10000 --#define MAC_CTRL_SCNT 0x20000 --#define MAC_CTRL_SRST_TX 0x40000 --#define MAC_CTRL_TX_SIMURST 0x80000 --#define MAC_CTRL_SPEED_SHIFT 20 --#define MAC_CTRL_SPEED_MASK 0x3 --#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 --#define MAC_CTRL_TX_HUGE 0x800000 --#define MAC_CTRL_RX_CHKSUM_EN 0x1000000 --#define MAC_CTRL_MC_ALL_EN 0x2000000 --#define MAC_CTRL_BC_EN 0x4000000 --#define MAC_CTRL_DBG 0x8000000 --#define MAC_CTRL_SINGLE_PAUSE_EN 0x10000000 --#define MAC_CTRL_HASH_ALG_CRC32 0x20000000 --#define MAC_CTRL_SPEED_MODE_SW 0x40000000 -+#define MAC_CTRL_SPEED_MODE_SW BIT(30) /* 0:phy,1:sw */ -+#define MAC_CTRL_HASH_ALG_CRC32 BIT(29) /* 1:legacy,0:lw_5b */ -+#define MAC_CTRL_SINGLE_PAUSE_EN BIT(28) -+#define MAC_CTRL_DBG BIT(27) -+#define MAC_CTRL_BC_EN BIT(26) -+#define MAC_CTRL_MC_ALL_EN BIT(25) -+#define MAC_CTRL_RX_CHKSUM_EN BIT(24) -+#define MAC_CTRL_TX_HUGE BIT(23) -+#define MAC_CTRL_DBG_TX_BKPRESURE BIT(22) -+#define MAC_CTRL_SPEED_MASK 3UL -+#define MAC_CTRL_SPEED_SHIFT 20 -+#define MAC_CTRL_SPEED_10_100 1 -+#define MAC_CTRL_SPEED_1000 2 -+#define MAC_CTRL_TX_SIMURST BIT(19) -+#define MAC_CTRL_SCNT BIT(17) -+#define MAC_CTRL_TX_PAUSE BIT(16) -+#define MAC_CTRL_PROMIS_EN BIT(15) -+#define MAC_CTRL_RMV_VLAN BIT(14) -+#define MAC_CTRL_PRMLEN_MASK 0xFUL -+#define MAC_CTRL_PRMLEN_SHIFT 10 -+#define MAC_CTRL_HUGE_EN BIT(9) -+#define MAC_CTRL_LENCHK BIT(8) -+#define MAC_CTRL_PAD BIT(7) -+#define MAC_CTRL_ADD_CRC BIT(6) -+#define MAC_CTRL_DUPLX BIT(5) -+#define MAC_CTRL_LOOPBACK BIT(4) -+#define MAC_CTRL_RX_FLOW BIT(3) -+#define MAC_CTRL_TX_FLOW BIT(2) -+#define MAC_CTRL_RX_EN BIT(1) -+#define MAC_CTRL_TX_EN BIT(0) - - /* MAC IPG/IFG Control Register */ - #define REG_MAC_IPG_IFG 0x1484 -@@ -386,34 +443,53 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - - /* Wake-On-Lan control register */ - #define REG_WOL_CTRL 0x14a0 --#define WOL_PATTERN_EN 0x00000001 --#define WOL_PATTERN_PME_EN 0x00000002 --#define WOL_MAGIC_EN 0x00000004 --#define WOL_MAGIC_PME_EN 0x00000008 --#define WOL_LINK_CHG_EN 0x00000010 --#define WOL_LINK_CHG_PME_EN 0x00000020 --#define WOL_PATTERN_ST 0x00000100 --#define WOL_MAGIC_ST 0x00000200 --#define WOL_LINKCHG_ST 0x00000400 --#define WOL_CLK_SWITCH_EN 0x00008000 --#define WOL_PT0_EN 0x00010000 --#define WOL_PT1_EN 0x00020000 --#define WOL_PT2_EN 0x00040000 --#define WOL_PT3_EN 0x00080000 --#define WOL_PT4_EN 0x00100000 --#define WOL_PT5_EN 0x00200000 --#define WOL_PT6_EN 0x00400000 -+#define WOL_PT7_MATCH BIT(31) -+#define WOL_PT6_MATCH BIT(30) -+#define WOL_PT5_MATCH BIT(29) -+#define WOL_PT4_MATCH BIT(28) -+#define WOL_PT3_MATCH BIT(27) -+#define WOL_PT2_MATCH BIT(26) -+#define WOL_PT1_MATCH BIT(25) -+#define WOL_PT0_MATCH BIT(24) -+#define WOL_PT7_EN BIT(23) -+#define WOL_PT6_EN BIT(22) -+#define WOL_PT5_EN BIT(21) -+#define WOL_PT4_EN BIT(20) -+#define WOL_PT3_EN BIT(19) -+#define WOL_PT2_EN BIT(18) -+#define WOL_PT1_EN BIT(17) -+#define WOL_PT0_EN BIT(16) -+#define WOL_LNKCHG_ST BIT(10) -+#define WOL_MAGIC_ST BIT(9) -+#define WOL_PATTERN_ST BIT(8) -+#define WOL_OOB_EN BIT(6) -+#define WOL_LINK_CHG_PME_EN BIT(5) -+#define WOL_LINK_CHG_EN BIT(4) -+#define WOL_MAGIC_PME_EN BIT(3) -+#define WOL_MAGIC_EN BIT(2) -+#define WOL_PATTERN_PME_EN BIT(1) -+#define WOL_PATTERN_EN BIT(0) - - /* WOL Length ( 2 DWORD ) */ --#define REG_WOL_PATTERN_LEN 0x14a4 --#define WOL_PT_LEN_MASK 0x7f --#define WOL_PT0_LEN_SHIFT 0 --#define WOL_PT1_LEN_SHIFT 8 --#define WOL_PT2_LEN_SHIFT 16 --#define WOL_PT3_LEN_SHIFT 24 --#define WOL_PT4_LEN_SHIFT 0 --#define WOL_PT5_LEN_SHIFT 8 --#define WOL_PT6_LEN_SHIFT 16 -+#define REG_WOL_PTLEN1 0x14A4 -+#define WOL_PTLEN1_3_MASK 0xFFUL -+#define WOL_PTLEN1_3_SHIFT 24 -+#define WOL_PTLEN1_2_MASK 0xFFUL -+#define WOL_PTLEN1_2_SHIFT 16 -+#define WOL_PTLEN1_1_MASK 0xFFUL -+#define WOL_PTLEN1_1_SHIFT 8 -+#define WOL_PTLEN1_0_MASK 0xFFUL -+#define WOL_PTLEN1_0_SHIFT 0 -+ -+#define REG_WOL_PTLEN2 0x14A8 -+#define WOL_PTLEN2_7_MASK 0xFFUL -+#define WOL_PTLEN2_7_SHIFT 24 -+#define WOL_PTLEN2_6_MASK 0xFFUL -+#define WOL_PTLEN2_6_SHIFT 16 -+#define WOL_PTLEN2_5_MASK 0xFFUL -+#define WOL_PTLEN2_5_SHIFT 8 -+#define WOL_PTLEN2_4_MASK 0xFFUL -+#define WOL_PTLEN2_4_SHIFT 0 - - /* Internal SRAM Partition Register */ - #define RFDX_HEAD_ADDR_MASK 0x03FF -@@ -458,66 +534,50 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - */ - #define REG_RX_BASE_ADDR_HI 0x1540 - #define REG_TX_BASE_ADDR_HI 0x1544 --#define REG_SMB_BASE_ADDR_HI 0x1548 --#define REG_SMB_BASE_ADDR_LO 0x154C - #define REG_RFD0_HEAD_ADDR_LO 0x1550 --#define REG_RFD1_HEAD_ADDR_LO 0x1554 --#define REG_RFD2_HEAD_ADDR_LO 0x1558 --#define REG_RFD3_HEAD_ADDR_LO 0x155C - #define REG_RFD_RING_SIZE 0x1560 - #define RFD_RING_SIZE_MASK 0x0FFF - #define REG_RX_BUF_SIZE 0x1564 - #define RX_BUF_SIZE_MASK 0xFFFF - #define REG_RRD0_HEAD_ADDR_LO 0x1568 --#define REG_RRD1_HEAD_ADDR_LO 0x156C --#define REG_RRD2_HEAD_ADDR_LO 0x1570 --#define REG_RRD3_HEAD_ADDR_LO 0x1574 - #define REG_RRD_RING_SIZE 0x1578 - #define RRD_RING_SIZE_MASK 0x0FFF --#define REG_HTPD_HEAD_ADDR_LO 0x157C --#define REG_NTPD_HEAD_ADDR_LO 0x1580 -+#define REG_TPD_PRI1_ADDR_LO 0x157C -+#define REG_TPD_PRI0_ADDR_LO 0x1580 - #define REG_TPD_RING_SIZE 0x1584 - #define TPD_RING_SIZE_MASK 0xFFFF --#define REG_CMB_BASE_ADDR_LO 0x1588 -- --/* RSS about */ --#define REG_RSS_KEY0 0x14B0 --#define REG_RSS_KEY1 0x14B4 --#define REG_RSS_KEY2 0x14B8 --#define REG_RSS_KEY3 0x14BC --#define REG_RSS_KEY4 0x14C0 --#define REG_RSS_KEY5 0x14C4 --#define REG_RSS_KEY6 0x14C8 --#define REG_RSS_KEY7 0x14CC --#define REG_RSS_KEY8 0x14D0 --#define REG_RSS_KEY9 0x14D4 --#define REG_IDT_TABLE0 0x14E0 --#define REG_IDT_TABLE1 0x14E4 --#define REG_IDT_TABLE2 0x14E8 --#define REG_IDT_TABLE3 0x14EC --#define REG_IDT_TABLE4 0x14F0 --#define REG_IDT_TABLE5 0x14F4 --#define REG_IDT_TABLE6 0x14F8 --#define REG_IDT_TABLE7 0x14FC --#define REG_IDT_TABLE REG_IDT_TABLE0 --#define REG_RSS_HASH_VALUE 0x15B0 --#define REG_RSS_HASH_FLAG 0x15B4 --#define REG_BASE_CPU_NUMBER 0x15B8 - - /* TXQ Control Register */ --#define REG_TXQ_CTRL 0x1590 --#define TXQ_NUM_TPD_BURST_MASK 0xF --#define TXQ_NUM_TPD_BURST_SHIFT 0 --#define TXQ_CTRL_IP_OPTION_EN 0x10 --#define TXQ_CTRL_EN 0x20 --#define TXQ_CTRL_ENH_MODE 0x40 --#define TXQ_CTRL_LS_8023_EN 0x80 --#define TXQ_TXF_BURST_NUM_SHIFT 16 --#define TXQ_TXF_BURST_NUM_MASK 0xFFFF -+#define REG_TXQ_CTRL 0x1590 -+#define TXQ_TXF_BURST_NUM_MASK 0xFFFFUL -+#define TXQ_TXF_BURST_NUM_SHIFT 16 -+#define L1C_TXQ_TXF_BURST_PREF 0x200 -+#define L2CB_TXQ_TXF_BURST_PREF 0x40 -+#define TXQ_CTRL_PEDING_CLR BIT(8) -+#define TXQ_CTRL_LS_8023_EN BIT(7) -+#define TXQ_CTRL_ENH_MODE BIT(6) -+#define TXQ_CTRL_EN BIT(5) -+#define TXQ_CTRL_IP_OPTION_EN BIT(4) -+#define TXQ_NUM_TPD_BURST_MASK 0xFUL -+#define TXQ_NUM_TPD_BURST_SHIFT 0 -+#define TXQ_NUM_TPD_BURST_DEF 5 -+#define TXQ_CFGV (\ -+ FIELDX(TXQ_NUM_TPD_BURST, TXQ_NUM_TPD_BURST_DEF) |\ -+ TXQ_CTRL_ENH_MODE |\ -+ TXQ_CTRL_LS_8023_EN |\ -+ TXQ_CTRL_IP_OPTION_EN) -+#define L1C_TXQ_CFGV (\ -+ TXQ_CFGV |\ -+ FIELDX(TXQ_TXF_BURST_NUM, L1C_TXQ_TXF_BURST_PREF)) -+#define L2CB_TXQ_CFGV (\ -+ TXQ_CFGV |\ -+ FIELDX(TXQ_TXF_BURST_NUM, L2CB_TXQ_TXF_BURST_PREF)) -+ - - /* Jumbo packet Threshold for task offload */ - #define REG_TX_TSO_OFFLOAD_THRESH 0x1594 /* In 8-bytes */ - #define TX_TSO_OFFLOAD_THRESH_MASK 0x07FF -+#define MAX_TSO_FRAME_SIZE (7*1024) - - #define REG_TXF_WATER_MARK 0x1598 /* In 8-bytes */ - #define TXF_WATER_MARK_MASK 0x0FFF -@@ -537,26 +597,21 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define ASPM_THRUPUT_LIMIT_NO 0x00 - #define ASPM_THRUPUT_LIMIT_1M 0x01 - #define ASPM_THRUPUT_LIMIT_10M 0x02 --#define ASPM_THRUPUT_LIMIT_100M 0x04 --#define RXQ1_CTRL_EN 0x10 --#define RXQ2_CTRL_EN 0x20 --#define RXQ3_CTRL_EN 0x40 --#define IPV6_CHKSUM_CTRL_EN 0x80 --#define RSS_HASH_BITS_MASK 0x00FF --#define RSS_HASH_BITS_SHIFT 8 --#define RSS_HASH_IPV4 0x10000 --#define RSS_HASH_IPV4_TCP 0x20000 --#define RSS_HASH_IPV6 0x40000 --#define RSS_HASH_IPV6_TCP 0x80000 -+#define ASPM_THRUPUT_LIMIT_100M 0x03 -+#define IPV6_CHKSUM_CTRL_EN BIT(7) - #define RXQ_RFD_BURST_NUM_MASK 0x003F - #define RXQ_RFD_BURST_NUM_SHIFT 20 --#define RSS_MODE_MASK 0x0003 -+#define RXQ_NUM_RFD_PREF_DEF 8 -+#define RSS_MODE_MASK 3UL - #define RSS_MODE_SHIFT 26 --#define RSS_NIP_QUEUE_SEL_MASK 0x1 --#define RSS_NIP_QUEUE_SEL_SHIFT 28 --#define RRS_HASH_CTRL_EN 0x20000000 --#define RX_CUT_THRU_EN 0x40000000 --#define RXQ_CTRL_EN 0x80000000 -+#define RSS_MODE_DIS 0 -+#define RSS_MODE_SQSI 1 -+#define RSS_MODE_MQSI 2 -+#define RSS_MODE_MQMI 3 -+#define RSS_NIP_QUEUE_SEL BIT(28) /* 0:q0, 1:table */ -+#define RRS_HASH_CTRL_EN BIT(29) -+#define RX_CUT_THRU_EN BIT(30) -+#define RXQ_CTRL_EN BIT(31) - - #define REG_RFD_FREE_THRESH 0x15A4 - #define RFD_FREE_THRESH_MASK 0x003F -@@ -577,57 +632,45 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define RXD_DMA_DOWN_TIMER_SHIFT 16 - - /* DMA Engine Control Register */ --#define REG_DMA_CTRL 0x15C0 --#define DMA_CTRL_DMAR_IN_ORDER 0x1 --#define DMA_CTRL_DMAR_ENH_ORDER 0x2 --#define DMA_CTRL_DMAR_OUT_ORDER 0x4 --#define DMA_CTRL_RCB_VALUE 0x8 --#define DMA_CTRL_DMAR_BURST_LEN_MASK 0x0007 --#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4 --#define DMA_CTRL_DMAW_BURST_LEN_MASK 0x0007 --#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7 --#define DMA_CTRL_DMAR_REQ_PRI 0x400 --#define DMA_CTRL_DMAR_DLY_CNT_MASK 0x001F --#define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11 --#define DMA_CTRL_DMAW_DLY_CNT_MASK 0x000F --#define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16 --#define DMA_CTRL_CMB_EN 0x100000 --#define DMA_CTRL_SMB_EN 0x200000 --#define DMA_CTRL_CMB_NOW 0x400000 --#define MAC_CTRL_SMB_DIS 0x1000000 --#define DMA_CTRL_SMB_NOW 0x80000000 -- --/* CMB/SMB Control Register */ -+#define REG_DMA_CTRL 0x15C0 -+#define DMA_CTRL_SMB_NOW BIT(31) -+#define DMA_CTRL_WPEND_CLR BIT(30) -+#define DMA_CTRL_RPEND_CLR BIT(29) -+#define DMA_CTRL_WDLY_CNT_MASK 0xFUL -+#define DMA_CTRL_WDLY_CNT_SHIFT 16 -+#define DMA_CTRL_WDLY_CNT_DEF 4 -+#define DMA_CTRL_RDLY_CNT_MASK 0x1FUL -+#define DMA_CTRL_RDLY_CNT_SHIFT 11 -+#define DMA_CTRL_RDLY_CNT_DEF 15 -+#define DMA_CTRL_RREQ_PRI_DATA BIT(10) /* 0:tpd, 1:data */ -+#define DMA_CTRL_WREQ_BLEN_MASK 7UL -+#define DMA_CTRL_WREQ_BLEN_SHIFT 7 -+#define DMA_CTRL_RREQ_BLEN_MASK 7UL -+#define DMA_CTRL_RREQ_BLEN_SHIFT 4 -+#define L1C_CTRL_DMA_RCB_LEN128 BIT(3) /* 0:64bytes,1:128bytes */ -+#define DMA_CTRL_RORDER_MODE_MASK 7UL -+#define DMA_CTRL_RORDER_MODE_SHIFT 0 -+#define DMA_CTRL_RORDER_MODE_OUT 4 -+#define DMA_CTRL_RORDER_MODE_ENHANCE 2 -+#define DMA_CTRL_RORDER_MODE_IN 1 -+ -+/* INT-triggle/SMB Control Register */ - #define REG_SMB_STAT_TIMER 0x15C4 /* 2us resolution */ - #define SMB_STAT_TIMER_MASK 0xFFFFFF --#define REG_CMB_TPD_THRESH 0x15C8 --#define CMB_TPD_THRESH_MASK 0xFFFF --#define REG_CMB_TX_TIMER 0x15CC /* 2us resolution */ --#define CMB_TX_TIMER_MASK 0xFFFF -+#define REG_TINT_TPD_THRESH 0x15C8 /* tpd th to trig intrrupt */ - - /* Mail box */ - #define MB_RFDX_PROD_IDX_MASK 0xFFFF - #define REG_MB_RFD0_PROD_IDX 0x15E0 --#define REG_MB_RFD1_PROD_IDX 0x15E4 --#define REG_MB_RFD2_PROD_IDX 0x15E8 --#define REG_MB_RFD3_PROD_IDX 0x15EC - --#define MB_PRIO_PROD_IDX_MASK 0xFFFF --#define REG_MB_PRIO_PROD_IDX 0x15F0 --#define MB_HTPD_PROD_IDX_SHIFT 0 --#define MB_NTPD_PROD_IDX_SHIFT 16 -- --#define MB_PRIO_CONS_IDX_MASK 0xFFFF --#define REG_MB_PRIO_CONS_IDX 0x15F4 --#define MB_HTPD_CONS_IDX_SHIFT 0 --#define MB_NTPD_CONS_IDX_SHIFT 16 -+#define REG_TPD_PRI1_PIDX 0x15F0 /* 16bit,hi-tpd producer idx */ -+#define REG_TPD_PRI0_PIDX 0x15F2 /* 16bit,lo-tpd producer idx */ -+#define REG_TPD_PRI1_CIDX 0x15F4 /* 16bit,hi-tpd consumer idx */ -+#define REG_TPD_PRI0_CIDX 0x15F6 /* 16bit,lo-tpd consumer idx */ - - #define REG_MB_RFD01_CONS_IDX 0x15F8 - #define MB_RFD0_CONS_IDX_MASK 0x0000FFFF - #define MB_RFD1_CONS_IDX_MASK 0xFFFF0000 --#define REG_MB_RFD23_CONS_IDX 0x15FC --#define MB_RFD2_CONS_IDX_MASK 0x0000FFFF --#define MB_RFD3_CONS_IDX_MASK 0xFFFF0000 - - /* Interrupt Status Register */ - #define REG_ISR 0x1600 -@@ -705,13 +748,6 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define REG_INT_RETRIG_TIMER 0x1608 - #define INT_RETRIG_TIMER_MASK 0xFFFF - --#define REG_HDS_CTRL 0x160C --#define HDS_CTRL_EN 0x0001 --#define HDS_CTRL_BACKFILLSIZE_SHIFT 8 --#define HDS_CTRL_BACKFILLSIZE_MASK 0x0FFF --#define HDS_CTRL_MAX_HDRSIZE_SHIFT 20 --#define HDS_CTRL_MAC_HDRSIZE_MASK 0x0FFF -- - #define REG_MAC_RX_STATUS_BIN 0x1700 - #define REG_MAC_RX_STATUS_END 0x175c - #define REG_MAC_TX_STATUS_BIN 0x1760 -@@ -796,73 +832,188 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); - #define MII_DBG_ADDR 0x1D - #define MII_DBG_DATA 0x1E - --#define MII_ANA_CTRL_0 0x0 --#define ANA_RESTART_CAL 0x0001 --#define ANA_MANUL_SWICH_ON_SHIFT 0x1 --#define ANA_MANUL_SWICH_ON_MASK 0xF --#define ANA_MAN_ENABLE 0x0020 --#define ANA_SEL_HSP 0x0040 --#define ANA_EN_HB 0x0080 --#define ANA_EN_HBIAS 0x0100 --#define ANA_OEN_125M 0x0200 --#define ANA_EN_LCKDT 0x0400 --#define ANA_LCKDT_PHY 0x0800 --#define ANA_AFE_MODE 0x1000 --#define ANA_VCO_SLOW 0x2000 --#define ANA_VCO_FAST 0x4000 --#define ANA_SEL_CLK125M_DSP 0x8000 -- --#define MII_ANA_CTRL_4 0x4 --#define ANA_IECHO_ADJ_MASK 0xF --#define ANA_IECHO_ADJ_3_SHIFT 0 --#define ANA_IECHO_ADJ_2_SHIFT 4 --#define ANA_IECHO_ADJ_1_SHIFT 8 --#define ANA_IECHO_ADJ_0_SHIFT 12 -- --#define MII_ANA_CTRL_5 0x5 --#define ANA_SERDES_CDR_BW_SHIFT 0 --#define ANA_SERDES_CDR_BW_MASK 0x3 --#define ANA_MS_PAD_DBG 0x0004 --#define ANA_SPEEDUP_DBG 0x0008 --#define ANA_SERDES_TH_LOS_SHIFT 4 --#define ANA_SERDES_TH_LOS_MASK 0x3 --#define ANA_SERDES_EN_DEEM 0x0040 --#define ANA_SERDES_TXELECIDLE 0x0080 --#define ANA_SERDES_BEACON 0x0100 --#define ANA_SERDES_HALFTXDR 0x0200 --#define ANA_SERDES_SEL_HSP 0x0400 --#define ANA_SERDES_EN_PLL 0x0800 --#define ANA_SERDES_EN 0x1000 --#define ANA_SERDES_EN_LCKDT 0x2000 -- --#define MII_ANA_CTRL_11 0xB --#define ANA_PS_HIB_EN 0x8000 -- --#define MII_ANA_CTRL_18 0x12 --#define ANA_TEST_MODE_10BT_01SHIFT 0 --#define ANA_TEST_MODE_10BT_01MASK 0x3 --#define ANA_LOOP_SEL_10BT 0x0004 --#define ANA_RGMII_MODE_SW 0x0008 --#define ANA_EN_LONGECABLE 0x0010 --#define ANA_TEST_MODE_10BT_2 0x0020 --#define ANA_EN_10BT_IDLE 0x0400 --#define ANA_EN_MASK_TB 0x0800 --#define ANA_TRIGGER_SEL_TIMER_SHIFT 12 --#define ANA_TRIGGER_SEL_TIMER_MASK 0x3 --#define ANA_INTERVAL_SEL_TIMER_SHIFT 14 --#define ANA_INTERVAL_SEL_TIMER_MASK 0x3 -- --#define MII_ANA_CTRL_41 0x29 --#define ANA_TOP_PS_EN 0x8000 -- --#define MII_ANA_CTRL_54 0x36 --#define ANA_LONG_CABLE_TH_100_SHIFT 0 --#define ANA_LONG_CABLE_TH_100_MASK 0x3F --#define ANA_DESERVED 0x0040 --#define ANA_EN_LIT_CH 0x0080 --#define ANA_SHORT_CABLE_TH_100_SHIFT 8 --#define ANA_SHORT_CABLE_TH_100_MASK 0x3F --#define ANA_BP_BAD_LINK_ACCUM 0x4000 --#define ANA_BP_SMALL_BW 0x8000 -+/***************************** debug port *************************************/ -+ -+#define MIIDBG_ANACTRL 0x00 -+#define ANACTRL_CLK125M_DELAY_EN 0x8000 -+#define ANACTRL_VCO_FAST 0x4000 -+#define ANACTRL_VCO_SLOW 0x2000 -+#define ANACTRL_AFE_MODE_EN 0x1000 -+#define ANACTRL_LCKDET_PHY 0x800 -+#define ANACTRL_LCKDET_EN 0x400 -+#define ANACTRL_OEN_125M 0x200 -+#define ANACTRL_HBIAS_EN 0x100 -+#define ANACTRL_HB_EN 0x80 -+#define ANACTRL_SEL_HSP 0x40 -+#define ANACTRL_CLASSA_EN 0x20 -+#define ANACTRL_MANUSWON_SWR_MASK 3U -+#define ANACTRL_MANUSWON_SWR_SHIFT 2 -+#define ANACTRL_MANUSWON_SWR_2V 0 -+#define ANACTRL_MANUSWON_SWR_1P9V 1 -+#define ANACTRL_MANUSWON_SWR_1P8V 2 -+#define ANACTRL_MANUSWON_SWR_1P7V 3 -+#define ANACTRL_MANUSWON_BW3_4M 0x2 -+#define ANACTRL_RESTART_CAL 0x1 -+#define ANACTRL_DEF 0x02EF -+ -+#define MIIDBG_SYSMODCTRL 0x04 -+#define SYSMODCTRL_IECHOADJ_PFMH_PHY 0x8000 -+#define SYSMODCTRL_IECHOADJ_BIASGEN 0x4000 -+#define SYSMODCTRL_IECHOADJ_PFML_PHY 0x2000 -+#define SYSMODCTRL_IECHOADJ_PS_MASK 3U -+#define SYSMODCTRL_IECHOADJ_PS_SHIFT 10 -+#define SYSMODCTRL_IECHOADJ_PS_40 3 -+#define SYSMODCTRL_IECHOADJ_PS_20 2 -+#define SYSMODCTRL_IECHOADJ_PS_0 1 -+#define SYSMODCTRL_IECHOADJ_10BT_100MV 0x40 /* 1:100mv, 0:200mv */ -+#define SYSMODCTRL_IECHOADJ_HLFAP_MASK 3U -+#define SYSMODCTRL_IECHOADJ_HLFAP_SHIFT 4 -+#define SYSMODCTRL_IECHOADJ_VDFULBW 0x8 -+#define SYSMODCTRL_IECHOADJ_VDBIASHLF 0x4 -+#define SYSMODCTRL_IECHOADJ_VDAMPHLF 0x2 -+#define SYSMODCTRL_IECHOADJ_VDLANSW 0x1 -+#define SYSMODCTRL_IECHOADJ_DEF 0x88BB /* ???? */ -+ -+/* for l1d & l2cb */ -+#define SYSMODCTRL_IECHOADJ_CUR_ADD 0x8000 -+#define SYSMODCTRL_IECHOADJ_CUR_MASK 7U -+#define SYSMODCTRL_IECHOADJ_CUR_SHIFT 12 -+#define SYSMODCTRL_IECHOADJ_VOL_MASK 0xFU -+#define SYSMODCTRL_IECHOADJ_VOL_SHIFT 8 -+#define SYSMODCTRL_IECHOADJ_VOL_17ALL 3 -+#define SYSMODCTRL_IECHOADJ_VOL_100M15 1 -+#define SYSMODCTRL_IECHOADJ_VOL_10M17 0 -+#define SYSMODCTRL_IECHOADJ_BIAS1_MASK 0xFU -+#define SYSMODCTRL_IECHOADJ_BIAS1_SHIFT 4 -+#define SYSMODCTRL_IECHOADJ_BIAS2_MASK 0xFU -+#define SYSMODCTRL_IECHOADJ_BIAS2_SHIFT 0 -+#define L1D_SYSMODCTRL_IECHOADJ_DEF 0x4FBB -+ -+#define MIIDBG_SRDSYSMOD 0x05 -+#define SRDSYSMOD_LCKDET_EN 0x2000 -+#define SRDSYSMOD_PLL_EN 0x800 -+#define SRDSYSMOD_SEL_HSP 0x400 -+#define SRDSYSMOD_HLFTXDR 0x200 -+#define SRDSYSMOD_TXCLK_DELAY_EN 0x100 -+#define SRDSYSMOD_TXELECIDLE 0x80 -+#define SRDSYSMOD_DEEMP_EN 0x40 -+#define SRDSYSMOD_MS_PAD 0x4 -+#define SRDSYSMOD_CDR_ADC_VLTG 0x2 -+#define SRDSYSMOD_CDR_DAC_1MA 0x1 -+#define SRDSYSMOD_DEF 0x2C46 -+ -+#define MIIDBG_CFGLPSPD 0x0A -+#define CFGLPSPD_RSTCNT_MASK 3U -+#define CFGLPSPD_RSTCNT_SHIFT 14 -+#define CFGLPSPD_RSTCNT_CLK125SW 0x2000 -+ -+#define MIIDBG_HIBNEG 0x0B -+#define HIBNEG_PSHIB_EN 0x8000 -+#define HIBNEG_WAKE_BOTH 0x4000 -+#define HIBNEG_ONOFF_ANACHG_SUDEN 0x2000 -+#define HIBNEG_HIB_PULSE 0x1000 -+#define HIBNEG_GATE_25M_EN 0x800 -+#define HIBNEG_RST_80U 0x400 -+#define HIBNEG_RST_TIMER_MASK 3U -+#define HIBNEG_RST_TIMER_SHIFT 8 -+#define HIBNEG_GTX_CLK_DELAY_MASK 3U -+#define HIBNEG_GTX_CLK_DELAY_SHIFT 5 -+#define HIBNEG_BYPSS_BRKTIMER 0x10 -+#define HIBNEG_DEF 0xBC40 -+ -+#define MIIDBG_TST10BTCFG 0x12 -+#define TST10BTCFG_INTV_TIMER_MASK 3U -+#define TST10BTCFG_INTV_TIMER_SHIFT 14 -+#define TST10BTCFG_TRIGER_TIMER_MASK 3U -+#define TST10BTCFG_TRIGER_TIMER_SHIFT 12 -+#define TST10BTCFG_DIV_MAN_MLT3_EN 0x800 -+#define TST10BTCFG_OFF_DAC_IDLE 0x400 -+#define TST10BTCFG_LPBK_DEEP 0x4 /* 1:deep,0:shallow */ -+#define TST10BTCFG_DEF 0x4C04 -+ -+#define MIIDBG_AZ_ANADECT 0x15 -+#define AZ_ANADECT_10BTRX_TH 0x8000 -+#define AZ_ANADECT_BOTH_01CHNL 0x4000 -+#define AZ_ANADECT_INTV_MASK 0x3FU -+#define AZ_ANADECT_INTV_SHIFT 8 -+#define AZ_ANADECT_THRESH_MASK 0xFU -+#define AZ_ANADECT_THRESH_SHIFT 4 -+#define AZ_ANADECT_CHNL_MASK 0xFU -+#define AZ_ANADECT_CHNL_SHIFT 0 -+#define AZ_ANADECT_DEF 0x3220 -+#define AZ_ANADECT_LONG 0xb210 -+ -+#define MIIDBG_MSE16DB 0x18 /* l1d */ -+#define L1D_MSE16DB_UP 0x05EA -+#define L1D_MSE16DB_DOWN 0x02EA -+ -+#define MIIDBG_LEGCYPS 0x29 -+#define LEGCYPS_EN 0x8000 -+#define LEGCYPS_DAC_AMP1000_MASK 7U -+#define LEGCYPS_DAC_AMP1000_SHIFT 12 -+#define LEGCYPS_DAC_AMP100_MASK 7U -+#define LEGCYPS_DAC_AMP100_SHIFT 9 -+#define LEGCYPS_DAC_AMP10_MASK 7U -+#define LEGCYPS_DAC_AMP10_SHIFT 6 -+#define LEGCYPS_UNPLUG_TIMER_MASK 7U -+#define LEGCYPS_UNPLUG_TIMER_SHIFT 3 -+#define LEGCYPS_UNPLUG_DECT_EN 0x4 -+#define LEGCYPS_ECNC_PS_EN 0x1 -+#define L1D_LEGCYPS_DEF 0x129D -+#define L1C_LEGCYPS_DEF 0x36DD -+ -+#define MIIDBG_TST100BTCFG 0x36 -+#define TST100BTCFG_NORMAL_BW_EN 0x8000 -+#define TST100BTCFG_BADLNK_BYPASS 0x4000 -+#define TST100BTCFG_SHORTCABL_TH_MASK 0x3FU -+#define TST100BTCFG_SHORTCABL_TH_SHIFT 8 -+#define TST100BTCFG_LITCH_EN 0x80 -+#define TST100BTCFG_VLT_SW 0x40 -+#define TST100BTCFG_LONGCABL_TH_MASK 0x3FU -+#define TST100BTCFG_LONGCABL_TH_SHIFT 0 -+#define TST100BTCFG_DEF 0xE12C -+ -+#define MIIDBG_VOLT_CTRL 0x3B /* only for l2cb 1 & 2 */ -+#define VOLT_CTRL_CABLE1TH_MASK 0x1FFU -+#define VOLT_CTRL_CABLE1TH_SHIFT 7 -+#define VOLT_CTRL_AMPCTRL_MASK 3U -+#define VOLT_CTRL_AMPCTRL_SHIFT 5 -+#define VOLT_CTRL_SW_BYPASS 0x10 -+#define VOLT_CTRL_SWLOWEST 0x8 -+#define VOLT_CTRL_DACAMP10_MASK 7U -+#define VOLT_CTRL_DACAMP10_SHIFT 0 -+ -+#define MIIDBG_CABLE1TH_DET 0x3E -+#define CABLE1TH_DET_EN 0x8000 -+ -+ -+/******* dev 3 *********/ -+#define MIIEXT_PCS 3 -+ -+#define MIIEXT_CLDCTRL3 0x8003 -+#define CLDCTRL3_BP_CABLE1TH_DET_GT 0x8000 -+#define CLDCTRL3_AZ_DISAMP 0x1000 -+#define L2CB_CLDCTRL3 0x4D19 -+#define L1D_CLDCTRL3 0xDD19 -+ -+#define MIIEXT_CLDCTRL6 0x8006 -+#define CLDCTRL6_CAB_LEN_MASK 0x1FFU -+#define CLDCTRL6_CAB_LEN_SHIFT 0 -+#define CLDCTRL6_CAB_LEN_SHORT 0x50 -+ -+/********* dev 7 **********/ -+#define MIIEXT_ANEG 7 -+ -+#define MIIEXT_LOCAL_EEEADV 0x3C -+#define LOCAL_EEEADV_1000BT 0x4 -+#define LOCAL_EEEADV_100BT 0x2 -+ -+#define MIIEXT_REMOTE_EEEADV 0x3D -+#define REMOTE_EEEADV_1000BT 0x4 -+#define REMOTE_EEEADV_100BT 0x2 -+ -+#define MIIEXT_EEE_ANEG 0x8000 -+#define EEE_ANEG_1000M 0x4 -+#define EEE_ANEG_100M 0x2 - - #endif /*_ATL1C_HW_H_*/ -diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c -index 1ef0c92..9cc1570 100644 ---- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c -+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c -@@ -24,14 +24,6 @@ - #define ATL1C_DRV_VERSION "1.0.1.0-NAPI" - char atl1c_driver_name[] = "atl1c"; - char atl1c_driver_version[] = ATL1C_DRV_VERSION; --#define PCI_DEVICE_ID_ATTANSIC_L2C 0x1062 --#define PCI_DEVICE_ID_ATTANSIC_L1C 0x1063 --#define PCI_DEVICE_ID_ATHEROS_L2C_B 0x2060 /* AR8152 v1.1 Fast 10/100 */ --#define PCI_DEVICE_ID_ATHEROS_L2C_B2 0x2062 /* AR8152 v2.0 Fast 10/100 */ --#define PCI_DEVICE_ID_ATHEROS_L1D 0x1073 /* AR8151 v1.0 Gigabit 1000 */ --#define PCI_DEVICE_ID_ATHEROS_L1D_2_0 0x1083 /* AR8151 v2.0 Gigabit 1000 */ --#define L2CB_V10 0xc0 --#define L2CB_V11 0xc1 - - /* - * atl1c_pci_tbl - PCI Device ID Table -@@ -54,70 +46,72 @@ static DEFINE_PCI_DEVICE_TABLE(atl1c_pci_tbl) = { - }; - MODULE_DEVICE_TABLE(pci, atl1c_pci_tbl); - --MODULE_AUTHOR("Jie Yang "); --MODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver"); -+MODULE_AUTHOR("Jie Yang"); -+MODULE_AUTHOR("Qualcomm Atheros Inc., "); -+MODULE_DESCRIPTION("Qualcom Atheros 100/1000M Ethernet Network Driver"); - MODULE_LICENSE("GPL"); - MODULE_VERSION(ATL1C_DRV_VERSION); - - static int atl1c_stop_mac(struct atl1c_hw *hw); --static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw); --static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw); - static void atl1c_disable_l0s_l1(struct atl1c_hw *hw); --static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup); --static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter); --static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, -+static void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed); -+static void atl1c_start_mac(struct atl1c_adapter *adapter); -+static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, - int *work_done, int work_to_do); - static int atl1c_up(struct atl1c_adapter *adapter); - static void atl1c_down(struct atl1c_adapter *adapter); -+static int atl1c_reset_mac(struct atl1c_hw *hw); -+static void atl1c_reset_dma_ring(struct atl1c_adapter *adapter); -+static int atl1c_configure(struct atl1c_adapter *adapter); -+static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter); - - static const u16 atl1c_pay_load_size[] = { - 128, 256, 512, 1024, 2048, 4096, - }; - --static const u16 atl1c_rfd_prod_idx_regs[AT_MAX_RECEIVE_QUEUE] = --{ -- REG_MB_RFD0_PROD_IDX, -- REG_MB_RFD1_PROD_IDX, -- REG_MB_RFD2_PROD_IDX, -- REG_MB_RFD3_PROD_IDX --}; -- --static const u16 atl1c_rfd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] = --{ -- REG_RFD0_HEAD_ADDR_LO, -- REG_RFD1_HEAD_ADDR_LO, -- REG_RFD2_HEAD_ADDR_LO, -- REG_RFD3_HEAD_ADDR_LO --}; -- --static const u16 atl1c_rrd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] = --{ -- REG_RRD0_HEAD_ADDR_LO, -- REG_RRD1_HEAD_ADDR_LO, -- REG_RRD2_HEAD_ADDR_LO, -- REG_RRD3_HEAD_ADDR_LO --}; - - static const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | - NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP; - static void atl1c_pcie_patch(struct atl1c_hw *hw) - { -- u32 data; -+ u32 mst_data, data; - -- AT_READ_REG(hw, REG_PCIE_PHYMISC, &data); -- data |= PCIE_PHYMISC_FORCE_RCV_DET; -- AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data); -+ /* pclk sel could switch to 25M */ -+ AT_READ_REG(hw, REG_MASTER_CTRL, &mst_data); -+ mst_data &= ~MASTER_CTRL_CLK_SEL_DIS; -+ AT_WRITE_REG(hw, REG_MASTER_CTRL, mst_data); - -+ /* WoL/PCIE related settings */ -+ if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) { -+ AT_READ_REG(hw, REG_PCIE_PHYMISC, &data); -+ data |= PCIE_PHYMISC_FORCE_RCV_DET; -+ AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data); -+ } else { /* new dev set bit5 of MASTER */ -+ if (!(mst_data & MASTER_CTRL_WAKEN_25M)) -+ AT_WRITE_REG(hw, REG_MASTER_CTRL, -+ mst_data | MASTER_CTRL_WAKEN_25M); -+ } -+ /* aspm/PCIE setting only for l2cb 1.0 */ - if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10) { - AT_READ_REG(hw, REG_PCIE_PHYMISC2, &data); -- -- data &= ~(PCIE_PHYMISC2_SERDES_CDR_MASK << -- PCIE_PHYMISC2_SERDES_CDR_SHIFT); -- data |= 3 << PCIE_PHYMISC2_SERDES_CDR_SHIFT; -- data &= ~(PCIE_PHYMISC2_SERDES_TH_MASK << -- PCIE_PHYMISC2_SERDES_TH_SHIFT); -- data |= 3 << PCIE_PHYMISC2_SERDES_TH_SHIFT; -+ data = FIELD_SETX(data, PCIE_PHYMISC2_CDR_BW, -+ L2CB1_PCIE_PHYMISC2_CDR_BW); -+ data = FIELD_SETX(data, PCIE_PHYMISC2_L0S_TH, -+ L2CB1_PCIE_PHYMISC2_L0S_TH); - AT_WRITE_REG(hw, REG_PCIE_PHYMISC2, data); -+ /* extend L1 sync timer */ -+ AT_READ_REG(hw, REG_LINK_CTRL, &data); -+ data |= LINK_CTRL_EXT_SYNC; -+ AT_WRITE_REG(hw, REG_LINK_CTRL, data); -+ } -+ /* l2cb 1.x & l1d 1.x */ -+ if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d) { -+ AT_READ_REG(hw, REG_PM_CTRL, &data); -+ data |= PM_CTRL_L0S_BUFSRX_EN; -+ AT_WRITE_REG(hw, REG_PM_CTRL, data); -+ /* clear vendor msg */ -+ AT_READ_REG(hw, REG_DMA_DBG, &data); -+ AT_WRITE_REG(hw, REG_DMA_DBG, data & ~DMA_DBG_VENDOR_MSG); - } - } - -@@ -130,6 +124,7 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag) - u32 data; - u32 pci_cmd; - struct pci_dev *pdev = hw->adapter->pdev; -+ int pos; - - AT_READ_REG(hw, PCI_COMMAND, &pci_cmd); - pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; -@@ -142,14 +137,23 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag) - */ - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_enable_wake(pdev, PCI_D3cold, 0); -+ /* wol sts read-clear */ -+ AT_READ_REG(hw, REG_WOL_CTRL, &data); -+ AT_WRITE_REG(hw, REG_WOL_CTRL, 0); - - /* - * Mask some pcie error bits - */ -- AT_READ_REG(hw, REG_PCIE_UC_SEVERITY, &data); -- data &= ~PCIE_UC_SERVRITY_DLP; -- data &= ~PCIE_UC_SERVRITY_FCP; -- AT_WRITE_REG(hw, REG_PCIE_UC_SEVERITY, data); -+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); -+ pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, &data); -+ data &= ~(PCI_ERR_UNC_DLP | PCI_ERR_UNC_FCP); -+ pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, data); -+ /* clear error status */ -+ pci_write_config_word(pdev, pci_pcie_cap(pdev) + PCI_EXP_DEVSTA, -+ PCI_EXP_DEVSTA_NFED | -+ PCI_EXP_DEVSTA_FED | -+ PCI_EXP_DEVSTA_CED | -+ PCI_EXP_DEVSTA_URD); - - AT_READ_REG(hw, REG_LTSSM_ID_CTRL, &data); - data &= ~LTSSM_ID_EN_WRO; -@@ -158,11 +162,6 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag) - atl1c_pcie_patch(hw); - if (flag & ATL1C_PCIE_L0S_L1_DISABLE) - atl1c_disable_l0s_l1(hw); -- if (flag & ATL1C_PCIE_PHY_RESET) -- AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT); -- else -- AT_WRITE_REG(hw, REG_GPHY_CTRL, -- GPHY_CTRL_DEFAULT | GPHY_CTRL_EXT_RESET); - - msleep(5); - } -@@ -207,14 +206,14 @@ static inline void atl1c_irq_reset(struct atl1c_adapter *adapter) - * atl1c_wait_until_idle - wait up to AT_HW_MAX_IDLE_DELAY reads - * of the idle status register until the device is actually idle - */ --static u32 atl1c_wait_until_idle(struct atl1c_hw *hw) -+static u32 atl1c_wait_until_idle(struct atl1c_hw *hw, u32 modu_ctrl) - { - int timeout; - u32 data; - - for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { - AT_READ_REG(hw, REG_IDLE_STATUS, &data); -- if ((data & IDLE_STATUS_MASK) == 0) -+ if ((data & modu_ctrl) == 0) - return 0; - msleep(1); - } -@@ -261,15 +260,16 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter) - - if ((phy_data & BMSR_LSTATUS) == 0) { - /* link down */ -- hw->hibernate = true; -- if (atl1c_stop_mac(hw) != 0) -- if (netif_msg_hw(adapter)) -- dev_warn(&pdev->dev, "stop mac failed\n"); -- atl1c_set_aspm(hw, false); - netif_carrier_off(netdev); - netif_stop_queue(netdev); -- atl1c_phy_reset(hw); -- atl1c_phy_init(&adapter->hw); -+ hw->hibernate = true; -+ if (atl1c_reset_mac(hw) != 0) -+ if (netif_msg_hw(adapter)) -+ dev_warn(&pdev->dev, "reset mac failed\n"); -+ atl1c_set_aspm(hw, SPEED_0); -+ atl1c_post_phy_linkchg(hw, SPEED_0); -+ atl1c_reset_dma_ring(adapter); -+ atl1c_configure(adapter); - } else { - /* Link Up */ - hw->hibernate = false; -@@ -283,10 +283,9 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter) - adapter->link_duplex != duplex) { - adapter->link_speed = speed; - adapter->link_duplex = duplex; -- atl1c_set_aspm(hw, true); -- atl1c_enable_tx_ctrl(hw); -- atl1c_enable_rx_ctrl(hw); -- atl1c_setup_mac_ctrl(adapter); -+ atl1c_set_aspm(hw, speed); -+ atl1c_post_phy_linkchg(hw, speed); -+ atl1c_start_mac(adapter); - if (netif_msg_link(adapter)) - dev_info(&pdev->dev, - "%s: %s NIC Link is Up<%d Mbps %s>\n", -@@ -337,6 +336,9 @@ static void atl1c_common_task(struct work_struct *work) - adapter = container_of(work, struct atl1c_adapter, common_task); - netdev = adapter->netdev; - -+ if (test_bit(__AT_DOWN, &adapter->flags)) -+ return; -+ - if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) { - netif_device_detach(netdev); - atl1c_down(adapter); -@@ -345,8 +347,11 @@ static void atl1c_common_task(struct work_struct *work) - } - - if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE, -- &adapter->work_event)) -+ &adapter->work_event)) { -+ atl1c_irq_disable(adapter); - atl1c_check_link_status(adapter); -+ atl1c_irq_enable(adapter); -+ } - } - - -@@ -470,7 +475,7 @@ static int atl1c_set_mac_addr(struct net_device *netdev, void *p) - memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; - -- atl1c_hw_set_mac_addr(&adapter->hw); -+ atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr); - - return 0; - } -@@ -523,11 +528,16 @@ static int atl1c_set_features(struct net_device *netdev, - static int atl1c_change_mtu(struct net_device *netdev, int new_mtu) - { - struct atl1c_adapter *adapter = netdev_priv(netdev); -+ struct atl1c_hw *hw = &adapter->hw; - int old_mtu = netdev->mtu; - int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; - -- if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || -- (max_frame > MAX_JUMBO_FRAME_SIZE)) { -+ /* Fast Ethernet controller doesn't support jumbo packet */ -+ if (((hw->nic_type == athr_l2c || -+ hw->nic_type == athr_l2c_b || -+ hw->nic_type == athr_l2c_b2) && new_mtu > ETH_DATA_LEN) || -+ max_frame < ETH_ZLEN + ETH_FCS_LEN || -+ max_frame > MAX_JUMBO_FRAME_SIZE) { - if (netif_msg_link(adapter)) - dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); - return -EINVAL; -@@ -543,14 +553,6 @@ static int atl1c_change_mtu(struct net_device *netdev, int new_mtu) - netdev_update_features(netdev); - atl1c_up(adapter); - clear_bit(__AT_RESETTING, &adapter->flags); -- if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) { -- u32 phy_data; -- -- AT_READ_REG(&adapter->hw, 0x1414, &phy_data); -- phy_data |= 0x10000000; -- AT_WRITE_REG(&adapter->hw, 0x1414, phy_data); -- } -- - } - return 0; - } -@@ -563,7 +565,7 @@ static int atl1c_mdio_read(struct net_device *netdev, int phy_id, int reg_num) - struct atl1c_adapter *adapter = netdev_priv(netdev); - u16 result; - -- atl1c_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result); -+ atl1c_read_phy_reg(&adapter->hw, reg_num, &result); - return result; - } - -@@ -572,7 +574,7 @@ static void atl1c_mdio_write(struct net_device *netdev, int phy_id, - { - struct atl1c_adapter *adapter = netdev_priv(netdev); - -- atl1c_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val); -+ atl1c_write_phy_reg(&adapter->hw, reg_num, val); - } - - /* -@@ -687,21 +689,15 @@ static void atl1c_set_mac_type(struct atl1c_hw *hw) - - static int atl1c_setup_mac_funcs(struct atl1c_hw *hw) - { -- u32 phy_status_data; - u32 link_ctrl_data; - - atl1c_set_mac_type(hw); -- AT_READ_REG(hw, REG_PHY_STATUS, &phy_status_data); - AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data); - - hw->ctrl_flags = ATL1C_INTR_MODRT_ENABLE | - ATL1C_TXQ_MODE_ENHANCE; -- if (link_ctrl_data & LINK_CTRL_L0S_EN) -- hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT; -- if (link_ctrl_data & LINK_CTRL_L1_EN) -- hw->ctrl_flags |= ATL1C_ASPM_L1_SUPPORT; -- if (link_ctrl_data & LINK_CTRL_EXT_SYNC) -- hw->ctrl_flags |= ATL1C_LINK_EXT_SYNC; -+ hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT | -+ ATL1C_ASPM_L1_SUPPORT; - hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON; - - if (hw->nic_type == athr_l1c || -@@ -710,6 +706,55 @@ static int atl1c_setup_mac_funcs(struct atl1c_hw *hw) - hw->link_cap_flags |= ATL1C_LINK_CAP_1000M; - return 0; - } -+ -+struct atl1c_platform_patch { -+ u16 pci_did; -+ u8 pci_revid; -+ u16 subsystem_vid; -+ u16 subsystem_did; -+ u32 patch_flag; -+#define ATL1C_LINK_PATCH 0x1 -+}; -+static const struct atl1c_platform_patch plats[] __devinitdata = { -+{0x2060, 0xC1, 0x1019, 0x8152, 0x1}, -+{0x2060, 0xC1, 0x1019, 0x2060, 0x1}, -+{0x2060, 0xC1, 0x1019, 0xE000, 0x1}, -+{0x2062, 0xC0, 0x1019, 0x8152, 0x1}, -+{0x2062, 0xC0, 0x1019, 0x2062, 0x1}, -+{0x2062, 0xC0, 0x1458, 0xE000, 0x1}, -+{0x2062, 0xC1, 0x1019, 0x8152, 0x1}, -+{0x2062, 0xC1, 0x1019, 0x2062, 0x1}, -+{0x2062, 0xC1, 0x1458, 0xE000, 0x1}, -+{0x2062, 0xC1, 0x1565, 0x2802, 0x1}, -+{0x2062, 0xC1, 0x1565, 0x2801, 0x1}, -+{0x1073, 0xC0, 0x1019, 0x8151, 0x1}, -+{0x1073, 0xC0, 0x1019, 0x1073, 0x1}, -+{0x1073, 0xC0, 0x1458, 0xE000, 0x1}, -+{0x1083, 0xC0, 0x1458, 0xE000, 0x1}, -+{0x1083, 0xC0, 0x1019, 0x8151, 0x1}, -+{0x1083, 0xC0, 0x1019, 0x1083, 0x1}, -+{0x1083, 0xC0, 0x1462, 0x7680, 0x1}, -+{0x1083, 0xC0, 0x1565, 0x2803, 0x1}, -+{0}, -+}; -+ -+static void __devinit atl1c_patch_assign(struct atl1c_hw *hw) -+{ -+ int i = 0; -+ -+ hw->msi_lnkpatch = false; -+ -+ while (plats[i].pci_did != 0) { -+ if (plats[i].pci_did == hw->device_id && -+ plats[i].pci_revid == hw->revision_id && -+ plats[i].subsystem_vid == hw->subsystem_vendor_id && -+ plats[i].subsystem_did == hw->subsystem_id) { -+ if (plats[i].patch_flag & ATL1C_LINK_PATCH) -+ hw->msi_lnkpatch = true; -+ } -+ i++; -+ } -+} - /* - * atl1c_sw_init - Initialize general software structures (struct atl1c_adapter) - * @adapter: board private structure to initialize -@@ -729,9 +774,8 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) - device_set_wakeup_enable(&pdev->dev, false); - adapter->link_speed = SPEED_0; - adapter->link_duplex = FULL_DUPLEX; -- adapter->num_rx_queues = AT_DEF_RECEIVE_QUEUE; - adapter->tpd_ring[0].count = 1024; -- adapter->rfd_ring[0].count = 512; -+ adapter->rfd_ring.count = 512; - - hw->vendor_id = pdev->vendor; - hw->device_id = pdev->device; -@@ -746,26 +790,18 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) - dev_err(&pdev->dev, "set mac function pointers failed\n"); - return -1; - } -+ atl1c_patch_assign(hw); -+ - hw->intr_mask = IMR_NORMAL_MASK; - hw->phy_configured = false; - hw->preamble_len = 7; - hw->max_frame_size = adapter->netdev->mtu; -- if (adapter->num_rx_queues < 2) { -- hw->rss_type = atl1c_rss_disable; -- hw->rss_mode = atl1c_rss_mode_disable; -- } else { -- hw->rss_type = atl1c_rss_ipv4; -- hw->rss_mode = atl1c_rss_mul_que_mul_int; -- hw->rss_hash_bits = 16; -- } - hw->autoneg_advertised = ADVERTISED_Autoneg; - hw->indirect_tab = 0xE4E4E4E4; - hw->base_cpu = 0; - - hw->ict = 50000; /* 100ms */ - hw->smb_timer = 200000; /* 400ms */ -- hw->cmb_tpd = 4; -- hw->cmb_tx_timer = 1; /* 2 us */ - hw->rx_imt = 200; - hw->tx_imt = 1000; - -@@ -773,9 +809,6 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) - hw->rfd_burst = 8; - hw->dma_order = atl1c_dma_ord_out; - hw->dmar_block = atl1c_dma_req_1024; -- hw->dmaw_block = atl1c_dma_req_1024; -- hw->dmar_dly_cnt = 15; -- hw->dmaw_dly_cnt = 4; - - if (atl1c_alloc_queues(adapter)) { - dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); -@@ -851,24 +884,22 @@ static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter, - */ - static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter) - { -- struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; -- struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; -+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; -+ struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; - struct atl1c_buffer *buffer_info; - struct pci_dev *pdev = adapter->pdev; -- int i, j; -+ int j; - -- for (i = 0; i < adapter->num_rx_queues; i++) { -- for (j = 0; j < rfd_ring[i].count; j++) { -- buffer_info = &rfd_ring[i].buffer_info[j]; -- atl1c_clean_buffer(pdev, buffer_info, 0); -- } -- /* zero out the descriptor ring */ -- memset(rfd_ring[i].desc, 0, rfd_ring[i].size); -- rfd_ring[i].next_to_clean = 0; -- rfd_ring[i].next_to_use = 0; -- rrd_ring[i].next_to_use = 0; -- rrd_ring[i].next_to_clean = 0; -+ for (j = 0; j < rfd_ring->count; j++) { -+ buffer_info = &rfd_ring->buffer_info[j]; -+ atl1c_clean_buffer(pdev, buffer_info, 0); - } -+ /* zero out the descriptor ring */ -+ memset(rfd_ring->desc, 0, rfd_ring->size); -+ rfd_ring->next_to_clean = 0; -+ rfd_ring->next_to_use = 0; -+ rrd_ring->next_to_use = 0; -+ rrd_ring->next_to_clean = 0; - } - - /* -@@ -877,8 +908,8 @@ static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter) - static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter) - { - struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; -- struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; -- struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; -+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; -+ struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; - struct atl1c_buffer *buffer_info; - int i, j; - -@@ -890,15 +921,13 @@ static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter) - ATL1C_SET_BUFFER_STATE(&buffer_info[i], - ATL1C_BUFFER_FREE); - } -- for (i = 0; i < adapter->num_rx_queues; i++) { -- rfd_ring[i].next_to_use = 0; -- rfd_ring[i].next_to_clean = 0; -- rrd_ring[i].next_to_use = 0; -- rrd_ring[i].next_to_clean = 0; -- for (j = 0; j < rfd_ring[i].count; j++) { -- buffer_info = &rfd_ring[i].buffer_info[j]; -- ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE); -- } -+ rfd_ring->next_to_use = 0; -+ rfd_ring->next_to_clean = 0; -+ rrd_ring->next_to_use = 0; -+ rrd_ring->next_to_clean = 0; -+ for (j = 0; j < rfd_ring->count; j++) { -+ buffer_info = &rfd_ring->buffer_info[j]; -+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE); - } - } - -@@ -935,27 +964,23 @@ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) - { - struct pci_dev *pdev = adapter->pdev; - struct atl1c_tpd_ring *tpd_ring = adapter->tpd_ring; -- struct atl1c_rfd_ring *rfd_ring = adapter->rfd_ring; -- struct atl1c_rrd_ring *rrd_ring = adapter->rrd_ring; -+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; -+ struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; - struct atl1c_ring_header *ring_header = &adapter->ring_header; -- int num_rx_queues = adapter->num_rx_queues; - int size; - int i; - int count = 0; - int rx_desc_count = 0; - u32 offset = 0; - -- rrd_ring[0].count = rfd_ring[0].count; -+ rrd_ring->count = rfd_ring->count; - for (i = 1; i < AT_MAX_TRANSMIT_QUEUE; i++) - tpd_ring[i].count = tpd_ring[0].count; - -- for (i = 1; i < adapter->num_rx_queues; i++) -- rfd_ring[i].count = rrd_ring[i].count = rfd_ring[0].count; -- - /* 2 tpd queue, one high priority queue, - * another normal priority queue */ - size = sizeof(struct atl1c_buffer) * (tpd_ring->count * 2 + -- rfd_ring->count * num_rx_queues); -+ rfd_ring->count); - tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL); - if (unlikely(!tpd_ring->buffer_info)) { - dev_err(&pdev->dev, "kzalloc failed, size = %d\n", -@@ -968,12 +993,11 @@ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) - count += tpd_ring[i].count; - } - -- for (i = 0; i < num_rx_queues; i++) { -- rfd_ring[i].buffer_info = -- (struct atl1c_buffer *) (tpd_ring->buffer_info + count); -- count += rfd_ring[i].count; -- rx_desc_count += rfd_ring[i].count; -- } -+ rfd_ring->buffer_info = -+ (struct atl1c_buffer *) (tpd_ring->buffer_info + count); -+ count += rfd_ring->count; -+ rx_desc_count += rfd_ring->count; -+ - /* - * real ring DMA buffer - * each ring/block may need up to 8 bytes for alignment, hence the -@@ -983,8 +1007,7 @@ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) - sizeof(struct atl1c_tpd_desc) * tpd_ring->count * 2 + - sizeof(struct atl1c_rx_free_desc) * rx_desc_count + - sizeof(struct atl1c_recv_ret_status) * rx_desc_count + -- sizeof(struct atl1c_hw_stats) + -- 8 * 4 + 8 * 2 * num_rx_queues; -+ 8 * 4; - - ring_header->desc = pci_alloc_consistent(pdev, ring_header->size, - &ring_header->dma); -@@ -1005,25 +1028,18 @@ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) - offset += roundup(tpd_ring[i].size, 8); - } - /* init RFD ring */ -- for (i = 0; i < num_rx_queues; i++) { -- rfd_ring[i].dma = ring_header->dma + offset; -- rfd_ring[i].desc = (u8 *) ring_header->desc + offset; -- rfd_ring[i].size = sizeof(struct atl1c_rx_free_desc) * -- rfd_ring[i].count; -- offset += roundup(rfd_ring[i].size, 8); -- } -+ rfd_ring->dma = ring_header->dma + offset; -+ rfd_ring->desc = (u8 *) ring_header->desc + offset; -+ rfd_ring->size = sizeof(struct atl1c_rx_free_desc) * rfd_ring->count; -+ offset += roundup(rfd_ring->size, 8); - - /* init RRD ring */ -- for (i = 0; i < num_rx_queues; i++) { -- rrd_ring[i].dma = ring_header->dma + offset; -- rrd_ring[i].desc = (u8 *) ring_header->desc + offset; -- rrd_ring[i].size = sizeof(struct atl1c_recv_ret_status) * -- rrd_ring[i].count; -- offset += roundup(rrd_ring[i].size, 8); -- } -+ rrd_ring->dma = ring_header->dma + offset; -+ rrd_ring->desc = (u8 *) ring_header->desc + offset; -+ rrd_ring->size = sizeof(struct atl1c_recv_ret_status) * -+ rrd_ring->count; -+ offset += roundup(rrd_ring->size, 8); - -- adapter->smb.dma = ring_header->dma + offset; -- adapter->smb.smb = (u8 *)ring_header->desc + offset; - return 0; - - err_nomem: -@@ -1034,26 +1050,20 @@ err_nomem: - static void atl1c_configure_des_ring(struct atl1c_adapter *adapter) - { - struct atl1c_hw *hw = &adapter->hw; -- struct atl1c_rfd_ring *rfd_ring = (struct atl1c_rfd_ring *) -- adapter->rfd_ring; -- struct atl1c_rrd_ring *rrd_ring = (struct atl1c_rrd_ring *) -- adapter->rrd_ring; -+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; -+ struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; - struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) - adapter->tpd_ring; -- struct atl1c_cmb *cmb = (struct atl1c_cmb *) &adapter->cmb; -- struct atl1c_smb *smb = (struct atl1c_smb *) &adapter->smb; -- int i; -- u32 data; - - /* TPD */ - AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI, - (u32)((tpd_ring[atl1c_trans_normal].dma & - AT_DMA_HI_ADDR_MASK) >> 32)); - /* just enable normal priority TX queue */ -- AT_WRITE_REG(hw, REG_NTPD_HEAD_ADDR_LO, -+ AT_WRITE_REG(hw, REG_TPD_PRI0_ADDR_LO, - (u32)(tpd_ring[atl1c_trans_normal].dma & - AT_DMA_LO_ADDR_MASK)); -- AT_WRITE_REG(hw, REG_HTPD_HEAD_ADDR_LO, -+ AT_WRITE_REG(hw, REG_TPD_PRI1_ADDR_LO, - (u32)(tpd_ring[atl1c_trans_high].dma & - AT_DMA_LO_ADDR_MASK)); - AT_WRITE_REG(hw, REG_TPD_RING_SIZE, -@@ -1062,31 +1072,21 @@ static void atl1c_configure_des_ring(struct atl1c_adapter *adapter) - - /* RFD */ - AT_WRITE_REG(hw, REG_RX_BASE_ADDR_HI, -- (u32)((rfd_ring[0].dma & AT_DMA_HI_ADDR_MASK) >> 32)); -- for (i = 0; i < adapter->num_rx_queues; i++) -- AT_WRITE_REG(hw, atl1c_rfd_addr_lo_regs[i], -- (u32)(rfd_ring[i].dma & AT_DMA_LO_ADDR_MASK)); -+ (u32)((rfd_ring->dma & AT_DMA_HI_ADDR_MASK) >> 32)); -+ AT_WRITE_REG(hw, REG_RFD0_HEAD_ADDR_LO, -+ (u32)(rfd_ring->dma & AT_DMA_LO_ADDR_MASK)); - - AT_WRITE_REG(hw, REG_RFD_RING_SIZE, -- rfd_ring[0].count & RFD_RING_SIZE_MASK); -+ rfd_ring->count & RFD_RING_SIZE_MASK); - AT_WRITE_REG(hw, REG_RX_BUF_SIZE, - adapter->rx_buffer_len & RX_BUF_SIZE_MASK); - - /* RRD */ -- for (i = 0; i < adapter->num_rx_queues; i++) -- AT_WRITE_REG(hw, atl1c_rrd_addr_lo_regs[i], -- (u32)(rrd_ring[i].dma & AT_DMA_LO_ADDR_MASK)); -+ AT_WRITE_REG(hw, REG_RRD0_HEAD_ADDR_LO, -+ (u32)(rrd_ring->dma & AT_DMA_LO_ADDR_MASK)); - AT_WRITE_REG(hw, REG_RRD_RING_SIZE, -- (rrd_ring[0].count & RRD_RING_SIZE_MASK)); -+ (rrd_ring->count & RRD_RING_SIZE_MASK)); - -- /* CMB */ -- AT_WRITE_REG(hw, REG_CMB_BASE_ADDR_LO, cmb->dma & AT_DMA_LO_ADDR_MASK); -- -- /* SMB */ -- AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_HI, -- (u32)((smb->dma & AT_DMA_HI_ADDR_MASK) >> 32)); -- AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_LO, -- (u32)(smb->dma & AT_DMA_LO_ADDR_MASK)); - if (hw->nic_type == athr_l2c_b) { - AT_WRITE_REG(hw, REG_SRAM_RXF_LEN, 0x02a0L); - AT_WRITE_REG(hw, REG_SRAM_TXF_LEN, 0x0100L); -@@ -1097,13 +1097,6 @@ static void atl1c_configure_des_ring(struct atl1c_adapter *adapter) - AT_WRITE_REG(hw, REG_TXF_WATER_MARK, 0); /* TX watermark, to enter l1 state.*/ - AT_WRITE_REG(hw, REG_RXD_DMA_CTRL, 0); /* RXD threshold.*/ - } -- if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d_2) { -- /* Power Saving for L2c_B */ -- AT_READ_REG(hw, REG_SERDES_LOCK, &data); -- data |= SERDES_MAC_CLK_SLOWDOWN; -- data |= SERDES_PYH_CLK_SLOWDOWN; -- AT_WRITE_REG(hw, REG_SERDES_LOCK, data); -- } - /* Load all of base address above */ - AT_WRITE_REG(hw, REG_LOAD_PTR, 1); - } -@@ -1111,32 +1104,26 @@ static void atl1c_configure_des_ring(struct atl1c_adapter *adapter) - static void atl1c_configure_tx(struct atl1c_adapter *adapter) - { - struct atl1c_hw *hw = &adapter->hw; -- u32 dev_ctrl_data; -- u32 max_pay_load; -+ int max_pay_load; - u16 tx_offload_thresh; - u32 txq_ctrl_data; -- u32 max_pay_load_data; - -- tx_offload_thresh = MAX_TX_OFFLOAD_THRESH; -+ tx_offload_thresh = MAX_TSO_FRAME_SIZE; - AT_WRITE_REG(hw, REG_TX_TSO_OFFLOAD_THRESH, - (tx_offload_thresh >> 3) & TX_TSO_OFFLOAD_THRESH_MASK); -- AT_READ_REG(hw, REG_DEVICE_CTRL, &dev_ctrl_data); -- max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT) & -- DEVICE_CTRL_MAX_PAYLOAD_MASK; -- hw->dmaw_block = min_t(u32, max_pay_load, hw->dmaw_block); -- max_pay_load = (dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT) & -- DEVICE_CTRL_MAX_RREQ_SZ_MASK; -+ max_pay_load = pcie_get_readrq(adapter->pdev) >> 8; - hw->dmar_block = min_t(u32, max_pay_load, hw->dmar_block); -- -- txq_ctrl_data = (hw->tpd_burst & TXQ_NUM_TPD_BURST_MASK) << -- TXQ_NUM_TPD_BURST_SHIFT; -- if (hw->ctrl_flags & ATL1C_TXQ_MODE_ENHANCE) -- txq_ctrl_data |= TXQ_CTRL_ENH_MODE; -- max_pay_load_data = (atl1c_pay_load_size[hw->dmar_block] & -- TXQ_TXF_BURST_NUM_MASK) << TXQ_TXF_BURST_NUM_SHIFT; -- if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) -- max_pay_load_data >>= 1; -- txq_ctrl_data |= max_pay_load_data; -+ /* -+ * if BIOS had changed the dam-read-max-length to an invalid value, -+ * restore it to default value -+ */ -+ if (hw->dmar_block < DEVICE_CTRL_MAXRRS_MIN) { -+ pcie_set_readrq(adapter->pdev, 128 << DEVICE_CTRL_MAXRRS_MIN); -+ hw->dmar_block = DEVICE_CTRL_MAXRRS_MIN; -+ } -+ txq_ctrl_data = -+ hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 ? -+ L2CB_TXQ_CFGV : L1C_TXQ_CFGV; - - AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data); - } -@@ -1151,34 +1138,13 @@ static void atl1c_configure_rx(struct atl1c_adapter *adapter) - - if (hw->ctrl_flags & ATL1C_RX_IPV6_CHKSUM) - rxq_ctrl_data |= IPV6_CHKSUM_CTRL_EN; -- if (hw->rss_type == atl1c_rss_ipv4) -- rxq_ctrl_data |= RSS_HASH_IPV4; -- if (hw->rss_type == atl1c_rss_ipv4_tcp) -- rxq_ctrl_data |= RSS_HASH_IPV4_TCP; -- if (hw->rss_type == atl1c_rss_ipv6) -- rxq_ctrl_data |= RSS_HASH_IPV6; -- if (hw->rss_type == atl1c_rss_ipv6_tcp) -- rxq_ctrl_data |= RSS_HASH_IPV6_TCP; -- if (hw->rss_type != atl1c_rss_disable) -- rxq_ctrl_data |= RRS_HASH_CTRL_EN; -- -- rxq_ctrl_data |= (hw->rss_mode & RSS_MODE_MASK) << -- RSS_MODE_SHIFT; -- rxq_ctrl_data |= (hw->rss_hash_bits & RSS_HASH_BITS_MASK) << -- RSS_HASH_BITS_SHIFT; -- if (hw->ctrl_flags & ATL1C_ASPM_CTRL_MON) -- rxq_ctrl_data |= (ASPM_THRUPUT_LIMIT_1M & -- ASPM_THRUPUT_LIMIT_MASK) << ASPM_THRUPUT_LIMIT_SHIFT; - -- AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); --} -- --static void atl1c_configure_rss(struct atl1c_adapter *adapter) --{ -- struct atl1c_hw *hw = &adapter->hw; -+ /* aspm for gigabit */ -+ if (hw->nic_type != athr_l1d_2 && (hw->device_id & 1) != 0) -+ rxq_ctrl_data = FIELD_SETX(rxq_ctrl_data, ASPM_THRUPUT_LIMIT, -+ ASPM_THRUPUT_LIMIT_100M); - -- AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab); -- AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu); -+ AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); - } - - static void atl1c_configure_dma(struct atl1c_adapter *adapter) -@@ -1186,36 +1152,11 @@ static void atl1c_configure_dma(struct atl1c_adapter *adapter) - struct atl1c_hw *hw = &adapter->hw; - u32 dma_ctrl_data; - -- dma_ctrl_data = DMA_CTRL_DMAR_REQ_PRI; -- if (hw->ctrl_flags & ATL1C_CMB_ENABLE) -- dma_ctrl_data |= DMA_CTRL_CMB_EN; -- if (hw->ctrl_flags & ATL1C_SMB_ENABLE) -- dma_ctrl_data |= DMA_CTRL_SMB_EN; -- else -- dma_ctrl_data |= MAC_CTRL_SMB_DIS; -- -- switch (hw->dma_order) { -- case atl1c_dma_ord_in: -- dma_ctrl_data |= DMA_CTRL_DMAR_IN_ORDER; -- break; -- case atl1c_dma_ord_enh: -- dma_ctrl_data |= DMA_CTRL_DMAR_ENH_ORDER; -- break; -- case atl1c_dma_ord_out: -- dma_ctrl_data |= DMA_CTRL_DMAR_OUT_ORDER; -- break; -- default: -- break; -- } -- -- dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) -- << DMA_CTRL_DMAR_BURST_LEN_SHIFT; -- dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) -- << DMA_CTRL_DMAW_BURST_LEN_SHIFT; -- dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK) -- << DMA_CTRL_DMAR_DLY_CNT_SHIFT; -- dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK) -- << DMA_CTRL_DMAW_DLY_CNT_SHIFT; -+ dma_ctrl_data = FIELDX(DMA_CTRL_RORDER_MODE, DMA_CTRL_RORDER_MODE_OUT) | -+ DMA_CTRL_RREQ_PRI_DATA | -+ FIELDX(DMA_CTRL_RREQ_BLEN, hw->dmar_block) | -+ FIELDX(DMA_CTRL_WDLY_CNT, DMA_CTRL_WDLY_CNT_DEF) | -+ FIELDX(DMA_CTRL_RDLY_CNT, DMA_CTRL_RDLY_CNT_DEF); - - AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data); - } -@@ -1230,52 +1171,53 @@ static int atl1c_stop_mac(struct atl1c_hw *hw) - u32 data; - - AT_READ_REG(hw, REG_RXQ_CTRL, &data); -- data &= ~(RXQ1_CTRL_EN | RXQ2_CTRL_EN | -- RXQ3_CTRL_EN | RXQ_CTRL_EN); -+ data &= ~RXQ_CTRL_EN; - AT_WRITE_REG(hw, REG_RXQ_CTRL, data); - - AT_READ_REG(hw, REG_TXQ_CTRL, &data); - data &= ~TXQ_CTRL_EN; -- AT_WRITE_REG(hw, REG_TWSI_CTRL, data); -+ AT_WRITE_REG(hw, REG_TXQ_CTRL, data); - -- atl1c_wait_until_idle(hw); -+ atl1c_wait_until_idle(hw, IDLE_STATUS_RXQ_BUSY | IDLE_STATUS_TXQ_BUSY); - - AT_READ_REG(hw, REG_MAC_CTRL, &data); - data &= ~(MAC_CTRL_TX_EN | MAC_CTRL_RX_EN); - AT_WRITE_REG(hw, REG_MAC_CTRL, data); - -- return (int)atl1c_wait_until_idle(hw); --} -- --static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw) --{ -- u32 data; -- -- AT_READ_REG(hw, REG_RXQ_CTRL, &data); -- switch (hw->adapter->num_rx_queues) { -- case 4: -- data |= (RXQ3_CTRL_EN | RXQ2_CTRL_EN | RXQ1_CTRL_EN); -- break; -- case 3: -- data |= (RXQ2_CTRL_EN | RXQ1_CTRL_EN); -- break; -- case 2: -- data |= RXQ1_CTRL_EN; -- break; -- default: -- break; -- } -- data |= RXQ_CTRL_EN; -- AT_WRITE_REG(hw, REG_RXQ_CTRL, data); -+ return (int)atl1c_wait_until_idle(hw, -+ IDLE_STATUS_TXMAC_BUSY | IDLE_STATUS_RXMAC_BUSY); - } - --static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw) -+static void atl1c_start_mac(struct atl1c_adapter *adapter) - { -- u32 data; -+ struct atl1c_hw *hw = &adapter->hw; -+ u32 mac, txq, rxq; -+ -+ hw->mac_duplex = adapter->link_duplex == FULL_DUPLEX ? true : false; -+ hw->mac_speed = adapter->link_speed == SPEED_1000 ? -+ atl1c_mac_speed_1000 : atl1c_mac_speed_10_100; -+ -+ AT_READ_REG(hw, REG_TXQ_CTRL, &txq); -+ AT_READ_REG(hw, REG_RXQ_CTRL, &rxq); -+ AT_READ_REG(hw, REG_MAC_CTRL, &mac); -+ -+ txq |= TXQ_CTRL_EN; -+ rxq |= RXQ_CTRL_EN; -+ mac |= MAC_CTRL_TX_EN | MAC_CTRL_TX_FLOW | -+ MAC_CTRL_RX_EN | MAC_CTRL_RX_FLOW | -+ MAC_CTRL_ADD_CRC | MAC_CTRL_PAD | -+ MAC_CTRL_BC_EN | MAC_CTRL_SINGLE_PAUSE_EN | -+ MAC_CTRL_HASH_ALG_CRC32; -+ if (hw->mac_duplex) -+ mac |= MAC_CTRL_DUPLX; -+ else -+ mac &= ~MAC_CTRL_DUPLX; -+ mac = FIELD_SETX(mac, MAC_CTRL_SPEED, hw->mac_speed); -+ mac = FIELD_SETX(mac, MAC_CTRL_PRMLEN, hw->preamble_len); - -- AT_READ_REG(hw, REG_TXQ_CTRL, &data); -- data |= TXQ_CTRL_EN; -- AT_WRITE_REG(hw, REG_TXQ_CTRL, data); -+ AT_WRITE_REG(hw, REG_TXQ_CTRL, txq); -+ AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq); -+ AT_WRITE_REG(hw, REG_MAC_CTRL, mac); - } - - /* -@@ -1287,10 +1229,7 @@ static int atl1c_reset_mac(struct atl1c_hw *hw) - { - struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; - struct pci_dev *pdev = adapter->pdev; -- u32 master_ctrl_data = 0; -- -- AT_WRITE_REG(hw, REG_IMR, 0); -- AT_WRITE_REG(hw, REG_ISR, ISR_DIS_INT); -+ u32 ctrl_data = 0; - - atl1c_stop_mac(hw); - /* -@@ -1299,194 +1238,148 @@ static int atl1c_reset_mac(struct atl1c_hw *hw) - * the current PCI configuration. The global reset bit is self- - * clearing, and should clear within a microsecond. - */ -- AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data); -- master_ctrl_data |= MASTER_CTRL_OOB_DIS_OFF; -- AT_WRITE_REGW(hw, REG_MASTER_CTRL, ((master_ctrl_data | MASTER_CTRL_SOFT_RST) -- & 0xFFFF)); -+ AT_READ_REG(hw, REG_MASTER_CTRL, &ctrl_data); -+ ctrl_data |= MASTER_CTRL_OOB_DIS; -+ AT_WRITE_REG(hw, REG_MASTER_CTRL, ctrl_data | MASTER_CTRL_SOFT_RST); - - AT_WRITE_FLUSH(hw); - msleep(10); - /* Wait at least 10ms for All module to be Idle */ - -- if (atl1c_wait_until_idle(hw)) { -+ if (atl1c_wait_until_idle(hw, IDLE_STATUS_MASK)) { - dev_err(&pdev->dev, - "MAC state machine can't be idle since" - " disabled for 10ms second\n"); - return -1; - } -+ AT_WRITE_REG(hw, REG_MASTER_CTRL, ctrl_data); -+ -+ /* driver control speed/duplex */ -+ AT_READ_REG(hw, REG_MAC_CTRL, &ctrl_data); -+ AT_WRITE_REG(hw, REG_MAC_CTRL, ctrl_data | MAC_CTRL_SPEED_MODE_SW); -+ -+ /* clk switch setting */ -+ AT_READ_REG(hw, REG_SERDES, &ctrl_data); -+ switch (hw->nic_type) { -+ case athr_l2c_b: -+ ctrl_data &= ~(SERDES_PHY_CLK_SLOWDOWN | -+ SERDES_MAC_CLK_SLOWDOWN); -+ AT_WRITE_REG(hw, REG_SERDES, ctrl_data); -+ break; -+ case athr_l2c_b2: -+ case athr_l1d_2: -+ ctrl_data |= SERDES_PHY_CLK_SLOWDOWN | SERDES_MAC_CLK_SLOWDOWN; -+ AT_WRITE_REG(hw, REG_SERDES, ctrl_data); -+ break; -+ default: -+ break; -+ } -+ - return 0; - } - - static void atl1c_disable_l0s_l1(struct atl1c_hw *hw) - { -- u32 pm_ctrl_data; -+ u16 ctrl_flags = hw->ctrl_flags; - -- AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data); -- pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK << -- PM_CTRL_L1_ENTRY_TIMER_SHIFT); -- pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1; -- pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; -- pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; -- pm_ctrl_data &= ~PM_CTRL_MAC_ASPM_CHK; -- pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1; -- -- pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN; -- pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN; -- pm_ctrl_data |= PM_CTRL_SERDES_L1_EN; -- AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data); -+ hw->ctrl_flags &= ~(ATL1C_ASPM_L0S_SUPPORT | ATL1C_ASPM_L1_SUPPORT); -+ atl1c_set_aspm(hw, SPEED_0); -+ hw->ctrl_flags = ctrl_flags; - } - - /* - * Set ASPM state. - * Enable/disable L0s/L1 depend on link state. - */ --static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup) -+static void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed) - { - u32 pm_ctrl_data; -- u32 link_ctrl_data; -- u32 link_l1_timer = 0xF; -+ u32 link_l1_timer; - - AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data); -- AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data); -+ pm_ctrl_data &= ~(PM_CTRL_ASPM_L1_EN | -+ PM_CTRL_ASPM_L0S_EN | -+ PM_CTRL_MAC_ASPM_CHK); -+ /* L1 timer */ -+ if (hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) { -+ pm_ctrl_data &= ~PMCTRL_TXL1_AFTER_L0S; -+ link_l1_timer = -+ link_speed == SPEED_1000 || link_speed == SPEED_100 ? -+ L1D_PMCTRL_L1_ENTRY_TM_16US : 1; -+ pm_ctrl_data = FIELD_SETX(pm_ctrl_data, -+ L1D_PMCTRL_L1_ENTRY_TM, link_l1_timer); -+ } else { -+ link_l1_timer = hw->nic_type == athr_l2c_b ? -+ L2CB1_PM_CTRL_L1_ENTRY_TM : L1C_PM_CTRL_L1_ENTRY_TM; -+ if (link_speed != SPEED_1000 && link_speed != SPEED_100) -+ link_l1_timer = 1; -+ pm_ctrl_data = FIELD_SETX(pm_ctrl_data, -+ PM_CTRL_L1_ENTRY_TIMER, link_l1_timer); -+ } - -- pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1; -- pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK << -- PM_CTRL_L1_ENTRY_TIMER_SHIFT); -- pm_ctrl_data &= ~(PM_CTRL_LCKDET_TIMER_MASK << -- PM_CTRL_LCKDET_TIMER_SHIFT); -- pm_ctrl_data |= AT_LCKDET_TIMER << PM_CTRL_LCKDET_TIMER_SHIFT; -+ /* L0S/L1 enable */ -+ if ((hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT) && link_speed != SPEED_0) -+ pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN | PM_CTRL_MAC_ASPM_CHK; -+ if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) -+ pm_ctrl_data |= PM_CTRL_ASPM_L1_EN | PM_CTRL_MAC_ASPM_CHK; - -+ /* l2cb & l1d & l2cb2 & l1d2 */ - if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d || -- hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) { -- link_ctrl_data &= ~LINK_CTRL_EXT_SYNC; -- if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE)) { -- if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10) -- link_ctrl_data |= LINK_CTRL_EXT_SYNC; -- } -- -- AT_WRITE_REG(hw, REG_LINK_CTRL, link_ctrl_data); -- -- pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER; -- pm_ctrl_data &= ~(PM_CTRL_PM_REQ_TIMER_MASK << -- PM_CTRL_PM_REQ_TIMER_SHIFT); -- pm_ctrl_data |= AT_ASPM_L1_TIMER << -- PM_CTRL_PM_REQ_TIMER_SHIFT; -- pm_ctrl_data &= ~PM_CTRL_SA_DLY_EN; -- pm_ctrl_data &= ~PM_CTRL_HOTRST; -- pm_ctrl_data |= 1 << PM_CTRL_L1_ENTRY_TIMER_SHIFT; -- pm_ctrl_data |= PM_CTRL_SERDES_PD_EX_L1; -- } -- pm_ctrl_data |= PM_CTRL_MAC_ASPM_CHK; -- if (linkup) { -- pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; -- pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; -- if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) -- pm_ctrl_data |= PM_CTRL_ASPM_L1_EN; -- if (hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT) -- pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN; -- -- if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d || -- hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) { -- if (hw->nic_type == athr_l2c_b) -- if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE)) -- pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; -- pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN; -- pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN; -- pm_ctrl_data &= ~PM_CTRL_SERDES_BUDS_RX_L1_EN; -- pm_ctrl_data |= PM_CTRL_CLK_SWH_L1; -- if (hw->adapter->link_speed == SPEED_100 || -- hw->adapter->link_speed == SPEED_1000) { -- pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK << -- PM_CTRL_L1_ENTRY_TIMER_SHIFT); -- if (hw->nic_type == athr_l2c_b) -- link_l1_timer = 7; -- else if (hw->nic_type == athr_l2c_b2 || -- hw->nic_type == athr_l1d_2) -- link_l1_timer = 4; -- pm_ctrl_data |= link_l1_timer << -- PM_CTRL_L1_ENTRY_TIMER_SHIFT; -- } -- } else { -- pm_ctrl_data |= PM_CTRL_SERDES_L1_EN; -- pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN; -- pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN; -- pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1; -+ hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) { -+ pm_ctrl_data = FIELD_SETX(pm_ctrl_data, -+ PM_CTRL_PM_REQ_TIMER, PM_CTRL_PM_REQ_TO_DEF); -+ pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER | -+ PM_CTRL_SERDES_PD_EX_L1 | -+ PM_CTRL_CLK_SWH_L1; -+ pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN | -+ PM_CTRL_SERDES_PLL_L1_EN | -+ PM_CTRL_SERDES_BUFS_RX_L1_EN | -+ PM_CTRL_SA_DLY_EN | -+ PM_CTRL_HOTRST); -+ /* disable l0s if link down or l2cb */ -+ if (link_speed == SPEED_0 || hw->nic_type == athr_l2c_b) - pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; -- pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; -- -+ } else { /* l1c */ -+ pm_ctrl_data = -+ FIELD_SETX(pm_ctrl_data, PM_CTRL_L1_ENTRY_TIMER, 0); -+ if (link_speed != SPEED_0) { -+ pm_ctrl_data |= PM_CTRL_SERDES_L1_EN | -+ PM_CTRL_SERDES_PLL_L1_EN | -+ PM_CTRL_SERDES_BUFS_RX_L1_EN; -+ pm_ctrl_data &= ~(PM_CTRL_SERDES_PD_EX_L1 | -+ PM_CTRL_CLK_SWH_L1 | -+ PM_CTRL_ASPM_L0S_EN | -+ PM_CTRL_ASPM_L1_EN); -+ } else { /* link down */ -+ pm_ctrl_data |= PM_CTRL_CLK_SWH_L1; -+ pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN | -+ PM_CTRL_SERDES_PLL_L1_EN | -+ PM_CTRL_SERDES_BUFS_RX_L1_EN | -+ PM_CTRL_ASPM_L0S_EN); - } -- } else { -- pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN; -- pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN; -- pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN; -- pm_ctrl_data |= PM_CTRL_CLK_SWH_L1; -- -- if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT) -- pm_ctrl_data |= PM_CTRL_ASPM_L1_EN; -- else -- pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN; - } - AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data); - - return; - } - --static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter) --{ -- struct atl1c_hw *hw = &adapter->hw; -- struct net_device *netdev = adapter->netdev; -- u32 mac_ctrl_data; -- -- mac_ctrl_data = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN; -- mac_ctrl_data |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); -- -- if (adapter->link_duplex == FULL_DUPLEX) { -- hw->mac_duplex = true; -- mac_ctrl_data |= MAC_CTRL_DUPLX; -- } -- -- if (adapter->link_speed == SPEED_1000) -- hw->mac_speed = atl1c_mac_speed_1000; -- else -- hw->mac_speed = atl1c_mac_speed_10_100; -- -- mac_ctrl_data |= (hw->mac_speed & MAC_CTRL_SPEED_MASK) << -- MAC_CTRL_SPEED_SHIFT; -- -- mac_ctrl_data |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); -- mac_ctrl_data |= ((hw->preamble_len & MAC_CTRL_PRMLEN_MASK) << -- MAC_CTRL_PRMLEN_SHIFT); -- -- __atl1c_vlan_mode(netdev->features, &mac_ctrl_data); -- -- mac_ctrl_data |= MAC_CTRL_BC_EN; -- if (netdev->flags & IFF_PROMISC) -- mac_ctrl_data |= MAC_CTRL_PROMIS_EN; -- if (netdev->flags & IFF_ALLMULTI) -- mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; -- -- mac_ctrl_data |= MAC_CTRL_SINGLE_PAUSE_EN; -- if (hw->nic_type == athr_l1d || hw->nic_type == athr_l2c_b2 || -- hw->nic_type == athr_l1d_2) { -- mac_ctrl_data |= MAC_CTRL_SPEED_MODE_SW; -- mac_ctrl_data |= MAC_CTRL_HASH_ALG_CRC32; -- } -- AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); --} -- - /* - * atl1c_configure - Configure Transmit&Receive Unit after Reset - * @adapter: board private structure - * - * Configure the Tx /Rx unit of the MAC after a reset. - */ --static int atl1c_configure(struct atl1c_adapter *adapter) -+static int atl1c_configure_mac(struct atl1c_adapter *adapter) - { - struct atl1c_hw *hw = &adapter->hw; - u32 master_ctrl_data = 0; - u32 intr_modrt_data; - u32 data; - -+ AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data); -+ master_ctrl_data &= ~(MASTER_CTRL_TX_ITIMER_EN | -+ MASTER_CTRL_RX_ITIMER_EN | -+ MASTER_CTRL_INT_RDCLR); - /* clear interrupt status */ - AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF); - /* Clear any WOL status */ -@@ -1525,30 +1418,39 @@ static int atl1c_configure(struct atl1c_adapter *adapter) - master_ctrl_data |= MASTER_CTRL_SA_TIMER_EN; - AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); - -- if (hw->ctrl_flags & ATL1C_CMB_ENABLE) { -- AT_WRITE_REG(hw, REG_CMB_TPD_THRESH, -- hw->cmb_tpd & CMB_TPD_THRESH_MASK); -- AT_WRITE_REG(hw, REG_CMB_TX_TIMER, -- hw->cmb_tx_timer & CMB_TX_TIMER_MASK); -- } -+ AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, -+ hw->smb_timer & SMB_STAT_TIMER_MASK); - -- if (hw->ctrl_flags & ATL1C_SMB_ENABLE) -- AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, -- hw->smb_timer & SMB_STAT_TIMER_MASK); - /* set MTU */ - AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN + - VLAN_HLEN + ETH_FCS_LEN); -- /* HDS, disable */ -- AT_WRITE_REG(hw, REG_HDS_CTRL, 0); - - atl1c_configure_tx(adapter); - atl1c_configure_rx(adapter); -- atl1c_configure_rss(adapter); - atl1c_configure_dma(adapter); - - return 0; - } - -+static int atl1c_configure(struct atl1c_adapter *adapter) -+{ -+ struct net_device *netdev = adapter->netdev; -+ int num; -+ -+ atl1c_init_ring_ptrs(adapter); -+ atl1c_set_multi(netdev); -+ atl1c_restore_vlan(adapter); -+ -+ num = atl1c_alloc_rx_buffer(adapter); -+ if (unlikely(num == 0)) -+ return -ENOMEM; -+ -+ if (atl1c_configure_mac(adapter)) -+ return -EIO; -+ -+ return 0; -+} -+ - static void atl1c_update_hw_stats(struct atl1c_adapter *adapter) - { - u16 hw_reg_addr = 0; -@@ -1635,16 +1537,11 @@ static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter, - struct pci_dev *pdev = adapter->pdev; - u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); - u16 hw_next_to_clean; -- u16 shift; -- u32 data; -+ u16 reg; - -- if (type == atl1c_trans_high) -- shift = MB_HTPD_CONS_IDX_SHIFT; -- else -- shift = MB_NTPD_CONS_IDX_SHIFT; -+ reg = type == atl1c_trans_high ? REG_TPD_PRI1_CIDX : REG_TPD_PRI0_CIDX; - -- AT_READ_REG(&adapter->hw, REG_MB_PRIO_CONS_IDX, &data); -- hw_next_to_clean = (data >> shift) & MB_PRIO_PROD_IDX_MASK; -+ AT_READ_REGW(&adapter->hw, reg, &hw_next_to_clean); - - while (next_to_clean != hw_next_to_clean) { - buffer_info = &tpd_ring->buffer_info[next_to_clean]; -@@ -1746,9 +1643,9 @@ static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter, - skb_checksum_none_assert(skb); - } - --static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid) -+static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter) - { -- struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[ringid]; -+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; - struct pci_dev *pdev = adapter->pdev; - struct atl1c_buffer *buffer_info, *next_info; - struct sk_buff *skb; -@@ -1800,7 +1697,7 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid - /* TODO: update mailbox here */ - wmb(); - rfd_ring->next_to_use = rfd_next_to_use; -- AT_WRITE_REG(&adapter->hw, atl1c_rfd_prod_idx_regs[ringid], -+ AT_WRITE_REG(&adapter->hw, REG_MB_RFD0_PROD_IDX, - rfd_ring->next_to_use & MB_RFDX_PROD_IDX_MASK); - } - -@@ -1839,7 +1736,7 @@ static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring, - rfd_ring->next_to_clean = rfd_index; - } - --static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, -+static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, - int *work_done, int work_to_do) - { - u16 rfd_num, rfd_index; -@@ -1847,8 +1744,8 @@ static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que, - u16 length; - struct pci_dev *pdev = adapter->pdev; - struct net_device *netdev = adapter->netdev; -- struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring[que]; -- struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring[que]; -+ struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; -+ struct atl1c_rrd_ring *rrd_ring = &adapter->rrd_ring; - struct sk_buff *skb; - struct atl1c_recv_ret_status *rrs; - struct atl1c_buffer *buffer_info; -@@ -1914,7 +1811,7 @@ rrs_checked: - count++; - } - if (count) -- atl1c_alloc_rx_buffer(adapter, que); -+ atl1c_alloc_rx_buffer(adapter); - } - - /* -@@ -1931,7 +1828,7 @@ static int atl1c_clean(struct napi_struct *napi, int budget) - if (!netif_carrier_ok(adapter->netdev)) - goto quit_polling; - /* just enable one RXQ */ -- atl1c_clean_rx_irq(adapter, 0, &work_done, budget); -+ atl1c_clean_rx_irq(adapter, &work_done, budget); - - if (work_done < budget) { - quit_polling: -@@ -2206,23 +2103,10 @@ static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb, - struct atl1c_tpd_desc *tpd, enum atl1c_trans_queue type) - { - struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; -- u32 prod_data; -+ u16 reg; - -- AT_READ_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, &prod_data); -- switch (type) { -- case atl1c_trans_high: -- prod_data &= 0xFFFF0000; -- prod_data |= tpd_ring->next_to_use & 0xFFFF; -- break; -- case atl1c_trans_normal: -- prod_data &= 0x0000FFFF; -- prod_data |= (tpd_ring->next_to_use & 0xFFFF) << 16; -- break; -- default: -- break; -- } -- wmb(); -- AT_WRITE_REG(&adapter->hw, REG_MB_PRIO_PROD_IDX, prod_data); -+ reg = type == atl1c_trans_high ? REG_TPD_PRI1_PIDX : REG_TPD_PRI0_PIDX; -+ AT_WRITE_REGW(&adapter->hw, reg, tpd_ring->next_to_use); - } - - static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb, -@@ -2307,8 +2191,7 @@ static int atl1c_request_irq(struct atl1c_adapter *adapter) - "Unable to allocate MSI interrupt Error: %d\n", - err); - adapter->have_msi = false; -- } else -- netdev->irq = pdev->irq; -+ } - - if (!adapter->have_msi) - flags |= IRQF_SHARED; -@@ -2328,44 +2211,38 @@ static int atl1c_request_irq(struct atl1c_adapter *adapter) - return err; - } - -+ -+static void atl1c_reset_dma_ring(struct atl1c_adapter *adapter) -+{ -+ /* release tx-pending skbs and reset tx/rx ring index */ -+ atl1c_clean_tx_ring(adapter, atl1c_trans_normal); -+ atl1c_clean_tx_ring(adapter, atl1c_trans_high); -+ atl1c_clean_rx_ring(adapter); -+} -+ - static int atl1c_up(struct atl1c_adapter *adapter) - { - struct net_device *netdev = adapter->netdev; -- int num; - int err; -- int i; - - netif_carrier_off(netdev); -- atl1c_init_ring_ptrs(adapter); -- atl1c_set_multi(netdev); -- atl1c_restore_vlan(adapter); - -- for (i = 0; i < adapter->num_rx_queues; i++) { -- num = atl1c_alloc_rx_buffer(adapter, i); -- if (unlikely(num == 0)) { -- err = -ENOMEM; -- goto err_alloc_rx; -- } -- } -- -- if (atl1c_configure(adapter)) { -- err = -EIO; -+ err = atl1c_configure(adapter); -+ if (unlikely(err)) - goto err_up; -- } - - err = atl1c_request_irq(adapter); - if (unlikely(err)) - goto err_up; - -+ atl1c_check_link_status(adapter); - clear_bit(__AT_DOWN, &adapter->flags); - napi_enable(&adapter->napi); - atl1c_irq_enable(adapter); -- atl1c_check_link_status(adapter); - netif_start_queue(netdev); - return err; - - err_up: --err_alloc_rx: - atl1c_clean_rx_ring(adapter); - return err; - } -@@ -2383,15 +2260,15 @@ static void atl1c_down(struct atl1c_adapter *adapter) - napi_disable(&adapter->napi); - atl1c_irq_disable(adapter); - atl1c_free_irq(adapter); -+ /* disable ASPM if device inactive */ -+ atl1c_disable_l0s_l1(&adapter->hw); - /* reset MAC to disable all RX/TX */ - atl1c_reset_mac(&adapter->hw); - msleep(1); - - adapter->link_speed = SPEED_0; - adapter->link_duplex = -1; -- atl1c_clean_tx_ring(adapter, atl1c_trans_normal); -- atl1c_clean_tx_ring(adapter, atl1c_trans_high); -- atl1c_clean_rx_ring(adapter); -+ atl1c_reset_dma_ring(adapter); - } - - /* -@@ -2424,13 +2301,6 @@ static int atl1c_open(struct net_device *netdev) - if (unlikely(err)) - goto err_up; - -- if (adapter->hw.ctrl_flags & ATL1C_FPGA_VERSION) { -- u32 phy_data; -- -- AT_READ_REG(&adapter->hw, REG_MDIO_CTRL, &phy_data); -- phy_data |= MDIO_AP_EN; -- AT_WRITE_REG(&adapter->hw, REG_MDIO_CTRL, phy_data); -- } - return 0; - - err_up: -@@ -2456,6 +2326,8 @@ static int atl1c_close(struct net_device *netdev) - struct atl1c_adapter *adapter = netdev_priv(netdev); - - WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); -+ set_bit(__AT_DOWN, &adapter->flags); -+ cancel_work_sync(&adapter->common_task); - atl1c_down(adapter); - atl1c_free_ring_resources(adapter); - return 0; -@@ -2467,10 +2339,6 @@ static int atl1c_suspend(struct device *dev) - struct net_device *netdev = pci_get_drvdata(pdev); - struct atl1c_adapter *adapter = netdev_priv(netdev); - struct atl1c_hw *hw = &adapter->hw; -- u32 mac_ctrl_data = 0; -- u32 master_ctrl_data = 0; -- u32 wol_ctrl_data = 0; -- u16 mii_intr_status_data = 0; - u32 wufc = adapter->wol; - - atl1c_disable_l0s_l1(hw); -@@ -2481,75 +2349,10 @@ static int atl1c_suspend(struct device *dev) - netif_device_detach(netdev); - - if (wufc) -- if (atl1c_phy_power_saving(hw) != 0) -+ if (atl1c_phy_to_ps_link(hw) != 0) - dev_dbg(&pdev->dev, "phy power saving failed"); - -- AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data); -- AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data); -- -- master_ctrl_data &= ~MASTER_CTRL_CLK_SEL_DIS; -- mac_ctrl_data &= ~(MAC_CTRL_PRMLEN_MASK << MAC_CTRL_PRMLEN_SHIFT); -- mac_ctrl_data |= (((u32)adapter->hw.preamble_len & -- MAC_CTRL_PRMLEN_MASK) << -- MAC_CTRL_PRMLEN_SHIFT); -- mac_ctrl_data &= ~(MAC_CTRL_SPEED_MASK << MAC_CTRL_SPEED_SHIFT); -- mac_ctrl_data &= ~MAC_CTRL_DUPLX; -- -- if (wufc) { -- mac_ctrl_data |= MAC_CTRL_RX_EN; -- if (adapter->link_speed == SPEED_1000 || -- adapter->link_speed == SPEED_0) { -- mac_ctrl_data |= atl1c_mac_speed_1000 << -- MAC_CTRL_SPEED_SHIFT; -- mac_ctrl_data |= MAC_CTRL_DUPLX; -- } else -- mac_ctrl_data |= atl1c_mac_speed_10_100 << -- MAC_CTRL_SPEED_SHIFT; -- -- if (adapter->link_duplex == DUPLEX_FULL) -- mac_ctrl_data |= MAC_CTRL_DUPLX; -- -- /* turn on magic packet wol */ -- if (wufc & AT_WUFC_MAG) -- wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN; -- -- if (wufc & AT_WUFC_LNKC) { -- wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; -- /* only link up can wake up */ -- if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) { -- dev_dbg(&pdev->dev, "%s: read write phy " -- "register failed.\n", -- atl1c_driver_name); -- } -- } -- /* clear phy interrupt */ -- atl1c_read_phy_reg(hw, MII_ISR, &mii_intr_status_data); -- /* Config MAC Ctrl register */ -- __atl1c_vlan_mode(netdev->features, &mac_ctrl_data); -- -- /* magic packet maybe Broadcast&multicast&Unicast frame */ -- if (wufc & AT_WUFC_MAG) -- mac_ctrl_data |= MAC_CTRL_BC_EN; -- -- dev_dbg(&pdev->dev, -- "%s: suspend MAC=0x%x\n", -- atl1c_driver_name, mac_ctrl_data); -- AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); -- AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data); -- AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); -- -- AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT | -- GPHY_CTRL_EXT_RESET); -- } else { -- AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_POWER_SAVING); -- master_ctrl_data |= MASTER_CTRL_CLK_SEL_DIS; -- mac_ctrl_data |= atl1c_mac_speed_10_100 << MAC_CTRL_SPEED_SHIFT; -- mac_ctrl_data |= MAC_CTRL_DUPLX; -- AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data); -- AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); -- AT_WRITE_REG(hw, REG_WOL_CTRL, 0); -- hw->phy_configured = false; /* re-init PHY when resume */ -- } -+ atl1c_power_saving(hw, wufc); - - return 0; - } -@@ -2562,8 +2365,7 @@ static int atl1c_resume(struct device *dev) - struct atl1c_adapter *adapter = netdev_priv(netdev); - - AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); -- atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE | -- ATL1C_PCIE_PHY_RESET); -+ atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE); - - atl1c_phy_reset(&adapter->hw); - atl1c_reset_mac(&adapter->hw); -@@ -2616,7 +2418,6 @@ static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev) - SET_NETDEV_DEV(netdev, &pdev->dev); - pci_set_drvdata(pdev, netdev); - -- netdev->irq = pdev->irq; - netdev->netdev_ops = &atl1c_netdev_ops; - netdev->watchdog_timeo = AT_TX_WATCHDOG; - atl1c_set_ethtool_ops(netdev); -@@ -2706,14 +2507,13 @@ static int __devinit atl1c_probe(struct pci_dev *pdev, - dev_err(&pdev->dev, "cannot map device registers\n"); - goto err_ioremap; - } -- netdev->base_addr = (unsigned long)adapter->hw.hw_addr; - - /* init mii data */ - adapter->mii.dev = netdev; - adapter->mii.mdio_read = atl1c_mdio_read; - adapter->mii.mdio_write = atl1c_mdio_write; - adapter->mii.phy_id_mask = 0x1f; -- adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK; -+ adapter->mii.reg_num_mask = MDIO_CTRL_REG_MASK; - netif_napi_add(netdev, &adapter->napi, atl1c_clean, 64); - setup_timer(&adapter->phy_config_timer, atl1c_phy_config, - (unsigned long)adapter); -@@ -2723,8 +2523,7 @@ static int __devinit atl1c_probe(struct pci_dev *pdev, - dev_err(&pdev->dev, "net device private data init failed\n"); - goto err_sw_init; - } -- atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE | -- ATL1C_PCIE_PHY_RESET); -+ atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE); - - /* Init GPHY as early as possible due to power saving issue */ - atl1c_phy_reset(&adapter->hw); -@@ -2752,7 +2551,7 @@ static int __devinit atl1c_probe(struct pci_dev *pdev, - dev_dbg(&pdev->dev, "mac address : %pM\n", - adapter->hw.mac_addr); - -- atl1c_hw_set_mac_addr(&adapter->hw); -+ atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr); - INIT_WORK(&adapter->common_task, atl1c_common_task); - adapter->work_event = 0; - err = register_netdev(netdev); -@@ -2796,6 +2595,8 @@ static void __devexit atl1c_remove(struct pci_dev *pdev) - struct atl1c_adapter *adapter = netdev_priv(netdev); - - unregister_netdev(netdev); -+ /* restore permanent address */ -+ atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.perm_mac_addr); - atl1c_phy_disable(&adapter->hw); - - iounmap(adapter->hw.hw_addr); diff --git a/config-arm-generic b/config-arm-generic index 80a7f2b87..98a11c133 100644 --- a/config-arm-generic +++ b/config-arm-generic @@ -138,6 +138,12 @@ CONFIG_RTC_DRV_PL031=y CONFIG_ARM_UNWIND=y CONFIG_RCU_FANOUT=32 +CONFIG_RCU_FANOUT_LEAF=8 + +# CONFIG_ARM_ARCH_TIMER is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_SERIAL_8250_EM is not set +# CONFIG_SND_SIMPLE_CARD is not set # CONFIG_USB_ULPI is not set # CONFIG_OC_ETM is not set diff --git a/config-arm-imx b/config-arm-imx index 493769814..d6bea108c 100644 --- a/config-arm-imx +++ b/config-arm-imx @@ -87,3 +87,13 @@ CONFIG_LEDS_RENESAS_TPU=y CONFIG_MFD_ANATOP=y +# CONFIG_MTD_NAND_GPMI_NAND is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_PINCTRL_IMX51 is not set +# CONFIG_GPIO_EM is not set +# CONFIG_FB_IMX is not set +# CONFIG_SND_SOC_IMX_SGTL5000 is not set +# CONFIG_RTC_DRV_MXC is not set +# CONFIG_COMMON_CLK_DEBUG is not set + diff --git a/config-arm-kirkwood b/config-arm-kirkwood index 3d3466bc4..c3d1891a2 100644 --- a/config-arm-kirkwood +++ b/config-arm-kirkwood @@ -52,3 +52,13 @@ CONFIG_CRYPTO_DEV_MV_CESA=m # CONFIG_VFPv3 is not set # CONFIG_NEON is not set # CONFIG_SMP is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MACH_ICONNECT_DT is not set +# CONFIG_MACH_DLINK_KIRKWOOD_DT is not set +# CONFIG_MACH_IB62X0_DT is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_TCM_QLA2XXX is not set +# CONFIG_GPIO_EM is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_COMMON_CLK_DEBUG is not set + diff --git a/config-arm-omap b/config-arm-omap index 283267c46..ae46b5a8b 100644 --- a/config-arm-omap +++ b/config-arm-omap @@ -1085,3 +1085,24 @@ CONFIG_USB_RENESAS_USBHS_HCD=m # CONFIG_VIDEO_AS3645A is not set # # CONFIG_TI_CPSW is not set +# CONFIG_SOC_TI81XX is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_MTD_NAND_OMAP_BCH is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_GPIO_EM is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_VIDEO_SMIAPP is not set +# CONFIG_PANEL_TFP410 is not set +# CONFIG_SND_OMAP_SOC_OMAP_HDMI is not set +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_OMAP is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_RENESAS_USBHS_UDC is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +# diff --git a/config-arm-tegra b/config-arm-tegra index ef4f53599..818008fae 100644 --- a/config-arm-tegra +++ b/config-arm-tegra @@ -97,3 +97,10 @@ CONFIG_SERIAL_OF_PLATFORM=y CONFIG_TEGRA_IOMMU_GART=y CONFIG_TEGRA_IOMMU_SMMU=y + +# CONFIG_TEGRA_AHB is not set +# CONFIG_TCM_QLA2XXX is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_GPIO_EM is not set +# CONFIG_SND_SOC_TEGRA_WM8753 is not set + diff --git a/config-generic b/config-generic index 4e79b1810..778535a0d 100644 --- a/config-generic +++ b/config-generic @@ -166,6 +166,7 @@ CONFIG_MLX4_INFINIBAND=m CONFIG_INFINIBAND_NES=m # CONFIG_INFINIBAND_NES_DEBUG is not set CONFIG_INFINIBAND_QIB=m +# CONFIG_INFINIBAND_OCRDMA is not set # # Executable file formats @@ -186,6 +187,8 @@ CONFIG_FW_LOADER=y # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_EXTRA_FIRMWARE="" +# CONFIG_CMA is not set + # CONFIG_SPI is not set # @@ -330,6 +333,7 @@ CONFIG_VIRTIO_BLK=m CONFIG_VIRTIO_PCI=y CONFIG_VIRTIO_BALLOON=m CONFIG_VIRTIO_MMIO=m +# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set CONFIG_VIRTIO_NET=m CONFIG_VMXNET3=m CONFIG_HW_RANDOM_VIRTIO=m @@ -472,6 +476,7 @@ CONFIG_SCSI_DC395x=m CONFIG_SCSI_DEBUG=m CONFIG_SCSI_DC390T=m CONFIG_SCSI_QLA_FC=m +CONFIG_TCM_QLA2XXX=m CONFIG_SCSI_QLA_ISCSI=m # CONFIG_SCSI_IPR is not set # CONFIG_SCSI_DPT_I2O is not set @@ -777,6 +782,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=m CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m CONFIG_NETFILTER_XT_TARGET_CT=m CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m CONFIG_NETFILTER_XT_TARGET_LED=m CONFIG_NETFILTER_XT_TARGET_LOG=m @@ -1031,6 +1037,8 @@ CONFIG_NET_SCH_MQPRIO=m CONFIG_NET_SCH_MULTIQ=m CONFIG_NET_SCH_CHOKE=m CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m CONFIG_NET_SCH_PLUG=m CONFIG_NET_CLS=y CONFIG_NET_CLS_ACT=y @@ -1231,12 +1239,15 @@ CONFIG_E1000=m CONFIG_E1000E=m CONFIG_IGB=m CONFIG_IGB_DCA=y +CONFIG_IGB_PTP=y CONFIG_IGBVF=m CONFIG_IXGB=m CONFIG_IXGBEVF=m CONFIG_IXGBE=m CONFIG_IXGBE_DCA=y CONFIG_IXGBE_DCB=y +CONFIG_IXGBE_HWMON=y +CONFIG_IXGBE_PTP=y # CONFIG_NET_VENDOR_I825XX is not set CONFIG_NET_VENDOR_MARVELL=y @@ -1335,6 +1346,10 @@ CONFIG_VIA_RHINE=m CONFIG_VIA_RHINE_MMIO=y CONFIG_VIA_VELOCITY=m +CONFIG_NET_VENDOR_WIZNET=y +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5300=m + CONFIG_NET_VENDOR_XIRCOM=y CONFIG_PCMCIA_XIRC2PS=m @@ -1387,6 +1402,7 @@ CONFIG_JME=m # CONFIG_IP1000=m CONFIG_MLX4_EN=m +CONFIG_MLX4_EN_DCB=y # CONFIG_MLX4_DEBUG is not set CONFIG_SFC=m CONFIG_SFC_MCDI_MON=y @@ -1503,6 +1519,7 @@ CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y CONFIG_BRCMSMAC=m CONFIG_BRCMFMAC=m CONFIG_BRCMFMAC_SDIO=y +CONFIG_BRCMFMAC_SDIO_OOB=y CONFIG_BRCMFMAC_USB=y # CONFIG_BRCMDBG is not set CONFIG_HERMES=m @@ -1585,6 +1602,7 @@ CONFIG_USB_NET_RNDIS_WLAN=m CONFIG_USB_NET_KALMIA=m CONFIG_USB_NET_QMI_WWAN=m CONFIG_USB_NET_SMSC75XX=m +# CONFIG_WL_TI is not set CONFIG_ZD1211RW=m # CONFIG_ZD1211RW_DEBUG is not set @@ -1607,6 +1625,7 @@ CONFIG_RTL8192DE=m CONFIG_MWIFIEX=m CONFIG_MWIFIEX_SDIO=m CONFIG_MWIFIEX_PCIE=m +CONFIG_MWIFIEX_USB=m # # Token Ring devices @@ -1866,6 +1885,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 CONFIG_INPUT_JOYDEV=m CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_MATRIXKMAP is not set CONFIG_INPUT_TABLET=y CONFIG_TABLET_USB_ACECAD=m @@ -1916,6 +1936,7 @@ CONFIG_KEYBOARD_ATKBD=y # CONFIG_KEYBOARD_STOWAWAY is not set # CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set # CONFIG_KEYBOARD_ADP5588 is not set # CONFIG_KEYBOARD_MAX7359 is not set # CONFIG_KEYBOARD_ADP5589 is not set @@ -1998,6 +2019,7 @@ CONFIG_TOUCHSCREEN_TOUCHWIN=m CONFIG_TOUCHSCREEN_PIXCIR=m CONFIG_TOUCHSCREEN_UCB1400=m CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_WACOM_I2C=m CONFIG_TOUCHSCREEN_USB_E2I=y CONFIG_TOUCHSCREEN_USB_COMPOSITE=m # CONFIG_TOUCHSCREEN_WM97XX is not set @@ -2252,6 +2274,7 @@ CONFIG_SENSORS_WM8350=m CONFIG_SENSORS_WM831X=m CONFIG_SENSORS_LM73=m CONFIG_SENSORS_AMC6821=m +CONFIG_SENSORS_INA2XX=m CONFIG_SENSORS_ADT7411=m CONFIG_SENSORS_ASC7621=m CONFIG_SENSORS_EMC1403=m @@ -2291,6 +2314,7 @@ CONFIG_SENSORS_MAX1668=m # CONFIG_HMC6352 is not set # CONFIG_BMP085 is not set +# CONFIG_BMP085_I2C is not set # CONFIG_PCH_PHUB is not set # CONFIG_SERIAL_PCH_UART is not set # CONFIG_USB_SWITCH_FSA9480 is not set @@ -2430,12 +2454,15 @@ CONFIG_VGA_ARB_MAX_GPUS=16 CONFIG_DRM=m # CONFIG_DRM_LOAD_EDID_FIRMWARE is not set +# CONFIG_DRM_AST is not set +# CONFIG_DRM_CIRRUS_QEMU is not set # CONFIG_DRM_TDFX is not set # CONFIG_DRM_R128 is not set CONFIG_DRM_RADEON=m CONFIG_DRM_RADEON_KMS=y # CONFIG_DRM_I810 is not set # CONFIG_DRM_MGA is not set +# CONFIG_DRM_MGAG200 is not set # CONFIG_DRM_SIS is not set # CONFIG_DRM_SAVAGE is not set CONFIG_DRM_I915=m @@ -2718,6 +2745,7 @@ CONFIG_FB=y # CONFIG_FB_ATY_CT is not set # CONFIG_FB_ATY_GX is not set # CONFIG_FB_ASILIANT is not set +# CONFIG_FB_AUO_K190X is not set # CONFIG_FB_CARMINE is not set # CONFIG_FB_CIRRUS is not set # CONFIG_FB_CYBER2000 is not set @@ -3038,7 +3066,8 @@ CONFIG_USB_HID=y CONFIG_HID_SUPPORT=y -CONFIG_HID=m +CONFIG_HID=y +CONFIG_HID_BATTERY_STRENGTH=y # debugging default is y upstream now CONFIG_HIDRAW=y CONFIG_HID_PID=y @@ -3103,6 +3132,8 @@ CONFIG_HID_WIIMOTE_EXT=y CONFIG_HID_KYE=m CONFIG_HID_SAITEK=m CONFIG_HID_TIVO=m +CONFIG_HID_GENERIC=y +CONFIG_HID_AUREAL=m # # USB Imaging devices @@ -3214,6 +3245,7 @@ CONFIG_USB_EPSON2888=y CONFIG_USB_KC2190=y # CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_CHIPIDEA is not set # # USB port drivers @@ -3290,6 +3322,7 @@ CONFIG_USB_SERIAL_QCAUX=m CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m CONFIG_USB_SERIAL_DEBUG=m CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m CONFIG_USB_SERIAL_CONSOLE=y @@ -3351,6 +3384,8 @@ CONFIG_USB_ZERO=m CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +# CONFIG_USB_ISP1301 is not set + # CONFIG_USB_OTG is not set # @@ -3389,6 +3424,7 @@ CONFIG_MFD_WM8400=m # CONFIG_MFD_WM8994 is not set # CONFIG_MFD_88PM860X is not set # CONFIG_LPC_SCH is not set +# CONFIG_LPC_ICH is not set # CONFIG_HTC_I2CPLD is not set # CONFIG_MFD_MAX8925 is not set # CONFIG_MFD_ASIC3 is not set @@ -3405,6 +3441,8 @@ CONFIG_MFD_WM8400=m # CONFIG_MFD_TC3589X is not set # CONFIG_MFD_WL1273_CORE is not set # CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_MC13XXX_I2C is not set # # File systems @@ -3541,6 +3579,7 @@ CONFIG_CUSE=m # CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m +CONFIG_NFS_V2=y CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y @@ -3675,6 +3714,17 @@ CONFIG_NLS_ISO8859_14=m CONFIG_NLS_ISO8859_15=m CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m +CONFIG_NLS_MAC_ROMAN=m +CONFIG_NLS_MAC_CELTIC=m +CONFIG_NLS_MAC_CENTEURO=m +CONFIG_NLS_MAC_CROATIAN=m +CONFIG_NLS_MAC_CYRILLIC=m +CONFIG_NLS_MAC_GAELIC=m +CONFIG_NLS_MAC_GREEK=m +CONFIG_NLS_MAC_ICELAND=m +CONFIG_NLS_MAC_INUIT=m +CONFIG_NLS_MAC_ROMANIAN=m +CONFIG_NLS_MAC_TURKISH=m CONFIG_NLS_UTF8=m CONFIG_NLS_ASCII=y @@ -3697,6 +3747,7 @@ CONFIG_FRAME_POINTER=y # CONFIG_DEBUG_DRIVER is not set CONFIG_HEADERS_CHECK=y # CONFIG_LKDTM is not set +# CONFIG_READABLE_ASM is not set # CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_LOCKDEP is not set @@ -3730,6 +3781,7 @@ CONFIG_LOCKUP_DETECTOR=y CONFIG_ATOMIC64_SELFTEST=y CONFIG_MEMORY_FAILURE=y CONFIG_HWPOISON_INJECT=m +CONFIG_CROSS_MEMORY_ATTACH=y # CONFIG_DEBUG_SECTION_MISMATCH is not set # CONFIG_BACKTRACE_SELF_TEST is not set CONFIG_LATENCYTOP=y @@ -3862,6 +3914,7 @@ CONFIG_CRC_T10DIF=m CONFIG_CRC8=m # CONFIG_CRC7 is not set CONFIG_CORDIC=m +# CONFIG_DDR is not set CONFIG_CRYPTO_ZLIB=m CONFIG_ZLIB_INFLATE=y @@ -3948,6 +4001,8 @@ CONFIG_PM_TRACE_RTC=y # CONFIG_PM_TEST_SUSPEND is not set CONFIG_PM_RUNTIME=y # CONFIG_PM_OPP is not set +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_GOV_PERFORMANCE=y @@ -4022,6 +4077,7 @@ CONFIG_LEDS_TRIGGER_IDE_DISK=y CONFIG_LEDS_TRIGGER_HEARTBEAT=m CONFIG_LEDS_TRIGGER_BACKLIGHT=m CONFIG_LEDS_TRIGGER_DEFAULT_ON=m +CONFIG_LEDS_TRIGGER_TRANSIENT=m CONFIG_LEDS_ALIX2=m CONFIG_LEDS_CLEVO_MAIL=m CONFIG_LEDS_INTEL_SS4200=m @@ -4082,7 +4138,7 @@ CONFIG_CFAG12864B_RATE=20 # CONFIG_PHANTOM is not set # CONFIG_INTEL_MID_PTI is not set -CONFIG_POWER_SUPPLY=m +CONFIG_POWER_SUPPLY=y # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_TEST_POWER is not set @@ -4197,6 +4253,8 @@ CONFIG_USB_WUSB_CBAF=m # CONFIG_USB_WUSB_CBAF_DEBUG is not set CONFIG_USB_WHCI_HCD=m CONFIG_USB_HWA_HCD=m +# CONFIG_USB_HCD_BCMA is not set +# CONFIG_USB_HCD_SSB is not set CONFIG_UWB=m CONFIG_UWB_HWA=m @@ -4267,6 +4325,8 @@ CONFIG_ALTERA_STAPL=m # CONFIG_ZSMALLOC is not set # CONFIG_RAMSTER is not set # CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_IPACK_BUS is not set # # END OF STAGING @@ -4307,6 +4367,12 @@ CONFIG_IEEE802154=m CONFIG_IEEE802154_6LOWPAN=m CONFIG_IEEE802154_DRIVERS=m CONFIG_IEEE802154_FAKEHARD=m +CONFIG_IEEE802154_FAKELB=m + +CONFIG_MAC802154=m + +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set CONFIG_PPS=m # CONFIG_PPS_CLIENT_KTIMER is not set @@ -4321,6 +4387,7 @@ CONFIG_PTP_1588_CLOCK=m CONFIG_PTP_1588_CLOCK_PCH=m CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y # CONFIG_MDIO_GPIO is not set # CONFIG_KEYBOARD_GPIO is not set @@ -4362,6 +4429,7 @@ CONFIG_TEST_KSTRTOX=y CONFIG_TARGET_CORE=m CONFIG_ISCSI_TARGET=m CONFIG_LOOPBACK_TARGET=m +CONFIG_SBP_TARGET=m CONFIG_TCM_IBLOCK=m CONFIG_TCM_FILEIO=m CONFIG_TCM_PSCSI=m @@ -4370,6 +4438,7 @@ CONFIG_TCM_FC=m CONFIG_HWSPINLOCK=m CONFIG_PSTORE=y +CONFIG_PSTORE_RAM=m # CONFIG_AVERAGE is not set diff --git a/config-powerpc-generic b/config-powerpc-generic index 8045c06e7..366756bd5 100644 --- a/config-powerpc-generic +++ b/config-powerpc-generic @@ -359,6 +359,9 @@ CONFIG_RFKILL_GPIO=m # CONFIG_INPUT_GPIO_TILT_POLLED is not set CONFIG_STRICT_DEVMEM=y +CONFIG_RCU_FANOUT_LEAF=16 + # CONFIG_IRQ_DOMAIN_DEBUG is not set # CONFIG_MPIC_MSGR is not set # CONFIG_FA_DUMP is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set diff --git a/config-powerpc64 b/config-powerpc64 index 2d5a7529b..5be9caaf3 100644 --- a/config-powerpc64 +++ b/config-powerpc64 @@ -1,6 +1,8 @@ +CONFIG_WINDFARM_PM72=y CONFIG_WINDFARM_PM81=y CONFIG_WINDFARM_PM91=y CONFIG_WINDFARM_PM121=y +CONFIG_WINDFARM_RM31=y CONFIG_PPC_PMAC64=y CONFIG_PPC_MAPLE=y # CONFIG_PPC_CELL is not set @@ -161,6 +163,10 @@ CONFIG_PPC_ICSWX=y CONFIG_IO_EVENT_IRQ=y CONFIG_HW_RANDOM_AMD=m +# CONFIG_HW_RANDOM_PSERIES is not set +# CONFIG_CRYPTO_DEV_NX is not set + + CONFIG_BPF_JIT=y # CONFIG_PPC_ICSWX_PID is not set # CONFIG_PPC_ICSWX_USE_SIGILL is not set diff --git a/config-s390x b/config-s390x index 848709dfb..451512e9a 100644 --- a/config-s390x +++ b/config-s390x @@ -211,6 +211,7 @@ CONFIG_CHSC_SCH=m CONFIG_HVC_IUCV=y CONFIG_RCU_FANOUT=64 +CONFIG_RCU_FANOUT_LEAF=16 CONFIG_SECCOMP=y diff --git a/config-sparc64-generic b/config-sparc64-generic index 2b29f9b7e..a2b72dfe6 100644 --- a/config-sparc64-generic +++ b/config-sparc64-generic @@ -173,6 +173,7 @@ CONFIG_LEDS_SUNFIRE=m CONFIG_TADPOLE_TS102_UCTRL=m CONFIG_RCU_FANOUT=64 +CONFIG_RCU_FANOUT_LEAF=16 CONFIG_LIRC_ENE0100=m # CONFIG_BATTERY_DS2782 is not set @@ -196,3 +197,5 @@ CONFIG_CRYPTO_DEV_NIAGARA2=y # CONFIG_MTD_PHYSMAP_OF is not set # CONFIG_MMC_SDHCI_OF is not set # CONFIG_OF_SELFTEST is not set + +CONFIG_BPF_JIT=y diff --git a/config-x86-32-generic b/config-x86-32-generic index 6eb6269ff..1c7522462 100644 --- a/config-x86-32-generic +++ b/config-x86-32-generic @@ -27,6 +27,7 @@ CONFIG_M686=y # CONFIG_MWINCHIP3D is not set # CONFIG_MCYRIXIII is not set # CONFIG_MVIAC3_2 is not set +# CONFIG_STA2X11 is not set CONFIG_NR_CPUS=32 CONFIG_X86_GENERIC=y @@ -212,5 +213,6 @@ CONFIG_I2O_BUS=m # CONFIG_INPUT_GPIO_TILT_POLLED is not set # CONFIG_GEOS is not set # CONFIG_NET5501 is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set # CONFIG_GPIO_SODAVILLE is not set # CONFIG_BACKLIGHT_OT200 is not set diff --git a/config-x86-generic b/config-x86-generic index bddec1644..4fab1ceef 100644 --- a/config-x86-generic +++ b/config-x86-generic @@ -222,6 +222,7 @@ CONFIG_XO15_EBOOK=m # CONFIG_SMSC37B787_WDT is not set CONFIG_W83697HF_WDT=m CONFIG_VIA_WDT=m +CONFIG_IE6XX_WDT=m CONFIG_CRASH_DUMP=y CONFIG_PROC_VMCORE=y @@ -354,6 +355,9 @@ CONFIG_TOSHIBA_BT_RFKILL=m CONFIG_VGA_SWITCHEROO=y CONFIG_LPC_SCH=m +CONFIG_LPC_ICH=m + +CONFIG_GPIO_ICH=m CONFIG_PCI_CNB20LE_QUIRK=y @@ -409,7 +413,10 @@ CONFIG_DRM_GMA500=m # CONFIG_DRM_GMA600 is not set CONFIG_DRM_GMA3600=y +CONFIG_RCU_FANOUT_LEAF=16 + # Maybe enable in debug kernels? # CONFIG_DEBUG_NMI_SELFTEST is not set CONFIG_APPLE_GMUX=m +CONFIG_INTEL_MEI=m diff --git a/drm-edid-Make-the-header-fixup-threshold-tunable.patch b/drm-edid-Make-the-header-fixup-threshold-tunable.patch deleted file mode 100644 index 0743f8bdc..000000000 --- a/drm-edid-Make-the-header-fixup-threshold-tunable.patch +++ /dev/null @@ -1,60 +0,0 @@ -From b6e2dca9522bfe0f5d54051b00c2ffd11fbf3204 Mon Sep 17 00:00:00 2001 -From: Adam Jackson -Date: Wed, 30 May 2012 16:42:39 -0400 -Subject: [PATCH] drm/edid: Make the header fixup threshold tunable - -6 bytes seems to be a reasonable default so far, but for the desperate -it's worth exposing this. - -[airlied: change include to module.h for this] - -Bugzilla: https://bugzilla.redhat.com/582559 -Signed-off-by: Adam Jackson -Signed-off-by: Dave Airlie ---- - drivers/gpu/drm/drm_edid.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c -index 5a18b0d..0a407dbc 100644 ---- a/drivers/gpu/drm/drm_edid.c -+++ b/drivers/gpu/drm/drm_edid.c -@@ -30,7 +30,7 @@ - #include - #include - #include --#include -+#include - #include "drmP.h" - #include "drm_edid.h" - #include "drm_edid_modes.h" -@@ -145,6 +145,11 @@ int drm_edid_header_is_valid(const u8 *raw_edid) - EXPORT_SYMBOL(drm_edid_header_is_valid); - - -+static int edid_fixup __read_mostly = 6; -+module_param_named(edid_fixup, edid_fixup, int, 0400); -+MODULE_PARM_DESC(edid_fixup, -+ "Minimum number of valid EDID header bytes (0-8, default 6)"); -+ - /* - * Sanity check the EDID block (base or extension). Return 0 if the block - * doesn't check out, or 1 if it's valid. -@@ -155,10 +160,13 @@ bool drm_edid_block_valid(u8 *raw_edid) - u8 csum = 0; - struct edid *edid = (struct edid *)raw_edid; - -+ if (edid_fixup > 8 || edid_fixup < 0) -+ edid_fixup = 6; -+ - if (raw_edid[0] == 0x00) { - int score = drm_edid_header_is_valid(raw_edid); - if (score == 8) ; -- else if (score >= 6) { -+ else if (score >= edid_fixup) { - DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); - memcpy(raw_edid, edid_header, sizeof(edid_header)); - } else { --- -1.7.10.2 - diff --git a/drm-i915-lvds-dual-channel.patch b/drm-i915-lvds-dual-channel.patch deleted file mode 100644 index b2ddff04a..000000000 --- a/drm-i915-lvds-dual-channel.patch +++ /dev/null @@ -1,161 +0,0 @@ -From b03543857fd75876b96e10d4320b775e95041bb7 Mon Sep 17 00:00:00 2001 -From: Takashi Iwai -Date: Tue, 20 Mar 2012 12:07:05 +0000 -Subject: drm/i915: Check VBIOS value for determining LVDS dual channel mode, too - -Currently i915 driver checks [PCH_]LVDS register bits to decide -whether to set up the dual-link or the single-link mode. This relies -implicitly on that BIOS initializes the register properly at boot. -However, BIOS doesn't initialize it always. When the machine is -booted with the closed lid, BIOS skips the LVDS reg initialization. -This ends up in blank output on a machine with a dual-link LVDS when -you open the lid after the boot. - -This patch adds a workaround for that problem by checking the initial -LVDS register value in VBT. - -Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=37742 -Tested-By: Paulo Zanoni -Reviewed-by: Rodrigo Vivi -Reviewed-by: Adam Jackson -Signed-off-by: Takashi Iwai -Signed-off-by: Daniel Vetter ---- -diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h -index b6098b0..4cbed7f 100644 ---- a/drivers/gpu/drm/i915/i915_drv.h -+++ b/drivers/gpu/drm/i915/i915_drv.h -@@ -406,6 +406,8 @@ typedef struct drm_i915_private { - unsigned int lvds_use_ssc:1; - unsigned int display_clock_mode:1; - int lvds_ssc_freq; -+ unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ -+ unsigned int lvds_val; /* used for checking LVDS channel mode */ - struct { - int rate; - int lanes; -diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c -index 0ae76d6..e4317da 100644 ---- a/drivers/gpu/drm/i915/intel_bios.c -+++ b/drivers/gpu/drm/i915/intel_bios.c -@@ -173,6 +173,28 @@ get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data, - return (struct lvds_dvo_timing *)(entry + dvo_timing_offset); - } - -+/* get lvds_fp_timing entry -+ * this function may return NULL if the corresponding entry is invalid -+ */ -+static const struct lvds_fp_timing * -+get_lvds_fp_timing(const struct bdb_header *bdb, -+ const struct bdb_lvds_lfp_data *data, -+ const struct bdb_lvds_lfp_data_ptrs *ptrs, -+ int index) -+{ -+ size_t data_ofs = (const u8 *)data - (const u8 *)bdb; -+ u16 data_size = ((const u16 *)data)[-1]; /* stored in header */ -+ size_t ofs; -+ -+ if (index >= ARRAY_SIZE(ptrs->ptr)) -+ return NULL; -+ ofs = ptrs->ptr[index].fp_timing_offset; -+ if (ofs < data_ofs || -+ ofs + sizeof(struct lvds_fp_timing) > data_ofs + data_size) -+ return NULL; -+ return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs); -+} -+ - /* Try to find integrated panel data */ - static void - parse_lfp_panel_data(struct drm_i915_private *dev_priv, -@@ -182,6 +204,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, - const struct bdb_lvds_lfp_data *lvds_lfp_data; - const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; - const struct lvds_dvo_timing *panel_dvo_timing; -+ const struct lvds_fp_timing *fp_timing; - struct drm_display_mode *panel_fixed_mode; - int i, downclock; - -@@ -243,6 +266,19 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, - "Normal Clock %dKHz, downclock %dKHz\n", - panel_fixed_mode->clock, 10*downclock); - } -+ -+ fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data, -+ lvds_lfp_data_ptrs, -+ lvds_options->panel_type); -+ if (fp_timing) { -+ /* check the resolution, just to be sure */ -+ if (fp_timing->x_res == panel_fixed_mode->hdisplay && -+ fp_timing->y_res == panel_fixed_mode->vdisplay) { -+ dev_priv->bios_lvds_val = fp_timing->lvds_reg_val; -+ DRM_DEBUG_KMS("VBT initial LVDS value %x\n", -+ dev_priv->bios_lvds_val); -+ } -+ } - } - - /* Try to find sdvo panel data */ -diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c -index 683002fb..a76ac2e 100644 ---- a/drivers/gpu/drm/i915/intel_display.c -+++ b/drivers/gpu/drm/i915/intel_display.c -@@ -360,6 +360,27 @@ static const intel_limit_t intel_limits_ironlake_display_port = { - .find_pll = intel_find_pll_ironlake_dp, - }; - -+static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, -+ unsigned int reg) -+{ -+ unsigned int val; -+ -+ if (dev_priv->lvds_val) -+ val = dev_priv->lvds_val; -+ else { -+ /* BIOS should set the proper LVDS register value at boot, but -+ * in reality, it doesn't set the value when the lid is closed; -+ * we need to check "the value to be set" in VBT when LVDS -+ * register is uninitialized. -+ */ -+ val = I915_READ(reg); -+ if (!(val & ~LVDS_DETECTED)) -+ val = dev_priv->bios_lvds_val; -+ dev_priv->lvds_val = val; -+ } -+ return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; -+} -+ - static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, - int refclk) - { -@@ -368,8 +389,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, - const intel_limit_t *limit; - - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { -- if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == -- LVDS_CLKB_POWER_UP) { -+ if (is_dual_link_lvds(dev_priv, PCH_LVDS)) { - /* LVDS dual channel */ - if (refclk == 100000) - limit = &intel_limits_ironlake_dual_lvds_100m; -@@ -397,8 +417,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) - const intel_limit_t *limit; - - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { -- if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == -- LVDS_CLKB_POWER_UP) -+ if (is_dual_link_lvds(dev_priv, LVDS)) - /* LVDS with dual channel */ - limit = &intel_limits_g4x_dual_channel_lvds; - else -@@ -536,8 +555,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, - * reliably set up different single/dual channel state, if we - * even can. - */ -- if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == -- LVDS_CLKB_POWER_UP) -+ if (is_dual_link_lvds(dev_priv, LVDS)) - clock.p2 = limit->p2.p2_fast; - else - clock.p2 = limit->p2.p2_slow; --- -cgit v0.9.0.2-2-gbebe diff --git a/highbank-secure-smc.patch b/highbank-secure-smc.patch deleted file mode 100644 index d9d93148d..000000000 --- a/highbank-secure-smc.patch +++ /dev/null @@ -1,104 +0,0 @@ -Linux runs in non-secure mode on highbank, so we need secure monitor calls -to enable and disable the PL310. Rather than invent new smc calls, the same -calling convention used by OMAP is used here. - -Signed-off-by: Rob Herring ---- - arch/arm/mach-highbank/Makefile | 6 +++++- - arch/arm/mach-highbank/core.h | 1 + - arch/arm/mach-highbank/highbank.c | 14 ++++++++++++++ - arch/arm/mach-highbank/smc.S | 29 +++++++++++++++++++++++++++++ - 4 files changed, 49 insertions(+), 1 deletion(-) - create mode 100644 arch/arm/mach-highbank/smc.S - - - -diff --git a/arch/arm/mach-highbank/Makefile b/arch/arm/mach-highbank/Makefile -index f8437dd..ded4652 100644 ---- a/arch/arm/mach-highbank/Makefile -+++ b/arch/arm/mach-highbank/Makefile -@@ -1,4 +1,8 @@ --obj-y := clock.o highbank.o system.o -+obj-y := clock.o highbank.o system.o smc.o -+ -+plus_sec := $(call as-instr,.arch_extension sec,+sec) -+AFLAGS_smc.o :=-Wa,-march=armv7-a$(plus_sec) -+ - obj-$(CONFIG_DEBUG_HIGHBANK_UART) += lluart.o - obj-$(CONFIG_SMP) += platsmp.o - obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -diff --git a/arch/arm/mach-highbank/core.h b/arch/arm/mach-highbank/core.h -index d8e2d0b..141ed51 100644 ---- a/arch/arm/mach-highbank/core.h -+++ b/arch/arm/mach-highbank/core.h -@@ -8,3 +8,4 @@ extern void highbank_lluart_map_io(void); - static inline void highbank_lluart_map_io(void) {} - #endif - -+extern void highbank_smc1(int fn, int arg); -diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c -index 410a112..8777612 100644 ---- a/arch/arm/mach-highbank/highbank.c -+++ b/arch/arm/mach-highbank/highbank.c -@@ -85,10 +85,24 @@ const static struct of_device_id irq_match[] = { - {} - }; - -+#ifdef CONFIG_CACHE_L2X0 -+static void highbank_l2x0_disable(void) -+{ -+ /* Disable PL310 L2 Cache controller */ -+ highbank_smc1(0x102, 0x0); -+} -+#endif -+ - static void __init highbank_init_irq(void) - { - of_irq_init(irq_match); -+ -+#ifdef CONFIG_CACHE_L2X0 -+ /* Enable PL310 L2 Cache controller */ -+ highbank_smc1(0x102, 0x1); - l2x0_of_init(0, ~0UL); -+ outer_cache.disable = highbank_l2x0_disable; -+#endif - } - - static void __init highbank_timer_init(void) -diff --git a/arch/arm/mach-highbank/smc.S b/arch/arm/mach-highbank/smc.S -new file mode 100644 -index 0000000..bba369e ---- /dev/null -+++ b/arch/arm/mach-highbank/smc.S -@@ -0,0 +1,29 @@ -+/* -+ * Copied from omap44xx-smc.S Copyright (C) 2010 Texas Instruments, Inc. -+ * Copyright 2012 Calxeda, Inc. -+ * -+ * This program is free software,you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include -+ -+/* -+ * This is common routine to manage secure monitor API -+ * used to modify the PL310 secure registers. -+ * 'r0' contains the value to be modified and 'r12' contains -+ * the monitor API number. It uses few CPU registers -+ * internally and hence they need be backed up including -+ * link register "lr". -+ * Function signature : void highbank_smc1(u32 fn, u32 arg) -+ */ -+ -+ENTRY(highbank_smc1) -+ stmfd sp!, {r2-r12, lr} -+ mov r12, r0 -+ mov r0, r1 -+ dsb -+ smc #0 -+ ldmfd sp!, {r2-r12, pc} -+ENDPROC(highbank_smc1) --- -1.7.9.5 diff --git a/kernel.spec b/kernel.spec index 42ef71f87..6f38205b5 100644 --- a/kernel.spec +++ b/kernel.spec @@ -54,19 +54,19 @@ Summary: The Linux kernel # For non-released -rc kernels, this will be appended after the rcX and # gitX tags, so a 3 here would become part of release "0.rcX.gitX.3" # -%global baserelease 4 +%global baserelease 1 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching # on top of -- for example, 2.6.22-rc7-git1 starts with a 2.6.21 base, # which yields a base_sublevel of 21. -%define base_sublevel 4 +%define base_sublevel 5 ## If this is a released kernel ## %if 0%{?released_kernel} # Do we have a -stable update to apply? -%define stable_update 6 +%define stable_update 0 # Is it a -stable RC? %define stable_rc 0 # Set rpm version accordingly @@ -677,7 +677,6 @@ Patch1555: fix_xen_guest_on_old_EC2.patch # DRM #atch1700: drm-edid-try-harder-to-fix-up-broken-headers.patch Patch1800: drm-vgem.patch -Patch1810: drm-edid-Make-the-header-fixup-threshold-tunable.patch # nouveau + drm fixes # intel drm is all merged upstream @@ -686,8 +685,6 @@ Patch1825: drm-i915-dp-stfu.patch Patch1900: linux-2.6-intel-iommu-igfx.patch -Patch1952: drm-i915-lvds-dual-channel.patch - # Quiet boot fixes # silence the ACPI blacklist code Patch2802: linux-2.6-silence-acpi-blacklist.patch @@ -715,27 +712,23 @@ Patch14015: team-update-from-net-next.patch Patch19000: ips-noirq.patch # Uprobes (rhbz 832083) -Patch20000: uprobes-3.4-backport.patch Patch20001: uprobes-3.4-tip.patch -Patch20002: uprobes-task_work_add-generic-process-context-callbacks.patch # ARM # Flattened devicetree support Patch21000: arm-omap-dt-compat.patch Patch21001: arm-smsc-support-reading-mac-address-from-device-tree.patch # drm register derived from http://www.digipedia.pl/usenet/thread/19013/36923/ -Patch21002: arm-omap-drm-register.patch +#atch21002: arm-omap-drm-register.patch # ARM tegra Patch21004: arm-tegra-nvec-kconfig.patch Patch21005: arm-tegra-usb-no-reset-linux33.patch -Patch21006: arm-beagle-usb-init.patch +#atch21006: arm-beagle-usb-init.patch # ARM highbank patches # Highbank clock functions need to be EXPORT for module builds Patch21010: highbank-export-clock-functions.patch -# http://lists.arm.linux.org.uk/lurker/message/20120605.031140.e7d9b601.en.html -Patch21011: highbank-secure-smc.patch Patch21094: power-x86-destdir.patch @@ -749,8 +742,6 @@ Patch21306: shlib_base_randomize.patch Patch21400: unhandled-irqs-switch-to-polling.patch -Patch21620: vgaarb-vga_default_device.patch - Patch22000: weird-root-dentry-name-debug.patch #selinux ptrace child permissions @@ -758,12 +749,6 @@ Patch22001: selinux-apply-different-permission-to-ptrace-child.patch Patch22014: efifb-skip-DMI-checks-if-bootloader-knows.patch -#rhbz 726143 -Patch22017: 0001-drm-radeon-don-t-mess-with-hot-plug-detect-for-eDP-o.patch - -#rhbz 749276 -Patch22018: atl1c_net_next_update-3.4.patch - #Fix FIPS for aesni hardare Patch22055: crypto-testmgr-allow-aesni-intel-and-ghash_clmulni-intel.patch Patch22056: crypto-aesni-intel-fix-wrong-kfree-pointer.patch @@ -1339,8 +1324,8 @@ ApplyPatch taint-rss.patch # ApplyPatch arm-smsc-support-reading-mac-address-from-device-tree.patch ApplyPatch arm-tegra-nvec-kconfig.patch ApplyPatch arm-tegra-usb-no-reset-linux33.patch -ApplyPatch arm-beagle-usb-init.patch -ApplyPatch arm-omap-drm-register.patch +#pplyPatch arm-beagle-usb-init.patch +#pplyPatch arm-omap-drm-register.patch # # bugfixes to drivers and filesystems @@ -1416,7 +1401,6 @@ ApplyPatch fix_xen_guest_on_old_EC2.patch # DRM core #ApplyPatch drm-edid-try-harder-to-fix-up-broken-headers.patch ApplyPatch drm-vgem.patch -ApplyPatch drm-edid-Make-the-header-fixup-threshold-tunable.patch # Nouveau DRM @@ -1426,8 +1410,6 @@ ApplyPatch drm-i915-dp-stfu.patch ApplyPatch linux-2.6-intel-iommu-igfx.patch -ApplyPatch drm-i915-lvds-dual-channel.patch - # silence the ACPI blacklist code ApplyPatch linux-2.6-silence-acpi-blacklist.patch ApplyPatch quite-apm.patch @@ -1453,9 +1435,7 @@ ApplyPatch team-update-from-net-next.patch ApplyPatch ips-noirq.patch # Uprobes (rhbz 832083) -ApplyPatch uprobes-3.4-backport.patch ApplyPatch uprobes-3.4-tip.patch -ApplyPatch uprobes-task_work_add-generic-process-context-callbacks.patch ApplyPatch power-x86-destdir.patch @@ -1468,22 +1448,12 @@ ApplyPatch weird-root-dentry-name-debug.patch #Highbank clock functions ApplyPatch highbank-export-clock-functions.patch -ApplyPatch highbank-secure-smc.patch #selinux ptrace child permissions ApplyPatch selinux-apply-different-permission-to-ptrace-child.patch -#vgaarb patches. blame mjg59 -ApplyPatch vgaarb-vga_default_device.patch - ApplyPatch efifb-skip-DMI-checks-if-bootloader-knows.patch -#rhbz 726143 -ApplyPatch 0001-drm-radeon-don-t-mess-with-hot-plug-detect-for-eDP-o.patch - -#rhbz 749276 -ApplyPatch atl1c_net_next_update-3.4.patch - #Fix FIPS for aesni hardare ApplyPatch crypto-testmgr-allow-aesni-intel-and-ghash_clmulni-intel.patch ApplyPatch crypto-aesni-intel-fix-wrong-kfree-pointer.patch @@ -2361,6 +2331,9 @@ fi # '-' | | # '-' %changelog +* Thu Jul 26 2012 Josh Boyer +- Rebase to Linux v3.5 + * Thu Jul 26 2012 Josh Boyer - kernel: recv{from,msg}() on an rds socket can leak kernel memory (rhbz 820039 843554) diff --git a/linux-2.6-input-kill-stupid-messages.patch b/linux-2.6-input-kill-stupid-messages.patch index cc1dd7470..ff7023f51 100644 --- a/linux-2.6-input-kill-stupid-messages.patch +++ b/linux-2.6-input-kill-stupid-messages.patch @@ -1,32 +1,20 @@ -From b2c6d55b2351152696aafb8c9bf3ec8968acf77c Mon Sep 17 00:00:00 2001 -From: Kyle McMartin -Date: Mon, 29 Mar 2010 23:59:58 -0400 -Subject: linux-2.6-input-kill-stupid-messages - ---- - drivers/input/keyboard/atkbd.c | 5 +++++ - 1 files changed, 5 insertions(+), 0 deletions(-) - diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c -index d358ef8..38db098 100644 +index add5ffd..5eb2f03 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c -@@ -425,11 +426,15 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, +@@ -430,11 +430,15 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, goto out; case ATKBD_RET_ACK: case ATKBD_RET_NAK: -+#if 0 ++# if 0 + /* Quite a few key switchers and other tools trigger this + * and it confuses people who can do nothing about it */ if (printk_ratelimit()) dev_warn(&serio->dev, "Spurious %s on %s. " - "Some program might be trying access hardware directly.\n", + "Some program might be trying to access hardware directly.\n", data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); +#endif goto out; case ATKBD_RET_ERR: atkbd->err_count++; --- -1.7.0.1 - diff --git a/linux-2.6-makefile-after_link.patch b/linux-2.6-makefile-after_link.patch index c2541b1d8..b520b1942 100644 --- a/linux-2.6-makefile-after_link.patch +++ b/linux-2.6-makefile-after_link.patch @@ -1,4 +1,4 @@ -From f072f7db2194c8255c003d985b61ad2f97ebbee0 Mon Sep 17 00:00:00 2001 +From b707aea6a4947c3806ced2c23e889943a0f36876 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Mon, 6 Oct 2008 23:03:03 -0700 Subject: [PATCH] kbuild: AFTER_LINK @@ -7,24 +7,9 @@ If the make variable AFTER_LINK is set, it is a command line to run after each final link. This includes vmlinux itself and vDSO images. Signed-off-by: Roland McGrath ---- -diff --git a/Makefile b/Makefile -index f908acc..960ff6f 100644 ---- a/Makefile -+++ b/Makefile -@@ -746,6 +746,10 @@ quiet_cmd_vmlinux__ ?= LD $@ - --start-group $(vmlinux-main) --end-group \ - $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^) - -+ifdef AFTER_LINK -+cmd_vmlinux__ += ; $(AFTER_LINK) -+endif -+ - # Generate new vmlinux version - quiet_cmd_vmlinux_version = GEN .version - cmd_vmlinux_version = set -e; \ + diff --git a/arch/powerpc/kernel/vdso32/Makefile b/arch/powerpc/kernel/vdso32/Makefile -index 51ead52..ad21273 100644 +index 9a7946c..28d6765 100644 --- a/arch/powerpc/kernel/vdso32/Makefile +++ b/arch/powerpc/kernel/vdso32/Makefile @@ -41,7 +41,8 @@ $(obj-vdso32): %.o: %.S @@ -38,7 +23,7 @@ index 51ead52..ad21273 100644 cmd_vdso32as = $(CROSS32CC) $(a_flags) -c -o $@ $< diff --git a/arch/powerpc/kernel/vdso64/Makefile b/arch/powerpc/kernel/vdso64/Makefile -index 79da65d..f11c21b 100644 +index 8c500d8..d27737b 100644 --- a/arch/powerpc/kernel/vdso64/Makefile +++ b/arch/powerpc/kernel/vdso64/Makefile @@ -36,7 +36,8 @@ $(obj-vdso64): %.o: %.S @@ -51,11 +36,39 @@ index 79da65d..f11c21b 100644 quiet_cmd_vdso64as = VDSO64A $@ cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $< +diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile +index 8ad2b34..e153572 100644 +--- a/arch/s390/kernel/vdso32/Makefile ++++ b/arch/s390/kernel/vdso32/Makefile +@@ -43,7 +43,8 @@ $(obj-vdso32): %.o: %.S + + # actual build commands + quiet_cmd_vdso32ld = VDSO32L $@ +- cmd_vdso32ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ ++ cmd_vdso32ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ \ ++ $(if $(AFTER_LINK),; $(AFTER_LINK)) + quiet_cmd_vdso32as = VDSO32A $@ + cmd_vdso32as = $(CC) $(a_flags) -c -o $@ $< + +diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile +index 2a8ddfd..452ca53 100644 +--- a/arch/s390/kernel/vdso64/Makefile ++++ b/arch/s390/kernel/vdso64/Makefile +@@ -43,7 +43,8 @@ $(obj-vdso64): %.o: %.S + + # actual build commands + quiet_cmd_vdso64ld = VDSO64L $@ +- cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ ++ cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ \ ++ $(if $(AFTER_LINK),; $(AFTER_LINK)) + quiet_cmd_vdso64as = VDSO64A $@ + cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $< + diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile -index 4a2afa1..12ad9f7 100644 +index fd14be1..1f3eb19 100644 --- a/arch/x86/vdso/Makefile +++ b/arch/x86/vdso/Makefile -@@ -120,8 +120,9 @@ $(obj)/vdso32-syms.lds: $(vdso32.so-y:%=$(obj)/vdso32-%-syms.lds) FORCE +@@ -178,8 +178,9 @@ $(obj)/vdso32-syms.lds: $(vdso32.so-y:%=$(obj)/vdso32-%-syms.lds) FORCE quiet_cmd_vdso = VDSO $@ cmd_vdso = $(CC) -nostdlib -o $@ \ $(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \ @@ -67,32 +80,21 @@ index 4a2afa1..12ad9f7 100644 VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) GCOV_PROFILE := n +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index cd9c6c6..3edf048 100644 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -65,6 +65,10 @@ vmlinux_link() + -lutil ${1} + rm -f linux + fi ++ if [ -n "${AFTER_LINK}" ]; then ++ /usr/lib/rpm/debugedit -b ${RPM_BUILD_DIR} -d /usr/src/debug -i ${2} \ ++ > ${2}.id ++ fi + } + + +-- +1.7.7.6 -diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile -index d13e875..28a3e1ad 100644 ---- a/arch/s390/kernel/vdso32/Makefile -+++ b/arch/s390/kernel/vdso32/Makefile -@@ -40,7 +40,8 @@ $(obj-vdso32): %.o: %.S - - # actual build commands - quiet_cmd_vdso32ld = VDSO32L $@ -- cmd_vdso32ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ -+ cmd_vdso32ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ \ -+ $(if $(AFTER_LINK),; $(AFTER_LINK)) - quiet_cmd_vdso32as = VDSO32A $@ - cmd_vdso32as = $(CC) $(a_flags) -c -o $@ $< - -diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile -index 449352d..e90e656 100644 ---- a/arch/s390/kernel/vdso64/Makefile -+++ b/arch/s390/kernel/vdso64/Makefile -@@ -40,7 +40,8 @@ $(obj-vdso64): %.o: %.S - - # actual build commands - quiet_cmd_vdso64ld = VDSO64L $@ -- cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ -+ cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ \ -+ $(if $(AFTER_LINK),; $(AFTER_LINK)) - quiet_cmd_vdso64as = VDSO64A $@ - cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $< - diff --git a/sources b/sources index 6f10d0c05..d239339bc 100644 --- a/sources +++ b/sources @@ -1,2 +1 @@ -967f72983655e2479f951195953e8480 linux-3.4.tar.xz -14443e53d3ab88e6eac45d954d891e00 patch-3.4.6.xz +24153eaaa81dedc9481ada8cd9c3b83d linux-3.5.tar.xz diff --git a/team-update-from-net-next.patch b/team-update-from-net-next.patch index 58fcbe5e1..14794ce08 100644 --- a/team-update-from-net-next.patch +++ b/team-update-from-net-next.patch @@ -1,28 +1,12 @@ Update team driver to latest net-next. Split patches available here: -http://people.redhat.com/jpirko/f17_team_update/ +http://people.redhat.com/jpirko/f18_team_update/ -Modification of the kernel config is needed: - +CONFIG_NET_TEAM_MODE_LOADBALANCE=m - -David S. Miller (2): - team: Stop using NLA_PUT*(). +David S. Miller (1): team: Revert previous two changes. -Jiri Pirko (37): - filter: Allow to create sk-unattached filters - filter: add XOR operation - team: add binary option type - team: add loadbalance mode - team: add support for per-port options - team: add bool option type - team: add user_linkup and user_linkup_enabled per-port option - team: ab: walk through port list non-rcu - team: add missed "statics" - team: lb: let userspace care about port macs - team: allow to enable/disable ports - team: add per-port option for enabling/disabling ports +Jiri Pirko (25): team: make team_mode struct const team: for nomode use dummy struct team_mode team: add mode priv to port @@ -50,51 +34,17 @@ Jiri Pirko (37): team: do not allow to map disabled ports team: remove unused rcu_head field from team_port struct - drivers/net/team/Kconfig | 11 + - drivers/net/team/Makefile | 1 + - drivers/net/team/team.c | 862 ++++++++++++++++++++++------- - drivers/net/team/team_mode_activebackup.c | 30 +- - drivers/net/team/team_mode_loadbalance.c | 673 ++++++++++++++++++++++ - drivers/net/team/team_mode_roundrobin.c | 6 +- - include/linux/filter.h | 7 +- - include/linux/if_team.h | 90 ++- - net/core/filter.c | 70 ++- - 9 files changed, 1514 insertions(+), 236 deletions(-) - create mode 100644 drivers/net/team/team_mode_loadbalance.c + drivers/net/team/team.c | 577 ++++++++++++++++++----------- + drivers/net/team/team_mode_activebackup.c | 14 +- + drivers/net/team/team_mode_loadbalance.c | 543 +++++++++++++++++++++++++-- + drivers/net/team/team_mode_roundrobin.c | 4 +- + include/linux/if_team.h | 25 +- + 5 files changed, 919 insertions(+), 244 deletions(-) Signed-off-by: Jiri Pirko -diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig -index 248a144..89024d5 100644 ---- a/drivers/net/team/Kconfig -+++ b/drivers/net/team/Kconfig -@@ -40,4 +40,15 @@ config NET_TEAM_MODE_ACTIVEBACKUP - To compile this team mode as a module, choose M here: the module - will be called team_mode_activebackup. - -+config NET_TEAM_MODE_LOADBALANCE -+ tristate "Load-balance mode support" -+ depends on NET_TEAM -+ ---help--- -+ This mode provides load balancing functionality. Tx port selection -+ is done using BPF function set up from userspace (bpf_hash_func -+ option) -+ -+ To compile this team mode as a module, choose M here: the module -+ will be called team_mode_loadbalance. -+ - endif # NET_TEAM -diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile -index 85f2028..fb9f4c1 100644 ---- a/drivers/net/team/Makefile -+++ b/drivers/net/team/Makefile -@@ -5,3 +5,4 @@ - obj-$(CONFIG_NET_TEAM) += team.o - obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o - obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o -+obj-$(CONFIG_NET_TEAM_MODE_LOADBALANCE) += team_mode_loadbalance.o diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c -index 8f81805..5350eea 100644 +index c61ae35..5350eea 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1,5 +1,5 @@ @@ -104,69 +54,51 @@ index 8f81805..5350eea 100644 * Copyright (c) 2011 Jiri Pirko * * This program is free software; you can redistribute it and/or modify -@@ -65,7 +65,7 @@ static int __set_port_mac(struct net_device *port_dev, - return dev_set_mac_address(port_dev, &addr); +@@ -82,14 +82,16 @@ static void team_refresh_port_linkup(struct team_port *port) + port->state.linkup; } --int team_port_set_orig_mac(struct team_port *port) -+static int team_port_set_orig_mac(struct team_port *port) - { - return __set_port_mac(port->dev, port->orig.dev_addr); - } -@@ -76,12 +76,28 @@ int team_port_set_team_mac(struct team_port *port) - } - EXPORT_SYMBOL(team_port_set_team_mac); - -+static void team_refresh_port_linkup(struct team_port *port) -+{ -+ port->linkup = port->user.linkup_enabled ? port->user.linkup : -+ port->state.linkup; -+} + - /******************* * Options handling *******************/ --struct team_option *__team_find_option(struct team *team, const char *opt_name) -+struct team_option_inst { /* One for each option instance */ -+ struct list_head list; + struct team_option_inst { /* One for each option instance */ + struct list_head list; + struct list_head tmp_list; -+ struct team_option *option; -+ struct team_option_inst_info info; -+ bool changed; -+ bool removed; -+}; -+ -+static struct team_option *__team_find_option(struct team *team, -+ const char *opt_name) - { struct team_option *option; - -@@ -92,9 +108,140 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name) +- struct team_port *port; /* != NULL if per-port */ ++ struct team_option_inst_info info; + bool changed; + bool removed; + }; +@@ -106,22 +108,6 @@ static struct team_option *__team_find_option(struct team *team, return NULL; } --int __team_options_register(struct team *team, -- const struct team_option *option, -- size_t option_count) -+static void __team_option_inst_del(struct team_option_inst *opt_inst) -+{ -+ list_del(&opt_inst->list); -+ kfree(opt_inst); -+} -+ -+static void __team_option_inst_del_option(struct team *team, -+ struct team_option *option) -+{ -+ struct team_option_inst *opt_inst, *tmp; -+ -+ list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { -+ if (opt_inst->option == option) -+ __team_option_inst_del(opt_inst); -+ } -+} -+ +-static int __team_option_inst_add(struct team *team, struct team_option *option, +- struct team_port *port) +-{ +- struct team_option_inst *opt_inst; +- +- opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL); +- if (!opt_inst) +- return -ENOMEM; +- opt_inst->option = option; +- opt_inst->port = port; +- opt_inst->changed = true; +- opt_inst->removed = false; +- list_add_tail(&opt_inst->list, &team->option_inst_list); +- return 0; +-} +- + static void __team_option_inst_del(struct team_option_inst *opt_inst) + { + list_del(&opt_inst->list); +@@ -139,14 +125,49 @@ static void __team_option_inst_del_option(struct team *team, + } + } + +static int __team_option_inst_add(struct team *team, struct team_option *option, + struct team_port *port) +{ @@ -199,175 +131,74 @@ index 8f81805..5350eea 100644 + return 0; +} + -+static int __team_option_inst_add_option(struct team *team, -+ struct team_option *option) -+{ -+ struct team_port *port; -+ int err; -+ + static int __team_option_inst_add_option(struct team *team, + struct team_option *option) + { + struct team_port *port; + int err; + +- if (!option->per_port) +- return __team_option_inst_add(team, option, 0); + if (!option->per_port) { + err = __team_option_inst_add(team, option, NULL); + if (err) + goto inst_del_option; + } -+ -+ list_for_each_entry(port, &team->port_list, list) { -+ err = __team_option_inst_add(team, option, port); -+ if (err) -+ goto inst_del_option; -+ } -+ return 0; -+ -+inst_del_option: -+ __team_option_inst_del_option(team, option); -+ return err; -+} -+ -+static void __team_option_inst_mark_removed_option(struct team *team, -+ struct team_option *option) -+{ -+ struct team_option_inst *opt_inst; -+ -+ list_for_each_entry(opt_inst, &team->option_inst_list, list) { -+ if (opt_inst->option == option) { -+ opt_inst->changed = true; -+ opt_inst->removed = true; -+ } -+ } -+} -+ -+static void __team_option_inst_del_port(struct team *team, -+ struct team_port *port) -+{ -+ struct team_option_inst *opt_inst, *tmp; -+ -+ list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { -+ if (opt_inst->option->per_port && + + list_for_each_entry(port, &team->port_list, list) { + err = __team_option_inst_add(team, option, port); +@@ -180,7 +201,7 @@ static void __team_option_inst_del_port(struct team *team, + + list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { + if (opt_inst->option->per_port && +- opt_inst->port == port) + opt_inst->info.port == port) -+ __team_option_inst_del(opt_inst); -+ } -+} -+ -+static int __team_option_inst_add_port(struct team *team, -+ struct team_port *port) -+{ -+ struct team_option *option; -+ int err; -+ -+ list_for_each_entry(option, &team->option_list, list) { -+ if (!option->per_port) -+ continue; -+ err = __team_option_inst_add(team, option, port); -+ if (err) -+ goto inst_del_port; -+ } -+ return 0; -+ -+inst_del_port: -+ __team_option_inst_del_port(team, port); -+ return err; -+} -+ -+static void __team_option_inst_mark_removed_port(struct team *team, -+ struct team_port *port) -+{ -+ struct team_option_inst *opt_inst; -+ -+ list_for_each_entry(opt_inst, &team->option_inst_list, list) { -+ if (opt_inst->info.port == port) { -+ opt_inst->changed = true; -+ opt_inst->removed = true; -+ } -+ } -+} -+ -+static int __team_options_register(struct team *team, -+ const struct team_option *option, -+ size_t option_count) - { - int i; - struct team_option **dst_opts; -@@ -107,26 +254,32 @@ int __team_options_register(struct team *team, - for (i = 0; i < option_count; i++, option++) { - if (__team_find_option(team, option->name)) { - err = -EEXIST; -- goto rollback; -+ goto alloc_rollback; - } - dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL); - if (!dst_opts[i]) { - err = -ENOMEM; -- goto rollback; -+ goto alloc_rollback; - } - } - - for (i = 0; i < option_count; i++) { -- dst_opts[i]->changed = true; -- dst_opts[i]->removed = false; -+ err = __team_option_inst_add_option(team, dst_opts[i]); -+ if (err) -+ goto inst_rollback; - list_add_tail(&dst_opts[i]->list, &team->option_list); - } - - kfree(dst_opts); - return 0; - --rollback: -- for (i = 0; i < option_count; i++) -+inst_rollback: -+ for (i--; i >= 0; i--) -+ __team_option_inst_del_option(team, dst_opts[i]); -+ -+ i = option_count - 1; -+alloc_rollback: -+ for (i--; i >= 0; i--) - kfree(dst_opts[i]); - - kfree(dst_opts); -@@ -143,10 +296,8 @@ static void __team_options_mark_removed(struct team *team, - struct team_option *del_opt; - - del_opt = __team_find_option(team, option->name); -- if (del_opt) { -- del_opt->changed = true; -- del_opt->removed = true; -- } -+ if (del_opt) -+ __team_option_inst_mark_removed_option(team, del_opt); + __team_option_inst_del(opt_inst); } } +@@ -211,7 +232,7 @@ static void __team_option_inst_mark_removed_port(struct team *team, + struct team_option_inst *opt_inst; -@@ -161,6 +312,7 @@ static void __team_options_unregister(struct team *team, - - del_opt = __team_find_option(team, option->name); - if (del_opt) { -+ __team_option_inst_del_option(team, del_opt); - list_del(&del_opt->list); - kfree(del_opt); + list_for_each_entry(opt_inst, &team->option_inst_list, list) { +- if (opt_inst->port == port) { ++ if (opt_inst->info.port == port) { + opt_inst->changed = true; + opt_inst->removed = true; } -@@ -193,25 +345,39 @@ void team_options_unregister(struct team *team, +@@ -324,28 +345,12 @@ void team_options_unregister(struct team *team, } EXPORT_SYMBOL(team_options_unregister); --static int team_option_get(struct team *team, struct team_option *option, -- void *arg) -+static int team_option_get(struct team *team, -+ struct team_option_inst *opt_inst, -+ struct team_gsetter_ctx *ctx) +-static int team_option_port_add(struct team *team, struct team_port *port) +-{ +- int err; +- +- err = __team_option_inst_add_port(team, port); +- if (err) +- return err; +- __team_options_change_check(team); +- return 0; +-} +- +-static void team_option_port_del(struct team *team, struct team_port *port) +-{ +- __team_option_inst_mark_removed_port(team, port); +- __team_options_change_check(team); +- __team_option_inst_del_port(team, port); +-} +- + static int team_option_get(struct team *team, + struct team_option_inst *opt_inst, + struct team_gsetter_ctx *ctx) { -- return option->getter(team, arg); + if (!opt_inst->option->getter) + return -EOPNOTSUPP; -+ return opt_inst->option->getter(team, ctx); + return opt_inst->option->getter(team, ctx); } --static int team_option_set(struct team *team, struct team_option *option, -- void *arg) -+static int team_option_set(struct team *team, -+ struct team_option_inst *opt_inst, -+ struct team_gsetter_ctx *ctx) +@@ -353,16 +358,26 @@ static int team_option_set(struct team *team, + struct team_option_inst *opt_inst, + struct team_gsetter_ctx *ctx) { - int err; + if (!opt_inst->option->setter) @@ -375,19 +206,18 @@ index 8f81805..5350eea 100644 + return opt_inst->option->setter(team, ctx); +} -- err = option->setter(team, arg); +- err = opt_inst->option->setter(team, ctx); - if (err) - return err; +void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info) +{ + struct team_option_inst *opt_inst; -+ + + opt_inst = container_of(opt_inst_info, struct team_option_inst, info); -+ opt_inst->changed = true; + opt_inst->changed = true; +} +EXPORT_SYMBOL(team_option_inst_set_change); - -- option->changed = true; ++ +void team_options_change_check(struct team *team) +{ __team_options_change_check(team); @@ -398,7 +228,7 @@ index 8f81805..5350eea 100644 /**************** * Mode handling -@@ -220,13 +386,18 @@ static int team_option_set(struct team *team, struct team_option *option, +@@ -371,13 +386,18 @@ static int team_option_set(struct team *team, static LIST_HEAD(mode_list); static DEFINE_SPINLOCK(mode_list_lock); @@ -422,7 +252,7 @@ index 8f81805..5350eea 100644 } return NULL; } -@@ -241,49 +412,65 @@ static bool is_good_mode_name(const char *name) +@@ -392,49 +412,65 @@ static bool is_good_mode_name(const char *name) return true; } @@ -499,7 +329,7 @@ index 8f81805..5350eea 100644 spin_unlock(&mode_list_lock); return mode; -@@ -307,26 +494,45 @@ rx_handler_result_t team_dummy_receive(struct team *team, +@@ -458,26 +494,45 @@ rx_handler_result_t team_dummy_receive(struct team *team, return RX_HANDLER_ANOTHER; } @@ -550,7 +380,7 @@ index 8f81805..5350eea 100644 /* * We can benefit from the fact that it's ensured no port is present * at the time of mode change. Therefore no packets are in fly so there's no -@@ -336,7 +542,7 @@ static int __team_change_mode(struct team *team, +@@ -487,7 +542,7 @@ static int __team_change_mode(struct team *team, const struct team_mode *new_mode) { /* Check if mode was previously set and do cleanup if so */ @@ -559,7 +389,7 @@ index 8f81805..5350eea 100644 void (*exit_op)(struct team *team) = team->ops.exit; /* Clear ops area so no callback is called any longer */ -@@ -346,7 +552,7 @@ static int __team_change_mode(struct team *team, +@@ -497,7 +552,7 @@ static int __team_change_mode(struct team *team, if (exit_op) exit_op(team); team_mode_put(team->mode); @@ -568,7 +398,7 @@ index 8f81805..5350eea 100644 /* zero private data area */ memset(&team->mode_priv, 0, sizeof(struct team) - offsetof(struct team, mode_priv)); -@@ -372,7 +578,7 @@ static int __team_change_mode(struct team *team, +@@ -523,7 +578,7 @@ static int __team_change_mode(struct team *team, static int team_change_mode(struct team *team, const char *kind) { @@ -577,7 +407,7 @@ index 8f81805..5350eea 100644 struct net_device *dev = team->dev; int err; -@@ -381,7 +587,7 @@ static int team_change_mode(struct team *team, const char *kind) +@@ -532,7 +587,7 @@ static int team_change_mode(struct team *team, const char *kind) return -EBUSY; } @@ -586,87 +416,53 @@ index 8f81805..5350eea 100644 netdev_err(dev, "Unable to change to the same mode the team is in\n"); return -EINVAL; } -@@ -424,8 +630,12 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) +@@ -559,8 +614,6 @@ static int team_change_mode(struct team *team, const char *kind) + * Rx path frame handler + ************************/ - port = team_port_get_rcu(skb->dev); - team = port->team; +-static bool team_port_enabled(struct team_port *port); - -- res = team->ops.receive(team, port, skb); -+ if (!team_port_enabled(port)) { -+ /* allow exact match delivery for disabled ports */ -+ res = RX_HANDLER_EXACT; -+ } else { -+ res = team->ops.receive(team, port, skb); -+ } - if (res == RX_HANDLER_ANOTHER) { - struct team_pcpu_stats *pcpu_stats; - -@@ -461,17 +671,29 @@ static bool team_port_find(const struct team *team, + /* note: already called with rcu_read_lock */ + static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) + { +@@ -618,10 +671,11 @@ static bool team_port_find(const struct team *team, return false; } +-static bool team_port_enabled(struct team_port *port) +bool team_port_enabled(struct team_port *port) -+{ -+ return port->index != -1; -+} -+EXPORT_SYMBOL(team_port_enabled); -+ - /* -- * Add/delete port to the team port list. Write guarded by rtnl_lock. -- * Takes care of correct port->index setup (might be racy). -+ * Enable/disable port by adding to enabled port hashlist and setting -+ * port->index (Might be racy so reader could see incorrect ifindex when -+ * processing a flying packet, but that is not a problem). Write guarded -+ * by team->lock. - */ --static void team_port_list_add_port(struct team *team, -- struct team_port *port) -+static void team_port_enable(struct team *team, -+ struct team_port *port) { -- port->index = team->port_count++; -+ if (team_port_enabled(port)) -+ return; -+ port->index = team->en_port_count++; + return port->index != -1; + } ++EXPORT_SYMBOL(team_port_enabled); + + /* + * Enable/disable port by adding to enabled port hashlist and setting +@@ -637,6 +691,9 @@ static void team_port_enable(struct team *team, + port->index = team->en_port_count++; hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); -- list_add_tail_rcu(&port->list, &team->port_list); + team_adjust_ops(team); + if (team->ops.port_enabled) + team->ops.port_enabled(team, port); } static void __reconstruct_port_hlist(struct team *team, int rm_index) -@@ -479,7 +701,7 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index) - int i; - struct team_port *port; - -- for (i = rm_index + 1; i < team->port_count; i++) { -+ for (i = rm_index + 1; i < team->en_port_count; i++) { - port = team_get_port_by_index(team, i); - hlist_del_rcu(&port->hlist); - port->index--; -@@ -488,15 +710,23 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index) - } - } - --static void team_port_list_del_port(struct team *team, -- struct team_port *port) -+static void team_port_disable(struct team *team, -+ struct team_port *port) +@@ -656,14 +713,20 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index) + static void team_port_disable(struct team *team, + struct team_port *port) { - int rm_index = port->index; - -+ if (!team_port_enabled(port)) -+ return; + if (!team_port_enabled(port)) + return; + if (team->ops.port_disabled) + team->ops.port_disabled(team, port); hlist_del_rcu(&port->hlist); -- list_del_rcu(&port->list); - __reconstruct_port_hlist(team, rm_index); -- team->port_count--; +- team->en_port_count--; + __reconstruct_port_hlist(team, port->index); -+ port->index = -1; + port->index = -1; + __team_adjust_ops(team, team->en_port_count - 1); + /* + * Wait until readers see adjusted ops. This ensures that @@ -677,7 +473,7 @@ index 8f81805..5350eea 100644 } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ -@@ -591,7 +821,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev) +@@ -758,7 +821,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev) return -EBUSY; } @@ -687,37 +483,27 @@ index 8f81805..5350eea 100644 if (!port) return -ENOMEM; -@@ -642,15 +873,27 @@ static int team_port_add(struct team *team, struct net_device *port_dev) +@@ -809,7 +873,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_handler_register; } -- team_port_list_add_port(team, port); -- team_adjust_ops(team); +- err = team_option_port_add(team, port); + err = __team_option_inst_add_port(team, port); -+ if (err) { -+ netdev_err(dev, "Device %s failed to add per-port options\n", -+ portname); -+ goto err_option_port_add; -+ } -+ -+ port->index = -1; -+ team_port_enable(team, port); -+ list_add_tail_rcu(&port->list, &team->port_list); + if (err) { + netdev_err(dev, "Device %s failed to add per-port options\n", + portname); +@@ -819,9 +883,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev) + port->index = -1; + team_port_enable(team, port); + list_add_tail_rcu(&port->list, &team->port_list); +- team_adjust_ops(team); __team_compute_features(team); __team_port_change_check(port, !!netif_carrier_ok(port_dev)); + __team_options_change_check(team); netdev_info(dev, "Port device %s added\n", portname); - return 0; - -+err_option_port_add: -+ netdev_rx_handler_unregister(port_dev); -+ - err_handler_register: - netdev_set_master(port_dev, NULL); - -@@ -686,10 +929,13 @@ static int team_port_del(struct team *team, struct net_device *port_dev) +@@ -865,12 +929,13 @@ static int team_port_del(struct team *team, struct net_device *port_dev) return -ENOENT; } @@ -726,124 +512,95 @@ index 8f81805..5350eea 100644 + __team_option_inst_del_port(team, port); port->removed = true; __team_port_change_check(port, false); -- team_port_list_del_port(team, port); + team_port_disable(team, port); + list_del_rcu(&port->list); - team_adjust_ops(team); -+ team_port_disable(team, port); -+ list_del_rcu(&port->list); +- team_option_port_del(team, port); netdev_rx_handler_unregister(port_dev); netdev_set_master(port_dev, NULL); vlan_vids_del_by_dev(port_dev, dev); -@@ -710,21 +956,74 @@ static int team_port_del(struct team *team, struct net_device *port_dev) +@@ -891,11 +956,9 @@ static int team_port_del(struct team *team, struct net_device *port_dev) * Net device ops *****************/ -static const char team_no_mode_kind[] = "*NOMODE*"; -+static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx) -+{ +- + static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx) + { +- ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind; + ctx->data.str_val = team->mode->kind; -+ return 0; -+} -+ -+static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) -+{ -+ return team_change_mode(team, ctx->data.str_val); -+} -+ -+static int team_port_en_option_get(struct team *team, -+ struct team_gsetter_ctx *ctx) -+{ + return 0; + } + +@@ -907,39 +970,47 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) + static int team_port_en_option_get(struct team *team, + struct team_gsetter_ctx *ctx) + { +- ctx->data.bool_val = team_port_enabled(ctx->port); + struct team_port *port = ctx->info->port; + + ctx->data.bool_val = team_port_enabled(port); -+ return 0; -+} -+ -+static int team_port_en_option_set(struct team *team, -+ struct team_gsetter_ctx *ctx) -+{ + return 0; + } + + static int team_port_en_option_set(struct team *team, + struct team_gsetter_ctx *ctx) + { + struct team_port *port = ctx->info->port; + -+ if (ctx->data.bool_val) + if (ctx->data.bool_val) +- team_port_enable(team, ctx->port); + team_port_enable(team, port); -+ else + else +- team_port_disable(team, ctx->port); + team_port_disable(team, port); -+ return 0; -+} -+ -+static int team_user_linkup_option_get(struct team *team, -+ struct team_gsetter_ctx *ctx) -+{ + return 0; + } + + static int team_user_linkup_option_get(struct team *team, + struct team_gsetter_ctx *ctx) + { +- ctx->data.bool_val = ctx->port->user.linkup; + struct team_port *port = ctx->info->port; + + ctx->data.bool_val = port->user.linkup; -+ return 0; -+} + return 0; + } --static int team_mode_option_get(struct team *team, void *arg) -+static int team_user_linkup_option_set(struct team *team, -+ struct team_gsetter_ctx *ctx) + static int team_user_linkup_option_set(struct team *team, + struct team_gsetter_ctx *ctx) { -- const char **str = arg; +- ctx->port->user.linkup = ctx->data.bool_val; +- team_refresh_port_linkup(ctx->port); + struct team_port *port = ctx->info->port; - -- *str = team->mode ? team->mode->kind : team_no_mode_kind; ++ + port->user.linkup = ctx->data.bool_val; + team_refresh_port_linkup(port); return 0; } --static int team_mode_option_set(struct team *team, void *arg) -+static int team_user_linkup_en_option_get(struct team *team, -+ struct team_gsetter_ctx *ctx) + static int team_user_linkup_en_option_get(struct team *team, + struct team_gsetter_ctx *ctx) { -- const char **str = arg; +- struct team_port *port = ctx->port; + struct team_port *port = ctx->info->port; -- return team_change_mode(team, *str); -+ ctx->data.bool_val = port->user.linkup_enabled; -+ return 0; -+} -+ -+static int team_user_linkup_en_option_set(struct team *team, -+ struct team_gsetter_ctx *ctx) -+{ + ctx->data.bool_val = port->user.linkup_enabled; + return 0; +@@ -948,10 +1019,10 @@ static int team_user_linkup_en_option_get(struct team *team, + static int team_user_linkup_en_option_set(struct team *team, + struct team_gsetter_ctx *ctx) + { +- struct team_port *port = ctx->port; + struct team_port *port = ctx->info->port; -+ -+ port->user.linkup_enabled = ctx->data.bool_val; + + port->user.linkup_enabled = ctx->data.bool_val; +- team_refresh_port_linkup(ctx->port); + team_refresh_port_linkup(port); -+ return 0; + return 0; } - static const struct team_option team_options[] = { -@@ -734,6 +1033,27 @@ static const struct team_option team_options[] = { - .getter = team_mode_option_get, - .setter = team_mode_option_set, - }, -+ { -+ .name = "enabled", -+ .type = TEAM_OPTION_TYPE_BOOL, -+ .per_port = true, -+ .getter = team_port_en_option_get, -+ .setter = team_port_en_option_set, -+ }, -+ { -+ .name = "user_linkup", -+ .type = TEAM_OPTION_TYPE_BOOL, -+ .per_port = true, -+ .getter = team_user_linkup_option_get, -+ .setter = team_user_linkup_option_set, -+ }, -+ { -+ .name = "user_linkup_enabled", -+ .type = TEAM_OPTION_TYPE_BOOL, -+ .per_port = true, -+ .getter = team_user_linkup_en_option_get, -+ .setter = team_user_linkup_en_option_set, -+ }, - }; - - static int team_init(struct net_device *dev) -@@ -744,18 +1064,20 @@ static int team_init(struct net_device *dev) +@@ -993,6 +1064,7 @@ static int team_init(struct net_device *dev) team->dev = dev; mutex_init(&team->lock); @@ -851,33 +608,7 @@ index 8f81805..5350eea 100644 team->pcpu_stats = alloc_percpu(struct team_pcpu_stats); if (!team->pcpu_stats) - return -ENOMEM; - - for (i = 0; i < TEAM_PORT_HASHENTRIES; i++) -- INIT_HLIST_HEAD(&team->port_hlist[i]); -+ INIT_HLIST_HEAD(&team->en_port_hlist[i]); - INIT_LIST_HEAD(&team->port_list); - - team_adjust_ops(team); - - INIT_LIST_HEAD(&team->option_list); -+ INIT_LIST_HEAD(&team->option_inst_list); - err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); - if (err) - goto err_options_register; -@@ -1145,10 +1467,7 @@ team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = { - }, - [TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG }, - [TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8 }, -- [TEAM_ATTR_OPTION_DATA] = { -- .type = NLA_BINARY, -- .len = TEAM_STRING_MAX_LEN, -- }, -+ [TEAM_ATTR_OPTION_DATA] = { .type = NLA_BINARY }, - }; - - static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) -@@ -1235,98 +1554,210 @@ err_fill: +@@ -1482,16 +1554,128 @@ err_fill: return err; } @@ -991,9 +722,8 @@ index 8f81805..5350eea 100644 struct nlattr *option_list; + struct nlmsghdr *nlh; void *hdr; -- struct team_option *option; -+ struct team_option_inst *opt_inst; -+ int err; + struct team_option_inst *opt_inst; + int err; + struct sk_buff *skb = NULL; + bool incomplete; + int i; @@ -1011,43 +741,79 @@ index 8f81805..5350eea 100644 TEAM_CMD_OPTIONS_GET); if (IS_ERR(hdr)) return PTR_ERR(hdr); - -- NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex); -+ if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) -+ goto nla_put_failure; +@@ -1500,122 +1684,80 @@ static int team_nl_fill_options_get(struct sk_buff *skb, + goto nla_put_failure; option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION); if (!option_list) - return -EMSGSIZE; +- +- list_for_each_entry(opt_inst, &team->option_inst_list, list) { +- struct nlattr *option_item; +- struct team_option *option = opt_inst->option; +- struct team_gsetter_ctx ctx; + goto nla_put_failure; -- list_for_each_entry(option, &team->option_list, list) { -- struct nlattr *option_item; -- long arg; -- - /* Include only changed options if fill all mode is not on */ -- if (!fillall && !option->changed) +- if (!fillall && !opt_inst->changed) - continue; - option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); - if (!option_item) - goto nla_put_failure; -- NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name); -- if (option->changed) { -- NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED); -- option->changed = false; +- if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) +- goto nla_put_failure; +- if (opt_inst->changed) { +- if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) +- goto nla_put_failure; +- opt_inst->changed = false; - } -- if (option->removed) -- NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED); +- if (opt_inst->removed && +- nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) +- goto nla_put_failure; +- if (opt_inst->port && +- nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, +- opt_inst->port->dev->ifindex)) +- goto nla_put_failure; +- ctx.port = opt_inst->port; - switch (option->type) { - case TEAM_OPTION_TYPE_U32: -- NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32); -- team_option_get(team, option, &arg); -- NLA_PUT_U32(skb, TEAM_ATTR_OPTION_DATA, arg); +- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) +- goto nla_put_failure; +- err = team_option_get(team, opt_inst, &ctx); +- if (err) +- goto errout; +- if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, +- ctx.data.u32_val)) +- goto nla_put_failure; - break; - case TEAM_OPTION_TYPE_STRING: -- NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING); -- team_option_get(team, option, &arg); -- NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_DATA, -- (char *) arg); +- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) +- goto nla_put_failure; +- err = team_option_get(team, opt_inst, &ctx); +- if (err) +- goto errout; +- if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, +- ctx.data.str_val)) +- goto nla_put_failure; +- break; +- case TEAM_OPTION_TYPE_BINARY: +- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) +- goto nla_put_failure; +- err = team_option_get(team, opt_inst, &ctx); +- if (err) +- goto errout; +- if (nla_put(skb, TEAM_ATTR_OPTION_DATA, +- ctx.data.bin_val.len, ctx.data.bin_val.ptr)) +- goto nla_put_failure; +- break; +- case TEAM_OPTION_TYPE_BOOL: +- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG)) +- goto nla_put_failure; +- err = team_option_get(team, opt_inst, &ctx); +- if (err) +- goto errout; +- if (ctx.data.bool_val && +- nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) +- goto nla_put_failure; - break; - default: - BUG(); @@ -1086,12 +852,13 @@ index 8f81805..5350eea 100644 + return send_func(skb, team, pid); nla_put_failure: -+ err = -EMSGSIZE; -+errout: + err = -EMSGSIZE; + errout: genlmsg_cancel(skb, hdr); -- return -EMSGSIZE; --} -- ++ nlmsg_free(skb); + return err; + } + -static int team_nl_fill_options_get_all(struct sk_buff *skb, - struct genl_info *info, int flags, - struct team *team) @@ -1099,10 +866,8 @@ index 8f81805..5350eea 100644 - return team_nl_fill_options_get(skb, info->snd_pid, - info->snd_seq, NLM_F_ACK, - team, true); -+ nlmsg_free(skb); -+ return err; - } - +-} +- static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) { struct team *team; @@ -1139,69 +904,27 @@ index 8f81805..5350eea 100644 team = team_nl_team_get(info); if (!team) -@@ -1339,9 +1770,14 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) - } +@@ -1629,10 +1771,12 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { -- struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1]; -+ struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1]; + struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1]; +- struct nlattr *attr_port_ifindex; + struct nlattr *attr; -+ struct nlattr *attr_data; + struct nlattr *attr_data; enum team_option_type opt_type; -- struct team_option *option; -+ int opt_port_ifindex = 0; /* != 0 for per-port options */ + int opt_port_ifindex = 0; /* != 0 for per-port options */ + u32 opt_array_index = 0; + bool opt_is_array = false; -+ struct team_option_inst *opt_inst; + struct team_option_inst *opt_inst; char *opt_name; bool opt_found = false; - -@@ -1349,50 +1785,92 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) - err = -EINVAL; - goto team_put; - } -- err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX, -+ err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX, - nl_option, team_nl_option_policy); - if (err) - goto team_put; -- if (!mode_attrs[TEAM_ATTR_OPTION_NAME] || -- !mode_attrs[TEAM_ATTR_OPTION_TYPE] || -- !mode_attrs[TEAM_ATTR_OPTION_DATA]) { -+ if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || -+ !opt_attrs[TEAM_ATTR_OPTION_TYPE]) { - err = -EINVAL; - goto team_put; - } -- switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) { -+ switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) { - case NLA_U32: - opt_type = TEAM_OPTION_TYPE_U32; - break; - case NLA_STRING: - opt_type = TEAM_OPTION_TYPE_STRING; - break; -+ case NLA_BINARY: -+ opt_type = TEAM_OPTION_TYPE_BINARY; -+ break; -+ case NLA_FLAG: -+ opt_type = TEAM_OPTION_TYPE_BOOL; -+ break; - default: - goto team_put; +@@ -1674,23 +1818,33 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) } -- opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]); -- list_for_each_entry(option, &team->option_list, list) { -- long arg; -- struct nlattr *opt_data_attr; -+ attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA]; -+ if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) { -+ err = -EINVAL; -+ goto team_put; -+ } -+ -+ opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]); + opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]); +- attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; +- if (attr_port_ifindex) +- opt_port_ifindex = nla_get_u32(attr_port_ifindex); + attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; + if (attr) + opt_port_ifindex = nla_get_u32(attr); @@ -1212,50 +935,32 @@ index 8f81805..5350eea 100644 + opt_array_index = nla_get_u32(attr); + } -+ list_for_each_entry(opt_inst, &team->option_inst_list, list) { -+ struct team_option *option = opt_inst->option; -+ struct team_gsetter_ctx ctx; + list_for_each_entry(opt_inst, &team->option_inst_list, list) { + struct team_option *option = opt_inst->option; + struct team_gsetter_ctx ctx; + struct team_option_inst_info *opt_inst_info; -+ int tmp_ifindex; -+ + int tmp_ifindex; + +- tmp_ifindex = opt_inst->port ? +- opt_inst->port->dev->ifindex : 0; + opt_inst_info = &opt_inst->info; + tmp_ifindex = opt_inst_info->port ? + opt_inst_info->port->dev->ifindex : 0; if (option->type != opt_type || -- strcmp(option->name, opt_name)) -+ strcmp(option->name, opt_name) || + strcmp(option->name, opt_name) || +- tmp_ifindex != opt_port_ifindex) + tmp_ifindex != opt_port_ifindex || + (option->array_size && !opt_is_array) || + opt_inst_info->array_index != opt_array_index) continue; opt_found = true; -- opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA]; +- ctx.port = opt_inst->port; + ctx.info = opt_inst_info; switch (opt_type) { case TEAM_OPTION_TYPE_U32: -- arg = nla_get_u32(opt_data_attr); -+ ctx.data.u32_val = nla_get_u32(attr_data); - break; - case TEAM_OPTION_TYPE_STRING: -- arg = (long) nla_data(opt_data_attr); -+ if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) { -+ err = -EINVAL; -+ goto team_put; -+ } -+ ctx.data.str_val = nla_data(attr_data); -+ break; -+ case TEAM_OPTION_TYPE_BINARY: -+ ctx.data.bin_val.len = nla_len(attr_data); -+ ctx.data.bin_val.ptr = nla_data(attr_data); -+ break; -+ case TEAM_OPTION_TYPE_BOOL: -+ ctx.data.bool_val = attr_data ? true : false; - break; - default: - BUG(); - } -- err = team_option_set(team, option, &arg); -+ err = team_option_set(team, opt_inst, &ctx); + ctx.data.u32_val = nla_get_u32(attr_data); +@@ -1715,6 +1869,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) + err = team_option_set(team, opt_inst, &ctx); if (err) goto team_put; + opt_inst->changed = true; @@ -1263,7 +968,7 @@ index 8f81805..5350eea 100644 } if (!opt_found) { err = -ENOENT; -@@ -1400,6 +1878,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) +@@ -1722,6 +1878,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) } } @@ -1272,13 +977,8 @@ index 8f81805..5350eea 100644 team_put: team_nl_team_put(team); -@@ -1420,10 +1900,11 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, - if (IS_ERR(hdr)) - return PTR_ERR(hdr); - -- NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex); -+ if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) -+ goto nla_put_failure; +@@ -1746,7 +1904,7 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, + goto nla_put_failure; port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT); if (!port_list) - return -EMSGSIZE; @@ -1286,36 +986,7 @@ index 8f81805..5350eea 100644 list_for_each_entry(port, &team->port_list, list) { struct nlattr *port_item; -@@ -1434,17 +1915,20 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, - port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); - if (!port_item) - goto nla_put_failure; -- NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex); -+ if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex)) -+ goto nla_put_failure; - if (port->changed) { -- NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED); -+ if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED)) -+ goto nla_put_failure; - port->changed = false; - } -- if (port->removed) -- NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED); -- if (port->linkup) -- NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP); -- NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed); -- NLA_PUT_U8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex); -+ if ((port->removed && -+ nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) || -+ (port->state.linkup && -+ nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) || -+ nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) || -+ nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex)) -+ goto nla_put_failure; - nla_nest_end(skb, port_item); - } - -@@ -1512,27 +1996,18 @@ static struct genl_multicast_group team_change_event_mcgrp = { +@@ -1838,27 +1996,18 @@ static struct genl_multicast_group team_change_event_mcgrp = { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, }; @@ -1353,7 +1024,7 @@ index 8f81805..5350eea 100644 } static int team_nl_send_event_port_list_get(struct team *team) -@@ -1592,10 +2067,17 @@ static void team_nl_fini(void) +@@ -1918,10 +2067,17 @@ static void team_nl_fini(void) static void __team_options_change_check(struct team *team) { int err; @@ -1373,38 +1044,7 @@ index 8f81805..5350eea 100644 } /* rtnl lock is held */ -@@ -1603,23 +2085,24 @@ static void __team_port_change_check(struct team_port *port, bool linkup) - { - int err; - -- if (!port->removed && port->linkup == linkup) -+ if (!port->removed && port->state.linkup == linkup) - return; - - port->changed = true; -- port->linkup = linkup; -+ port->state.linkup = linkup; -+ team_refresh_port_linkup(port); - if (linkup) { - struct ethtool_cmd ecmd; - - err = __ethtool_get_settings(port->dev, &ecmd); - if (!err) { -- port->speed = ethtool_cmd_speed(&ecmd); -- port->duplex = ecmd.duplex; -+ port->state.speed = ethtool_cmd_speed(&ecmd); -+ port->state.duplex = ecmd.duplex; - goto send_event; - } - } -- port->speed = 0; -- port->duplex = 0; -+ port->state.speed = 0; -+ port->state.duplex = 0; - - send_event: - err = team_nl_send_event_port_list_get(port->team); -@@ -1638,6 +2121,7 @@ static void team_port_change_check(struct team_port *port, bool linkup) +@@ -1965,6 +2121,7 @@ static void team_port_change_check(struct team_port *port, bool linkup) mutex_unlock(&team->lock); } @@ -1413,7 +1053,7 @@ index 8f81805..5350eea 100644 * Net device notifier event handler ************************************/ diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c -index f4d960e..253b8a5 100644 +index fd6bd03..253b8a5 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -1,5 +1,5 @@ @@ -1432,57 +1072,22 @@ index f4d960e..253b8a5 100644 if (unlikely(!active_port)) goto drop; skb->dev = active_port->dev; -@@ -59,23 +59,25 @@ static void ab_port_leave(struct team *team, struct team_port *port) - RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); - } +@@ -61,8 +61,12 @@ static void ab_port_leave(struct team *team, struct team_port *port) --static int ab_active_port_get(struct team *team, void *arg) -+static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) + static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) { -- u32 *ifindex = arg; -+ struct team_port *active_port; - -- *ifindex = 0; - if (ab_priv(team)->active_port) -- *ifindex = ab_priv(team)->active_port->dev->ifindex; +- ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex; ++ struct team_port *active_port; ++ + active_port = rcu_dereference_protected(ab_priv(team)->active_port, + lockdep_is_held(&team->lock)); + if (active_port) + ctx->data.u32_val = active_port->dev->ifindex; -+ else -+ ctx->data.u32_val = 0; + else + ctx->data.u32_val = 0; return 0; - } - --static int ab_active_port_set(struct team *team, void *arg) -+static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx) - { -- u32 *ifindex = arg; - struct team_port *port; - -- list_for_each_entry_rcu(port, &team->port_list, list) { -- if (port->dev->ifindex == *ifindex) { -+ list_for_each_entry(port, &team->port_list, list) { -+ if (port->dev->ifindex == ctx->data.u32_val) { - rcu_assign_pointer(ab_priv(team)->active_port, port); - return 0; - } -@@ -92,12 +94,12 @@ static const struct team_option ab_options[] = { - }, - }; - --int ab_init(struct team *team) -+static int ab_init(struct team *team) - { - return team_options_register(team, ab_options, ARRAY_SIZE(ab_options)); - } - --void ab_exit(struct team *team) -+static void ab_exit(struct team *team) - { - team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options)); - } -@@ -110,7 +112,7 @@ static const struct team_mode_ops ab_mode_ops = { +@@ -108,7 +112,7 @@ static const struct team_mode_ops ab_mode_ops = { .port_leave = ab_port_leave, }; @@ -1492,30 +1097,13 @@ index f4d960e..253b8a5 100644 .owner = THIS_MODULE, .priv_size = sizeof(struct ab_priv), diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c -new file mode 100644 -index 0000000..51a4b19 ---- /dev/null +index 86e8183..51a4b19 100644 +--- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c -@@ -0,0 +1,673 @@ -+/* -+ * drivers/net/team/team_mode_loadbalance.c - Load-balancing mode for team -+ * Copyright (c) 2012 Jiri Pirko -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ +@@ -17,34 +17,210 @@ + #include + #include + +struct lb_priv; + +typedef struct team_port *lb_select_tx_port_func_t(struct team *, @@ -1556,18 +1144,21 @@ index 0000000..51a4b19 + } stats; +}; + -+struct lb_priv { -+ struct sk_filter __rcu *fp; + struct lb_priv { + struct sk_filter __rcu *fp; +- struct sock_fprog *orig_fprog; + lb_select_tx_port_func_t __rcu *select_tx_port_func; + struct lb_pcpu_stats __percpu *pcpu_stats; + struct lb_priv_ex *ex; /* priv extension */ -+}; -+ + }; + +-static struct lb_priv *lb_priv(struct team *team) +static struct lb_priv *get_lb_priv(struct team *team) -+{ -+ return (struct lb_priv *) &team->mode_priv; -+} -+ + { + return (struct lb_priv *) &team->mode_priv; + } + +-static bool lb_transmit(struct team *team, struct sk_buff *skb) +struct lb_port_priv { + struct lb_stats __percpu *pcpu_stats; + struct lb_stats_info stats_info; @@ -1610,10 +1201,18 @@ index 0000000..51a4b19 + struct lb_priv *lb_priv, + struct sk_buff *skb, + unsigned char hash) -+{ -+ int port_index; -+ -+ port_index = hash % team->en_port_count; + { +- struct sk_filter *fp; +- struct team_port *port; +- unsigned int hash; + int port_index; + +- fp = rcu_dereference(lb_priv(team)->fp); +- if (unlikely(!fp)) +- goto drop; +- hash = SK_RUN_FILTER(fp, skb); + port_index = hash % team->en_port_count; +- port = team_get_port_by_index_rcu(team, port_index); + return team_get_port_by_index_rcu(team, port_index); +} + @@ -1714,89 +1313,62 @@ index 0000000..51a4b19 + hash = lb_get_skb_hash(lb_priv, skb); + select_tx_port_func = rcu_dereference_bh(lb_priv->select_tx_port_func); + port = select_tx_port_func(team, lb_priv, skb, hash); -+ if (unlikely(!port)) -+ goto drop; -+ skb->dev = port->dev; -+ if (dev_queue_xmit(skb)) -+ return false; + if (unlikely(!port)) + goto drop; + skb->dev = port->dev; + if (dev_queue_xmit(skb)) + return false; + lb_update_tx_stats(tx_bytes, lb_priv, get_lb_port_priv(port), hash); -+ return true; -+ -+drop: -+ dev_kfree_skb_any(skb); -+ return false; -+} -+ -+static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) -+{ + return true; + + drop: +@@ -54,14 +230,16 @@ drop: + + static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) + { +- if (!lb_priv(team)->orig_fprog) { + struct lb_priv *lb_priv = get_lb_priv(team); + + if (!lb_priv->ex->orig_fprog) { -+ ctx->data.bin_val.len = 0; -+ ctx->data.bin_val.ptr = NULL; -+ return 0; -+ } + ctx->data.bin_val.len = 0; + ctx->data.bin_val.ptr = NULL; + return 0; + } +- ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len * + ctx->data.bin_val.len = lb_priv->ex->orig_fprog->len * -+ sizeof(struct sock_filter); + sizeof(struct sock_filter); +- ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter; + ctx->data.bin_val.ptr = lb_priv->ex->orig_fprog->filter; -+ return 0; -+} -+ -+static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, -+ const void *data) -+{ -+ struct sock_fprog *fprog; -+ struct sock_filter *filter = (struct sock_filter *) data; -+ -+ if (data_len % sizeof(struct sock_filter)) -+ return -EINVAL; -+ fprog = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL); -+ if (!fprog) -+ return -ENOMEM; -+ fprog->filter = kmemdup(filter, data_len, GFP_KERNEL); -+ if (!fprog->filter) { -+ kfree(fprog); -+ return -ENOMEM; -+ } -+ fprog->len = data_len / sizeof(struct sock_filter); -+ *pfprog = fprog; -+ return 0; -+} -+ -+static void __fprog_destroy(struct sock_fprog *fprog) -+{ -+ kfree(fprog->filter); -+ kfree(fprog); -+} -+ -+static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) -+{ + return 0; + } + +@@ -94,7 +272,9 @@ static void __fprog_destroy(struct sock_fprog *fprog) + + static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) + { + struct lb_priv *lb_priv = get_lb_priv(team); -+ struct sk_filter *fp = NULL; + struct sk_filter *fp = NULL; + struct sk_filter *orig_fp; -+ struct sock_fprog *fprog = NULL; -+ int err; -+ -+ if (ctx->data.bin_val.len) { -+ err = __fprog_create(&fprog, ctx->data.bin_val.len, -+ ctx->data.bin_val.ptr); -+ if (err) -+ return err; -+ err = sk_unattached_filter_create(&fp, fprog); -+ if (err) { -+ __fprog_destroy(fprog); -+ return err; -+ } -+ } -+ + struct sock_fprog *fprog = NULL; + int err; + +@@ -110,14 +290,238 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) + } + } + +- if (lb_priv(team)->orig_fprog) { + if (lb_priv->ex->orig_fprog) { -+ /* Clear old filter data */ + /* Clear old filter data */ +- __fprog_destroy(lb_priv(team)->orig_fprog); +- sk_unattached_filter_destroy(lb_priv(team)->fp); + __fprog_destroy(lb_priv->ex->orig_fprog); + orig_fp = rcu_dereference_protected(lb_priv->fp, + lockdep_is_held(&team->lock)); + sk_unattached_filter_destroy(orig_fp); -+ } -+ + } + +- rcu_assign_pointer(lb_priv(team)->fp, fp); +- lb_priv(team)->orig_fprog = fprog; + rcu_assign_pointer(lb_priv->fp, fp); + lb_priv->ex->orig_fprog = fprog; + return 0; @@ -2021,16 +1593,13 @@ index 0000000..51a4b19 + schedule_delayed_work(&lb_priv->ex->stats.refresh_dw, 0); + else + cancel_delayed_work(&lb_priv->ex->stats.refresh_dw); -+ return 0; -+} -+ -+static const struct team_option lb_options[] = { -+ { -+ .name = "bpf_hash_func", -+ .type = TEAM_OPTION_TYPE_BINARY, -+ .getter = lb_bpf_func_get, -+ .setter = lb_bpf_func_set, -+ }, + return 0; + } + +@@ -128,30 +532,125 @@ static const struct team_option lb_options[] = { + .getter = lb_bpf_func_get, + .setter = lb_bpf_func_set, + }, + { + .name = "lb_tx_method", + .type = TEAM_OPTION_TYPE_STRING, @@ -2065,10 +1634,12 @@ index 0000000..51a4b19 + .getter = lb_stats_refresh_interval_get, + .setter = lb_stats_refresh_interval_set, + }, -+}; -+ -+static int lb_init(struct team *team) -+{ + }; + + static int lb_init(struct team *team) + { +- return team_options_register(team, lb_options, +- ARRAY_SIZE(lb_options)); + struct lb_priv *lb_priv = get_lb_priv(team); + lb_select_tx_port_func_t *func; + int err; @@ -2101,14 +1672,14 @@ index 0000000..51a4b19 +err_alloc_pcpu_stats: + kfree(lb_priv->ex); + return err; -+} -+ -+static void lb_exit(struct team *team) -+{ + } + + static void lb_exit(struct team *team) + { + struct lb_priv *lb_priv = get_lb_priv(team); + -+ team_options_unregister(team, lb_options, -+ ARRAY_SIZE(lb_options)); + team_options_unregister(team, lb_options, + ARRAY_SIZE(lb_options)); + cancel_delayed_work_sync(&lb_priv->ex->stats.refresh_dw); + free_percpu(lb_priv->pcpu_stats); + kfree(lb_priv->ex); @@ -2134,44 +1705,28 @@ index 0000000..51a4b19 +static void lb_port_disabled(struct team *team, struct team_port *port) +{ + lb_tx_hash_to_port_mapping_null_port(team, port); -+} -+ -+static const struct team_mode_ops lb_mode_ops = { -+ .init = lb_init, -+ .exit = lb_exit, + } + + static const struct team_mode_ops lb_mode_ops = { + .init = lb_init, + .exit = lb_exit, + .port_enter = lb_port_enter, + .port_leave = lb_port_leave, + .port_disabled = lb_port_disabled, -+ .transmit = lb_transmit, -+}; -+ + .transmit = lb_transmit, + }; + +-static struct team_mode lb_mode = { +static const struct team_mode lb_mode = { -+ .kind = "loadbalance", -+ .owner = THIS_MODULE, -+ .priv_size = sizeof(struct lb_priv), + .kind = "loadbalance", + .owner = THIS_MODULE, + .priv_size = sizeof(struct lb_priv), + .port_priv_size = sizeof(struct lb_port_priv), -+ .ops = &lb_mode_ops, -+}; -+ -+static int __init lb_init_module(void) -+{ -+ return team_mode_register(&lb_mode); -+} -+ -+static void __exit lb_cleanup_module(void) -+{ -+ team_mode_unregister(&lb_mode); -+} -+ -+module_init(lb_init_module); -+module_exit(lb_cleanup_module); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Jiri Pirko "); -+MODULE_DESCRIPTION("Load-balancing mode for team"); -+MODULE_ALIAS("team-mode-loadbalance"); + .ops = &lb_mode_ops, + }; + diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c -index a0e8f80..52dd0ec 100644 +index 6abfbdc..52dd0ec 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -1,5 +1,5 @@ @@ -2181,15 +1736,6 @@ index a0e8f80..52dd0ec 100644 * Copyright (c) 2011 Jiri Pirko * * This program is free software; you can redistribute it and/or modify -@@ -50,7 +50,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb) - struct team_port *port; - int port_index; - -- port_index = rr_priv(team)->sent_packets++ % team->port_count; -+ port_index = rr_priv(team)->sent_packets++ % team->en_port_count; - port = team_get_port_by_index_rcu(team, port_index); - port = __get_first_port_up(team, port); - if (unlikely(!port)) @@ -81,7 +81,7 @@ static const struct team_mode_ops rr_mode_ops = { .port_change_mac = rr_port_change_mac, }; @@ -2199,85 +1745,14 @@ index a0e8f80..52dd0ec 100644 .kind = "roundrobin", .owner = THIS_MODULE, .priv_size = sizeof(struct rr_priv), -diff --git a/include/linux/filter.h b/include/linux/filter.h -index 8eeb205..7209099 100644 ---- a/include/linux/filter.h -+++ b/include/linux/filter.h -@@ -126,7 +126,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ - #define SKF_AD_HATYPE 28 - #define SKF_AD_RXHASH 32 - #define SKF_AD_CPU 36 --#define SKF_AD_MAX 40 -+#define SKF_AD_ALU_XOR_X 40 -+#define SKF_AD_MAX 44 - #define SKF_NET_OFF (-0x100000) - #define SKF_LL_OFF (-0x200000) - -@@ -153,6 +154,9 @@ static inline unsigned int sk_filter_len(const struct sk_filter *fp) - extern int sk_filter(struct sock *sk, struct sk_buff *skb); - extern unsigned int sk_run_filter(const struct sk_buff *skb, - const struct sock_filter *filter); -+extern int sk_unattached_filter_create(struct sk_filter **pfp, -+ struct sock_fprog *fprog); -+extern void sk_unattached_filter_destroy(struct sk_filter *fp); - extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); - extern int sk_detach_filter(struct sock *sk); - extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen); -@@ -228,6 +232,7 @@ enum { - BPF_S_ANC_HATYPE, - BPF_S_ANC_RXHASH, - BPF_S_ANC_CPU, -+ BPF_S_ANC_ALU_XOR_X, - }; - - #endif /* __KERNEL__ */ diff --git a/include/linux/if_team.h b/include/linux/if_team.h -index 58404b0..99efd60 100644 +index 8185f57..99efd60 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h -@@ -28,10 +28,28 @@ struct team; - - struct team_port { - struct net_device *dev; -- struct hlist_node hlist; /* node in hash list */ -+ struct hlist_node hlist; /* node in enabled ports hash list */ - struct list_head list; /* node in ordinary list */ - struct team *team; -- int index; -+ int index; /* index of enabled port. If disabled, it's set to -1 */ -+ -+ bool linkup; /* either state.linkup or user.linkup */ -+ -+ struct { -+ bool linkup; -+ u32 speed; -+ u8 duplex; -+ } state; -+ -+ /* Values set by userspace */ -+ struct { -+ bool linkup; -+ bool linkup_enabled; -+ } user; -+ -+ /* Custom gennetlink interface related flags */ -+ bool changed; -+ bool removed; - - /* - * A place for storing original values of the device before it -@@ -42,17 +60,11 @@ struct team_port { +@@ -60,9 +60,11 @@ struct team_port { unsigned int mtu; } orig; -- bool linkup; -- u32 speed; -- u8 duplex; -- -- /* Custom gennetlink interface related flags */ -- bool changed; -- bool removed; -- - struct rcu_head rcu; + long mode_priv[0]; }; @@ -2287,7 +1762,7 @@ index 58404b0..99efd60 100644 struct team_mode_ops { int (*init)(struct team *team); void (*exit)(struct team *team); -@@ -63,30 +75,54 @@ struct team_mode_ops { +@@ -73,6 +75,8 @@ struct team_mode_ops { int (*port_enter)(struct team *team, struct team_port *port); void (*port_leave)(struct team *team, struct team_port *port); void (*port_change_mac)(struct team *team, struct team_port *port); @@ -2296,45 +1771,35 @@ index 58404b0..99efd60 100644 }; enum team_option_type { - TEAM_OPTION_TYPE_U32, - TEAM_OPTION_TYPE_STRING, -+ TEAM_OPTION_TYPE_BINARY, -+ TEAM_OPTION_TYPE_BOOL, -+}; -+ +@@ -82,6 +86,11 @@ enum team_option_type { + TEAM_OPTION_TYPE_BOOL, + }; + +struct team_option_inst_info { + u32 array_index; + struct team_port *port; /* != NULL if per-port */ +}; + -+struct team_gsetter_ctx { -+ union { -+ u32 u32_val; -+ const char *str_val; -+ struct { -+ const void *ptr; -+ u32 len; -+ } bin_val; -+ bool bool_val; -+ } data; + struct team_gsetter_ctx { + union { + u32 u32_val; +@@ -92,23 +101,28 @@ struct team_gsetter_ctx { + } bin_val; + bool bool_val; + } data; +- struct team_port *port; + struct team_option_inst_info *info; }; struct team_option { struct list_head list; const char *name; -+ bool per_port; + bool per_port; + unsigned int array_size; /* != 0 means the option is array */ enum team_option_type type; -- int (*getter)(struct team *team, void *arg); -- int (*setter)(struct team *team, void *arg); -- -- /* Custom gennetlink interface related flags */ -- bool changed; -- bool removed; + int (*init)(struct team *team, struct team_option_inst_info *info); -+ int (*getter)(struct team *team, struct team_gsetter_ctx *ctx); -+ int (*setter)(struct team *team, struct team_gsetter_ctx *ctx); + int (*getter)(struct team *team, struct team_gsetter_ctx *ctx); + int (*setter)(struct team *team, struct team_gsetter_ctx *ctx); }; +extern void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info); @@ -2349,36 +1814,7 @@ index 58404b0..99efd60 100644 const struct team_mode_ops *ops; }; -@@ -103,13 +139,15 @@ struct team { - struct mutex lock; /* used for overall locking, e.g. port lists write */ - - /* -- * port lists with port count -+ * List of enabled ports and their count - */ -- int port_count; -- struct hlist_head port_hlist[TEAM_PORT_HASHENTRIES]; -- struct list_head port_list; -+ int en_port_count; -+ struct hlist_head en_port_hlist[TEAM_PORT_HASHENTRIES]; -+ -+ struct list_head port_list; /* list of all ports */ - - struct list_head option_list; -+ struct list_head option_inst_list; /* list of option instances */ - - const struct team_mode *mode; - struct team_mode_ops ops; -@@ -119,7 +157,7 @@ struct team { - static inline struct hlist_head *team_port_index_hash(struct team *team, - int port_index) - { -- return &team->port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)]; -+ return &team->en_port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)]; - } - - static inline struct team_port *team_get_port_by_index(struct team *team, -@@ -154,8 +192,8 @@ extern int team_options_register(struct team *team, +@@ -178,8 +192,8 @@ extern int team_options_register(struct team *team, extern void team_options_unregister(struct team *team, const struct team_option *option, size_t option_count); @@ -2389,123 +1825,14 @@ index 58404b0..99efd60 100644 #endif /* __KERNEL__ */ -@@ -216,6 +254,8 @@ enum { - TEAM_ATTR_OPTION_TYPE, /* u8 */ +@@ -241,6 +255,7 @@ enum { TEAM_ATTR_OPTION_DATA, /* dynamic */ TEAM_ATTR_OPTION_REMOVED, /* flag */ -+ TEAM_ATTR_OPTION_PORT_IFINDEX, /* u32 */ /* for per-port options */ + TEAM_ATTR_OPTION_PORT_IFINDEX, /* u32 */ /* for per-port options */ + TEAM_ATTR_OPTION_ARRAY_INDEX, /* u32 */ /* for array options */ __TEAM_ATTR_OPTION_MAX, TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, -diff --git a/net/core/filter.c b/net/core/filter.c -index 6f755cc..95d05a6 100644 ---- a/net/core/filter.c -+++ b/net/core/filter.c -@@ -317,6 +317,9 @@ load_b: - case BPF_S_ANC_CPU: - A = raw_smp_processor_id(); - continue; -+ case BPF_S_ANC_ALU_XOR_X: -+ A ^= X; -+ continue; - case BPF_S_ANC_NLATTR: { - struct nlattr *nla; - -@@ -561,6 +564,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) - ANCILLARY(HATYPE); - ANCILLARY(RXHASH); - ANCILLARY(CPU); -+ ANCILLARY(ALU_XOR_X); - } - } - ftest->code = code; -@@ -589,6 +593,67 @@ void sk_filter_release_rcu(struct rcu_head *rcu) - } - EXPORT_SYMBOL(sk_filter_release_rcu); - -+static int __sk_prepare_filter(struct sk_filter *fp) -+{ -+ int err; -+ -+ fp->bpf_func = sk_run_filter; -+ -+ err = sk_chk_filter(fp->insns, fp->len); -+ if (err) -+ return err; -+ -+ bpf_jit_compile(fp); -+ return 0; -+} -+ -+/** -+ * sk_unattached_filter_create - create an unattached filter -+ * @fprog: the filter program -+ * @sk: the socket to use -+ * -+ * Create a filter independent ofr any socket. We first run some -+ * sanity checks on it to make sure it does not explode on us later. -+ * If an error occurs or there is insufficient memory for the filter -+ * a negative errno code is returned. On success the return is zero. -+ */ -+int sk_unattached_filter_create(struct sk_filter **pfp, -+ struct sock_fprog *fprog) -+{ -+ struct sk_filter *fp; -+ unsigned int fsize = sizeof(struct sock_filter) * fprog->len; -+ int err; -+ -+ /* Make sure new filter is there and in the right amounts. */ -+ if (fprog->filter == NULL) -+ return -EINVAL; -+ -+ fp = kmalloc(fsize + sizeof(*fp), GFP_KERNEL); -+ if (!fp) -+ return -ENOMEM; -+ memcpy(fp->insns, fprog->filter, fsize); -+ -+ atomic_set(&fp->refcnt, 1); -+ fp->len = fprog->len; -+ -+ err = __sk_prepare_filter(fp); -+ if (err) -+ goto free_mem; -+ -+ *pfp = fp; -+ return 0; -+free_mem: -+ kfree(fp); -+ return err; -+} -+EXPORT_SYMBOL_GPL(sk_unattached_filter_create); -+ -+void sk_unattached_filter_destroy(struct sk_filter *fp) -+{ -+ sk_filter_release(fp); -+} -+EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy); -+ - /** - * sk_attach_filter - attach a socket filter - * @fprog: the filter program -@@ -619,16 +684,13 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) - - atomic_set(&fp->refcnt, 1); - fp->len = fprog->len; -- fp->bpf_func = sk_run_filter; - -- err = sk_chk_filter(fp->insns, fp->len); -+ err = __sk_prepare_filter(fp); - if (err) { - sk_filter_uncharge(sk, fp); - return err; - } - -- bpf_jit_compile(fp); -- - old_fp = rcu_dereference_protected(sk->sk_filter, - sock_owned_by_user(sk)); - rcu_assign_pointer(sk->sk_filter, fp); _______________________________________________ kernel mailing list kernel@lists.fedoraproject.org diff --git a/uprobes-3.4-backport.patch b/uprobes-3.4-backport.patch deleted file mode 100644 index 4a5bfe60b..000000000 --- a/uprobes-3.4-backport.patch +++ /dev/null @@ -1,6983 +0,0 @@ -Modification of the kernel config is needed: - +CONFIG_ARCH_SUPPORTS_UPROBES=y - +CONFIG_UPROBES=y - +CONFIG_UPROBE_EVENT=y - +CONFIG_PROBE_EVENTS=y - -The split-out series is available in the git repository at: - - git://fedorapeople.org/home/fedora/aarapov/public_git/kernel-uprobes.git f17_uprobes_upstream - -Ingo Molnar (3): - uprobes/core: Clean up, refactor and improve the code - uprobes: Move to kernel/events/ - uprobes: Update copyright notices - -Srikar Dronamraju (20): - uprobes, mm, x86: Add the ability to install and remove uprobes breakpoints - uprobes/core: Make instruction tables volatile - uprobes/core: Remove uprobe_opcode_sz - uprobes/core: Move insn to arch specific structure - uprobes/core: Make macro names consistent - uprobes/core: Make order of function parameters consistent across functions - uprobes/core: Rename bkpt to swbp - uprobes/core: Handle breakpoint and singlestep exceptions - uprobes/core: Allocate XOL slots for uprobes use - uprobes/core: Optimize probe hits with the help of a counter - uprobes/core: Make background page replacement logic account for rss_stat counters - uprobes/core: Decrement uprobe count before the pages are unmapped - tracing: Modify is_delete, is_return from int to bool - tracing: Extract out common code for kprobes/uprobes trace events - tracing: Provide trace events interface for uprobes - tracing: Fix kconfig warning due to a typo - perf probe: Provide perf interface for uprobes - perf probe: Detect probe target when m/x options are absent - perf symbols: Check for valid dso before creating map - perf uprobes: Remove unnecessary check before strlist__delete - -Signed-off-by: Anton Arapov ---- - Documentation/trace/uprobetracer.txt | 113 +++ - arch/Kconfig | 17 + - arch/x86/Kconfig | 5 +- - arch/x86/include/asm/thread_info.h | 2 + - arch/x86/include/asm/uprobes.h | 57 ++ - arch/x86/kernel/Makefile | 1 + - arch/x86/kernel/signal.c | 6 + - arch/x86/kernel/uprobes.c | 674 +++++++++++++ - include/linux/mm_types.h | 2 + - include/linux/sched.h | 4 + - include/linux/uprobes.h | 165 +++ - kernel/events/Makefile | 3 + - kernel/events/uprobes.c | 1667 +++++++++++++++++++++++++++++++ - kernel/fork.c | 9 + - kernel/signal.c | 4 + - kernel/trace/Kconfig | 20 + - kernel/trace/Makefile | 2 + - kernel/trace/trace.h | 5 + - kernel/trace/trace_kprobe.c | 899 +---------------- - kernel/trace/trace_probe.c | 839 ++++++++++++++++ - kernel/trace/trace_probe.h | 161 +++ - kernel/trace/trace_uprobe.c | 788 +++++++++++++++ - mm/memory.c | 3 + - mm/mmap.c | 33 +- - tools/perf/Documentation/perf-probe.txt | 19 +- - tools/perf/builtin-probe.c | 86 +- - tools/perf/util/probe-event.c | 418 ++++++-- - tools/perf/util/probe-event.h | 12 +- - tools/perf/util/symbol.c | 11 + - tools/perf/util/symbol.h | 1 + - 30 files changed, 5047 insertions(+), 979 deletions(-) - create mode 100644 Documentation/trace/uprobetracer.txt - create mode 100644 arch/x86/include/asm/uprobes.h - create mode 100644 arch/x86/kernel/uprobes.c - create mode 100644 include/linux/uprobes.h - create mode 100644 kernel/events/uprobes.c - create mode 100644 kernel/trace/trace_probe.c - create mode 100644 kernel/trace/trace_probe.h - create mode 100644 kernel/trace/trace_uprobe.c - -diff --git a/Documentation/trace/uprobetracer.txt b/Documentation/trace/uprobetracer.txt -new file mode 100644 -index 0000000..24ce682 ---- /dev/null -+++ b/Documentation/trace/uprobetracer.txt -@@ -0,0 +1,113 @@ -+ Uprobe-tracer: Uprobe-based Event Tracing -+ ========================================= -+ Documentation written by Srikar Dronamraju -+ -+Overview -+-------- -+Uprobe based trace events are similar to kprobe based trace events. -+To enable this feature, build your kernel with CONFIG_UPROBE_EVENT=y. -+ -+Similar to the kprobe-event tracer, this doesn't need to be activated via -+current_tracer. Instead of that, add probe points via -+/sys/kernel/debug/tracing/uprobe_events, and enable it via -+/sys/kernel/debug/tracing/events/uprobes//enabled. -+ -+However unlike kprobe-event tracer, the uprobe event interface expects the -+user to calculate the offset of the probepoint in the object -+ -+Synopsis of uprobe_tracer -+------------------------- -+ p[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS] : Set a probe -+ -+ GRP : Group name. If omitted, use "uprobes" for it. -+ EVENT : Event name. If omitted, the event name is generated -+ based on SYMBOL+offs. -+ PATH : path to an executable or a library. -+ SYMBOL[+offs] : Symbol+offset where the probe is inserted. -+ -+ FETCHARGS : Arguments. Each probe can have up to 128 args. -+ %REG : Fetch register REG -+ -+Event Profiling -+--------------- -+ You can check the total number of probe hits and probe miss-hits via -+/sys/kernel/debug/tracing/uprobe_profile. -+ The first column is event name, the second is the number of probe hits, -+the third is the number of probe miss-hits. -+ -+Usage examples -+-------------- -+To add a probe as a new event, write a new definition to uprobe_events -+as below. -+ -+ echo 'p: /bin/bash:0x4245c0' > /sys/kernel/debug/tracing/uprobe_events -+ -+ This sets a uprobe at an offset of 0x4245c0 in the executable /bin/bash -+ -+ echo > /sys/kernel/debug/tracing/uprobe_events -+ -+ This clears all probe points. -+ -+The following example shows how to dump the instruction pointer and %ax -+a register at the probed text address. Here we are trying to probe -+function zfree in /bin/zsh -+ -+ # cd /sys/kernel/debug/tracing/ -+ # cat /proc/`pgrep zsh`/maps | grep /bin/zsh | grep r-xp -+ 00400000-0048a000 r-xp 00000000 08:03 130904 /bin/zsh -+ # objdump -T /bin/zsh | grep -w zfree -+ 0000000000446420 g DF .text 0000000000000012 Base zfree -+ -+0x46420 is the offset of zfree in object /bin/zsh that is loaded at -+0x00400000. Hence the command to probe would be : -+ -+ # echo 'p /bin/zsh:0x46420 %ip %ax' > uprobe_events -+ -+Please note: User has to explicitly calculate the offset of the probepoint -+in the object. We can see the events that are registered by looking at the -+uprobe_events file. -+ -+ # cat uprobe_events -+ p:uprobes/p_zsh_0x46420 /bin/zsh:0x00046420 arg1=%ip arg2=%ax -+ -+The format of events can be seen by viewing the file events/uprobes/p_zsh_0x46420/format -+ -+ # cat events/uprobes/p_zsh_0x46420/format -+ name: p_zsh_0x46420 -+ ID: 922 -+ format: -+ field:unsigned short common_type; offset:0; size:2; signed:0; -+ field:unsigned char common_flags; offset:2; size:1; signed:0; -+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0; -+ field:int common_pid; offset:4; size:4; signed:1; -+ field:int common_padding; offset:8; size:4; signed:1; -+ -+ field:unsigned long __probe_ip; offset:12; size:4; signed:0; -+ field:u32 arg1; offset:16; size:4; signed:0; -+ field:u32 arg2; offset:20; size:4; signed:0; -+ -+ print fmt: "(%lx) arg1=%lx arg2=%lx", REC->__probe_ip, REC->arg1, REC->arg2 -+ -+Right after definition, each event is disabled by default. For tracing these -+events, you need to enable it by: -+ -+ # echo 1 > events/uprobes/enable -+ -+Lets disable the event after sleeping for some time. -+ # sleep 20 -+ # echo 0 > events/uprobes/enable -+ -+And you can see the traced information via /sys/kernel/debug/tracing/trace. -+ -+ # cat trace -+ # tracer: nop -+ # -+ # TASK-PID CPU# TIMESTAMP FUNCTION -+ # | | | | | -+ zsh-24842 [006] 258544.995456: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 -+ zsh-24842 [007] 258545.000270: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 -+ zsh-24842 [002] 258545.043929: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 -+ zsh-24842 [004] 258547.046129: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 -+ -+Each line shows us probes were triggered for a pid 24842 with ip being -+0x446421 and contents of ax register being 79. -diff --git a/arch/Kconfig b/arch/Kconfig -index 684eb5a..2880abf 100644 ---- a/arch/Kconfig -+++ b/arch/Kconfig -@@ -76,6 +76,23 @@ config OPTPROBES - depends on KPROBES && HAVE_OPTPROBES - depends on !PREEMPT - -+config UPROBES -+ bool "Transparent user-space probes (EXPERIMENTAL)" -+ depends on UPROBE_EVENT && PERF_EVENTS -+ default n -+ help -+ Uprobes is the user-space counterpart to kprobes: they -+ enable instrumentation applications (such as 'perf probe') -+ to establish unintrusive probes in user-space binaries and -+ libraries, by executing handler functions when the probes -+ are hit by user-space applications. -+ -+ ( These probes come in the form of single-byte breakpoints, -+ managed by the kernel and kept transparent to the probed -+ application. ) -+ -+ If in doubt, say "N". -+ - config HAVE_EFFICIENT_UNALIGNED_ACCESS - bool - help -diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig -index c9866b0..1f5c307 100644 ---- a/arch/x86/Kconfig -+++ b/arch/x86/Kconfig -@@ -84,7 +84,7 @@ config X86 - select DCACHE_WORD_ACCESS - - config INSTRUCTION_DECODER -- def_bool (KPROBES || PERF_EVENTS) -+ def_bool (KPROBES || PERF_EVENTS || UPROBES) - - config OUTPUT_FORMAT - string -@@ -243,6 +243,9 @@ config ARCH_CPU_PROBE_RELEASE - def_bool y - depends on HOTPLUG_CPU - -+config ARCH_SUPPORTS_UPROBES -+ def_bool y -+ - source "init/Kconfig" - source "kernel/Kconfig.freezer" - -diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h -index ad6df8c..0710c11 100644 ---- a/arch/x86/include/asm/thread_info.h -+++ b/arch/x86/include/asm/thread_info.h -@@ -85,6 +85,7 @@ struct thread_info { - #define TIF_SECCOMP 8 /* secure computing */ - #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ - #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ -+#define TIF_UPROBE 12 /* breakpointed or singlestepping */ - #define TIF_NOTSC 16 /* TSC is not accessible in userland */ - #define TIF_IA32 17 /* IA32 compatibility process */ - #define TIF_FORK 18 /* ret_from_fork */ -@@ -109,6 +110,7 @@ struct thread_info { - #define _TIF_SECCOMP (1 << TIF_SECCOMP) - #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) - #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) -+#define _TIF_UPROBE (1 << TIF_UPROBE) - #define _TIF_NOTSC (1 << TIF_NOTSC) - #define _TIF_IA32 (1 << TIF_IA32) - #define _TIF_FORK (1 << TIF_FORK) -diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h -new file mode 100644 -index 0000000..1e9bed1 ---- /dev/null -+++ b/arch/x86/include/asm/uprobes.h -@@ -0,0 +1,57 @@ -+#ifndef _ASM_UPROBES_H -+#define _ASM_UPROBES_H -+/* -+ * User-space Probes (UProbes) for x86 -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * Copyright (C) IBM Corporation, 2008-2011 -+ * Authors: -+ * Srikar Dronamraju -+ * Jim Keniston -+ */ -+ -+#include -+ -+typedef u8 uprobe_opcode_t; -+ -+#define MAX_UINSN_BYTES 16 -+#define UPROBE_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ -+ -+#define UPROBE_SWBP_INSN 0xcc -+#define UPROBE_SWBP_INSN_SIZE 1 -+ -+struct arch_uprobe { -+ u16 fixups; -+ u8 insn[MAX_UINSN_BYTES]; -+#ifdef CONFIG_X86_64 -+ unsigned long rip_rela_target_address; -+#endif -+}; -+ -+struct arch_uprobe_task { -+ unsigned long saved_trap_nr; -+#ifdef CONFIG_X86_64 -+ unsigned long saved_scratch_register; -+#endif -+}; -+ -+extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); -+extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); -+extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); -+extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); -+extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); -+extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); -+#endif /* _ASM_UPROBES_H */ -diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile -index 532d2e0..d23d835 100644 ---- a/arch/x86/kernel/Makefile -+++ b/arch/x86/kernel/Makefile -@@ -101,6 +101,7 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o - - obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o - obj-$(CONFIG_OF) += devicetree.o -+obj-$(CONFIG_UPROBES) += uprobes.o - - ### - # 64 bit specific files -diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c -index 115eac4..041af2f 100644 ---- a/arch/x86/kernel/signal.c -+++ b/arch/x86/kernel/signal.c -@@ -18,6 +18,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -824,6 +825,11 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) - mce_notify_process(); - #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ - -+ if (thread_info_flags & _TIF_UPROBE) { -+ clear_thread_flag(TIF_UPROBE); -+ uprobe_notify_resume(regs); -+ } -+ - /* deal with pending signal delivery */ - if (thread_info_flags & _TIF_SIGPENDING) - do_signal(regs); -diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c -new file mode 100644 -index 0000000..dc4e910 ---- /dev/null -+++ b/arch/x86/kernel/uprobes.c -@@ -0,0 +1,674 @@ -+/* -+ * User-space Probes (UProbes) for x86 -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * Copyright (C) IBM Corporation, 2008-2011 -+ * Authors: -+ * Srikar Dronamraju -+ * Jim Keniston -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+/* Post-execution fixups. */ -+ -+/* No fixup needed */ -+#define UPROBE_FIX_NONE 0x0 -+ -+/* Adjust IP back to vicinity of actual insn */ -+#define UPROBE_FIX_IP 0x1 -+ -+/* Adjust the return address of a call insn */ -+#define UPROBE_FIX_CALL 0x2 -+ -+#define UPROBE_FIX_RIP_AX 0x8000 -+#define UPROBE_FIX_RIP_CX 0x4000 -+ -+#define UPROBE_TRAP_NR UINT_MAX -+ -+/* Adaptations for mhiramat x86 decoder v14. */ -+#define OPCODE1(insn) ((insn)->opcode.bytes[0]) -+#define OPCODE2(insn) ((insn)->opcode.bytes[1]) -+#define OPCODE3(insn) ((insn)->opcode.bytes[2]) -+#define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value) -+ -+#define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ -+ (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ -+ (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \ -+ (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \ -+ (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \ -+ << (row % 32)) -+ -+/* -+ * Good-instruction tables for 32-bit apps. This is non-const and volatile -+ * to keep gcc from statically optimizing it out, as variable_test_bit makes -+ * some versions of gcc to think only *(unsigned long*) is used. -+ */ -+static volatile u32 good_insns_32[256 / 32] = { -+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+ /* ---------------------------------------------- */ -+ W(0x00, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) | /* 00 */ -+ W(0x10, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) , /* 10 */ -+ W(0x20, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1) | /* 20 */ -+ W(0x30, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1) , /* 30 */ -+ W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */ -+ W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ -+ W(0x60, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */ -+ W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */ -+ W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ -+ W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ -+ W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */ -+ W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ -+ W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */ -+ W(0xd0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ -+ W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */ -+ W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1) /* f0 */ -+ /* ---------------------------------------------- */ -+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+}; -+ -+/* Using this for both 64-bit and 32-bit apps */ -+static volatile u32 good_2byte_insns[256 / 32] = { -+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+ /* ---------------------------------------------- */ -+ W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */ -+ W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* 10 */ -+ W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 20 */ -+ W(0x30, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 30 */ -+ W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */ -+ W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ -+ W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 60 */ -+ W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) , /* 70 */ -+ W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ -+ W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ -+ W(0xa0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1) | /* a0 */ -+ W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ -+ W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* c0 */ -+ W(0xd0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ -+ W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* e0 */ -+ W(0xf0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) /* f0 */ -+ /* ---------------------------------------------- */ -+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+}; -+ -+#ifdef CONFIG_X86_64 -+/* Good-instruction tables for 64-bit apps */ -+static volatile u32 good_insns_64[256 / 32] = { -+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+ /* ---------------------------------------------- */ -+ W(0x00, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 00 */ -+ W(0x10, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 10 */ -+ W(0x20, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 20 */ -+ W(0x30, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 30 */ -+ W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 40 */ -+ W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ -+ W(0x60, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */ -+ W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */ -+ W(0x80, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ -+ W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ -+ W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */ -+ W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ -+ W(0xc0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */ -+ W(0xd0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ -+ W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */ -+ W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1) /* f0 */ -+ /* ---------------------------------------------- */ -+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+}; -+#endif -+#undef W -+ -+/* -+ * opcodes we'll probably never support: -+ * -+ * 6c-6d, e4-e5, ec-ed - in -+ * 6e-6f, e6-e7, ee-ef - out -+ * cc, cd - int3, int -+ * cf - iret -+ * d6 - illegal instruction -+ * f1 - int1/icebp -+ * f4 - hlt -+ * fa, fb - cli, sti -+ * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2 -+ * -+ * invalid opcodes in 64-bit mode: -+ * -+ * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, 82, c4-c5, d4-d5 -+ * 63 - we support this opcode in x86_64 but not in i386. -+ * -+ * opcodes we may need to refine support for: -+ * -+ * 0f - 2-byte instructions: For many of these instructions, the validity -+ * depends on the prefix and/or the reg field. On such instructions, we -+ * just consider the opcode combination valid if it corresponds to any -+ * valid instruction. -+ * -+ * 8f - Group 1 - only reg = 0 is OK -+ * c6-c7 - Group 11 - only reg = 0 is OK -+ * d9-df - fpu insns with some illegal encodings -+ * f2, f3 - repnz, repz prefixes. These are also the first byte for -+ * certain floating-point instructions, such as addsd. -+ * -+ * fe - Group 4 - only reg = 0 or 1 is OK -+ * ff - Group 5 - only reg = 0-6 is OK -+ * -+ * others -- Do we need to support these? -+ * -+ * 0f - (floating-point?) prefetch instructions -+ * 07, 17, 1f - pop es, pop ss, pop ds -+ * 26, 2e, 36, 3e - es:, cs:, ss:, ds: segment prefixes -- -+ * but 64 and 65 (fs: and gs:) seem to be used, so we support them -+ * 67 - addr16 prefix -+ * ce - into -+ * f0 - lock prefix -+ */ -+ -+/* -+ * TODO: -+ * - Where necessary, examine the modrm byte and allow only valid instructions -+ * in the different Groups and fpu instructions. -+ */ -+ -+static bool is_prefix_bad(struct insn *insn) -+{ -+ int i; -+ -+ for (i = 0; i < insn->prefixes.nbytes; i++) { -+ switch (insn->prefixes.bytes[i]) { -+ case 0x26: /* INAT_PFX_ES */ -+ case 0x2E: /* INAT_PFX_CS */ -+ case 0x36: /* INAT_PFX_DS */ -+ case 0x3E: /* INAT_PFX_SS */ -+ case 0xF0: /* INAT_PFX_LOCK */ -+ return true; -+ } -+ } -+ return false; -+} -+ -+static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn) -+{ -+ insn_init(insn, auprobe->insn, false); -+ -+ /* Skip good instruction prefixes; reject "bad" ones. */ -+ insn_get_opcode(insn); -+ if (is_prefix_bad(insn)) -+ return -ENOTSUPP; -+ -+ if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_32)) -+ return 0; -+ -+ if (insn->opcode.nbytes == 2) { -+ if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) -+ return 0; -+ } -+ -+ return -ENOTSUPP; -+} -+ -+/* -+ * Figure out which fixups arch_uprobe_post_xol() will need to perform, and -+ * annotate arch_uprobe->fixups accordingly. To start with, -+ * arch_uprobe->fixups is either zero or it reflects rip-related fixups. -+ */ -+static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) -+{ -+ bool fix_ip = true, fix_call = false; /* defaults */ -+ int reg; -+ -+ insn_get_opcode(insn); /* should be a nop */ -+ -+ switch (OPCODE1(insn)) { -+ case 0xc3: /* ret/lret */ -+ case 0xcb: -+ case 0xc2: -+ case 0xca: -+ /* ip is correct */ -+ fix_ip = false; -+ break; -+ case 0xe8: /* call relative - Fix return addr */ -+ fix_call = true; -+ break; -+ case 0x9a: /* call absolute - Fix return addr, not ip */ -+ fix_call = true; -+ fix_ip = false; -+ break; -+ case 0xff: -+ insn_get_modrm(insn); -+ reg = MODRM_REG(insn); -+ if (reg == 2 || reg == 3) { -+ /* call or lcall, indirect */ -+ /* Fix return addr; ip is correct. */ -+ fix_call = true; -+ fix_ip = false; -+ } else if (reg == 4 || reg == 5) { -+ /* jmp or ljmp, indirect */ -+ /* ip is correct. */ -+ fix_ip = false; -+ } -+ break; -+ case 0xea: /* jmp absolute -- ip is correct */ -+ fix_ip = false; -+ break; -+ default: -+ break; -+ } -+ if (fix_ip) -+ auprobe->fixups |= UPROBE_FIX_IP; -+ if (fix_call) -+ auprobe->fixups |= UPROBE_FIX_CALL; -+} -+ -+#ifdef CONFIG_X86_64 -+/* -+ * If arch_uprobe->insn doesn't use rip-relative addressing, return -+ * immediately. Otherwise, rewrite the instruction so that it accesses -+ * its memory operand indirectly through a scratch register. Set -+ * arch_uprobe->fixups and arch_uprobe->rip_rela_target_address -+ * accordingly. (The contents of the scratch register will be saved -+ * before we single-step the modified instruction, and restored -+ * afterward.) -+ * -+ * We do this because a rip-relative instruction can access only a -+ * relatively small area (+/- 2 GB from the instruction), and the XOL -+ * area typically lies beyond that area. At least for instructions -+ * that store to memory, we can't execute the original instruction -+ * and "fix things up" later, because the misdirected store could be -+ * disastrous. -+ * -+ * Some useful facts about rip-relative instructions: -+ * -+ * - There's always a modrm byte. -+ * - There's never a SIB byte. -+ * - The displacement is always 4 bytes. -+ */ -+static void -+handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) -+{ -+ u8 *cursor; -+ u8 reg; -+ -+ if (mm->context.ia32_compat) -+ return; -+ -+ auprobe->rip_rela_target_address = 0x0; -+ if (!insn_rip_relative(insn)) -+ return; -+ -+ /* -+ * insn_rip_relative() would have decoded rex_prefix, modrm. -+ * Clear REX.b bit (extension of MODRM.rm field): -+ * we want to encode rax/rcx, not r8/r9. -+ */ -+ if (insn->rex_prefix.nbytes) { -+ cursor = auprobe->insn + insn_offset_rex_prefix(insn); -+ *cursor &= 0xfe; /* Clearing REX.B bit */ -+ } -+ -+ /* -+ * Point cursor at the modrm byte. The next 4 bytes are the -+ * displacement. Beyond the displacement, for some instructions, -+ * is the immediate operand. -+ */ -+ cursor = auprobe->insn + insn_offset_modrm(insn); -+ insn_get_length(insn); -+ -+ /* -+ * Convert from rip-relative addressing to indirect addressing -+ * via a scratch register. Change the r/m field from 0x5 (%rip) -+ * to 0x0 (%rax) or 0x1 (%rcx), and squeeze out the offset field. -+ */ -+ reg = MODRM_REG(insn); -+ if (reg == 0) { -+ /* -+ * The register operand (if any) is either the A register -+ * (%rax, %eax, etc.) or (if the 0x4 bit is set in the -+ * REX prefix) %r8. In any case, we know the C register -+ * is NOT the register operand, so we use %rcx (register -+ * #1) for the scratch register. -+ */ -+ auprobe->fixups = UPROBE_FIX_RIP_CX; -+ /* Change modrm from 00 000 101 to 00 000 001. */ -+ *cursor = 0x1; -+ } else { -+ /* Use %rax (register #0) for the scratch register. */ -+ auprobe->fixups = UPROBE_FIX_RIP_AX; -+ /* Change modrm from 00 xxx 101 to 00 xxx 000 */ -+ *cursor = (reg << 3); -+ } -+ -+ /* Target address = address of next instruction + (signed) offset */ -+ auprobe->rip_rela_target_address = (long)insn->length + insn->displacement.value; -+ -+ /* Displacement field is gone; slide immediate field (if any) over. */ -+ if (insn->immediate.nbytes) { -+ cursor++; -+ memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes); -+ } -+ return; -+} -+ -+static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) -+{ -+ insn_init(insn, auprobe->insn, true); -+ -+ /* Skip good instruction prefixes; reject "bad" ones. */ -+ insn_get_opcode(insn); -+ if (is_prefix_bad(insn)) -+ return -ENOTSUPP; -+ -+ if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_64)) -+ return 0; -+ -+ if (insn->opcode.nbytes == 2) { -+ if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) -+ return 0; -+ } -+ return -ENOTSUPP; -+} -+ -+static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) -+{ -+ if (mm->context.ia32_compat) -+ return validate_insn_32bits(auprobe, insn); -+ return validate_insn_64bits(auprobe, insn); -+} -+#else /* 32-bit: */ -+static void handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) -+{ -+ /* No RIP-relative addressing on 32-bit */ -+} -+ -+static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) -+{ -+ return validate_insn_32bits(auprobe, insn); -+} -+#endif /* CONFIG_X86_64 */ -+ -+/** -+ * arch_uprobe_analyze_insn - instruction analysis including validity and fixups. -+ * @mm: the probed address space. -+ * @arch_uprobe: the probepoint information. -+ * Return 0 on success or a -ve number on error. -+ */ -+int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) -+{ -+ int ret; -+ struct insn insn; -+ -+ auprobe->fixups = 0; -+ ret = validate_insn_bits(auprobe, mm, &insn); -+ if (ret != 0) -+ return ret; -+ -+ handle_riprel_insn(auprobe, mm, &insn); -+ prepare_fixups(auprobe, &insn); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_X86_64 -+/* -+ * If we're emulating a rip-relative instruction, save the contents -+ * of the scratch register and store the target address in that register. -+ */ -+static void -+pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, -+ struct arch_uprobe_task *autask) -+{ -+ if (auprobe->fixups & UPROBE_FIX_RIP_AX) { -+ autask->saved_scratch_register = regs->ax; -+ regs->ax = current->utask->vaddr; -+ regs->ax += auprobe->rip_rela_target_address; -+ } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) { -+ autask->saved_scratch_register = regs->cx; -+ regs->cx = current->utask->vaddr; -+ regs->cx += auprobe->rip_rela_target_address; -+ } -+} -+#else -+static void -+pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, -+ struct arch_uprobe_task *autask) -+{ -+ /* No RIP-relative addressing on 32-bit */ -+} -+#endif -+ -+/* -+ * arch_uprobe_pre_xol - prepare to execute out of line. -+ * @auprobe: the probepoint information. -+ * @regs: reflects the saved user state of current task. -+ */ -+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -+{ -+ struct arch_uprobe_task *autask; -+ -+ autask = ¤t->utask->autask; -+ autask->saved_trap_nr = current->thread.trap_nr; -+ current->thread.trap_nr = UPROBE_TRAP_NR; -+ regs->ip = current->utask->xol_vaddr; -+ pre_xol_rip_insn(auprobe, regs, autask); -+ -+ return 0; -+} -+ -+/* -+ * This function is called by arch_uprobe_post_xol() to adjust the return -+ * address pushed by a call instruction executed out of line. -+ */ -+static int adjust_ret_addr(unsigned long sp, long correction) -+{ -+ int rasize, ncopied; -+ long ra = 0; -+ -+ if (is_ia32_task()) -+ rasize = 4; -+ else -+ rasize = 8; -+ -+ ncopied = copy_from_user(&ra, (void __user *)sp, rasize); -+ if (unlikely(ncopied)) -+ return -EFAULT; -+ -+ ra += correction; -+ ncopied = copy_to_user((void __user *)sp, &ra, rasize); -+ if (unlikely(ncopied)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+#ifdef CONFIG_X86_64 -+static bool is_riprel_insn(struct arch_uprobe *auprobe) -+{ -+ return ((auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) != 0); -+} -+ -+static void -+handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) -+{ -+ if (is_riprel_insn(auprobe)) { -+ struct arch_uprobe_task *autask; -+ -+ autask = ¤t->utask->autask; -+ if (auprobe->fixups & UPROBE_FIX_RIP_AX) -+ regs->ax = autask->saved_scratch_register; -+ else -+ regs->cx = autask->saved_scratch_register; -+ -+ /* -+ * The original instruction includes a displacement, and so -+ * is 4 bytes longer than what we've just single-stepped. -+ * Fall through to handle stuff like "jmpq *...(%rip)" and -+ * "callq *...(%rip)". -+ */ -+ if (correction) -+ *correction += 4; -+ } -+} -+#else -+static void -+handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) -+{ -+ /* No RIP-relative addressing on 32-bit */ -+} -+#endif -+ -+/* -+ * If xol insn itself traps and generates a signal(Say, -+ * SIGILL/SIGSEGV/etc), then detect the case where a singlestepped -+ * instruction jumps back to its own address. It is assumed that anything -+ * like do_page_fault/do_trap/etc sets thread.trap_nr != -1. -+ * -+ * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr, -+ * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to -+ * UPROBE_TRAP_NR == -1 set by arch_uprobe_pre_xol(). -+ */ -+bool arch_uprobe_xol_was_trapped(struct task_struct *t) -+{ -+ if (t->thread.trap_nr != UPROBE_TRAP_NR) -+ return true; -+ -+ return false; -+} -+ -+/* -+ * Called after single-stepping. To avoid the SMP problems that can -+ * occur when we temporarily put back the original opcode to -+ * single-step, we single-stepped a copy of the instruction. -+ * -+ * This function prepares to resume execution after the single-step. -+ * We have to fix things up as follows: -+ * -+ * Typically, the new ip is relative to the copied instruction. We need -+ * to make it relative to the original instruction (FIX_IP). Exceptions -+ * are return instructions and absolute or indirect jump or call instructions. -+ * -+ * If the single-stepped instruction was a call, the return address that -+ * is atop the stack is the address following the copied instruction. We -+ * need to make it the address following the original instruction (FIX_CALL). -+ * -+ * If the original instruction was a rip-relative instruction such as -+ * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent -+ * instruction using a scratch register -- e.g., "movl %edx,(%rax)". -+ * We need to restore the contents of the scratch register and adjust -+ * the ip, keeping in mind that the instruction we executed is 4 bytes -+ * shorter than the original instruction (since we squeezed out the offset -+ * field). (FIX_RIP_AX or FIX_RIP_CX) -+ */ -+int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -+{ -+ struct uprobe_task *utask; -+ long correction; -+ int result = 0; -+ -+ WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); -+ -+ utask = current->utask; -+ current->thread.trap_nr = utask->autask.saved_trap_nr; -+ correction = (long)(utask->vaddr - utask->xol_vaddr); -+ handle_riprel_post_xol(auprobe, regs, &correction); -+ if (auprobe->fixups & UPROBE_FIX_IP) -+ regs->ip += correction; -+ -+ if (auprobe->fixups & UPROBE_FIX_CALL) -+ result = adjust_ret_addr(regs->sp, correction); -+ -+ return result; -+} -+ -+/* callback routine for handling exceptions. */ -+int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data) -+{ -+ struct die_args *args = data; -+ struct pt_regs *regs = args->regs; -+ int ret = NOTIFY_DONE; -+ -+ /* We are only interested in userspace traps */ -+ if (regs && !user_mode_vm(regs)) -+ return NOTIFY_DONE; -+ -+ switch (val) { -+ case DIE_INT3: -+ if (uprobe_pre_sstep_notifier(regs)) -+ ret = NOTIFY_STOP; -+ -+ break; -+ -+ case DIE_DEBUG: -+ if (uprobe_post_sstep_notifier(regs)) -+ ret = NOTIFY_STOP; -+ -+ default: -+ break; -+ } -+ -+ return ret; -+} -+ -+/* -+ * This function gets called when XOL instruction either gets trapped or -+ * the thread has a fatal signal, so reset the instruction pointer to its -+ * probed address. -+ */ -+void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -+{ -+ struct uprobe_task *utask = current->utask; -+ -+ current->thread.trap_nr = utask->autask.saved_trap_nr; -+ handle_riprel_post_xol(auprobe, regs, NULL); -+ instruction_pointer_set(regs, utask->vaddr); -+} -+ -+/* -+ * Skip these instructions as per the currently known x86 ISA. -+ * 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 } -+ */ -+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) -+{ -+ int i; -+ -+ for (i = 0; i < MAX_UINSN_BYTES; i++) { -+ if ((auprobe->insn[i] == 0x66)) -+ continue; -+ -+ if (auprobe->insn[i] == 0x90) -+ return true; -+ -+ if (i == (MAX_UINSN_BYTES - 1)) -+ break; -+ -+ if ((auprobe->insn[i] == 0x0f) && (auprobe->insn[i+1] == 0x1f)) -+ return true; -+ -+ if ((auprobe->insn[i] == 0x0f) && (auprobe->insn[i+1] == 0x19)) -+ return true; -+ -+ if ((auprobe->insn[i] == 0x87) && (auprobe->insn[i+1] == 0xc0)) -+ return true; -+ -+ break; -+ } -+ return false; -+} -diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h -index 3cc3062..26574c7 100644 ---- a/include/linux/mm_types.h -+++ b/include/linux/mm_types.h -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -388,6 +389,7 @@ struct mm_struct { - #ifdef CONFIG_CPUMASK_OFFSTACK - struct cpumask cpumask_allocation; - #endif -+ struct uprobes_state uprobes_state; - }; - - static inline void mm_init_cpumask(struct mm_struct *mm) -diff --git a/include/linux/sched.h b/include/linux/sched.h -index 81a173c..cff94cd 100644 ---- a/include/linux/sched.h -+++ b/include/linux/sched.h -@@ -1617,6 +1617,10 @@ struct task_struct { - #ifdef CONFIG_HAVE_HW_BREAKPOINT - atomic_t ptrace_bp_refcnt; - #endif -+#ifdef CONFIG_UPROBES -+ struct uprobe_task *utask; -+ int uprobe_srcu_id; -+#endif - }; - - /* Future-safe accessor for struct task_struct's cpus_allowed. */ -diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h -new file mode 100644 -index 0000000..efe4b33 ---- /dev/null -+++ b/include/linux/uprobes.h -@@ -0,0 +1,165 @@ -+#ifndef _LINUX_UPROBES_H -+#define _LINUX_UPROBES_H -+/* -+ * User-space Probes (UProbes) -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * Copyright (C) IBM Corporation, 2008-2012 -+ * Authors: -+ * Srikar Dronamraju -+ * Jim Keniston -+ * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra -+ */ -+ -+#include -+#include -+ -+struct vm_area_struct; -+struct mm_struct; -+struct inode; -+ -+#ifdef CONFIG_ARCH_SUPPORTS_UPROBES -+# include -+#endif -+ -+/* flags that denote/change uprobes behaviour */ -+ -+/* Have a copy of original instruction */ -+#define UPROBE_COPY_INSN 0x1 -+ -+/* Dont run handlers when first register/ last unregister in progress*/ -+#define UPROBE_RUN_HANDLER 0x2 -+/* Can skip singlestep */ -+#define UPROBE_SKIP_SSTEP 0x4 -+ -+struct uprobe_consumer { -+ int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); -+ /* -+ * filter is optional; If a filter exists, handler is run -+ * if and only if filter returns true. -+ */ -+ bool (*filter)(struct uprobe_consumer *self, struct task_struct *task); -+ -+ struct uprobe_consumer *next; -+}; -+ -+#ifdef CONFIG_UPROBES -+enum uprobe_task_state { -+ UTASK_RUNNING, -+ UTASK_BP_HIT, -+ UTASK_SSTEP, -+ UTASK_SSTEP_ACK, -+ UTASK_SSTEP_TRAPPED, -+}; -+ -+/* -+ * uprobe_task: Metadata of a task while it singlesteps. -+ */ -+struct uprobe_task { -+ enum uprobe_task_state state; -+ struct arch_uprobe_task autask; -+ -+ struct uprobe *active_uprobe; -+ -+ unsigned long xol_vaddr; -+ unsigned long vaddr; -+}; -+ -+/* -+ * On a breakpoint hit, thread contests for a slot. It frees the -+ * slot after singlestep. Currently a fixed number of slots are -+ * allocated. -+ */ -+struct xol_area { -+ wait_queue_head_t wq; /* if all slots are busy */ -+ atomic_t slot_count; /* number of in-use slots */ -+ unsigned long *bitmap; /* 0 = free slot */ -+ struct page *page; -+ -+ /* -+ * We keep the vma's vm_start rather than a pointer to the vma -+ * itself. The probed process or a naughty kernel module could make -+ * the vma go away, and we must handle that reasonably gracefully. -+ */ -+ unsigned long vaddr; /* Page(s) of instruction slots */ -+}; -+ -+struct uprobes_state { -+ struct xol_area *xol_area; -+ atomic_t count; -+}; -+extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); -+extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); -+extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); -+extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); -+extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); -+extern int uprobe_mmap(struct vm_area_struct *vma); -+extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end); -+extern void uprobe_free_utask(struct task_struct *t); -+extern void uprobe_copy_process(struct task_struct *t); -+extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); -+extern int uprobe_post_sstep_notifier(struct pt_regs *regs); -+extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); -+extern void uprobe_notify_resume(struct pt_regs *regs); -+extern bool uprobe_deny_signal(void); -+extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); -+extern void uprobe_clear_state(struct mm_struct *mm); -+extern void uprobe_reset_state(struct mm_struct *mm); -+#else /* !CONFIG_UPROBES */ -+struct uprobes_state { -+}; -+static inline int -+uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) -+{ -+ return -ENOSYS; -+} -+static inline void -+uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) -+{ -+} -+static inline int uprobe_mmap(struct vm_area_struct *vma) -+{ -+ return 0; -+} -+static inline void -+uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) -+{ -+} -+static inline void uprobe_notify_resume(struct pt_regs *regs) -+{ -+} -+static inline bool uprobe_deny_signal(void) -+{ -+ return false; -+} -+static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) -+{ -+ return 0; -+} -+static inline void uprobe_free_utask(struct task_struct *t) -+{ -+} -+static inline void uprobe_copy_process(struct task_struct *t) -+{ -+} -+static inline void uprobe_clear_state(struct mm_struct *mm) -+{ -+} -+static inline void uprobe_reset_state(struct mm_struct *mm) -+{ -+} -+#endif /* !CONFIG_UPROBES */ -+#endif /* _LINUX_UPROBES_H */ -diff --git a/kernel/events/Makefile b/kernel/events/Makefile -index 22d901f..103f5d1 100644 ---- a/kernel/events/Makefile -+++ b/kernel/events/Makefile -@@ -3,4 +3,7 @@ CFLAGS_REMOVE_core.o = -pg - endif - - obj-y := core.o ring_buffer.o callchain.o -+ - obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o -+obj-$(CONFIG_UPROBES) += uprobes.o -+ -diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c -new file mode 100644 -index 0000000..985be4d ---- /dev/null -+++ b/kernel/events/uprobes.c -@@ -0,0 +1,1667 @@ -+/* -+ * User-space Probes (UProbes) -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * Copyright (C) IBM Corporation, 2008-2012 -+ * Authors: -+ * Srikar Dronamraju -+ * Jim Keniston -+ * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra -+ */ -+ -+#include -+#include -+#include /* read_mapping_page */ -+#include -+#include -+#include /* anon_vma_prepare */ -+#include /* set_pte_at_notify */ -+#include /* try_to_free_swap */ -+#include /* user_enable_single_step */ -+#include /* notifier mechanism */ -+ -+#include -+ -+#define UINSNS_PER_PAGE (PAGE_SIZE/UPROBE_XOL_SLOT_BYTES) -+#define MAX_UPROBE_XOL_SLOTS UINSNS_PER_PAGE -+ -+static struct srcu_struct uprobes_srcu; -+static struct rb_root uprobes_tree = RB_ROOT; -+ -+static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ -+ -+#define UPROBES_HASH_SZ 13 -+ -+/* serialize (un)register */ -+static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; -+ -+#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) -+ -+/* serialize uprobe->pending_list */ -+static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; -+#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) -+ -+/* -+ * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe -+ * events active at this time. Probably a fine grained per inode count is -+ * better? -+ */ -+static atomic_t uprobe_events = ATOMIC_INIT(0); -+ -+/* -+ * Maintain a temporary per vma info that can be used to search if a vma -+ * has already been handled. This structure is introduced since extending -+ * vm_area_struct wasnt recommended. -+ */ -+struct vma_info { -+ struct list_head probe_list; -+ struct mm_struct *mm; -+ loff_t vaddr; -+}; -+ -+struct uprobe { -+ struct rb_node rb_node; /* node in the rb tree */ -+ atomic_t ref; -+ struct rw_semaphore consumer_rwsem; -+ struct list_head pending_list; -+ struct uprobe_consumer *consumers; -+ struct inode *inode; /* Also hold a ref to inode */ -+ loff_t offset; -+ int flags; -+ struct arch_uprobe arch; -+}; -+ -+/* -+ * valid_vma: Verify if the specified vma is an executable vma -+ * Relax restrictions while unregistering: vm_flags might have -+ * changed after breakpoint was inserted. -+ * - is_register: indicates if we are in register context. -+ * - Return 1 if the specified virtual address is in an -+ * executable vma. -+ */ -+static bool valid_vma(struct vm_area_struct *vma, bool is_register) -+{ -+ if (!vma->vm_file) -+ return false; -+ -+ if (!is_register) -+ return true; -+ -+ if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == (VM_READ|VM_EXEC)) -+ return true; -+ -+ return false; -+} -+ -+static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) -+{ -+ loff_t vaddr; -+ -+ vaddr = vma->vm_start + offset; -+ vaddr -= vma->vm_pgoff << PAGE_SHIFT; -+ -+ return vaddr; -+} -+ -+/** -+ * __replace_page - replace page in vma by new page. -+ * based on replace_page in mm/ksm.c -+ * -+ * @vma: vma that holds the pte pointing to page -+ * @page: the cowed page we are replacing by kpage -+ * @kpage: the modified page we replace page by -+ * -+ * Returns 0 on success, -EFAULT on failure. -+ */ -+static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage) -+{ -+ struct mm_struct *mm = vma->vm_mm; -+ pgd_t *pgd; -+ pud_t *pud; -+ pmd_t *pmd; -+ pte_t *ptep; -+ spinlock_t *ptl; -+ unsigned long addr; -+ int err = -EFAULT; -+ -+ addr = page_address_in_vma(page, vma); -+ if (addr == -EFAULT) -+ goto out; -+ -+ pgd = pgd_offset(mm, addr); -+ if (!pgd_present(*pgd)) -+ goto out; -+ -+ pud = pud_offset(pgd, addr); -+ if (!pud_present(*pud)) -+ goto out; -+ -+ pmd = pmd_offset(pud, addr); -+ if (!pmd_present(*pmd)) -+ goto out; -+ -+ ptep = pte_offset_map_lock(mm, pmd, addr, &ptl); -+ if (!ptep) -+ goto out; -+ -+ get_page(kpage); -+ page_add_new_anon_rmap(kpage, vma, addr); -+ -+ if (!PageAnon(page)) { -+ dec_mm_counter(mm, MM_FILEPAGES); -+ inc_mm_counter(mm, MM_ANONPAGES); -+ } -+ -+ flush_cache_page(vma, addr, pte_pfn(*ptep)); -+ ptep_clear_flush(vma, addr, ptep); -+ set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot)); -+ -+ page_remove_rmap(page); -+ if (!page_mapped(page)) -+ try_to_free_swap(page); -+ put_page(page); -+ pte_unmap_unlock(ptep, ptl); -+ err = 0; -+ -+out: -+ return err; -+} -+ -+/** -+ * is_swbp_insn - check if instruction is breakpoint instruction. -+ * @insn: instruction to be checked. -+ * Default implementation of is_swbp_insn -+ * Returns true if @insn is a breakpoint instruction. -+ */ -+bool __weak is_swbp_insn(uprobe_opcode_t *insn) -+{ -+ return *insn == UPROBE_SWBP_INSN; -+} -+ -+/* -+ * NOTE: -+ * Expect the breakpoint instruction to be the smallest size instruction for -+ * the architecture. If an arch has variable length instruction and the -+ * breakpoint instruction is not of the smallest length instruction -+ * supported by that architecture then we need to modify read_opcode / -+ * write_opcode accordingly. This would never be a problem for archs that -+ * have fixed length instructions. -+ */ -+ -+/* -+ * write_opcode - write the opcode at a given virtual address. -+ * @auprobe: arch breakpointing information. -+ * @mm: the probed process address space. -+ * @vaddr: the virtual address to store the opcode. -+ * @opcode: opcode to be written at @vaddr. -+ * -+ * Called with mm->mmap_sem held (for read and with a reference to -+ * mm). -+ * -+ * For mm @mm, write the opcode at @vaddr. -+ * Return 0 (success) or a negative errno. -+ */ -+static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, -+ unsigned long vaddr, uprobe_opcode_t opcode) -+{ -+ struct page *old_page, *new_page; -+ struct address_space *mapping; -+ void *vaddr_old, *vaddr_new; -+ struct vm_area_struct *vma; -+ struct uprobe *uprobe; -+ loff_t addr; -+ int ret; -+ -+ /* Read the page with vaddr into memory */ -+ ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); -+ if (ret <= 0) -+ return ret; -+ -+ ret = -EINVAL; -+ -+ /* -+ * We are interested in text pages only. Our pages of interest -+ * should be mapped for read and execute only. We desist from -+ * adding probes in write mapped pages since the breakpoints -+ * might end up in the file copy. -+ */ -+ if (!valid_vma(vma, is_swbp_insn(&opcode))) -+ goto put_out; -+ -+ uprobe = container_of(auprobe, struct uprobe, arch); -+ mapping = uprobe->inode->i_mapping; -+ if (mapping != vma->vm_file->f_mapping) -+ goto put_out; -+ -+ addr = vma_address(vma, uprobe->offset); -+ if (vaddr != (unsigned long)addr) -+ goto put_out; -+ -+ ret = -ENOMEM; -+ new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); -+ if (!new_page) -+ goto put_out; -+ -+ __SetPageUptodate(new_page); -+ -+ /* -+ * lock page will serialize against do_wp_page()'s -+ * PageAnon() handling -+ */ -+ lock_page(old_page); -+ /* copy the page now that we've got it stable */ -+ vaddr_old = kmap_atomic(old_page); -+ vaddr_new = kmap_atomic(new_page); -+ -+ memcpy(vaddr_new, vaddr_old, PAGE_SIZE); -+ -+ /* poke the new insn in, ASSUMES we don't cross page boundary */ -+ vaddr &= ~PAGE_MASK; -+ BUG_ON(vaddr + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE); -+ memcpy(vaddr_new + vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); -+ -+ kunmap_atomic(vaddr_new); -+ kunmap_atomic(vaddr_old); -+ -+ ret = anon_vma_prepare(vma); -+ if (ret) -+ goto unlock_out; -+ -+ lock_page(new_page); -+ ret = __replace_page(vma, old_page, new_page); -+ unlock_page(new_page); -+ -+unlock_out: -+ unlock_page(old_page); -+ page_cache_release(new_page); -+ -+put_out: -+ put_page(old_page); -+ -+ return ret; -+} -+ -+/** -+ * read_opcode - read the opcode at a given virtual address. -+ * @mm: the probed process address space. -+ * @vaddr: the virtual address to read the opcode. -+ * @opcode: location to store the read opcode. -+ * -+ * Called with mm->mmap_sem held (for read and with a reference to -+ * mm. -+ * -+ * For mm @mm, read the opcode at @vaddr and store it in @opcode. -+ * Return 0 (success) or a negative errno. -+ */ -+static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t *opcode) -+{ -+ struct page *page; -+ void *vaddr_new; -+ int ret; -+ -+ ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &page, NULL); -+ if (ret <= 0) -+ return ret; -+ -+ lock_page(page); -+ vaddr_new = kmap_atomic(page); -+ vaddr &= ~PAGE_MASK; -+ memcpy(opcode, vaddr_new + vaddr, UPROBE_SWBP_INSN_SIZE); -+ kunmap_atomic(vaddr_new); -+ unlock_page(page); -+ -+ put_page(page); -+ -+ return 0; -+} -+ -+static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr) -+{ -+ uprobe_opcode_t opcode; -+ int result; -+ -+ result = read_opcode(mm, vaddr, &opcode); -+ if (result) -+ return result; -+ -+ if (is_swbp_insn(&opcode)) -+ return 1; -+ -+ return 0; -+} -+ -+/** -+ * set_swbp - store breakpoint at a given address. -+ * @auprobe: arch specific probepoint information. -+ * @mm: the probed process address space. -+ * @vaddr: the virtual address to insert the opcode. -+ * -+ * For mm @mm, store the breakpoint instruction at @vaddr. -+ * Return 0 (success) or a negative errno. -+ */ -+int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) -+{ -+ int result; -+ -+ result = is_swbp_at_addr(mm, vaddr); -+ if (result == 1) -+ return -EEXIST; -+ -+ if (result) -+ return result; -+ -+ return write_opcode(auprobe, mm, vaddr, UPROBE_SWBP_INSN); -+} -+ -+/** -+ * set_orig_insn - Restore the original instruction. -+ * @mm: the probed process address space. -+ * @auprobe: arch specific probepoint information. -+ * @vaddr: the virtual address to insert the opcode. -+ * @verify: if true, verify existance of breakpoint instruction. -+ * -+ * For mm @mm, restore the original opcode (opcode) at @vaddr. -+ * Return 0 (success) or a negative errno. -+ */ -+int __weak -+set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, bool verify) -+{ -+ if (verify) { -+ int result; -+ -+ result = is_swbp_at_addr(mm, vaddr); -+ if (!result) -+ return -EINVAL; -+ -+ if (result != 1) -+ return result; -+ } -+ return write_opcode(auprobe, mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); -+} -+ -+static int match_uprobe(struct uprobe *l, struct uprobe *r) -+{ -+ if (l->inode < r->inode) -+ return -1; -+ -+ if (l->inode > r->inode) -+ return 1; -+ -+ if (l->offset < r->offset) -+ return -1; -+ -+ if (l->offset > r->offset) -+ return 1; -+ -+ return 0; -+} -+ -+static struct uprobe *__find_uprobe(struct inode *inode, loff_t offset) -+{ -+ struct uprobe u = { .inode = inode, .offset = offset }; -+ struct rb_node *n = uprobes_tree.rb_node; -+ struct uprobe *uprobe; -+ int match; -+ -+ while (n) { -+ uprobe = rb_entry(n, struct uprobe, rb_node); -+ match = match_uprobe(&u, uprobe); -+ if (!match) { -+ atomic_inc(&uprobe->ref); -+ return uprobe; -+ } -+ -+ if (match < 0) -+ n = n->rb_left; -+ else -+ n = n->rb_right; -+ } -+ return NULL; -+} -+ -+/* -+ * Find a uprobe corresponding to a given inode:offset -+ * Acquires uprobes_treelock -+ */ -+static struct uprobe *find_uprobe(struct inode *inode, loff_t offset) -+{ -+ struct uprobe *uprobe; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&uprobes_treelock, flags); -+ uprobe = __find_uprobe(inode, offset); -+ spin_unlock_irqrestore(&uprobes_treelock, flags); -+ -+ return uprobe; -+} -+ -+static struct uprobe *__insert_uprobe(struct uprobe *uprobe) -+{ -+ struct rb_node **p = &uprobes_tree.rb_node; -+ struct rb_node *parent = NULL; -+ struct uprobe *u; -+ int match; -+ -+ while (*p) { -+ parent = *p; -+ u = rb_entry(parent, struct uprobe, rb_node); -+ match = match_uprobe(uprobe, u); -+ if (!match) { -+ atomic_inc(&u->ref); -+ return u; -+ } -+ -+ if (match < 0) -+ p = &parent->rb_left; -+ else -+ p = &parent->rb_right; -+ -+ } -+ -+ u = NULL; -+ rb_link_node(&uprobe->rb_node, parent, p); -+ rb_insert_color(&uprobe->rb_node, &uprobes_tree); -+ /* get access + creation ref */ -+ atomic_set(&uprobe->ref, 2); -+ -+ return u; -+} -+ -+/* -+ * Acquire uprobes_treelock. -+ * Matching uprobe already exists in rbtree; -+ * increment (access refcount) and return the matching uprobe. -+ * -+ * No matching uprobe; insert the uprobe in rb_tree; -+ * get a double refcount (access + creation) and return NULL. -+ */ -+static struct uprobe *insert_uprobe(struct uprobe *uprobe) -+{ -+ unsigned long flags; -+ struct uprobe *u; -+ -+ spin_lock_irqsave(&uprobes_treelock, flags); -+ u = __insert_uprobe(uprobe); -+ spin_unlock_irqrestore(&uprobes_treelock, flags); -+ -+ /* For now assume that the instruction need not be single-stepped */ -+ uprobe->flags |= UPROBE_SKIP_SSTEP; -+ -+ return u; -+} -+ -+static void put_uprobe(struct uprobe *uprobe) -+{ -+ if (atomic_dec_and_test(&uprobe->ref)) -+ kfree(uprobe); -+} -+ -+static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) -+{ -+ struct uprobe *uprobe, *cur_uprobe; -+ -+ uprobe = kzalloc(sizeof(struct uprobe), GFP_KERNEL); -+ if (!uprobe) -+ return NULL; -+ -+ uprobe->inode = igrab(inode); -+ uprobe->offset = offset; -+ init_rwsem(&uprobe->consumer_rwsem); -+ INIT_LIST_HEAD(&uprobe->pending_list); -+ -+ /* add to uprobes_tree, sorted on inode:offset */ -+ cur_uprobe = insert_uprobe(uprobe); -+ -+ /* a uprobe exists for this inode:offset combination */ -+ if (cur_uprobe) { -+ kfree(uprobe); -+ uprobe = cur_uprobe; -+ iput(inode); -+ } else { -+ atomic_inc(&uprobe_events); -+ } -+ -+ return uprobe; -+} -+ -+static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) -+{ -+ struct uprobe_consumer *uc; -+ -+ if (!(uprobe->flags & UPROBE_RUN_HANDLER)) -+ return; -+ -+ down_read(&uprobe->consumer_rwsem); -+ for (uc = uprobe->consumers; uc; uc = uc->next) { -+ if (!uc->filter || uc->filter(uc, current)) -+ uc->handler(uc, regs); -+ } -+ up_read(&uprobe->consumer_rwsem); -+} -+ -+/* Returns the previous consumer */ -+static struct uprobe_consumer * -+consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc) -+{ -+ down_write(&uprobe->consumer_rwsem); -+ uc->next = uprobe->consumers; -+ uprobe->consumers = uc; -+ up_write(&uprobe->consumer_rwsem); -+ -+ return uc->next; -+} -+ -+/* -+ * For uprobe @uprobe, delete the consumer @uc. -+ * Return true if the @uc is deleted successfully -+ * or return false. -+ */ -+static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc) -+{ -+ struct uprobe_consumer **con; -+ bool ret = false; -+ -+ down_write(&uprobe->consumer_rwsem); -+ for (con = &uprobe->consumers; *con; con = &(*con)->next) { -+ if (*con == uc) { -+ *con = uc->next; -+ ret = true; -+ break; -+ } -+ } -+ up_write(&uprobe->consumer_rwsem); -+ -+ return ret; -+} -+ -+static int -+__copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insn, -+ unsigned long nbytes, unsigned long offset) -+{ -+ struct file *filp = vma->vm_file; -+ struct page *page; -+ void *vaddr; -+ unsigned long off1; -+ unsigned long idx; -+ -+ if (!filp) -+ return -EINVAL; -+ -+ idx = (unsigned long)(offset >> PAGE_CACHE_SHIFT); -+ off1 = offset &= ~PAGE_MASK; -+ -+ /* -+ * Ensure that the page that has the original instruction is -+ * populated and in page-cache. -+ */ -+ page = read_mapping_page(mapping, idx, filp); -+ if (IS_ERR(page)) -+ return PTR_ERR(page); -+ -+ vaddr = kmap_atomic(page); -+ memcpy(insn, vaddr + off1, nbytes); -+ kunmap_atomic(vaddr); -+ page_cache_release(page); -+ -+ return 0; -+} -+ -+static int -+copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) -+{ -+ struct address_space *mapping; -+ unsigned long nbytes; -+ int bytes; -+ -+ addr &= ~PAGE_MASK; -+ nbytes = PAGE_SIZE - addr; -+ mapping = uprobe->inode->i_mapping; -+ -+ /* Instruction at end of binary; copy only available bytes */ -+ if (uprobe->offset + MAX_UINSN_BYTES > uprobe->inode->i_size) -+ bytes = uprobe->inode->i_size - uprobe->offset; -+ else -+ bytes = MAX_UINSN_BYTES; -+ -+ /* Instruction at the page-boundary; copy bytes in second page */ -+ if (nbytes < bytes) { -+ if (__copy_insn(mapping, vma, uprobe->arch.insn + nbytes, -+ bytes - nbytes, uprobe->offset + nbytes)) -+ return -ENOMEM; -+ -+ bytes = nbytes; -+ } -+ return __copy_insn(mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); -+} -+ -+/* -+ * How mm->uprobes_state.count gets updated -+ * uprobe_mmap() increments the count if -+ * - it successfully adds a breakpoint. -+ * - it cannot add a breakpoint, but sees that there is a underlying -+ * breakpoint (via a is_swbp_at_addr()). -+ * -+ * uprobe_munmap() decrements the count if -+ * - it sees a underlying breakpoint, (via is_swbp_at_addr) -+ * (Subsequent uprobe_unregister wouldnt find the breakpoint -+ * unless a uprobe_mmap kicks in, since the old vma would be -+ * dropped just after uprobe_munmap.) -+ * -+ * uprobe_register increments the count if: -+ * - it successfully adds a breakpoint. -+ * -+ * uprobe_unregister decrements the count if: -+ * - it sees a underlying breakpoint and removes successfully. -+ * (via is_swbp_at_addr) -+ * (Subsequent uprobe_munmap wouldnt find the breakpoint -+ * since there is no underlying breakpoint after the -+ * breakpoint removal.) -+ */ -+static int -+install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, -+ struct vm_area_struct *vma, loff_t vaddr) -+{ -+ unsigned long addr; -+ int ret; -+ -+ /* -+ * If probe is being deleted, unregister thread could be done with -+ * the vma-rmap-walk through. Adding a probe now can be fatal since -+ * nobody will be able to cleanup. Also we could be from fork or -+ * mremap path, where the probe might have already been inserted. -+ * Hence behave as if probe already existed. -+ */ -+ if (!uprobe->consumers) -+ return -EEXIST; -+ -+ addr = (unsigned long)vaddr; -+ -+ if (!(uprobe->flags & UPROBE_COPY_INSN)) { -+ ret = copy_insn(uprobe, vma, addr); -+ if (ret) -+ return ret; -+ -+ if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn)) -+ return -EEXIST; -+ -+ ret = arch_uprobe_analyze_insn(&uprobe->arch, mm); -+ if (ret) -+ return ret; -+ -+ uprobe->flags |= UPROBE_COPY_INSN; -+ } -+ -+ /* -+ * Ideally, should be updating the probe count after the breakpoint -+ * has been successfully inserted. However a thread could hit the -+ * breakpoint we just inserted even before the probe count is -+ * incremented. If this is the first breakpoint placed, breakpoint -+ * notifier might ignore uprobes and pass the trap to the thread. -+ * Hence increment before and decrement on failure. -+ */ -+ atomic_inc(&mm->uprobes_state.count); -+ ret = set_swbp(&uprobe->arch, mm, addr); -+ if (ret) -+ atomic_dec(&mm->uprobes_state.count); -+ -+ return ret; -+} -+ -+static void -+remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) -+{ -+ if (!set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true)) -+ atomic_dec(&mm->uprobes_state.count); -+} -+ -+/* -+ * There could be threads that have hit the breakpoint and are entering the -+ * notifier code and trying to acquire the uprobes_treelock. The thread -+ * calling delete_uprobe() that is removing the uprobe from the rb_tree can -+ * race with these threads and might acquire the uprobes_treelock compared -+ * to some of the breakpoint hit threads. In such a case, the breakpoint -+ * hit threads will not find the uprobe. The current unregistering thread -+ * waits till all other threads have hit a breakpoint, to acquire the -+ * uprobes_treelock before the uprobe is removed from the rbtree. -+ */ -+static void delete_uprobe(struct uprobe *uprobe) -+{ -+ unsigned long flags; -+ -+ synchronize_srcu(&uprobes_srcu); -+ spin_lock_irqsave(&uprobes_treelock, flags); -+ rb_erase(&uprobe->rb_node, &uprobes_tree); -+ spin_unlock_irqrestore(&uprobes_treelock, flags); -+ iput(uprobe->inode); -+ put_uprobe(uprobe); -+ atomic_dec(&uprobe_events); -+} -+ -+static struct vma_info * -+__find_next_vma_info(struct address_space *mapping, struct list_head *head, -+ struct vma_info *vi, loff_t offset, bool is_register) -+{ -+ struct prio_tree_iter iter; -+ struct vm_area_struct *vma; -+ struct vma_info *tmpvi; -+ unsigned long pgoff; -+ int existing_vma; -+ loff_t vaddr; -+ -+ pgoff = offset >> PAGE_SHIFT; -+ -+ vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { -+ if (!valid_vma(vma, is_register)) -+ continue; -+ -+ existing_vma = 0; -+ vaddr = vma_address(vma, offset); -+ -+ list_for_each_entry(tmpvi, head, probe_list) { -+ if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { -+ existing_vma = 1; -+ break; -+ } -+ } -+ -+ /* -+ * Another vma needs a probe to be installed. However skip -+ * installing the probe if the vma is about to be unlinked. -+ */ -+ if (!existing_vma && atomic_inc_not_zero(&vma->vm_mm->mm_users)) { -+ vi->mm = vma->vm_mm; -+ vi->vaddr = vaddr; -+ list_add(&vi->probe_list, head); -+ -+ return vi; -+ } -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Iterate in the rmap prio tree and find a vma where a probe has not -+ * yet been inserted. -+ */ -+static struct vma_info * -+find_next_vma_info(struct address_space *mapping, struct list_head *head, -+ loff_t offset, bool is_register) -+{ -+ struct vma_info *vi, *retvi; -+ -+ vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL); -+ if (!vi) -+ return ERR_PTR(-ENOMEM); -+ -+ mutex_lock(&mapping->i_mmap_mutex); -+ retvi = __find_next_vma_info(mapping, head, vi, offset, is_register); -+ mutex_unlock(&mapping->i_mmap_mutex); -+ -+ if (!retvi) -+ kfree(vi); -+ -+ return retvi; -+} -+ -+static int register_for_each_vma(struct uprobe *uprobe, bool is_register) -+{ -+ struct list_head try_list; -+ struct vm_area_struct *vma; -+ struct address_space *mapping; -+ struct vma_info *vi, *tmpvi; -+ struct mm_struct *mm; -+ loff_t vaddr; -+ int ret; -+ -+ mapping = uprobe->inode->i_mapping; -+ INIT_LIST_HEAD(&try_list); -+ -+ ret = 0; -+ -+ for (;;) { -+ vi = find_next_vma_info(mapping, &try_list, uprobe->offset, is_register); -+ if (!vi) -+ break; -+ -+ if (IS_ERR(vi)) { -+ ret = PTR_ERR(vi); -+ break; -+ } -+ -+ mm = vi->mm; -+ down_read(&mm->mmap_sem); -+ vma = find_vma(mm, (unsigned long)vi->vaddr); -+ if (!vma || !valid_vma(vma, is_register)) { -+ list_del(&vi->probe_list); -+ kfree(vi); -+ up_read(&mm->mmap_sem); -+ mmput(mm); -+ continue; -+ } -+ vaddr = vma_address(vma, uprobe->offset); -+ if (vma->vm_file->f_mapping->host != uprobe->inode || -+ vaddr != vi->vaddr) { -+ list_del(&vi->probe_list); -+ kfree(vi); -+ up_read(&mm->mmap_sem); -+ mmput(mm); -+ continue; -+ } -+ -+ if (is_register) -+ ret = install_breakpoint(uprobe, mm, vma, vi->vaddr); -+ else -+ remove_breakpoint(uprobe, mm, vi->vaddr); -+ -+ up_read(&mm->mmap_sem); -+ mmput(mm); -+ if (is_register) { -+ if (ret && ret == -EEXIST) -+ ret = 0; -+ if (ret) -+ break; -+ } -+ } -+ -+ list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) { -+ list_del(&vi->probe_list); -+ kfree(vi); -+ } -+ -+ return ret; -+} -+ -+static int __uprobe_register(struct uprobe *uprobe) -+{ -+ return register_for_each_vma(uprobe, true); -+} -+ -+static void __uprobe_unregister(struct uprobe *uprobe) -+{ -+ if (!register_for_each_vma(uprobe, false)) -+ delete_uprobe(uprobe); -+ -+ /* TODO : cant unregister? schedule a worker thread */ -+} -+ -+/* -+ * uprobe_register - register a probe -+ * @inode: the file in which the probe has to be placed. -+ * @offset: offset from the start of the file. -+ * @uc: information on howto handle the probe.. -+ * -+ * Apart from the access refcount, uprobe_register() takes a creation -+ * refcount (thro alloc_uprobe) if and only if this @uprobe is getting -+ * inserted into the rbtree (i.e first consumer for a @inode:@offset -+ * tuple). Creation refcount stops uprobe_unregister from freeing the -+ * @uprobe even before the register operation is complete. Creation -+ * refcount is released when the last @uc for the @uprobe -+ * unregisters. -+ * -+ * Return errno if it cannot successully install probes -+ * else return 0 (success) -+ */ -+int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) -+{ -+ struct uprobe *uprobe; -+ int ret; -+ -+ if (!inode || !uc || uc->next) -+ return -EINVAL; -+ -+ if (offset > i_size_read(inode)) -+ return -EINVAL; -+ -+ ret = 0; -+ mutex_lock(uprobes_hash(inode)); -+ uprobe = alloc_uprobe(inode, offset); -+ -+ if (uprobe && !consumer_add(uprobe, uc)) { -+ ret = __uprobe_register(uprobe); -+ if (ret) { -+ uprobe->consumers = NULL; -+ __uprobe_unregister(uprobe); -+ } else { -+ uprobe->flags |= UPROBE_RUN_HANDLER; -+ } -+ } -+ -+ mutex_unlock(uprobes_hash(inode)); -+ put_uprobe(uprobe); -+ -+ return ret; -+} -+ -+/* -+ * uprobe_unregister - unregister a already registered probe. -+ * @inode: the file in which the probe has to be removed. -+ * @offset: offset from the start of the file. -+ * @uc: identify which probe if multiple probes are colocated. -+ */ -+void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) -+{ -+ struct uprobe *uprobe; -+ -+ if (!inode || !uc) -+ return; -+ -+ uprobe = find_uprobe(inode, offset); -+ if (!uprobe) -+ return; -+ -+ mutex_lock(uprobes_hash(inode)); -+ -+ if (consumer_del(uprobe, uc)) { -+ if (!uprobe->consumers) { -+ __uprobe_unregister(uprobe); -+ uprobe->flags &= ~UPROBE_RUN_HANDLER; -+ } -+ } -+ -+ mutex_unlock(uprobes_hash(inode)); -+ if (uprobe) -+ put_uprobe(uprobe); -+} -+ -+/* -+ * Of all the nodes that correspond to the given inode, return the node -+ * with the least offset. -+ */ -+static struct rb_node *find_least_offset_node(struct inode *inode) -+{ -+ struct uprobe u = { .inode = inode, .offset = 0}; -+ struct rb_node *n = uprobes_tree.rb_node; -+ struct rb_node *close_node = NULL; -+ struct uprobe *uprobe; -+ int match; -+ -+ while (n) { -+ uprobe = rb_entry(n, struct uprobe, rb_node); -+ match = match_uprobe(&u, uprobe); -+ -+ if (uprobe->inode == inode) -+ close_node = n; -+ -+ if (!match) -+ return close_node; -+ -+ if (match < 0) -+ n = n->rb_left; -+ else -+ n = n->rb_right; -+ } -+ -+ return close_node; -+} -+ -+/* -+ * For a given inode, build a list of probes that need to be inserted. -+ */ -+static void build_probe_list(struct inode *inode, struct list_head *head) -+{ -+ struct uprobe *uprobe; -+ unsigned long flags; -+ struct rb_node *n; -+ -+ spin_lock_irqsave(&uprobes_treelock, flags); -+ -+ n = find_least_offset_node(inode); -+ -+ for (; n; n = rb_next(n)) { -+ uprobe = rb_entry(n, struct uprobe, rb_node); -+ if (uprobe->inode != inode) -+ break; -+ -+ list_add(&uprobe->pending_list, head); -+ atomic_inc(&uprobe->ref); -+ } -+ -+ spin_unlock_irqrestore(&uprobes_treelock, flags); -+} -+ -+/* -+ * Called from mmap_region. -+ * called with mm->mmap_sem acquired. -+ * -+ * Return -ve no if we fail to insert probes and we cannot -+ * bail-out. -+ * Return 0 otherwise. i.e: -+ * -+ * - successful insertion of probes -+ * - (or) no possible probes to be inserted. -+ * - (or) insertion of probes failed but we can bail-out. -+ */ -+int uprobe_mmap(struct vm_area_struct *vma) -+{ -+ struct list_head tmp_list; -+ struct uprobe *uprobe, *u; -+ struct inode *inode; -+ int ret, count; -+ -+ if (!atomic_read(&uprobe_events) || !valid_vma(vma, true)) -+ return 0; -+ -+ inode = vma->vm_file->f_mapping->host; -+ if (!inode) -+ return 0; -+ -+ INIT_LIST_HEAD(&tmp_list); -+ mutex_lock(uprobes_mmap_hash(inode)); -+ build_probe_list(inode, &tmp_list); -+ -+ ret = 0; -+ count = 0; -+ -+ list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { -+ loff_t vaddr; -+ -+ list_del(&uprobe->pending_list); -+ if (!ret) { -+ vaddr = vma_address(vma, uprobe->offset); -+ -+ if (vaddr < vma->vm_start || vaddr >= vma->vm_end) { -+ put_uprobe(uprobe); -+ continue; -+ } -+ -+ ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); -+ -+ /* Ignore double add: */ -+ if (ret == -EEXIST) { -+ ret = 0; -+ -+ if (!is_swbp_at_addr(vma->vm_mm, vaddr)) -+ continue; -+ -+ /* -+ * Unable to insert a breakpoint, but -+ * breakpoint lies underneath. Increment the -+ * probe count. -+ */ -+ atomic_inc(&vma->vm_mm->uprobes_state.count); -+ } -+ -+ if (!ret) -+ count++; -+ } -+ put_uprobe(uprobe); -+ } -+ -+ mutex_unlock(uprobes_mmap_hash(inode)); -+ -+ if (ret) -+ atomic_sub(count, &vma->vm_mm->uprobes_state.count); -+ -+ return ret; -+} -+ -+/* -+ * Called in context of a munmap of a vma. -+ */ -+void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) -+{ -+ struct list_head tmp_list; -+ struct uprobe *uprobe, *u; -+ struct inode *inode; -+ -+ if (!atomic_read(&uprobe_events) || !valid_vma(vma, false)) -+ return; -+ -+ if (!atomic_read(&vma->vm_mm->uprobes_state.count)) -+ return; -+ -+ inode = vma->vm_file->f_mapping->host; -+ if (!inode) -+ return; -+ -+ INIT_LIST_HEAD(&tmp_list); -+ mutex_lock(uprobes_mmap_hash(inode)); -+ build_probe_list(inode, &tmp_list); -+ -+ list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { -+ loff_t vaddr; -+ -+ list_del(&uprobe->pending_list); -+ vaddr = vma_address(vma, uprobe->offset); -+ -+ if (vaddr >= start && vaddr < end) { -+ /* -+ * An unregister could have removed the probe before -+ * unmap. So check before we decrement the count. -+ */ -+ if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1) -+ atomic_dec(&vma->vm_mm->uprobes_state.count); -+ } -+ put_uprobe(uprobe); -+ } -+ mutex_unlock(uprobes_mmap_hash(inode)); -+} -+ -+/* Slot allocation for XOL */ -+static int xol_add_vma(struct xol_area *area) -+{ -+ struct mm_struct *mm; -+ int ret; -+ -+ area->page = alloc_page(GFP_HIGHUSER); -+ if (!area->page) -+ return -ENOMEM; -+ -+ ret = -EALREADY; -+ mm = current->mm; -+ -+ down_write(&mm->mmap_sem); -+ if (mm->uprobes_state.xol_area) -+ goto fail; -+ -+ ret = -ENOMEM; -+ -+ /* Try to map as high as possible, this is only a hint. */ -+ area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0); -+ if (area->vaddr & ~PAGE_MASK) { -+ ret = area->vaddr; -+ goto fail; -+ } -+ -+ ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE, -+ VM_EXEC|VM_MAYEXEC|VM_DONTCOPY|VM_IO, &area->page); -+ if (ret) -+ goto fail; -+ -+ smp_wmb(); /* pairs with get_xol_area() */ -+ mm->uprobes_state.xol_area = area; -+ ret = 0; -+ -+fail: -+ up_write(&mm->mmap_sem); -+ if (ret) -+ __free_page(area->page); -+ -+ return ret; -+} -+ -+static struct xol_area *get_xol_area(struct mm_struct *mm) -+{ -+ struct xol_area *area; -+ -+ area = mm->uprobes_state.xol_area; -+ smp_read_barrier_depends(); /* pairs with wmb in xol_add_vma() */ -+ -+ return area; -+} -+ -+/* -+ * xol_alloc_area - Allocate process's xol_area. -+ * This area will be used for storing instructions for execution out of -+ * line. -+ * -+ * Returns the allocated area or NULL. -+ */ -+static struct xol_area *xol_alloc_area(void) -+{ -+ struct xol_area *area; -+ -+ area = kzalloc(sizeof(*area), GFP_KERNEL); -+ if (unlikely(!area)) -+ return NULL; -+ -+ area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long), GFP_KERNEL); -+ -+ if (!area->bitmap) -+ goto fail; -+ -+ init_waitqueue_head(&area->wq); -+ if (!xol_add_vma(area)) -+ return area; -+ -+fail: -+ kfree(area->bitmap); -+ kfree(area); -+ -+ return get_xol_area(current->mm); -+} -+ -+/* -+ * uprobe_clear_state - Free the area allocated for slots. -+ */ -+void uprobe_clear_state(struct mm_struct *mm) -+{ -+ struct xol_area *area = mm->uprobes_state.xol_area; -+ -+ if (!area) -+ return; -+ -+ put_page(area->page); -+ kfree(area->bitmap); -+ kfree(area); -+} -+ -+/* -+ * uprobe_reset_state - Free the area allocated for slots. -+ */ -+void uprobe_reset_state(struct mm_struct *mm) -+{ -+ mm->uprobes_state.xol_area = NULL; -+ atomic_set(&mm->uprobes_state.count, 0); -+} -+ -+/* -+ * - search for a free slot. -+ */ -+static unsigned long xol_take_insn_slot(struct xol_area *area) -+{ -+ unsigned long slot_addr; -+ int slot_nr; -+ -+ do { -+ slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE); -+ if (slot_nr < UINSNS_PER_PAGE) { -+ if (!test_and_set_bit(slot_nr, area->bitmap)) -+ break; -+ -+ slot_nr = UINSNS_PER_PAGE; -+ continue; -+ } -+ wait_event(area->wq, (atomic_read(&area->slot_count) < UINSNS_PER_PAGE)); -+ } while (slot_nr >= UINSNS_PER_PAGE); -+ -+ slot_addr = area->vaddr + (slot_nr * UPROBE_XOL_SLOT_BYTES); -+ atomic_inc(&area->slot_count); -+ -+ return slot_addr; -+} -+ -+/* -+ * xol_get_insn_slot - If was not allocated a slot, then -+ * allocate a slot. -+ * Returns the allocated slot address or 0. -+ */ -+static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot_addr) -+{ -+ struct xol_area *area; -+ unsigned long offset; -+ void *vaddr; -+ -+ area = get_xol_area(current->mm); -+ if (!area) { -+ area = xol_alloc_area(); -+ if (!area) -+ return 0; -+ } -+ current->utask->xol_vaddr = xol_take_insn_slot(area); -+ -+ /* -+ * Initialize the slot if xol_vaddr points to valid -+ * instruction slot. -+ */ -+ if (unlikely(!current->utask->xol_vaddr)) -+ return 0; -+ -+ current->utask->vaddr = slot_addr; -+ offset = current->utask->xol_vaddr & ~PAGE_MASK; -+ vaddr = kmap_atomic(area->page); -+ memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES); -+ kunmap_atomic(vaddr); -+ -+ return current->utask->xol_vaddr; -+} -+ -+/* -+ * xol_free_insn_slot - If slot was earlier allocated by -+ * @xol_get_insn_slot(), make the slot available for -+ * subsequent requests. -+ */ -+static void xol_free_insn_slot(struct task_struct *tsk) -+{ -+ struct xol_area *area; -+ unsigned long vma_end; -+ unsigned long slot_addr; -+ -+ if (!tsk->mm || !tsk->mm->uprobes_state.xol_area || !tsk->utask) -+ return; -+ -+ slot_addr = tsk->utask->xol_vaddr; -+ -+ if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr))) -+ return; -+ -+ area = tsk->mm->uprobes_state.xol_area; -+ vma_end = area->vaddr + PAGE_SIZE; -+ if (area->vaddr <= slot_addr && slot_addr < vma_end) { -+ unsigned long offset; -+ int slot_nr; -+ -+ offset = slot_addr - area->vaddr; -+ slot_nr = offset / UPROBE_XOL_SLOT_BYTES; -+ if (slot_nr >= UINSNS_PER_PAGE) -+ return; -+ -+ clear_bit(slot_nr, area->bitmap); -+ atomic_dec(&area->slot_count); -+ if (waitqueue_active(&area->wq)) -+ wake_up(&area->wq); -+ -+ tsk->utask->xol_vaddr = 0; -+ } -+} -+ -+/** -+ * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs -+ * @regs: Reflects the saved state of the task after it has hit a breakpoint -+ * instruction. -+ * Return the address of the breakpoint instruction. -+ */ -+unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs) -+{ -+ return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE; -+} -+ -+/* -+ * Called with no locks held. -+ * Called in context of a exiting or a exec-ing thread. -+ */ -+void uprobe_free_utask(struct task_struct *t) -+{ -+ struct uprobe_task *utask = t->utask; -+ -+ if (t->uprobe_srcu_id != -1) -+ srcu_read_unlock_raw(&uprobes_srcu, t->uprobe_srcu_id); -+ -+ if (!utask) -+ return; -+ -+ if (utask->active_uprobe) -+ put_uprobe(utask->active_uprobe); -+ -+ xol_free_insn_slot(t); -+ kfree(utask); -+ t->utask = NULL; -+} -+ -+/* -+ * Called in context of a new clone/fork from copy_process. -+ */ -+void uprobe_copy_process(struct task_struct *t) -+{ -+ t->utask = NULL; -+ t->uprobe_srcu_id = -1; -+} -+ -+/* -+ * Allocate a uprobe_task object for the task. -+ * Called when the thread hits a breakpoint for the first time. -+ * -+ * Returns: -+ * - pointer to new uprobe_task on success -+ * - NULL otherwise -+ */ -+static struct uprobe_task *add_utask(void) -+{ -+ struct uprobe_task *utask; -+ -+ utask = kzalloc(sizeof *utask, GFP_KERNEL); -+ if (unlikely(!utask)) -+ return NULL; -+ -+ utask->active_uprobe = NULL; -+ current->utask = utask; -+ return utask; -+} -+ -+/* Prepare to single-step probed instruction out of line. */ -+static int -+pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long vaddr) -+{ -+ if (xol_get_insn_slot(uprobe, vaddr) && !arch_uprobe_pre_xol(&uprobe->arch, regs)) -+ return 0; -+ -+ return -EFAULT; -+} -+ -+/* -+ * If we are singlestepping, then ensure this thread is not connected to -+ * non-fatal signals until completion of singlestep. When xol insn itself -+ * triggers the signal, restart the original insn even if the task is -+ * already SIGKILL'ed (since coredump should report the correct ip). This -+ * is even more important if the task has a handler for SIGSEGV/etc, The -+ * _same_ instruction should be repeated again after return from the signal -+ * handler, and SSTEP can never finish in this case. -+ */ -+bool uprobe_deny_signal(void) -+{ -+ struct task_struct *t = current; -+ struct uprobe_task *utask = t->utask; -+ -+ if (likely(!utask || !utask->active_uprobe)) -+ return false; -+ -+ WARN_ON_ONCE(utask->state != UTASK_SSTEP); -+ -+ if (signal_pending(t)) { -+ spin_lock_irq(&t->sighand->siglock); -+ clear_tsk_thread_flag(t, TIF_SIGPENDING); -+ spin_unlock_irq(&t->sighand->siglock); -+ -+ if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) { -+ utask->state = UTASK_SSTEP_TRAPPED; -+ set_tsk_thread_flag(t, TIF_UPROBE); -+ set_tsk_thread_flag(t, TIF_NOTIFY_RESUME); -+ } -+ } -+ -+ return true; -+} -+ -+/* -+ * Avoid singlestepping the original instruction if the original instruction -+ * is a NOP or can be emulated. -+ */ -+static bool can_skip_sstep(struct uprobe *uprobe, struct pt_regs *regs) -+{ -+ if (arch_uprobe_skip_sstep(&uprobe->arch, regs)) -+ return true; -+ -+ uprobe->flags &= ~UPROBE_SKIP_SSTEP; -+ return false; -+} -+ -+/* -+ * Run handler and ask thread to singlestep. -+ * Ensure all non-fatal signals cannot interrupt thread while it singlesteps. -+ */ -+static void handle_swbp(struct pt_regs *regs) -+{ -+ struct vm_area_struct *vma; -+ struct uprobe_task *utask; -+ struct uprobe *uprobe; -+ struct mm_struct *mm; -+ unsigned long bp_vaddr; -+ -+ uprobe = NULL; -+ bp_vaddr = uprobe_get_swbp_addr(regs); -+ mm = current->mm; -+ down_read(&mm->mmap_sem); -+ vma = find_vma(mm, bp_vaddr); -+ -+ if (vma && vma->vm_start <= bp_vaddr && valid_vma(vma, false)) { -+ struct inode *inode; -+ loff_t offset; -+ -+ inode = vma->vm_file->f_mapping->host; -+ offset = bp_vaddr - vma->vm_start; -+ offset += (vma->vm_pgoff << PAGE_SHIFT); -+ uprobe = find_uprobe(inode, offset); -+ } -+ -+ srcu_read_unlock_raw(&uprobes_srcu, current->uprobe_srcu_id); -+ current->uprobe_srcu_id = -1; -+ up_read(&mm->mmap_sem); -+ -+ if (!uprobe) { -+ /* No matching uprobe; signal SIGTRAP. */ -+ send_sig(SIGTRAP, current, 0); -+ return; -+ } -+ -+ utask = current->utask; -+ if (!utask) { -+ utask = add_utask(); -+ /* Cannot allocate; re-execute the instruction. */ -+ if (!utask) -+ goto cleanup_ret; -+ } -+ utask->active_uprobe = uprobe; -+ handler_chain(uprobe, regs); -+ if (uprobe->flags & UPROBE_SKIP_SSTEP && can_skip_sstep(uprobe, regs)) -+ goto cleanup_ret; -+ -+ utask->state = UTASK_SSTEP; -+ if (!pre_ssout(uprobe, regs, bp_vaddr)) { -+ user_enable_single_step(current); -+ return; -+ } -+ -+cleanup_ret: -+ if (utask) { -+ utask->active_uprobe = NULL; -+ utask->state = UTASK_RUNNING; -+ } -+ if (uprobe) { -+ if (!(uprobe->flags & UPROBE_SKIP_SSTEP)) -+ -+ /* -+ * cannot singlestep; cannot skip instruction; -+ * re-execute the instruction. -+ */ -+ instruction_pointer_set(regs, bp_vaddr); -+ -+ put_uprobe(uprobe); -+ } -+} -+ -+/* -+ * Perform required fix-ups and disable singlestep. -+ * Allow pending signals to take effect. -+ */ -+static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) -+{ -+ struct uprobe *uprobe; -+ -+ uprobe = utask->active_uprobe; -+ if (utask->state == UTASK_SSTEP_ACK) -+ arch_uprobe_post_xol(&uprobe->arch, regs); -+ else if (utask->state == UTASK_SSTEP_TRAPPED) -+ arch_uprobe_abort_xol(&uprobe->arch, regs); -+ else -+ WARN_ON_ONCE(1); -+ -+ put_uprobe(uprobe); -+ utask->active_uprobe = NULL; -+ utask->state = UTASK_RUNNING; -+ user_disable_single_step(current); -+ xol_free_insn_slot(current); -+ -+ spin_lock_irq(¤t->sighand->siglock); -+ recalc_sigpending(); /* see uprobe_deny_signal() */ -+ spin_unlock_irq(¤t->sighand->siglock); -+} -+ -+/* -+ * On breakpoint hit, breakpoint notifier sets the TIF_UPROBE flag. (and on -+ * subsequent probe hits on the thread sets the state to UTASK_BP_HIT) and -+ * allows the thread to return from interrupt. -+ * -+ * On singlestep exception, singlestep notifier sets the TIF_UPROBE flag and -+ * also sets the state to UTASK_SSTEP_ACK and allows the thread to return from -+ * interrupt. -+ * -+ * While returning to userspace, thread notices the TIF_UPROBE flag and calls -+ * uprobe_notify_resume(). -+ */ -+void uprobe_notify_resume(struct pt_regs *regs) -+{ -+ struct uprobe_task *utask; -+ -+ utask = current->utask; -+ if (!utask || utask->state == UTASK_BP_HIT) -+ handle_swbp(regs); -+ else -+ handle_singlestep(utask, regs); -+} -+ -+/* -+ * uprobe_pre_sstep_notifier gets called from interrupt context as part of -+ * notifier mechanism. Set TIF_UPROBE flag and indicate breakpoint hit. -+ */ -+int uprobe_pre_sstep_notifier(struct pt_regs *regs) -+{ -+ struct uprobe_task *utask; -+ -+ if (!current->mm || !atomic_read(¤t->mm->uprobes_state.count)) -+ /* task is currently not uprobed */ -+ return 0; -+ -+ utask = current->utask; -+ if (utask) -+ utask->state = UTASK_BP_HIT; -+ -+ set_thread_flag(TIF_UPROBE); -+ current->uprobe_srcu_id = srcu_read_lock_raw(&uprobes_srcu); -+ -+ return 1; -+} -+ -+/* -+ * uprobe_post_sstep_notifier gets called in interrupt context as part of notifier -+ * mechanism. Set TIF_UPROBE flag and indicate completion of singlestep. -+ */ -+int uprobe_post_sstep_notifier(struct pt_regs *regs) -+{ -+ struct uprobe_task *utask = current->utask; -+ -+ if (!current->mm || !utask || !utask->active_uprobe) -+ /* task is currently not uprobed */ -+ return 0; -+ -+ utask->state = UTASK_SSTEP_ACK; -+ set_thread_flag(TIF_UPROBE); -+ return 1; -+} -+ -+static struct notifier_block uprobe_exception_nb = { -+ .notifier_call = arch_uprobe_exception_notify, -+ .priority = INT_MAX-1, /* notified after kprobes, kgdb */ -+}; -+ -+static int __init init_uprobes(void) -+{ -+ int i; -+ -+ for (i = 0; i < UPROBES_HASH_SZ; i++) { -+ mutex_init(&uprobes_mutex[i]); -+ mutex_init(&uprobes_mmap_mutex[i]); -+ } -+ init_srcu_struct(&uprobes_srcu); -+ -+ return register_die_notifier(&uprobe_exception_nb); -+} -+module_init(init_uprobes); -+ -+static void __exit exit_uprobes(void) -+{ -+} -+module_exit(exit_uprobes); -diff --git a/kernel/fork.c b/kernel/fork.c -index c3eafd8..5b87e9f 100644 ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -68,6 +68,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -423,6 +424,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) - - if (retval) - goto out; -+ -+ if (file && uprobe_mmap(tmp)) -+ goto out; - } - /* a new mm has just been created */ - arch_dup_mmap(oldmm, mm); -@@ -571,6 +575,7 @@ void mmput(struct mm_struct *mm) - might_sleep(); - - if (atomic_dec_and_test(&mm->mm_users)) { -+ uprobe_clear_state(mm); - exit_aio(mm); - ksm_exit(mm); - khugepaged_exit(mm); /* must run before exit_mmap */ -@@ -749,6 +754,8 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm) - exit_pi_state_list(tsk); - #endif - -+ uprobe_free_utask(tsk); -+ - /* Get rid of any cached register state */ - deactivate_mm(tsk, mm); - -@@ -803,6 +810,7 @@ struct mm_struct *dup_mm(struct task_struct *tsk) - #ifdef CONFIG_TRANSPARENT_HUGEPAGE - mm->pmd_huge_pte = NULL; - #endif -+ uprobe_reset_state(mm); - - if (!mm_init(mm, tsk)) - goto fail_nomem; -@@ -1344,6 +1352,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, - INIT_LIST_HEAD(&p->pi_state_list); - p->pi_state_cache = NULL; - #endif -+ uprobe_copy_process(p); - /* - * sigaltstack should be cleared when sharing the same VM - */ -diff --git a/kernel/signal.c b/kernel/signal.c -index 17afcaf..60d80ab 100644 ---- a/kernel/signal.c -+++ b/kernel/signal.c -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - #define CREATE_TRACE_POINTS - #include - -@@ -2202,6 +2203,9 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, - struct signal_struct *signal = current->signal; - int signr; - -+ if (unlikely(uprobe_deny_signal())) -+ return 0; -+ - relock: - /* - * We'll jump back here after any time we were stopped in TASK_STOPPED. -diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig -index a1d2849..ea4bff6 100644 ---- a/kernel/trace/Kconfig -+++ b/kernel/trace/Kconfig -@@ -373,6 +373,7 @@ config KPROBE_EVENT - depends on HAVE_REGS_AND_STACK_ACCESS_API - bool "Enable kprobes-based dynamic events" - select TRACING -+ select PROBE_EVENTS - default y - help - This allows the user to add tracing events (similar to tracepoints) -@@ -385,6 +386,25 @@ config KPROBE_EVENT - This option is also required by perf-probe subcommand of perf tools. - If you want to use perf tools, this option is strongly recommended. - -+config UPROBE_EVENT -+ bool "Enable uprobes-based dynamic events" -+ depends on ARCH_SUPPORTS_UPROBES -+ depends on MMU -+ select UPROBES -+ select PROBE_EVENTS -+ select TRACING -+ default n -+ help -+ This allows the user to add tracing events on top of userspace -+ dynamic events (similar to tracepoints) on the fly via the trace -+ events interface. Those events can be inserted wherever uprobes -+ can probe, and record various registers. -+ This option is required if you plan to use perf-probe subcommand -+ of perf tools on user space applications. -+ -+config PROBE_EVENTS -+ def_bool n -+ - config DYNAMIC_FTRACE - bool "enable/disable ftrace tracepoints dynamically" - depends on FUNCTION_TRACER -diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile -index 5f39a07..1734c03 100644 ---- a/kernel/trace/Makefile -+++ b/kernel/trace/Makefile -@@ -61,5 +61,7 @@ endif - ifeq ($(CONFIG_TRACING),y) - obj-$(CONFIG_KGDB_KDB) += trace_kdb.o - endif -+obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o -+obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o - - libftrace-y := ftrace.o -diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h -index f95d65d..a6bf705 100644 ---- a/kernel/trace/trace.h -+++ b/kernel/trace/trace.h -@@ -103,6 +103,11 @@ struct kretprobe_trace_entry_head { - unsigned long ret_ip; - }; - -+struct uprobe_trace_entry_head { -+ struct trace_entry ent; -+ unsigned long ip; -+}; -+ - /* - * trace_flag_type is an enumeration that holds different - * states when a trace occurs. These are: -diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c -index 580a05e..b31d3d5 100644 ---- a/kernel/trace/trace_kprobe.c -+++ b/kernel/trace/trace_kprobe.c -@@ -19,547 +19,15 @@ - - #include - #include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "trace.h" --#include "trace_output.h" -- --#define MAX_TRACE_ARGS 128 --#define MAX_ARGSTR_LEN 63 --#define MAX_EVENT_NAME_LEN 64 --#define MAX_STRING_SIZE PATH_MAX --#define KPROBE_EVENT_SYSTEM "kprobes" -- --/* Reserved field names */ --#define FIELD_STRING_IP "__probe_ip" --#define FIELD_STRING_RETIP "__probe_ret_ip" --#define FIELD_STRING_FUNC "__probe_func" -- --const char *reserved_field_names[] = { -- "common_type", -- "common_flags", -- "common_preempt_count", -- "common_pid", -- "common_tgid", -- FIELD_STRING_IP, -- FIELD_STRING_RETIP, -- FIELD_STRING_FUNC, --}; -- --/* Printing function type */ --typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *, -- void *); --#define PRINT_TYPE_FUNC_NAME(type) print_type_##type --#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type -- --/* Printing in basic type function template */ --#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \ --static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ -- const char *name, \ -- void *data, void *ent)\ --{ \ -- return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\ --} \ --static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; -- --DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int) --DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int) --DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long) --DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long) --DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int) --DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int) --DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long) --DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) -- --/* data_rloc: data relative location, compatible with u32 */ --#define make_data_rloc(len, roffs) \ -- (((u32)(len) << 16) | ((u32)(roffs) & 0xffff)) --#define get_rloc_len(dl) ((u32)(dl) >> 16) --#define get_rloc_offs(dl) ((u32)(dl) & 0xffff) -- --static inline void *get_rloc_data(u32 *dl) --{ -- return (u8 *)dl + get_rloc_offs(*dl); --} -- --/* For data_loc conversion */ --static inline void *get_loc_data(u32 *dl, void *ent) --{ -- return (u8 *)ent + get_rloc_offs(*dl); --} -- --/* -- * Convert data_rloc to data_loc: -- * data_rloc stores the offset from data_rloc itself, but data_loc -- * stores the offset from event entry. -- */ --#define convert_rloc_to_loc(dl, offs) ((u32)(dl) + (offs)) -- --/* For defining macros, define string/string_size types */ --typedef u32 string; --typedef u32 string_size; -- --/* Print type function for string type */ --static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, -- const char *name, -- void *data, void *ent) --{ -- int len = *(u32 *)data >> 16; -- -- if (!len) -- return trace_seq_printf(s, " %s=(fault)", name); -- else -- return trace_seq_printf(s, " %s=\"%s\"", name, -- (const char *)get_loc_data(data, ent)); --} --static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; -- --/* Data fetch function type */ --typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); -- --struct fetch_param { -- fetch_func_t fn; -- void *data; --}; -- --static __kprobes void call_fetch(struct fetch_param *fprm, -- struct pt_regs *regs, void *dest) --{ -- return fprm->fn(regs, fprm->data, dest); --} -- --#define FETCH_FUNC_NAME(method, type) fetch_##method##_##type --/* -- * Define macro for basic types - we don't need to define s* types, because -- * we have to care only about bitwidth at recording time. -- */ --#define DEFINE_BASIC_FETCH_FUNCS(method) \ --DEFINE_FETCH_##method(u8) \ --DEFINE_FETCH_##method(u16) \ --DEFINE_FETCH_##method(u32) \ --DEFINE_FETCH_##method(u64) -- --#define CHECK_FETCH_FUNCS(method, fn) \ -- (((FETCH_FUNC_NAME(method, u8) == fn) || \ -- (FETCH_FUNC_NAME(method, u16) == fn) || \ -- (FETCH_FUNC_NAME(method, u32) == fn) || \ -- (FETCH_FUNC_NAME(method, u64) == fn) || \ -- (FETCH_FUNC_NAME(method, string) == fn) || \ -- (FETCH_FUNC_NAME(method, string_size) == fn)) \ -- && (fn != NULL)) -- --/* Data fetch function templates */ --#define DEFINE_FETCH_reg(type) \ --static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ -- void *offset, void *dest) \ --{ \ -- *(type *)dest = (type)regs_get_register(regs, \ -- (unsigned int)((unsigned long)offset)); \ --} --DEFINE_BASIC_FETCH_FUNCS(reg) --/* No string on the register */ --#define fetch_reg_string NULL --#define fetch_reg_string_size NULL -- --#define DEFINE_FETCH_stack(type) \ --static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ -- void *offset, void *dest) \ --{ \ -- *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ -- (unsigned int)((unsigned long)offset)); \ --} --DEFINE_BASIC_FETCH_FUNCS(stack) --/* No string on the stack entry */ --#define fetch_stack_string NULL --#define fetch_stack_string_size NULL -- --#define DEFINE_FETCH_retval(type) \ --static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ -- void *dummy, void *dest) \ --{ \ -- *(type *)dest = (type)regs_return_value(regs); \ --} --DEFINE_BASIC_FETCH_FUNCS(retval) --/* No string on the retval */ --#define fetch_retval_string NULL --#define fetch_retval_string_size NULL -- --#define DEFINE_FETCH_memory(type) \ --static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ -- void *addr, void *dest) \ --{ \ -- type retval; \ -- if (probe_kernel_address(addr, retval)) \ -- *(type *)dest = 0; \ -- else \ -- *(type *)dest = retval; \ --} --DEFINE_BASIC_FETCH_FUNCS(memory) --/* -- * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max -- * length and relative data location. -- */ --static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, -- void *addr, void *dest) --{ -- long ret; -- int maxlen = get_rloc_len(*(u32 *)dest); -- u8 *dst = get_rloc_data(dest); -- u8 *src = addr; -- mm_segment_t old_fs = get_fs(); -- if (!maxlen) -- return; -- /* -- * Try to get string again, since the string can be changed while -- * probing. -- */ -- set_fs(KERNEL_DS); -- pagefault_disable(); -- do -- ret = __copy_from_user_inatomic(dst++, src++, 1); -- while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen); -- dst[-1] = '\0'; -- pagefault_enable(); -- set_fs(old_fs); -- -- if (ret < 0) { /* Failed to fetch string */ -- ((u8 *)get_rloc_data(dest))[0] = '\0'; -- *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); -- } else -- *(u32 *)dest = make_data_rloc(src - (u8 *)addr, -- get_rloc_offs(*(u32 *)dest)); --} --/* Return the length of string -- including null terminal byte */ --static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, -- void *addr, void *dest) --{ -- int ret, len = 0; -- u8 c; -- mm_segment_t old_fs = get_fs(); -- -- set_fs(KERNEL_DS); -- pagefault_disable(); -- do { -- ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1); -- len++; -- } while (c && ret == 0 && len < MAX_STRING_SIZE); -- pagefault_enable(); -- set_fs(old_fs); -- -- if (ret < 0) /* Failed to check the length */ -- *(u32 *)dest = 0; -- else -- *(u32 *)dest = len; --} -- --/* Memory fetching by symbol */ --struct symbol_cache { -- char *symbol; -- long offset; -- unsigned long addr; --}; -- --static unsigned long update_symbol_cache(struct symbol_cache *sc) --{ -- sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); -- if (sc->addr) -- sc->addr += sc->offset; -- return sc->addr; --} -- --static void free_symbol_cache(struct symbol_cache *sc) --{ -- kfree(sc->symbol); -- kfree(sc); --} -- --static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) --{ -- struct symbol_cache *sc; -- -- if (!sym || strlen(sym) == 0) -- return NULL; -- sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); -- if (!sc) -- return NULL; -- -- sc->symbol = kstrdup(sym, GFP_KERNEL); -- if (!sc->symbol) { -- kfree(sc); -- return NULL; -- } -- sc->offset = offset; - -- update_symbol_cache(sc); -- return sc; --} -- --#define DEFINE_FETCH_symbol(type) \ --static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ -- void *data, void *dest) \ --{ \ -- struct symbol_cache *sc = data; \ -- if (sc->addr) \ -- fetch_memory_##type(regs, (void *)sc->addr, dest); \ -- else \ -- *(type *)dest = 0; \ --} --DEFINE_BASIC_FETCH_FUNCS(symbol) --DEFINE_FETCH_symbol(string) --DEFINE_FETCH_symbol(string_size) -- --/* Dereference memory access function */ --struct deref_fetch_param { -- struct fetch_param orig; -- long offset; --}; -- --#define DEFINE_FETCH_deref(type) \ --static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ -- void *data, void *dest) \ --{ \ -- struct deref_fetch_param *dprm = data; \ -- unsigned long addr; \ -- call_fetch(&dprm->orig, regs, &addr); \ -- if (addr) { \ -- addr += dprm->offset; \ -- fetch_memory_##type(regs, (void *)addr, dest); \ -- } else \ -- *(type *)dest = 0; \ --} --DEFINE_BASIC_FETCH_FUNCS(deref) --DEFINE_FETCH_deref(string) --DEFINE_FETCH_deref(string_size) -- --static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data) --{ -- if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) -- update_deref_fetch_param(data->orig.data); -- else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) -- update_symbol_cache(data->orig.data); --} -- --static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) --{ -- 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); --} -- --/* Bitfield fetch function */ --struct bitfield_fetch_param { -- struct fetch_param orig; -- unsigned char hi_shift; -- unsigned char low_shift; --}; -+#include "trace_probe.h" - --#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 --update_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)) -- update_deref_fetch_param(data->orig.data); -- else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) -- update_symbol_cache(data->orig.data); --} -- --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) --#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) --#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) -- --/* Fetch types */ --enum { -- FETCH_MTD_reg = 0, -- FETCH_MTD_stack, -- FETCH_MTD_retval, -- FETCH_MTD_memory, -- FETCH_MTD_symbol, -- FETCH_MTD_deref, -- FETCH_MTD_bitfield, -- FETCH_MTD_END, --}; -- --#define ASSIGN_FETCH_FUNC(method, type) \ -- [FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) -- --#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ -- {.name = _name, \ -- .size = _size, \ -- .is_signed = sign, \ -- .print = PRINT_TYPE_FUNC_NAME(ptype), \ -- .fmt = PRINT_TYPE_FMT_NAME(ptype), \ -- .fmttype = _fmttype, \ -- .fetch = { \ --ASSIGN_FETCH_FUNC(reg, ftype), \ --ASSIGN_FETCH_FUNC(stack, ftype), \ --ASSIGN_FETCH_FUNC(retval, ftype), \ --ASSIGN_FETCH_FUNC(memory, ftype), \ --ASSIGN_FETCH_FUNC(symbol, ftype), \ --ASSIGN_FETCH_FUNC(deref, ftype), \ --ASSIGN_FETCH_FUNC(bitfield, ftype), \ -- } \ -- } -- --#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ -- __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) -- --#define FETCH_TYPE_STRING 0 --#define FETCH_TYPE_STRSIZE 1 -- --/* Fetch type information table */ --static const struct fetch_type { -- const char *name; /* Name of type */ -- size_t size; /* Byte size of type */ -- int is_signed; /* Signed flag */ -- print_type_func_t print; /* Print functions */ -- const char *fmt; /* Fromat string */ -- const char *fmttype; /* Name in format file */ -- /* Fetch functions */ -- fetch_func_t fetch[FETCH_MTD_END]; --} fetch_type_table[] = { -- /* Special types */ -- [FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string, -- sizeof(u32), 1, "__data_loc char[]"), -- [FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32, -- string_size, sizeof(u32), 0, "u32"), -- /* Basic types */ -- ASSIGN_FETCH_TYPE(u8, u8, 0), -- ASSIGN_FETCH_TYPE(u16, u16, 0), -- ASSIGN_FETCH_TYPE(u32, u32, 0), -- ASSIGN_FETCH_TYPE(u64, u64, 0), -- ASSIGN_FETCH_TYPE(s8, u8, 1), -- ASSIGN_FETCH_TYPE(s16, u16, 1), -- ASSIGN_FETCH_TYPE(s32, u32, 1), -- ASSIGN_FETCH_TYPE(s64, u64, 1), --}; -- --static const struct fetch_type *find_fetch_type(const char *type) --{ -- int i; -- -- 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; --} -- --/* Special function : only accept unsigned long */ --static __kprobes void fetch_stack_address(struct pt_regs *regs, -- void *dummy, void *dest) --{ -- *(unsigned long *)dest = kernel_stack_pointer(regs); --} -- --static fetch_func_t get_fetch_size_function(const struct fetch_type *type, -- fetch_func_t orig_fn) --{ -- int i; -- -- if (type != &fetch_type_table[FETCH_TYPE_STRING]) -- return NULL; /* Only string type needs size function */ -- for (i = 0; i < FETCH_MTD_END; i++) -- if (type->fetch[i] == orig_fn) -- return fetch_type_table[FETCH_TYPE_STRSIZE].fetch[i]; -- -- WARN_ON(1); /* This should not happen */ -- return NULL; --} -+#define KPROBE_EVENT_SYSTEM "kprobes" - - /** - * Kprobe event core functions - */ - --struct probe_arg { -- struct fetch_param fetch; -- struct fetch_param fetch_size; -- unsigned int offset; /* Offset from argument entry */ -- const char *name; /* Name of this argument */ -- const char *comm; /* Command of this argument */ -- const struct fetch_type *type; /* Type of this argument */ --}; -- --/* Flags for trace_probe */ --#define TP_FLAG_TRACE 1 --#define TP_FLAG_PROFILE 2 --#define TP_FLAG_REGISTERED 4 -- - struct trace_probe { - struct list_head list; - struct kretprobe rp; /* Use rp.kp for kprobe use */ -@@ -631,18 +99,6 @@ static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); - static int kretprobe_dispatcher(struct kretprobe_instance *ri, - struct pt_regs *regs); - --/* Check the name is good for event/group/fields */ --static int is_good_name(const char *name) --{ -- if (!isalpha(*name) && *name != '_') -- return 0; -- while (*++name != '\0') { -- if (!isalpha(*name) && !isdigit(*name) && *name != '_') -- return 0; -- } -- return 1; --} -- - /* - * Allocate new trace_probe and initialize it (including kprobes). - */ -@@ -651,7 +107,7 @@ static struct trace_probe *alloc_trace_probe(const char *group, - void *addr, - const char *symbol, - unsigned long offs, -- int nargs, int is_return) -+ int nargs, bool is_return) - { - struct trace_probe *tp; - int ret = -ENOMEM; -@@ -702,34 +158,12 @@ error: - return ERR_PTR(ret); - } - --static void update_probe_arg(struct probe_arg *arg) --{ -- if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn)) -- update_bitfield_fetch_param(arg->fetch.data); -- else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) -- update_deref_fetch_param(arg->fetch.data); -- else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) -- update_symbol_cache(arg->fetch.data); --} -- --static void free_probe_arg(struct probe_arg *arg) --{ -- 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); -- kfree(arg->name); -- kfree(arg->comm); --} -- - static void free_trace_probe(struct trace_probe *tp) - { - int i; - - for (i = 0; i < tp->nr_args; i++) -- free_probe_arg(&tp->args[i]); -+ traceprobe_free_probe_arg(&tp->args[i]); - - kfree(tp->call.class->system); - kfree(tp->call.name); -@@ -787,7 +221,7 @@ static int __register_trace_probe(struct trace_probe *tp) - return -EINVAL; - - for (i = 0; i < tp->nr_args; i++) -- update_probe_arg(&tp->args[i]); -+ traceprobe_update_arg(&tp->args[i]); - - /* Set/clear disabled flag according to tp->flag */ - if (trace_probe_is_enabled(tp)) -@@ -919,227 +353,6 @@ static struct notifier_block trace_probe_module_nb = { - .priority = 1 /* Invoked after kprobe module callback */ - }; - --/* Split symbol and offset. */ --static int split_symbol_offset(char *symbol, unsigned long *offset) --{ -- char *tmp; -- int ret; -- -- if (!offset) -- return -EINVAL; -- -- tmp = strchr(symbol, '+'); -- if (tmp) { -- /* skip sign because strict_strtol doesn't accept '+' */ -- ret = strict_strtoul(tmp + 1, 0, offset); -- if (ret) -- return ret; -- *tmp = '\0'; -- } else -- *offset = 0; -- return 0; --} -- --#define PARAM_MAX_ARGS 16 --#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) -- --static int parse_probe_vars(char *arg, const struct fetch_type *t, -- struct fetch_param *f, int is_return) --{ -- int ret = 0; -- unsigned long param; -- -- if (strcmp(arg, "retval") == 0) { -- if (is_return) -- f->fn = t->fetch[FETCH_MTD_retval]; -- else -- ret = -EINVAL; -- } else if (strncmp(arg, "stack", 5) == 0) { -- if (arg[5] == '\0') { -- if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0) -- f->fn = fetch_stack_address; -- else -- ret = -EINVAL; -- } else if (isdigit(arg[5])) { -- ret = strict_strtoul(arg + 5, 10, ¶m); -- if (ret || param > PARAM_MAX_STACK) -- ret = -EINVAL; -- else { -- f->fn = t->fetch[FETCH_MTD_stack]; -- f->data = (void *)param; -- } -- } else -- ret = -EINVAL; -- } else -- ret = -EINVAL; -- return ret; --} -- --/* Recursive argument parser */ --static int __parse_probe_arg(char *arg, const struct fetch_type *t, -- struct fetch_param *f, int is_return) --{ -- int ret = 0; -- unsigned long param; -- long offset; -- char *tmp; -- -- switch (arg[0]) { -- case '$': -- ret = parse_probe_vars(arg + 1, t, f, is_return); -- break; -- case '%': /* named register */ -- ret = regs_query_register_offset(arg + 1); -- if (ret >= 0) { -- f->fn = t->fetch[FETCH_MTD_reg]; -- f->data = (void *)(unsigned long)ret; -- ret = 0; -- } -- break; -- case '@': /* memory or symbol */ -- if (isdigit(arg[1])) { -- ret = strict_strtoul(arg + 1, 0, ¶m); -- if (ret) -- break; -- f->fn = t->fetch[FETCH_MTD_memory]; -- f->data = (void *)param; -- } else { -- ret = split_symbol_offset(arg + 1, &offset); -- if (ret) -- break; -- f->data = alloc_symbol_cache(arg + 1, offset); -- if (f->data) -- f->fn = t->fetch[FETCH_MTD_symbol]; -- } -- break; -- case '+': /* deref memory */ -- arg++; /* Skip '+', because strict_strtol() rejects it. */ -- case '-': -- tmp = strchr(arg, '('); -- if (!tmp) -- break; -- *tmp = '\0'; -- ret = strict_strtol(arg, 0, &offset); -- if (ret) -- break; -- arg = tmp + 1; -- tmp = strrchr(arg, ')'); -- if (tmp) { -- struct deref_fetch_param *dprm; -- const struct fetch_type *t2 = find_fetch_type(NULL); -- *tmp = '\0'; -- dprm = kzalloc(sizeof(struct deref_fetch_param), -- GFP_KERNEL); -- if (!dprm) -- return -ENOMEM; -- dprm->offset = offset; -- ret = __parse_probe_arg(arg, t2, &dprm->orig, -- is_return); -- if (ret) -- kfree(dprm); -- else { -- f->fn = t->fetch[FETCH_MTD_deref]; -- f->data = (void *)dprm; -- } -- } -- break; -- } -- if (!ret && !f->fn) { /* Parsed, but do not find fetch method */ -- pr_info("%s type has no corresponding fetch method.\n", -- t->name); -- ret = -EINVAL; -- } -- 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) --{ -- const char *t; -- int ret; -- -- if (strlen(arg) > MAX_ARGSTR_LEN) { -- pr_info("Argument is too long.: %s\n", arg); -- return -ENOSPC; -- } -- parg->comm = kstrdup(arg, GFP_KERNEL); -- if (!parg->comm) { -- pr_info("Failed to allocate memory for command '%s'.\n", arg); -- return -ENOMEM; -- } -- t = strchr(parg->comm, ':'); -- if (t) { -- arg[t - parg->comm] = '\0'; -- t++; -- } -- parg->type = find_fetch_type(t); -- if (!parg->type) { -- pr_info("Unsupported type: %s\n", t); -- return -EINVAL; -- } -- parg->offset = tp->size; -- tp->size += parg->type->size; -- 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); -- parg->fetch_size.data = parg->fetch.data; -- } -- return ret; --} -- --/* Return 1 if name is reserved or already used by another argument */ --static int conflict_field_name(const char *name, -- struct probe_arg *args, int narg) --{ -- int i; -- for (i = 0; i < ARRAY_SIZE(reserved_field_names); i++) -- if (strcmp(reserved_field_names[i], name) == 0) -- return 1; -- for (i = 0; i < narg; i++) -- if (strcmp(args[i].name, name) == 0) -- return 1; -- return 0; --} -- - static int create_trace_probe(int argc, char **argv) - { - /* -@@ -1162,7 +375,7 @@ static int create_trace_probe(int argc, char **argv) - */ - struct trace_probe *tp; - int i, ret = 0; -- int is_return = 0, is_delete = 0; -+ bool is_return = false, is_delete = false; - char *symbol = NULL, *event = NULL, *group = NULL; - char *arg; - unsigned long offset = 0; -@@ -1171,11 +384,11 @@ static int create_trace_probe(int argc, char **argv) - - /* argc must be >= 1 */ - if (argv[0][0] == 'p') -- is_return = 0; -+ is_return = false; - else if (argv[0][0] == 'r') -- is_return = 1; -+ is_return = true; - else if (argv[0][0] == '-') -- is_delete = 1; -+ is_delete = true; - else { - pr_info("Probe definition must be started with 'p', 'r' or" - " '-'.\n"); -@@ -1240,7 +453,7 @@ static int create_trace_probe(int argc, char **argv) - /* a symbol specified */ - symbol = argv[1]; - /* TODO: support .init module functions */ -- ret = split_symbol_offset(symbol, &offset); -+ ret = traceprobe_split_symbol_offset(symbol, &offset); - if (ret) { - pr_info("Failed to parse symbol.\n"); - return ret; -@@ -1302,7 +515,8 @@ static int create_trace_probe(int argc, char **argv) - goto error; - } - -- if (conflict_field_name(tp->args[i].name, tp->args, i)) { -+ if (traceprobe_conflict_field_name(tp->args[i].name, -+ tp->args, i)) { - pr_info("Argument[%d] name '%s' conflicts with " - "another field.\n", i, argv[i]); - ret = -EINVAL; -@@ -1310,7 +524,8 @@ static int create_trace_probe(int argc, char **argv) - } - - /* Parse fetch argument */ -- ret = parse_probe_arg(arg, tp, &tp->args[i], is_return); -+ ret = traceprobe_parse_probe_arg(arg, &tp->size, &tp->args[i], -+ is_return, true); - if (ret) { - pr_info("Parse error at argument[%d]. (%d)\n", i, ret); - goto error; -@@ -1412,70 +627,11 @@ static int probes_open(struct inode *inode, struct file *file) - return seq_open(file, &probes_seq_op); - } - --static int command_trace_probe(const char *buf) --{ -- char **argv; -- int argc = 0, ret = 0; -- -- argv = argv_split(GFP_KERNEL, buf, &argc); -- if (!argv) -- return -ENOMEM; -- -- if (argc) -- ret = create_trace_probe(argc, argv); -- -- argv_free(argv); -- return ret; --} -- --#define WRITE_BUFSIZE 4096 -- - static ssize_t probes_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) - { -- char *kbuf, *tmp; -- int ret; -- size_t done; -- size_t size; -- -- kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL); -- if (!kbuf) -- return -ENOMEM; -- -- ret = done = 0; -- while (done < count) { -- size = count - done; -- if (size >= WRITE_BUFSIZE) -- size = WRITE_BUFSIZE - 1; -- if (copy_from_user(kbuf, buffer + done, size)) { -- ret = -EFAULT; -- goto out; -- } -- kbuf[size] = '\0'; -- tmp = strchr(kbuf, '\n'); -- if (tmp) { -- *tmp = '\0'; -- size = tmp - kbuf + 1; -- } else if (done + size < count) { -- pr_warning("Line length is too long: " -- "Should be less than %d.", WRITE_BUFSIZE); -- ret = -EINVAL; -- goto out; -- } -- done += size; -- /* Remove comments */ -- tmp = strchr(kbuf, '#'); -- if (tmp) -- *tmp = '\0'; -- -- ret = command_trace_probe(kbuf); -- if (ret) -- goto out; -- } -- ret = done; --out: -- kfree(kbuf); -- return ret; -+ return traceprobe_probes_write(file, buffer, count, ppos, -+ create_trace_probe); - } - - static const struct file_operations kprobe_events_ops = { -@@ -1711,16 +867,6 @@ partial: - return TRACE_TYPE_PARTIAL_LINE; - } - --#undef DEFINE_FIELD --#define DEFINE_FIELD(type, item, name, is_signed) \ -- do { \ -- ret = trace_define_field(event_call, #type, name, \ -- offsetof(typeof(field), item), \ -- sizeof(field.item), is_signed, \ -- FILTER_OTHER); \ -- if (ret) \ -- return ret; \ -- } while (0) - - static int kprobe_event_define_fields(struct ftrace_event_call *event_call) - { -@@ -2051,8 +1197,9 @@ static __init int kprobe_trace_self_tests_init(void) - - pr_info("Testing kprobe tracing: "); - -- ret = command_trace_probe("p:testprobe kprobe_trace_selftest_target " -- "$stack $stack0 +0($stack)"); -+ ret = traceprobe_command("p:testprobe kprobe_trace_selftest_target " -+ "$stack $stack0 +0($stack)", -+ create_trace_probe); - if (WARN_ON_ONCE(ret)) { - pr_warning("error on probing function entry.\n"); - warn++; -@@ -2066,8 +1213,8 @@ static __init int kprobe_trace_self_tests_init(void) - enable_trace_probe(tp, TP_FLAG_TRACE); - } - -- ret = command_trace_probe("r:testprobe2 kprobe_trace_selftest_target " -- "$retval"); -+ ret = traceprobe_command("r:testprobe2 kprobe_trace_selftest_target " -+ "$retval", create_trace_probe); - if (WARN_ON_ONCE(ret)) { - pr_warning("error on probing function return.\n"); - warn++; -@@ -2101,13 +1248,13 @@ static __init int kprobe_trace_self_tests_init(void) - } else - disable_trace_probe(tp, TP_FLAG_TRACE); - -- ret = command_trace_probe("-:testprobe"); -+ ret = traceprobe_command("-:testprobe", create_trace_probe); - if (WARN_ON_ONCE(ret)) { - pr_warning("error on deleting a probe.\n"); - warn++; - } - -- ret = command_trace_probe("-:testprobe2"); -+ ret = traceprobe_command("-:testprobe2", create_trace_probe); - if (WARN_ON_ONCE(ret)) { - pr_warning("error on deleting a probe.\n"); - warn++; -diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c -new file mode 100644 -index 0000000..daa9980 ---- /dev/null -+++ b/kernel/trace/trace_probe.c -@@ -0,0 +1,839 @@ -+/* -+ * Common code for probe-based Dynamic events. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ * This code was copied from kernel/trace/trace_kprobe.c written by -+ * Masami Hiramatsu -+ * -+ * Updates to make this generic: -+ * Copyright (C) IBM Corporation, 2010-2011 -+ * Author: Srikar Dronamraju -+ */ -+ -+#include "trace_probe.h" -+ -+const char *reserved_field_names[] = { -+ "common_type", -+ "common_flags", -+ "common_preempt_count", -+ "common_pid", -+ "common_tgid", -+ FIELD_STRING_IP, -+ FIELD_STRING_RETIP, -+ FIELD_STRING_FUNC, -+}; -+ -+/* Printing function type */ -+#define PRINT_TYPE_FUNC_NAME(type) print_type_##type -+#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type -+ -+/* Printing in basic type function template */ -+#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \ -+static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ -+ const char *name, \ -+ void *data, void *ent)\ -+{ \ -+ return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\ -+} \ -+static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; -+ -+DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int) -+DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int) -+DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long) -+DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long) -+DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int) -+DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int) -+DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long) -+DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) -+ -+static inline void *get_rloc_data(u32 *dl) -+{ -+ return (u8 *)dl + get_rloc_offs(*dl); -+} -+ -+/* For data_loc conversion */ -+static inline void *get_loc_data(u32 *dl, void *ent) -+{ -+ return (u8 *)ent + get_rloc_offs(*dl); -+} -+ -+/* For defining macros, define string/string_size types */ -+typedef u32 string; -+typedef u32 string_size; -+ -+/* Print type function for string type */ -+static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, -+ const char *name, -+ void *data, void *ent) -+{ -+ int len = *(u32 *)data >> 16; -+ -+ if (!len) -+ return trace_seq_printf(s, " %s=(fault)", name); -+ else -+ return trace_seq_printf(s, " %s=\"%s\"", name, -+ (const char *)get_loc_data(data, ent)); -+} -+ -+static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; -+ -+#define FETCH_FUNC_NAME(method, type) fetch_##method##_##type -+/* -+ * Define macro for basic types - we don't need to define s* types, because -+ * we have to care only about bitwidth at recording time. -+ */ -+#define DEFINE_BASIC_FETCH_FUNCS(method) \ -+DEFINE_FETCH_##method(u8) \ -+DEFINE_FETCH_##method(u16) \ -+DEFINE_FETCH_##method(u32) \ -+DEFINE_FETCH_##method(u64) -+ -+#define CHECK_FETCH_FUNCS(method, fn) \ -+ (((FETCH_FUNC_NAME(method, u8) == fn) || \ -+ (FETCH_FUNC_NAME(method, u16) == fn) || \ -+ (FETCH_FUNC_NAME(method, u32) == fn) || \ -+ (FETCH_FUNC_NAME(method, u64) == fn) || \ -+ (FETCH_FUNC_NAME(method, string) == fn) || \ -+ (FETCH_FUNC_NAME(method, string_size) == fn)) \ -+ && (fn != NULL)) -+ -+/* Data fetch function templates */ -+#define DEFINE_FETCH_reg(type) \ -+static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ -+ void *offset, void *dest) \ -+{ \ -+ *(type *)dest = (type)regs_get_register(regs, \ -+ (unsigned int)((unsigned long)offset)); \ -+} -+DEFINE_BASIC_FETCH_FUNCS(reg) -+/* No string on the register */ -+#define fetch_reg_string NULL -+#define fetch_reg_string_size NULL -+ -+#define DEFINE_FETCH_stack(type) \ -+static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ -+ void *offset, void *dest) \ -+{ \ -+ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ -+ (unsigned int)((unsigned long)offset)); \ -+} -+DEFINE_BASIC_FETCH_FUNCS(stack) -+/* No string on the stack entry */ -+#define fetch_stack_string NULL -+#define fetch_stack_string_size NULL -+ -+#define DEFINE_FETCH_retval(type) \ -+static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ -+ void *dummy, void *dest) \ -+{ \ -+ *(type *)dest = (type)regs_return_value(regs); \ -+} -+DEFINE_BASIC_FETCH_FUNCS(retval) -+/* No string on the retval */ -+#define fetch_retval_string NULL -+#define fetch_retval_string_size NULL -+ -+#define DEFINE_FETCH_memory(type) \ -+static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ -+ void *addr, void *dest) \ -+{ \ -+ type retval; \ -+ if (probe_kernel_address(addr, retval)) \ -+ *(type *)dest = 0; \ -+ else \ -+ *(type *)dest = retval; \ -+} -+DEFINE_BASIC_FETCH_FUNCS(memory) -+/* -+ * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max -+ * length and relative data location. -+ */ -+static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, -+ void *addr, void *dest) -+{ -+ long ret; -+ int maxlen = get_rloc_len(*(u32 *)dest); -+ u8 *dst = get_rloc_data(dest); -+ u8 *src = addr; -+ mm_segment_t old_fs = get_fs(); -+ -+ if (!maxlen) -+ return; -+ -+ /* -+ * Try to get string again, since the string can be changed while -+ * probing. -+ */ -+ set_fs(KERNEL_DS); -+ pagefault_disable(); -+ -+ do -+ ret = __copy_from_user_inatomic(dst++, src++, 1); -+ while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen); -+ -+ dst[-1] = '\0'; -+ pagefault_enable(); -+ set_fs(old_fs); -+ -+ if (ret < 0) { /* Failed to fetch string */ -+ ((u8 *)get_rloc_data(dest))[0] = '\0'; -+ *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); -+ } else { -+ *(u32 *)dest = make_data_rloc(src - (u8 *)addr, -+ get_rloc_offs(*(u32 *)dest)); -+ } -+} -+ -+/* Return the length of string -- including null terminal byte */ -+static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, -+ void *addr, void *dest) -+{ -+ mm_segment_t old_fs; -+ int ret, len = 0; -+ u8 c; -+ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ pagefault_disable(); -+ -+ do { -+ ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1); -+ len++; -+ } while (c && ret == 0 && len < MAX_STRING_SIZE); -+ -+ pagefault_enable(); -+ set_fs(old_fs); -+ -+ if (ret < 0) /* Failed to check the length */ -+ *(u32 *)dest = 0; -+ else -+ *(u32 *)dest = len; -+} -+ -+/* Memory fetching by symbol */ -+struct symbol_cache { -+ char *symbol; -+ long offset; -+ unsigned long addr; -+}; -+ -+static unsigned long update_symbol_cache(struct symbol_cache *sc) -+{ -+ sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); -+ -+ if (sc->addr) -+ sc->addr += sc->offset; -+ -+ return sc->addr; -+} -+ -+static void free_symbol_cache(struct symbol_cache *sc) -+{ -+ kfree(sc->symbol); -+ kfree(sc); -+} -+ -+static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) -+{ -+ struct symbol_cache *sc; -+ -+ if (!sym || strlen(sym) == 0) -+ return NULL; -+ -+ sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); -+ if (!sc) -+ return NULL; -+ -+ sc->symbol = kstrdup(sym, GFP_KERNEL); -+ if (!sc->symbol) { -+ kfree(sc); -+ return NULL; -+ } -+ sc->offset = offset; -+ update_symbol_cache(sc); -+ -+ return sc; -+} -+ -+#define DEFINE_FETCH_symbol(type) \ -+static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ -+ void *data, void *dest) \ -+{ \ -+ struct symbol_cache *sc = data; \ -+ if (sc->addr) \ -+ fetch_memory_##type(regs, (void *)sc->addr, dest); \ -+ else \ -+ *(type *)dest = 0; \ -+} -+DEFINE_BASIC_FETCH_FUNCS(symbol) -+DEFINE_FETCH_symbol(string) -+DEFINE_FETCH_symbol(string_size) -+ -+/* Dereference memory access function */ -+struct deref_fetch_param { -+ struct fetch_param orig; -+ long offset; -+}; -+ -+#define DEFINE_FETCH_deref(type) \ -+static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ -+ void *data, void *dest) \ -+{ \ -+ struct deref_fetch_param *dprm = data; \ -+ unsigned long addr; \ -+ call_fetch(&dprm->orig, regs, &addr); \ -+ if (addr) { \ -+ addr += dprm->offset; \ -+ fetch_memory_##type(regs, (void *)addr, dest); \ -+ } else \ -+ *(type *)dest = 0; \ -+} -+DEFINE_BASIC_FETCH_FUNCS(deref) -+DEFINE_FETCH_deref(string) -+DEFINE_FETCH_deref(string_size) -+ -+static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data) -+{ -+ if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) -+ update_deref_fetch_param(data->orig.data); -+ else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) -+ update_symbol_cache(data->orig.data); -+} -+ -+static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) -+{ -+ 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); -+} -+ -+/* 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 -+update_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)) -+ update_deref_fetch_param(data->orig.data); -+ else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) -+ update_symbol_cache(data->orig.data); -+} -+ -+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) -+#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) -+#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) -+ -+#define ASSIGN_FETCH_FUNC(method, type) \ -+ [FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) -+ -+#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ -+ {.name = _name, \ -+ .size = _size, \ -+ .is_signed = sign, \ -+ .print = PRINT_TYPE_FUNC_NAME(ptype), \ -+ .fmt = PRINT_TYPE_FMT_NAME(ptype), \ -+ .fmttype = _fmttype, \ -+ .fetch = { \ -+ASSIGN_FETCH_FUNC(reg, ftype), \ -+ASSIGN_FETCH_FUNC(stack, ftype), \ -+ASSIGN_FETCH_FUNC(retval, ftype), \ -+ASSIGN_FETCH_FUNC(memory, ftype), \ -+ASSIGN_FETCH_FUNC(symbol, ftype), \ -+ASSIGN_FETCH_FUNC(deref, ftype), \ -+ASSIGN_FETCH_FUNC(bitfield, ftype), \ -+ } \ -+ } -+ -+#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ -+ __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) -+ -+#define FETCH_TYPE_STRING 0 -+#define FETCH_TYPE_STRSIZE 1 -+ -+/* Fetch type information table */ -+static const struct fetch_type fetch_type_table[] = { -+ /* Special types */ -+ [FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string, -+ sizeof(u32), 1, "__data_loc char[]"), -+ [FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32, -+ string_size, sizeof(u32), 0, "u32"), -+ /* Basic types */ -+ ASSIGN_FETCH_TYPE(u8, u8, 0), -+ ASSIGN_FETCH_TYPE(u16, u16, 0), -+ ASSIGN_FETCH_TYPE(u32, u32, 0), -+ ASSIGN_FETCH_TYPE(u64, u64, 0), -+ ASSIGN_FETCH_TYPE(s8, u8, 1), -+ ASSIGN_FETCH_TYPE(s16, u16, 1), -+ ASSIGN_FETCH_TYPE(s32, u32, 1), -+ ASSIGN_FETCH_TYPE(s64, u64, 1), -+}; -+ -+static const struct fetch_type *find_fetch_type(const char *type) -+{ -+ int i; -+ -+ 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; -+} -+ -+/* Special function : only accept unsigned long */ -+static __kprobes void fetch_stack_address(struct pt_regs *regs, -+ void *dummy, void *dest) -+{ -+ *(unsigned long *)dest = kernel_stack_pointer(regs); -+} -+ -+static fetch_func_t get_fetch_size_function(const struct fetch_type *type, -+ fetch_func_t orig_fn) -+{ -+ int i; -+ -+ if (type != &fetch_type_table[FETCH_TYPE_STRING]) -+ return NULL; /* Only string type needs size function */ -+ -+ for (i = 0; i < FETCH_MTD_END; i++) -+ if (type->fetch[i] == orig_fn) -+ return fetch_type_table[FETCH_TYPE_STRSIZE].fetch[i]; -+ -+ WARN_ON(1); /* This should not happen */ -+ -+ return NULL; -+} -+ -+/* Split symbol and offset. */ -+int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset) -+{ -+ char *tmp; -+ int ret; -+ -+ if (!offset) -+ return -EINVAL; -+ -+ tmp = strchr(symbol, '+'); -+ if (tmp) { -+ /* skip sign because strict_strtol doesn't accept '+' */ -+ ret = strict_strtoul(tmp + 1, 0, offset); -+ if (ret) -+ return ret; -+ -+ *tmp = '\0'; -+ } else -+ *offset = 0; -+ -+ return 0; -+} -+ -+#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) -+ -+static int parse_probe_vars(char *arg, const struct fetch_type *t, -+ struct fetch_param *f, bool is_return) -+{ -+ int ret = 0; -+ unsigned long param; -+ -+ if (strcmp(arg, "retval") == 0) { -+ if (is_return) -+ f->fn = t->fetch[FETCH_MTD_retval]; -+ else -+ ret = -EINVAL; -+ } else if (strncmp(arg, "stack", 5) == 0) { -+ if (arg[5] == '\0') { -+ if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0) -+ f->fn = fetch_stack_address; -+ else -+ ret = -EINVAL; -+ } else if (isdigit(arg[5])) { -+ ret = strict_strtoul(arg + 5, 10, ¶m); -+ if (ret || param > PARAM_MAX_STACK) -+ ret = -EINVAL; -+ else { -+ f->fn = t->fetch[FETCH_MTD_stack]; -+ f->data = (void *)param; -+ } -+ } else -+ ret = -EINVAL; -+ } else -+ ret = -EINVAL; -+ -+ return ret; -+} -+ -+/* Recursive argument parser */ -+static int parse_probe_arg(char *arg, const struct fetch_type *t, -+ struct fetch_param *f, bool is_return, bool is_kprobe) -+{ -+ unsigned long param; -+ long offset; -+ char *tmp; -+ int ret; -+ -+ ret = 0; -+ -+ /* Until uprobe_events supports only reg arguments */ -+ if (!is_kprobe && arg[0] != '%') -+ return -EINVAL; -+ -+ switch (arg[0]) { -+ case '$': -+ ret = parse_probe_vars(arg + 1, t, f, is_return); -+ break; -+ -+ case '%': /* named register */ -+ ret = regs_query_register_offset(arg + 1); -+ if (ret >= 0) { -+ f->fn = t->fetch[FETCH_MTD_reg]; -+ f->data = (void *)(unsigned long)ret; -+ ret = 0; -+ } -+ break; -+ -+ case '@': /* memory or symbol */ -+ if (isdigit(arg[1])) { -+ ret = strict_strtoul(arg + 1, 0, ¶m); -+ if (ret) -+ break; -+ -+ f->fn = t->fetch[FETCH_MTD_memory]; -+ f->data = (void *)param; -+ } else { -+ ret = traceprobe_split_symbol_offset(arg + 1, &offset); -+ if (ret) -+ break; -+ -+ f->data = alloc_symbol_cache(arg + 1, offset); -+ if (f->data) -+ f->fn = t->fetch[FETCH_MTD_symbol]; -+ } -+ break; -+ -+ case '+': /* deref memory */ -+ arg++; /* Skip '+', because strict_strtol() rejects it. */ -+ case '-': -+ tmp = strchr(arg, '('); -+ if (!tmp) -+ break; -+ -+ *tmp = '\0'; -+ ret = strict_strtol(arg, 0, &offset); -+ -+ if (ret) -+ break; -+ -+ arg = tmp + 1; -+ tmp = strrchr(arg, ')'); -+ -+ if (tmp) { -+ struct deref_fetch_param *dprm; -+ const struct fetch_type *t2; -+ -+ t2 = find_fetch_type(NULL); -+ *tmp = '\0'; -+ dprm = kzalloc(sizeof(struct deref_fetch_param), GFP_KERNEL); -+ -+ if (!dprm) -+ return -ENOMEM; -+ -+ dprm->offset = offset; -+ ret = parse_probe_arg(arg, t2, &dprm->orig, is_return, -+ is_kprobe); -+ if (ret) -+ kfree(dprm); -+ else { -+ f->fn = t->fetch[FETCH_MTD_deref]; -+ f->data = (void *)dprm; -+ } -+ } -+ break; -+ } -+ if (!ret && !f->fn) { /* Parsed, but do not find fetch method */ -+ pr_info("%s type has no corresponding fetch method.\n", t->name); -+ ret = -EINVAL; -+ } -+ -+ 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 */ -+int traceprobe_parse_probe_arg(char *arg, ssize_t *size, -+ struct probe_arg *parg, bool is_return, bool is_kprobe) -+{ -+ const char *t; -+ int ret; -+ -+ if (strlen(arg) > MAX_ARGSTR_LEN) { -+ pr_info("Argument is too long.: %s\n", arg); -+ return -ENOSPC; -+ } -+ parg->comm = kstrdup(arg, GFP_KERNEL); -+ if (!parg->comm) { -+ pr_info("Failed to allocate memory for command '%s'.\n", arg); -+ return -ENOMEM; -+ } -+ t = strchr(parg->comm, ':'); -+ if (t) { -+ arg[t - parg->comm] = '\0'; -+ t++; -+ } -+ parg->type = find_fetch_type(t); -+ if (!parg->type) { -+ pr_info("Unsupported type: %s\n", t); -+ return -EINVAL; -+ } -+ parg->offset = *size; -+ *size += parg->type->size; -+ ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return, is_kprobe); -+ -+ 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); -+ parg->fetch_size.data = parg->fetch.data; -+ } -+ -+ return ret; -+} -+ -+/* Return 1 if name is reserved or already used by another argument */ -+int traceprobe_conflict_field_name(const char *name, -+ struct probe_arg *args, int narg) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(reserved_field_names); i++) -+ if (strcmp(reserved_field_names[i], name) == 0) -+ return 1; -+ -+ for (i = 0; i < narg; i++) -+ if (strcmp(args[i].name, name) == 0) -+ return 1; -+ -+ return 0; -+} -+ -+void traceprobe_update_arg(struct probe_arg *arg) -+{ -+ if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn)) -+ update_bitfield_fetch_param(arg->fetch.data); -+ else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) -+ update_deref_fetch_param(arg->fetch.data); -+ else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) -+ update_symbol_cache(arg->fetch.data); -+} -+ -+void traceprobe_free_probe_arg(struct probe_arg *arg) -+{ -+ 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); -+ -+ kfree(arg->name); -+ kfree(arg->comm); -+} -+ -+int traceprobe_command(const char *buf, int (*createfn)(int, char **)) -+{ -+ char **argv; -+ int argc, ret; -+ -+ argc = 0; -+ ret = 0; -+ argv = argv_split(GFP_KERNEL, buf, &argc); -+ if (!argv) -+ return -ENOMEM; -+ -+ if (argc) -+ ret = createfn(argc, argv); -+ -+ argv_free(argv); -+ -+ return ret; -+} -+ -+#define WRITE_BUFSIZE 4096 -+ -+ssize_t traceprobe_probes_write(struct file *file, const char __user *buffer, -+ size_t count, loff_t *ppos, -+ int (*createfn)(int, char **)) -+{ -+ char *kbuf, *tmp; -+ int ret = 0; -+ size_t done = 0; -+ size_t size; -+ -+ kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL); -+ if (!kbuf) -+ return -ENOMEM; -+ -+ while (done < count) { -+ size = count - done; -+ -+ if (size >= WRITE_BUFSIZE) -+ size = WRITE_BUFSIZE - 1; -+ -+ if (copy_from_user(kbuf, buffer + done, size)) { -+ ret = -EFAULT; -+ goto out; -+ } -+ kbuf[size] = '\0'; -+ tmp = strchr(kbuf, '\n'); -+ -+ if (tmp) { -+ *tmp = '\0'; -+ size = tmp - kbuf + 1; -+ } else if (done + size < count) { -+ pr_warning("Line length is too long: " -+ "Should be less than %d.", WRITE_BUFSIZE); -+ ret = -EINVAL; -+ goto out; -+ } -+ done += size; -+ /* Remove comments */ -+ tmp = strchr(kbuf, '#'); -+ -+ if (tmp) -+ *tmp = '\0'; -+ -+ ret = traceprobe_command(kbuf, createfn); -+ if (ret) -+ goto out; -+ } -+ ret = done; -+ -+out: -+ kfree(kbuf); -+ -+ return ret; -+} -diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h -new file mode 100644 -index 0000000..9337086 ---- /dev/null -+++ b/kernel/trace/trace_probe.h -@@ -0,0 +1,161 @@ -+/* -+ * Common header file for probe-based Dynamic events. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ * This code was copied from kernel/trace/trace_kprobe.h written by -+ * Masami Hiramatsu -+ * -+ * Updates to make this generic: -+ * Copyright (C) IBM Corporation, 2010-2011 -+ * Author: Srikar Dronamraju -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "trace.h" -+#include "trace_output.h" -+ -+#define MAX_TRACE_ARGS 128 -+#define MAX_ARGSTR_LEN 63 -+#define MAX_EVENT_NAME_LEN 64 -+#define MAX_STRING_SIZE PATH_MAX -+ -+/* Reserved field names */ -+#define FIELD_STRING_IP "__probe_ip" -+#define FIELD_STRING_RETIP "__probe_ret_ip" -+#define FIELD_STRING_FUNC "__probe_func" -+ -+#undef DEFINE_FIELD -+#define DEFINE_FIELD(type, item, name, is_signed) \ -+ do { \ -+ ret = trace_define_field(event_call, #type, name, \ -+ offsetof(typeof(field), item), \ -+ sizeof(field.item), is_signed, \ -+ FILTER_OTHER); \ -+ if (ret) \ -+ return ret; \ -+ } while (0) -+ -+ -+/* Flags for trace_probe */ -+#define TP_FLAG_TRACE 1 -+#define TP_FLAG_PROFILE 2 -+#define TP_FLAG_REGISTERED 4 -+#define TP_FLAG_UPROBE 8 -+ -+ -+/* data_rloc: data relative location, compatible with u32 */ -+#define make_data_rloc(len, roffs) \ -+ (((u32)(len) << 16) | ((u32)(roffs) & 0xffff)) -+#define get_rloc_len(dl) ((u32)(dl) >> 16) -+#define get_rloc_offs(dl) ((u32)(dl) & 0xffff) -+ -+/* -+ * Convert data_rloc to data_loc: -+ * data_rloc stores the offset from data_rloc itself, but data_loc -+ * stores the offset from event entry. -+ */ -+#define convert_rloc_to_loc(dl, offs) ((u32)(dl) + (offs)) -+ -+/* Data fetch function type */ -+typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); -+/* Printing function type */ -+typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *, void *); -+ -+/* Fetch types */ -+enum { -+ FETCH_MTD_reg = 0, -+ FETCH_MTD_stack, -+ FETCH_MTD_retval, -+ FETCH_MTD_memory, -+ FETCH_MTD_symbol, -+ FETCH_MTD_deref, -+ FETCH_MTD_bitfield, -+ FETCH_MTD_END, -+}; -+ -+/* Fetch type information table */ -+struct fetch_type { -+ const char *name; /* Name of type */ -+ size_t size; /* Byte size of type */ -+ int is_signed; /* Signed flag */ -+ print_type_func_t print; /* Print functions */ -+ const char *fmt; /* Fromat string */ -+ const char *fmttype; /* Name in format file */ -+ /* Fetch functions */ -+ fetch_func_t fetch[FETCH_MTD_END]; -+}; -+ -+struct fetch_param { -+ fetch_func_t fn; -+ void *data; -+}; -+ -+struct probe_arg { -+ struct fetch_param fetch; -+ struct fetch_param fetch_size; -+ unsigned int offset; /* Offset from argument entry */ -+ const char *name; /* Name of this argument */ -+ const char *comm; /* Command of this argument */ -+ const struct fetch_type *type; /* Type of this argument */ -+}; -+ -+static inline __kprobes void call_fetch(struct fetch_param *fprm, -+ struct pt_regs *regs, void *dest) -+{ -+ return fprm->fn(regs, fprm->data, dest); -+} -+ -+/* Check the name is good for event/group/fields */ -+static inline int is_good_name(const char *name) -+{ -+ if (!isalpha(*name) && *name != '_') -+ return 0; -+ while (*++name != '\0') { -+ if (!isalpha(*name) && !isdigit(*name) && *name != '_') -+ return 0; -+ } -+ return 1; -+} -+ -+extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size, -+ struct probe_arg *parg, bool is_return, bool is_kprobe); -+ -+extern int traceprobe_conflict_field_name(const char *name, -+ struct probe_arg *args, int narg); -+ -+extern void traceprobe_update_arg(struct probe_arg *arg); -+extern void traceprobe_free_probe_arg(struct probe_arg *arg); -+ -+extern int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset); -+ -+extern ssize_t traceprobe_probes_write(struct file *file, -+ const char __user *buffer, size_t count, loff_t *ppos, -+ int (*createfn)(int, char**)); -+ -+extern int traceprobe_command(const char *buf, int (*createfn)(int, char**)); -diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c -new file mode 100644 -index 0000000..2b36ac6 ---- /dev/null -+++ b/kernel/trace/trace_uprobe.c -@@ -0,0 +1,788 @@ -+/* -+ * uprobes-based tracing events -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ * Copyright (C) IBM Corporation, 2010-2012 -+ * Author: Srikar Dronamraju -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "trace_probe.h" -+ -+#define UPROBE_EVENT_SYSTEM "uprobes" -+ -+/* -+ * uprobe event core functions -+ */ -+struct trace_uprobe; -+struct uprobe_trace_consumer { -+ struct uprobe_consumer cons; -+ struct trace_uprobe *tu; -+}; -+ -+struct trace_uprobe { -+ struct list_head list; -+ struct ftrace_event_class class; -+ struct ftrace_event_call call; -+ struct uprobe_trace_consumer *consumer; -+ struct inode *inode; -+ char *filename; -+ unsigned long offset; -+ unsigned long nhit; -+ unsigned int flags; /* For TP_FLAG_* */ -+ ssize_t size; /* trace entry size */ -+ unsigned int nr_args; -+ struct probe_arg args[]; -+}; -+ -+#define SIZEOF_TRACE_UPROBE(n) \ -+ (offsetof(struct trace_uprobe, args) + \ -+ (sizeof(struct probe_arg) * (n))) -+ -+static int register_uprobe_event(struct trace_uprobe *tu); -+static void unregister_uprobe_event(struct trace_uprobe *tu); -+ -+static DEFINE_MUTEX(uprobe_lock); -+static LIST_HEAD(uprobe_list); -+ -+static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs); -+ -+/* -+ * Allocate new trace_uprobe and initialize it (including uprobes). -+ */ -+static struct trace_uprobe * -+alloc_trace_uprobe(const char *group, const char *event, int nargs) -+{ -+ struct trace_uprobe *tu; -+ -+ if (!event || !is_good_name(event)) -+ return ERR_PTR(-EINVAL); -+ -+ if (!group || !is_good_name(group)) -+ return ERR_PTR(-EINVAL); -+ -+ tu = kzalloc(SIZEOF_TRACE_UPROBE(nargs), GFP_KERNEL); -+ if (!tu) -+ return ERR_PTR(-ENOMEM); -+ -+ tu->call.class = &tu->class; -+ tu->call.name = kstrdup(event, GFP_KERNEL); -+ if (!tu->call.name) -+ goto error; -+ -+ tu->class.system = kstrdup(group, GFP_KERNEL); -+ if (!tu->class.system) -+ goto error; -+ -+ INIT_LIST_HEAD(&tu->list); -+ return tu; -+ -+error: -+ kfree(tu->call.name); -+ kfree(tu); -+ -+ return ERR_PTR(-ENOMEM); -+} -+ -+static void free_trace_uprobe(struct trace_uprobe *tu) -+{ -+ int i; -+ -+ for (i = 0; i < tu->nr_args; i++) -+ traceprobe_free_probe_arg(&tu->args[i]); -+ -+ iput(tu->inode); -+ kfree(tu->call.class->system); -+ kfree(tu->call.name); -+ kfree(tu->filename); -+ kfree(tu); -+} -+ -+static struct trace_uprobe *find_probe_event(const char *event, const char *group) -+{ -+ struct trace_uprobe *tu; -+ -+ list_for_each_entry(tu, &uprobe_list, list) -+ if (strcmp(tu->call.name, event) == 0 && -+ strcmp(tu->call.class->system, group) == 0) -+ return tu; -+ -+ return NULL; -+} -+ -+/* Unregister a trace_uprobe and probe_event: call with locking uprobe_lock */ -+static void unregister_trace_uprobe(struct trace_uprobe *tu) -+{ -+ list_del(&tu->list); -+ unregister_uprobe_event(tu); -+ free_trace_uprobe(tu); -+} -+ -+/* Register a trace_uprobe and probe_event */ -+static int register_trace_uprobe(struct trace_uprobe *tu) -+{ -+ struct trace_uprobe *old_tp; -+ int ret; -+ -+ mutex_lock(&uprobe_lock); -+ -+ /* register as an event */ -+ old_tp = find_probe_event(tu->call.name, tu->call.class->system); -+ if (old_tp) -+ /* delete old event */ -+ unregister_trace_uprobe(old_tp); -+ -+ ret = register_uprobe_event(tu); -+ if (ret) { -+ pr_warning("Failed to register probe event(%d)\n", ret); -+ goto end; -+ } -+ -+ list_add_tail(&tu->list, &uprobe_list); -+ -+end: -+ mutex_unlock(&uprobe_lock); -+ -+ return ret; -+} -+ -+/* -+ * Argument syntax: -+ * - Add uprobe: p[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS] -+ * -+ * - Remove uprobe: -:[GRP/]EVENT -+ */ -+static int create_trace_uprobe(int argc, char **argv) -+{ -+ struct trace_uprobe *tu; -+ struct inode *inode; -+ char *arg, *event, *group, *filename; -+ char buf[MAX_EVENT_NAME_LEN]; -+ struct path path; -+ unsigned long offset; -+ bool is_delete; -+ int i, ret; -+ -+ inode = NULL; -+ ret = 0; -+ is_delete = false; -+ event = NULL; -+ group = NULL; -+ -+ /* argc must be >= 1 */ -+ if (argv[0][0] == '-') -+ is_delete = true; -+ else if (argv[0][0] != 'p') { -+ pr_info("Probe definition must be started with 'p', 'r' or" " '-'.\n"); -+ return -EINVAL; -+ } -+ -+ if (argv[0][1] == ':') { -+ event = &argv[0][2]; -+ arg = strchr(event, '/'); -+ -+ if (arg) { -+ group = event; -+ event = arg + 1; -+ event[-1] = '\0'; -+ -+ if (strlen(group) == 0) { -+ pr_info("Group name is not specified\n"); -+ return -EINVAL; -+ } -+ } -+ if (strlen(event) == 0) { -+ pr_info("Event name is not specified\n"); -+ return -EINVAL; -+ } -+ } -+ if (!group) -+ group = UPROBE_EVENT_SYSTEM; -+ -+ if (is_delete) { -+ if (!event) { -+ pr_info("Delete command needs an event name.\n"); -+ return -EINVAL; -+ } -+ mutex_lock(&uprobe_lock); -+ tu = find_probe_event(event, group); -+ -+ if (!tu) { -+ mutex_unlock(&uprobe_lock); -+ pr_info("Event %s/%s doesn't exist.\n", group, event); -+ return -ENOENT; -+ } -+ /* delete an event */ -+ unregister_trace_uprobe(tu); -+ mutex_unlock(&uprobe_lock); -+ return 0; -+ } -+ -+ if (argc < 2) { -+ pr_info("Probe point is not specified.\n"); -+ return -EINVAL; -+ } -+ if (isdigit(argv[1][0])) { -+ pr_info("probe point must be have a filename.\n"); -+ return -EINVAL; -+ } -+ arg = strchr(argv[1], ':'); -+ if (!arg) -+ goto fail_address_parse; -+ -+ *arg++ = '\0'; -+ filename = argv[1]; -+ ret = kern_path(filename, LOOKUP_FOLLOW, &path); -+ if (ret) -+ goto fail_address_parse; -+ -+ ret = strict_strtoul(arg, 0, &offset); -+ if (ret) -+ goto fail_address_parse; -+ -+ inode = igrab(path.dentry->d_inode); -+ -+ argc -= 2; -+ argv += 2; -+ -+ /* setup a probe */ -+ if (!event) { -+ char *tail = strrchr(filename, '/'); -+ char *ptr; -+ -+ ptr = kstrdup((tail ? tail + 1 : filename), GFP_KERNEL); -+ if (!ptr) { -+ ret = -ENOMEM; -+ goto fail_address_parse; -+ } -+ -+ tail = ptr; -+ ptr = strpbrk(tail, ".-_"); -+ if (ptr) -+ *ptr = '\0'; -+ -+ snprintf(buf, MAX_EVENT_NAME_LEN, "%c_%s_0x%lx", 'p', tail, offset); -+ event = buf; -+ kfree(tail); -+ } -+ -+ tu = alloc_trace_uprobe(group, event, argc); -+ if (IS_ERR(tu)) { -+ pr_info("Failed to allocate trace_uprobe.(%d)\n", (int)PTR_ERR(tu)); -+ ret = PTR_ERR(tu); -+ goto fail_address_parse; -+ } -+ tu->offset = offset; -+ tu->inode = inode; -+ tu->filename = kstrdup(filename, GFP_KERNEL); -+ -+ if (!tu->filename) { -+ pr_info("Failed to allocate filename.\n"); -+ ret = -ENOMEM; -+ goto error; -+ } -+ -+ /* parse arguments */ -+ ret = 0; -+ for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { -+ /* Increment count for freeing args in error case */ -+ tu->nr_args++; -+ -+ /* Parse argument name */ -+ arg = strchr(argv[i], '='); -+ if (arg) { -+ *arg++ = '\0'; -+ tu->args[i].name = kstrdup(argv[i], GFP_KERNEL); -+ } else { -+ arg = argv[i]; -+ /* If argument name is omitted, set "argN" */ -+ snprintf(buf, MAX_EVENT_NAME_LEN, "arg%d", i + 1); -+ tu->args[i].name = kstrdup(buf, GFP_KERNEL); -+ } -+ -+ if (!tu->args[i].name) { -+ pr_info("Failed to allocate argument[%d] name.\n", i); -+ ret = -ENOMEM; -+ goto error; -+ } -+ -+ if (!is_good_name(tu->args[i].name)) { -+ pr_info("Invalid argument[%d] name: %s\n", i, tu->args[i].name); -+ ret = -EINVAL; -+ goto error; -+ } -+ -+ if (traceprobe_conflict_field_name(tu->args[i].name, tu->args, i)) { -+ pr_info("Argument[%d] name '%s' conflicts with " -+ "another field.\n", i, argv[i]); -+ ret = -EINVAL; -+ goto error; -+ } -+ -+ /* Parse fetch argument */ -+ ret = traceprobe_parse_probe_arg(arg, &tu->size, &tu->args[i], false, false); -+ if (ret) { -+ pr_info("Parse error at argument[%d]. (%d)\n", i, ret); -+ goto error; -+ } -+ } -+ -+ ret = register_trace_uprobe(tu); -+ if (ret) -+ goto error; -+ return 0; -+ -+error: -+ free_trace_uprobe(tu); -+ return ret; -+ -+fail_address_parse: -+ if (inode) -+ iput(inode); -+ -+ pr_info("Failed to parse address.\n"); -+ -+ return ret; -+} -+ -+static void cleanup_all_probes(void) -+{ -+ struct trace_uprobe *tu; -+ -+ mutex_lock(&uprobe_lock); -+ while (!list_empty(&uprobe_list)) { -+ tu = list_entry(uprobe_list.next, struct trace_uprobe, list); -+ unregister_trace_uprobe(tu); -+ } -+ mutex_unlock(&uprobe_lock); -+} -+ -+/* Probes listing interfaces */ -+static void *probes_seq_start(struct seq_file *m, loff_t *pos) -+{ -+ mutex_lock(&uprobe_lock); -+ return seq_list_start(&uprobe_list, *pos); -+} -+ -+static void *probes_seq_next(struct seq_file *m, void *v, loff_t *pos) -+{ -+ return seq_list_next(v, &uprobe_list, pos); -+} -+ -+static void probes_seq_stop(struct seq_file *m, void *v) -+{ -+ mutex_unlock(&uprobe_lock); -+} -+ -+static int probes_seq_show(struct seq_file *m, void *v) -+{ -+ struct trace_uprobe *tu = v; -+ int i; -+ -+ seq_printf(m, "p:%s/%s", tu->call.class->system, tu->call.name); -+ seq_printf(m, " %s:0x%p", tu->filename, (void *)tu->offset); -+ -+ for (i = 0; i < tu->nr_args; i++) -+ seq_printf(m, " %s=%s", tu->args[i].name, tu->args[i].comm); -+ -+ seq_printf(m, "\n"); -+ return 0; -+} -+ -+static const struct seq_operations probes_seq_op = { -+ .start = probes_seq_start, -+ .next = probes_seq_next, -+ .stop = probes_seq_stop, -+ .show = probes_seq_show -+}; -+ -+static int probes_open(struct inode *inode, struct file *file) -+{ -+ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) -+ cleanup_all_probes(); -+ -+ return seq_open(file, &probes_seq_op); -+} -+ -+static ssize_t probes_write(struct file *file, const char __user *buffer, -+ size_t count, loff_t *ppos) -+{ -+ return traceprobe_probes_write(file, buffer, count, ppos, create_trace_uprobe); -+} -+ -+static const struct file_operations uprobe_events_ops = { -+ .owner = THIS_MODULE, -+ .open = probes_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = seq_release, -+ .write = probes_write, -+}; -+ -+/* Probes profiling interfaces */ -+static int probes_profile_seq_show(struct seq_file *m, void *v) -+{ -+ struct trace_uprobe *tu = v; -+ -+ seq_printf(m, " %s %-44s %15lu\n", tu->filename, tu->call.name, tu->nhit); -+ return 0; -+} -+ -+static const struct seq_operations profile_seq_op = { -+ .start = probes_seq_start, -+ .next = probes_seq_next, -+ .stop = probes_seq_stop, -+ .show = probes_profile_seq_show -+}; -+ -+static int profile_open(struct inode *inode, struct file *file) -+{ -+ return seq_open(file, &profile_seq_op); -+} -+ -+static const struct file_operations uprobe_profile_ops = { -+ .owner = THIS_MODULE, -+ .open = profile_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = seq_release, -+}; -+ -+/* uprobe handler */ -+static void uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) -+{ -+ struct uprobe_trace_entry_head *entry; -+ struct ring_buffer_event *event; -+ struct ring_buffer *buffer; -+ u8 *data; -+ int size, i, pc; -+ unsigned long irq_flags; -+ struct ftrace_event_call *call = &tu->call; -+ -+ tu->nhit++; -+ -+ local_save_flags(irq_flags); -+ pc = preempt_count(); -+ -+ size = sizeof(*entry) + tu->size; -+ -+ event = trace_current_buffer_lock_reserve(&buffer, call->event.type, -+ size, irq_flags, pc); -+ if (!event) -+ return; -+ -+ entry = ring_buffer_event_data(event); -+ entry->ip = uprobe_get_swbp_addr(task_pt_regs(current)); -+ data = (u8 *)&entry[1]; -+ for (i = 0; i < tu->nr_args; i++) -+ call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); -+ -+ if (!filter_current_check_discard(buffer, call, entry, event)) -+ trace_buffer_unlock_commit(buffer, event, irq_flags, pc); -+} -+ -+/* Event entry printers */ -+static enum print_line_t -+print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *event) -+{ -+ struct uprobe_trace_entry_head *field; -+ struct trace_seq *s = &iter->seq; -+ struct trace_uprobe *tu; -+ u8 *data; -+ int i; -+ -+ field = (struct uprobe_trace_entry_head *)iter->ent; -+ tu = container_of(event, struct trace_uprobe, call.event); -+ -+ if (!trace_seq_printf(s, "%s: (", tu->call.name)) -+ goto partial; -+ -+ if (!seq_print_ip_sym(s, field->ip, flags | TRACE_ITER_SYM_OFFSET)) -+ goto partial; -+ -+ if (!trace_seq_puts(s, ")")) -+ goto partial; -+ -+ data = (u8 *)&field[1]; -+ for (i = 0; i < tu->nr_args; i++) { -+ if (!tu->args[i].type->print(s, tu->args[i].name, -+ data + tu->args[i].offset, field)) -+ goto partial; -+ } -+ -+ if (trace_seq_puts(s, "\n")) -+ return TRACE_TYPE_HANDLED; -+ -+partial: -+ return TRACE_TYPE_PARTIAL_LINE; -+} -+ -+static int probe_event_enable(struct trace_uprobe *tu, int flag) -+{ -+ struct uprobe_trace_consumer *utc; -+ int ret = 0; -+ -+ if (!tu->inode || tu->consumer) -+ return -EINTR; -+ -+ utc = kzalloc(sizeof(struct uprobe_trace_consumer), GFP_KERNEL); -+ if (!utc) -+ return -EINTR; -+ -+ utc->cons.handler = uprobe_dispatcher; -+ utc->cons.filter = NULL; -+ ret = uprobe_register(tu->inode, tu->offset, &utc->cons); -+ if (ret) { -+ kfree(utc); -+ return ret; -+ } -+ -+ tu->flags |= flag; -+ utc->tu = tu; -+ tu->consumer = utc; -+ -+ return 0; -+} -+ -+static void probe_event_disable(struct trace_uprobe *tu, int flag) -+{ -+ if (!tu->inode || !tu->consumer) -+ return; -+ -+ uprobe_unregister(tu->inode, tu->offset, &tu->consumer->cons); -+ tu->flags &= ~flag; -+ kfree(tu->consumer); -+ tu->consumer = NULL; -+} -+ -+static int uprobe_event_define_fields(struct ftrace_event_call *event_call) -+{ -+ int ret, i; -+ struct uprobe_trace_entry_head field; -+ struct trace_uprobe *tu = (struct trace_uprobe *)event_call->data; -+ -+ DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); -+ /* Set argument names as fields */ -+ for (i = 0; i < tu->nr_args; i++) { -+ ret = trace_define_field(event_call, tu->args[i].type->fmttype, -+ tu->args[i].name, -+ sizeof(field) + tu->args[i].offset, -+ tu->args[i].type->size, -+ tu->args[i].type->is_signed, -+ FILTER_OTHER); -+ -+ if (ret) -+ return ret; -+ } -+ return 0; -+} -+ -+#define LEN_OR_ZERO (len ? len - pos : 0) -+static int __set_print_fmt(struct trace_uprobe *tu, char *buf, int len) -+{ -+ const char *fmt, *arg; -+ int i; -+ int pos = 0; -+ -+ fmt = "(%lx)"; -+ arg = "REC->" FIELD_STRING_IP; -+ -+ /* When len=0, we just calculate the needed length */ -+ -+ pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt); -+ -+ for (i = 0; i < tu->nr_args; i++) { -+ pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s", -+ tu->args[i].name, tu->args[i].type->fmt); -+ } -+ -+ pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg); -+ -+ for (i = 0; i < tu->nr_args; i++) { -+ pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s", -+ tu->args[i].name); -+ } -+ -+ return pos; /* return the length of print_fmt */ -+} -+#undef LEN_OR_ZERO -+ -+static int set_print_fmt(struct trace_uprobe *tu) -+{ -+ char *print_fmt; -+ int len; -+ -+ /* First: called with 0 length to calculate the needed length */ -+ len = __set_print_fmt(tu, NULL, 0); -+ print_fmt = kmalloc(len + 1, GFP_KERNEL); -+ if (!print_fmt) -+ return -ENOMEM; -+ -+ /* Second: actually write the @print_fmt */ -+ __set_print_fmt(tu, print_fmt, len + 1); -+ tu->call.print_fmt = print_fmt; -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PERF_EVENTS -+/* uprobe profile handler */ -+static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) -+{ -+ struct ftrace_event_call *call = &tu->call; -+ struct uprobe_trace_entry_head *entry; -+ struct hlist_head *head; -+ u8 *data; -+ int size, __size, i; -+ int rctx; -+ -+ __size = sizeof(*entry) + tu->size; -+ size = ALIGN(__size + sizeof(u32), sizeof(u64)); -+ size -= sizeof(u32); -+ if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough")) -+ return; -+ -+ preempt_disable(); -+ -+ entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx); -+ if (!entry) -+ goto out; -+ -+ entry->ip = uprobe_get_swbp_addr(task_pt_regs(current)); -+ data = (u8 *)&entry[1]; -+ for (i = 0; i < tu->nr_args; i++) -+ call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); -+ -+ head = this_cpu_ptr(call->perf_events); -+ perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head); -+ -+ out: -+ preempt_enable(); -+} -+#endif /* CONFIG_PERF_EVENTS */ -+ -+static -+int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, void *data) -+{ -+ struct trace_uprobe *tu = (struct trace_uprobe *)event->data; -+ -+ switch (type) { -+ case TRACE_REG_REGISTER: -+ return probe_event_enable(tu, TP_FLAG_TRACE); -+ -+ case TRACE_REG_UNREGISTER: -+ probe_event_disable(tu, TP_FLAG_TRACE); -+ return 0; -+ -+#ifdef CONFIG_PERF_EVENTS -+ case TRACE_REG_PERF_REGISTER: -+ return probe_event_enable(tu, TP_FLAG_PROFILE); -+ -+ case TRACE_REG_PERF_UNREGISTER: -+ probe_event_disable(tu, TP_FLAG_PROFILE); -+ return 0; -+#endif -+ default: -+ return 0; -+ } -+ return 0; -+} -+ -+static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) -+{ -+ struct uprobe_trace_consumer *utc; -+ struct trace_uprobe *tu; -+ -+ utc = container_of(con, struct uprobe_trace_consumer, cons); -+ tu = utc->tu; -+ if (!tu || tu->consumer != utc) -+ return 0; -+ -+ if (tu->flags & TP_FLAG_TRACE) -+ uprobe_trace_func(tu, regs); -+ -+#ifdef CONFIG_PERF_EVENTS -+ if (tu->flags & TP_FLAG_PROFILE) -+ uprobe_perf_func(tu, regs); -+#endif -+ return 0; -+} -+ -+static struct trace_event_functions uprobe_funcs = { -+ .trace = print_uprobe_event -+}; -+ -+static int register_uprobe_event(struct trace_uprobe *tu) -+{ -+ struct ftrace_event_call *call = &tu->call; -+ int ret; -+ -+ /* Initialize ftrace_event_call */ -+ INIT_LIST_HEAD(&call->class->fields); -+ call->event.funcs = &uprobe_funcs; -+ call->class->define_fields = uprobe_event_define_fields; -+ -+ if (set_print_fmt(tu) < 0) -+ return -ENOMEM; -+ -+ ret = register_ftrace_event(&call->event); -+ if (!ret) { -+ kfree(call->print_fmt); -+ return -ENODEV; -+ } -+ call->flags = 0; -+ call->class->reg = trace_uprobe_register; -+ call->data = tu; -+ ret = trace_add_event_call(call); -+ -+ if (ret) { -+ pr_info("Failed to register uprobe event: %s\n", call->name); -+ kfree(call->print_fmt); -+ unregister_ftrace_event(&call->event); -+ } -+ -+ return ret; -+} -+ -+static void unregister_uprobe_event(struct trace_uprobe *tu) -+{ -+ /* tu->event is unregistered in trace_remove_event_call() */ -+ trace_remove_event_call(&tu->call); -+ kfree(tu->call.print_fmt); -+ tu->call.print_fmt = NULL; -+} -+ -+/* Make a trace interface for controling probe points */ -+static __init int init_uprobe_trace(void) -+{ -+ struct dentry *d_tracer; -+ -+ d_tracer = tracing_init_dentry(); -+ if (!d_tracer) -+ return 0; -+ -+ trace_create_file("uprobe_events", 0644, d_tracer, -+ NULL, &uprobe_events_ops); -+ /* Profile interface */ -+ trace_create_file("uprobe_profile", 0444, d_tracer, -+ NULL, &uprobe_profile_ops); -+ return 0; -+} -+ -+fs_initcall(init_uprobe_trace); -diff --git a/mm/memory.c b/mm/memory.c -index 6105f47..bf8b403 100644 ---- a/mm/memory.c -+++ b/mm/memory.c -@@ -1307,6 +1307,9 @@ static void unmap_single_vma(struct mmu_gather *tlb, - if (end <= vma->vm_start) - return; - -+ if (vma->vm_file) -+ uprobe_munmap(vma, start, end); -+ - if (vma->vm_flags & VM_ACCOUNT) - *nr_accounted += (end - start) >> PAGE_SHIFT; - -diff --git a/mm/mmap.c b/mm/mmap.c -index 848ef52..b8c4072 100644 ---- a/mm/mmap.c -+++ b/mm/mmap.c -@@ -30,6 +30,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -546,8 +547,15 @@ again: remove_next = 1 + (end > next->vm_end); - - if (file) { - mapping = file->f_mapping; -- if (!(vma->vm_flags & VM_NONLINEAR)) -+ if (!(vma->vm_flags & VM_NONLINEAR)) { - root = &mapping->i_mmap; -+ uprobe_munmap(vma, vma->vm_start, vma->vm_end); -+ -+ if (adjust_next) -+ uprobe_munmap(next, next->vm_start, -+ next->vm_end); -+ } -+ - mutex_lock(&mapping->i_mmap_mutex); - if (insert) { - /* -@@ -617,8 +625,16 @@ again: remove_next = 1 + (end > next->vm_end); - if (mapping) - mutex_unlock(&mapping->i_mmap_mutex); - -+ if (root) { -+ uprobe_mmap(vma); -+ -+ if (adjust_next) -+ uprobe_mmap(next); -+ } -+ - if (remove_next) { - if (file) { -+ uprobe_munmap(next, next->vm_start, next->vm_end); - fput(file); - if (next->vm_flags & VM_EXECUTABLE) - removed_exe_file_vma(mm); -@@ -638,6 +654,8 @@ again: remove_next = 1 + (end > next->vm_end); - goto again; - } - } -+ if (insert && file) -+ uprobe_mmap(insert); - - validate_mm(mm); - -@@ -1371,6 +1389,11 @@ out: - mm->locked_vm += (len >> PAGE_SHIFT); - } else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) - make_pages_present(addr, addr + len); -+ -+ if (file && uprobe_mmap(vma)) -+ /* matching probes but cannot insert */ -+ goto unmap_and_free_vma; -+ - return addr; - - unmap_and_free_vma: -@@ -2352,6 +2375,10 @@ int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma) - if ((vma->vm_flags & VM_ACCOUNT) && - security_vm_enough_memory_mm(mm, vma_pages(vma))) - return -ENOMEM; -+ -+ if (vma->vm_file && uprobe_mmap(vma)) -+ return -EINVAL; -+ - vma_link(mm, vma, prev, rb_link, rb_parent); - return 0; - } -@@ -2421,6 +2448,10 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, - new_vma->vm_pgoff = pgoff; - if (new_vma->vm_file) { - get_file(new_vma->vm_file); -+ -+ if (uprobe_mmap(new_vma)) -+ goto out_free_mempol; -+ - if (vma->vm_flags & VM_EXECUTABLE) - added_exe_file_vma(mm); - } -diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt -index 2780d9c..b715cb7 100644 ---- a/tools/perf/Documentation/perf-probe.txt -+++ b/tools/perf/Documentation/perf-probe.txt -@@ -77,7 +77,8 @@ OPTIONS - - -F:: - --funcs:: -- Show available functions in given module or kernel. -+ Show available functions in given module or kernel. With -x/--exec, -+ can also list functions in a user space executable / shared library. - - --filter=FILTER:: - (Only for --vars and --funcs) Set filter. FILTER is a combination of glob -@@ -98,6 +99,15 @@ OPTIONS - --max-probes:: - Set the maximum number of probe points for an event. Default is 128. - -+-x:: -+--exec=PATH:: -+ Specify path to the executable or shared library file for user -+ space tracing. Can also be used with --funcs option. -+ -+In absence of -m/-x options, perf probe checks if the first argument after -+the options is an absolute path name. If its an absolute path, perf probe -+uses it as a target module/target user space binary to probe. -+ - PROBE SYNTAX - ------------ - Probe points are defined by following syntax. -@@ -182,6 +192,13 @@ Delete all probes on schedule(). - - ./perf probe --del='schedule*' - -+Add probes at zfree() function on /bin/zsh -+ -+ ./perf probe -x /bin/zsh zfree or ./perf probe /bin/zsh zfree -+ -+Add probes at malloc() function on libc -+ -+ ./perf probe -x /lib/libc.so.6 malloc or ./perf probe /lib/libc.so.6 malloc - - SEE ALSO - -------- -diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c -index 4935c09..e215ae6 100644 ---- a/tools/perf/builtin-probe.c -+++ b/tools/perf/builtin-probe.c -@@ -54,6 +54,7 @@ static struct { - bool show_ext_vars; - bool show_funcs; - bool mod_events; -+ bool uprobes; - int nevents; - struct perf_probe_event events[MAX_PROBES]; - struct strlist *dellist; -@@ -75,6 +76,8 @@ static int parse_probe_event(const char *str) - return -1; - } - -+ pev->uprobes = params.uprobes; -+ - /* Parse a perf-probe command into event */ - ret = parse_perf_probe_command(str, pev); - pr_debug("%d arguments\n", pev->nargs); -@@ -82,21 +85,58 @@ static int parse_probe_event(const char *str) - return ret; - } - -+static int set_target(const char *ptr) -+{ -+ int found = 0; -+ const char *buf; -+ -+ /* -+ * The first argument after options can be an absolute path -+ * to an executable / library or kernel module. -+ * -+ * TODO: Support relative path, and $PATH, $LD_LIBRARY_PATH, -+ * short module name. -+ */ -+ if (!params.target && ptr && *ptr == '/') { -+ params.target = ptr; -+ found = 1; -+ buf = ptr + (strlen(ptr) - 3); -+ -+ if (strcmp(buf, ".ko")) -+ params.uprobes = true; -+ -+ } -+ -+ return found; -+} -+ - static int parse_probe_event_argv(int argc, const char **argv) - { -- int i, len, ret; -+ int i, len, ret, found_target; - char *buf; - -+ found_target = set_target(argv[0]); -+ if (found_target && argc == 1) -+ return 0; -+ - /* Bind up rest arguments */ - len = 0; -- for (i = 0; i < argc; i++) -+ for (i = 0; i < argc; i++) { -+ if (i == 0 && found_target) -+ continue; -+ - len += strlen(argv[i]) + 1; -+ } - buf = zalloc(len + 1); - if (buf == NULL) - return -ENOMEM; - len = 0; -- for (i = 0; i < argc; i++) -+ for (i = 0; i < argc; i++) { -+ if (i == 0 && found_target) -+ continue; -+ - len += sprintf(&buf[len], "%s ", argv[i]); -+ } - params.mod_events = true; - ret = parse_probe_event(buf); - free(buf); -@@ -125,6 +165,28 @@ static int opt_del_probe_event(const struct option *opt __used, - return 0; - } - -+static int opt_set_target(const struct option *opt, const char *str, -+ int unset __used) -+{ -+ int ret = -ENOENT; -+ -+ if (str && !params.target) { -+ if (!strcmp(opt->long_name, "exec")) -+ params.uprobes = true; -+#ifdef DWARF_SUPPORT -+ else if (!strcmp(opt->long_name, "module")) -+ params.uprobes = false; -+#endif -+ else -+ return ret; -+ -+ params.target = str; -+ ret = 0; -+ } -+ -+ return ret; -+} -+ - #ifdef DWARF_SUPPORT - static int opt_show_lines(const struct option *opt __used, - const char *str, int unset __used) -@@ -246,9 +308,9 @@ static const struct option options[] = { - "file", "vmlinux pathname"), - OPT_STRING('s', "source", &symbol_conf.source_prefix, - "directory", "path to kernel source"), -- OPT_STRING('m', "module", ¶ms.target, -- "modname|path", -- "target module name (for online) or path (for offline)"), -+ OPT_CALLBACK('m', "module", NULL, "modname|path", -+ "target module name (for online) or path (for offline)", -+ opt_set_target), - #endif - OPT__DRY_RUN(&probe_event_dry_run), - OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, -@@ -260,6 +322,8 @@ static const struct option options[] = { - "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n" - "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)", - opt_set_filter), -+ OPT_CALLBACK('x', "exec", NULL, "executable|path", -+ "target executable name or path", opt_set_target), - OPT_END() - }; - -@@ -310,6 +374,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) - pr_err(" Error: Don't use --list with --funcs.\n"); - usage_with_options(probe_usage, options); - } -+ if (params.uprobes) { -+ pr_warning(" Error: Don't use --list with --exec.\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", -@@ -333,8 +401,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) - if (!params.filter) - params.filter = strfilter__new(DEFAULT_FUNC_FILTER, - NULL); -- ret = show_available_funcs(params.target, -- params.filter); -+ ret = show_available_funcs(params.target, params.filter, -+ params.uprobes); - strfilter__delete(params.filter); - if (ret < 0) - pr_err(" Error: Failed to show functions." -@@ -343,7 +411,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) - } - - #ifdef DWARF_SUPPORT -- if (params.show_lines) { -+ if (params.show_lines && !params.uprobes) { - if (params.mod_events) { - pr_err(" Error: Don't use --line with" - " --add/--del.\n"); -diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c -index 8a8ee64..0dda25d 100644 ---- a/tools/perf/util/probe-event.c -+++ b/tools/perf/util/probe-event.c -@@ -44,6 +44,7 @@ - #include "trace-event.h" /* For __unused */ - #include "probe-event.h" - #include "probe-finder.h" -+#include "session.h" - - #define MAX_CMDLEN 256 - #define MAX_PROBE_ARGS 128 -@@ -70,6 +71,8 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) - } - - static char *synthesize_perf_probe_point(struct perf_probe_point *pp); -+static int convert_name_to_addr(struct perf_probe_event *pev, -+ const char *exec); - static struct machine machine; - - /* Initialize symbol maps and path of vmlinux/modules */ -@@ -170,6 +173,34 @@ const char *kernel_get_module_path(const char *module) - return (dso) ? dso->long_name : NULL; - } - -+static int init_user_exec(void) -+{ -+ int ret = 0; -+ -+ symbol_conf.try_vmlinux_path = false; -+ symbol_conf.sort_by_name = true; -+ ret = symbol__init(); -+ -+ if (ret < 0) -+ pr_debug("Failed to init symbol map.\n"); -+ -+ return ret; -+} -+ -+static int convert_to_perf_probe_point(struct probe_trace_point *tp, -+ struct perf_probe_point *pp) -+{ -+ pp->function = strdup(tp->symbol); -+ -+ if (pp->function == NULL) -+ return -ENOMEM; -+ -+ pp->offset = tp->offset; -+ pp->retprobe = tp->retprobe; -+ -+ return 0; -+} -+ - #ifdef DWARF_SUPPORT - /* Open new debuginfo of given module */ - static struct debuginfo *open_debuginfo(const char *module) -@@ -224,10 +255,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, - if (ret <= 0) { - pr_debug("Failed to find corresponding probes from " - "debuginfo. Use kprobe event information.\n"); -- pp->function = strdup(tp->symbol); -- if (pp->function == NULL) -- return -ENOMEM; -- pp->offset = tp->offset; -+ return convert_to_perf_probe_point(tp, pp); - } - pp->retprobe = tp->retprobe; - -@@ -275,9 +303,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, - int max_tevs, const char *target) - { - bool need_dwarf = perf_probe_event_need_dwarf(pev); -- struct debuginfo *dinfo = open_debuginfo(target); -+ struct debuginfo *dinfo; - int ntevs, ret = 0; - -+ if (pev->uprobes) { -+ if (need_dwarf) { -+ pr_warning("Debuginfo-analysis is not yet supported" -+ " with -x/--exec option.\n"); -+ return -ENOSYS; -+ } -+ return convert_name_to_addr(pev, target); -+ } -+ -+ dinfo = open_debuginfo(target); -+ - if (!dinfo) { - if (need_dwarf) { - pr_warning("Failed to open debuginfo file.\n"); -@@ -603,23 +642,22 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, - pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); - return -ENOENT; - } -- pp->function = strdup(tp->symbol); -- if (pp->function == NULL) -- return -ENOMEM; -- pp->offset = tp->offset; -- pp->retprobe = tp->retprobe; - -- return 0; -+ return convert_to_perf_probe_point(tp, pp); - } - - static int try_to_find_probe_trace_events(struct perf_probe_event *pev, - struct probe_trace_event **tevs __unused, -- int max_tevs __unused, const char *mod __unused) -+ int max_tevs __unused, const char *target) - { - if (perf_probe_event_need_dwarf(pev)) { - pr_warning("Debuginfo-analysis is not supported.\n"); - return -ENOSYS; - } -+ -+ if (pev->uprobes) -+ return convert_name_to_addr(pev, target); -+ - return 0; - } - -@@ -1341,11 +1379,18 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) - if (buf == NULL) - return NULL; - -- len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", -- tp->retprobe ? 'r' : 'p', -- tev->group, tev->event, -- tp->module ?: "", tp->module ? ":" : "", -- tp->symbol, tp->offset); -+ if (tev->uprobes) -+ len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s", -+ tp->retprobe ? 'r' : 'p', -+ tev->group, tev->event, -+ tp->module, tp->symbol); -+ else -+ len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", -+ tp->retprobe ? 'r' : 'p', -+ tev->group, tev->event, -+ tp->module ?: "", tp->module ? ":" : "", -+ tp->symbol, tp->offset); -+ - if (len <= 0) - goto error; - -@@ -1364,7 +1409,7 @@ error: - } - - static int convert_to_perf_probe_event(struct probe_trace_event *tev, -- struct perf_probe_event *pev) -+ struct perf_probe_event *pev, bool is_kprobe) - { - char buf[64] = ""; - int i, ret; -@@ -1376,7 +1421,11 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev, - return -ENOMEM; - - /* Convert trace_point to probe_point */ -- ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); -+ if (is_kprobe) -+ ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); -+ else -+ ret = convert_to_perf_probe_point(&tev->point, &pev->point); -+ - if (ret < 0) - return ret; - -@@ -1472,7 +1521,26 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) - memset(tev, 0, sizeof(*tev)); - } - --static int open_kprobe_events(bool readwrite) -+static void print_warn_msg(const char *file, bool is_kprobe) -+{ -+ -+ if (errno == ENOENT) { -+ const char *config; -+ -+ if (!is_kprobe) -+ config = "CONFIG_UPROBE_EVENTS"; -+ else -+ config = "CONFIG_KPROBE_EVENTS"; -+ -+ pr_warning("%s file does not exist - please rebuild kernel" -+ " with %s.\n", file, config); -+ } else -+ pr_warning("Failed to open %s file: %s\n", file, -+ strerror(errno)); -+} -+ -+static int open_probe_events(const char *trace_file, bool readwrite, -+ bool is_kprobe) - { - char buf[PATH_MAX]; - const char *__debugfs; -@@ -1484,27 +1552,31 @@ static int open_kprobe_events(bool readwrite) - return -ENOENT; - } - -- ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); -+ ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); - if (ret >= 0) { - pr_debug("Opening %s write=%d\n", buf, readwrite); - if (readwrite && !probe_event_dry_run) - ret = open(buf, O_RDWR, O_APPEND); - else - ret = open(buf, O_RDONLY, 0); -- } - -- if (ret < 0) { -- if (errno == ENOENT) -- pr_warning("kprobe_events file does not exist - please" -- " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); -- else -- pr_warning("Failed to open kprobe_events file: %s\n", -- strerror(errno)); -+ if (ret < 0) -+ print_warn_msg(buf, is_kprobe); - } - return ret; - } - --/* Get raw string list of current kprobe_events */ -+static int open_kprobe_events(bool readwrite) -+{ -+ return open_probe_events("tracing/kprobe_events", readwrite, true); -+} -+ -+static int open_uprobe_events(bool readwrite) -+{ -+ return open_probe_events("tracing/uprobe_events", readwrite, false); -+} -+ -+/* Get raw string list of current kprobe_events or uprobe_events */ - static struct strlist *get_probe_trace_command_rawlist(int fd) - { - int ret, idx; -@@ -1569,36 +1641,26 @@ static int show_perf_probe_event(struct perf_probe_event *pev) - return ret; - } - --/* List up current perf-probe events */ --int show_perf_probe_events(void) -+static int __show_perf_probe_events(int fd, bool is_kprobe) - { -- int fd, ret; -+ int ret = 0; - struct probe_trace_event tev; - struct perf_probe_event pev; - struct strlist *rawlist; - struct str_node *ent; - -- setup_pager(); -- ret = init_vmlinux(); -- if (ret < 0) -- return ret; -- - memset(&tev, 0, sizeof(tev)); - memset(&pev, 0, sizeof(pev)); - -- fd = open_kprobe_events(false); -- if (fd < 0) -- return fd; -- - rawlist = get_probe_trace_command_rawlist(fd); -- close(fd); - if (!rawlist) - return -ENOENT; - - strlist__for_each(ent, rawlist) { - ret = parse_probe_trace_command(ent->s, &tev); - if (ret >= 0) { -- ret = convert_to_perf_probe_event(&tev, &pev); -+ ret = convert_to_perf_probe_event(&tev, &pev, -+ is_kprobe); - if (ret >= 0) - ret = show_perf_probe_event(&pev); - } -@@ -1612,6 +1674,33 @@ int show_perf_probe_events(void) - return ret; - } - -+/* List up current perf-probe events */ -+int show_perf_probe_events(void) -+{ -+ int fd, ret; -+ -+ setup_pager(); -+ fd = open_kprobe_events(false); -+ -+ if (fd < 0) -+ return fd; -+ -+ ret = init_vmlinux(); -+ if (ret < 0) -+ return ret; -+ -+ ret = __show_perf_probe_events(fd, true); -+ close(fd); -+ -+ fd = open_uprobe_events(false); -+ if (fd >= 0) { -+ ret = __show_perf_probe_events(fd, false); -+ close(fd); -+ } -+ -+ return ret; -+} -+ - /* Get current perf-probe event names */ - static struct strlist *get_probe_trace_event_names(int fd, bool include_group) - { -@@ -1717,7 +1806,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, - const char *event, *group; - struct strlist *namelist; - -- fd = open_kprobe_events(true); -+ if (pev->uprobes) -+ fd = open_uprobe_events(true); -+ else -+ fd = open_kprobe_events(true); -+ - if (fd < 0) - return fd; - /* Get current event names */ -@@ -1829,6 +1922,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, - tev->point.offset = pev->point.offset; - tev->point.retprobe = pev->point.retprobe; - tev->nargs = pev->nargs; -+ tev->uprobes = pev->uprobes; -+ - if (tev->nargs) { - tev->args = zalloc(sizeof(struct probe_trace_arg) - * tev->nargs); -@@ -1859,6 +1954,9 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, - } - } - -+ if (pev->uprobes) -+ return 1; -+ - /* Currently just checking function name from symbol map */ - sym = __find_kernel_function_by_name(tev->point.symbol, NULL); - if (!sym) { -@@ -1894,12 +1992,18 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - int i, j, ret; - struct __event_package *pkgs; - -+ ret = 0; - pkgs = zalloc(sizeof(struct __event_package) * npevs); -+ - if (pkgs == NULL) - return -ENOMEM; - -- /* Init vmlinux path */ -- ret = init_vmlinux(); -+ if (!pevs->uprobes) -+ /* Init vmlinux path */ -+ ret = init_vmlinux(); -+ else -+ ret = init_user_exec(); -+ - if (ret < 0) { - free(pkgs); - return ret; -@@ -1971,23 +2075,15 @@ error: - return ret; - } - --static int del_trace_probe_event(int fd, const char *group, -- const char *event, struct strlist *namelist) -+static int del_trace_probe_event(int fd, const char *buf, -+ struct strlist *namelist) - { -- char buf[128]; - struct str_node *ent, *n; -- int found = 0, ret = 0; -- -- ret = e_snprintf(buf, 128, "%s:%s", group, event); -- if (ret < 0) { -- pr_err("Failed to copy event.\n"); -- return ret; -- } -+ int ret = -1; - - if (strpbrk(buf, "*?")) { /* Glob-exp */ - strlist__for_each_safe(ent, n, namelist) - if (strglobmatch(ent->s, buf)) { -- found++; - ret = __del_trace_probe_event(fd, ent); - if (ret < 0) - break; -@@ -1996,40 +2092,43 @@ static int del_trace_probe_event(int fd, const char *group, - } else { - ent = strlist__find(namelist, buf); - if (ent) { -- found++; - ret = __del_trace_probe_event(fd, ent); - if (ret >= 0) - strlist__remove(namelist, ent); - } - } -- if (found == 0 && ret >= 0) -- pr_info("Info: Event \"%s\" does not exist.\n", buf); - - return ret; - } - - int del_perf_probe_events(struct strlist *dellist) - { -- int fd, ret = 0; -+ int ret = -1, ufd = -1, kfd = -1; -+ char buf[128]; - const char *group, *event; - char *p, *str; - struct str_node *ent; -- struct strlist *namelist; -- -- fd = open_kprobe_events(true); -- if (fd < 0) -- return fd; -+ struct strlist *namelist = NULL, *unamelist = NULL; - - /* Get current event names */ -- namelist = get_probe_trace_event_names(fd, true); -- if (namelist == NULL) -- return -EINVAL; -+ kfd = open_kprobe_events(true); -+ if (kfd < 0) -+ return kfd; -+ -+ namelist = get_probe_trace_event_names(kfd, true); -+ ufd = open_uprobe_events(true); -+ -+ if (ufd >= 0) -+ unamelist = get_probe_trace_event_names(ufd, true); -+ -+ if (namelist == NULL && unamelist == NULL) -+ goto error; - - strlist__for_each(ent, dellist) { - str = strdup(ent->s); - if (str == NULL) { - ret = -ENOMEM; -- break; -+ goto error; - } - pr_debug("Parsing: %s\n", str); - p = strchr(str, ':'); -@@ -2041,17 +2140,42 @@ int del_perf_probe_events(struct strlist *dellist) - group = "*"; - event = str; - } -+ -+ ret = e_snprintf(buf, 128, "%s:%s", group, event); -+ if (ret < 0) { -+ pr_err("Failed to copy event."); -+ free(str); -+ goto error; -+ } -+ - pr_debug("Group: %s, Event: %s\n", group, event); -- ret = del_trace_probe_event(fd, group, event, namelist); -+ -+ if (namelist) -+ ret = del_trace_probe_event(kfd, buf, namelist); -+ -+ if (unamelist && ret != 0) -+ ret = del_trace_probe_event(ufd, buf, unamelist); -+ -+ if (ret != 0) -+ pr_info("Info: Event \"%s\" does not exist.\n", buf); -+ - free(str); -- if (ret < 0) -- break; - } -- strlist__delete(namelist); -- close(fd); -+ -+error: -+ if (kfd >= 0) { -+ strlist__delete(namelist); -+ close(kfd); -+ } -+ -+ if (ufd >= 0) { -+ strlist__delete(unamelist); -+ close(ufd); -+ } - - return ret; - } -+ - /* TODO: don't use a global variable for filter ... */ - static struct strfilter *available_func_filter; - -@@ -2068,30 +2192,152 @@ static int filter_available_functions(struct map *map __unused, - return 1; - } - --int show_available_funcs(const char *target, struct strfilter *_filter) -+static int __show_available_funcs(struct map *map) -+{ -+ 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; -+} -+ -+static int available_kernel_funcs(const char *module) - { - struct map *map; - int ret; - -- setup_pager(); -- - ret = init_vmlinux(); - if (ret < 0) - return ret; - -- map = kernel_get_module_map(target); -+ map = kernel_get_module_map(module); - if (!map) { -- pr_err("Failed to find %s map.\n", (target) ? : "kernel"); -+ pr_err("Failed to find %s map.\n", (module) ? : "kernel"); - return -EINVAL; - } -+ return __show_available_funcs(map); -+} -+ -+static int available_user_funcs(const char *target) -+{ -+ struct map *map; -+ int ret; -+ -+ ret = init_user_exec(); -+ if (ret < 0) -+ return ret; -+ -+ map = dso__new_map(target); -+ ret = __show_available_funcs(map); -+ dso__delete(map->dso); -+ map__delete(map); -+ return ret; -+} -+ -+int show_available_funcs(const char *target, struct strfilter *_filter, -+ bool user) -+{ -+ setup_pager(); - available_func_filter = _filter; -+ -+ if (!user) -+ return available_kernel_funcs(target); -+ -+ return available_user_funcs(target); -+} -+ -+/* -+ * uprobe_events only accepts address: -+ * Convert function and any offset to address -+ */ -+static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) -+{ -+ struct perf_probe_point *pp = &pev->point; -+ struct symbol *sym; -+ struct map *map = NULL; -+ char *function = NULL, *name = NULL; -+ int ret = -EINVAL; -+ unsigned long long vaddr = 0; -+ -+ if (!pp->function) { -+ pr_warning("No function specified for uprobes"); -+ goto out; -+ } -+ -+ function = strdup(pp->function); -+ if (!function) { -+ pr_warning("Failed to allocate memory by strdup.\n"); -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ name = realpath(exec, NULL); -+ if (!name) { -+ pr_warning("Cannot find realpath for %s.\n", exec); -+ goto out; -+ } -+ map = dso__new_map(name); -+ if (!map) { -+ pr_warning("Cannot find appropriate DSO for %s.\n", exec); -+ goto out; -+ } -+ available_func_filter = strfilter__new(function, NULL); - if (map__load(map, filter_available_functions)) { - pr_err("Failed to load map.\n"); -- return -EINVAL; -+ goto out; - } -- 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; -+ sym = map__find_symbol_by_name(map, function, NULL); -+ if (!sym) { -+ pr_warning("Cannot find %s in DSO %s\n", function, exec); -+ goto out; -+ } -+ -+ if (map->start > sym->start) -+ vaddr = map->start; -+ vaddr += sym->start + pp->offset + map->pgoff; -+ pp->offset = 0; -+ -+ if (!pev->event) { -+ pev->event = function; -+ function = NULL; -+ } -+ if (!pev->group) { -+ char *ptr1, *ptr2; -+ -+ pev->group = zalloc(sizeof(char *) * 64); -+ ptr1 = strdup(basename(exec)); -+ if (ptr1) { -+ ptr2 = strpbrk(ptr1, "-._"); -+ if (ptr2) -+ *ptr2 = '\0'; -+ e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP, -+ ptr1); -+ free(ptr1); -+ } -+ } -+ free(pp->function); -+ pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS); -+ if (!pp->function) { -+ ret = -ENOMEM; -+ pr_warning("Failed to allocate memory by zalloc.\n"); -+ goto out; -+ } -+ e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr); -+ ret = 0; -+ -+out: -+ if (map) { -+ dso__delete(map->dso); -+ map__delete(map); -+ } -+ if (function) -+ free(function); -+ if (name) -+ free(name); -+ return ret; - } -diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h -index a7dee83..f9f3de8 100644 ---- a/tools/perf/util/probe-event.h -+++ b/tools/perf/util/probe-event.h -@@ -7,7 +7,7 @@ - - extern bool probe_event_dry_run; - --/* kprobe-tracer tracing point */ -+/* kprobe-tracer and uprobe-tracer tracing point */ - struct probe_trace_point { - char *symbol; /* Base symbol */ - char *module; /* Module name */ -@@ -21,7 +21,7 @@ struct probe_trace_arg_ref { - long offset; /* Offset value */ - }; - --/* kprobe-tracer tracing argument */ -+/* kprobe-tracer and uprobe-tracer tracing argument */ - struct probe_trace_arg { - char *name; /* Argument name */ - char *value; /* Base value */ -@@ -29,12 +29,13 @@ struct probe_trace_arg { - struct probe_trace_arg_ref *ref; /* Referencing offset */ - }; - --/* kprobe-tracer tracing event (point + arg) */ -+/* kprobe-tracer and uprobe-tracer tracing event (point + arg) */ - struct probe_trace_event { - char *event; /* Event name */ - char *group; /* Group name */ - struct probe_trace_point point; /* Trace point */ - int nargs; /* Number of args */ -+ bool uprobes; /* uprobes only */ - struct probe_trace_arg *args; /* Arguments */ - }; - -@@ -70,6 +71,7 @@ struct perf_probe_event { - char *group; /* Group name */ - struct perf_probe_point point; /* Probe point */ - int nargs; /* Number of arguments */ -+ bool uprobes; - struct perf_probe_arg *args; /* Arguments */ - }; - -@@ -129,8 +131,8 @@ 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, - struct strfilter *filter, bool externs); --extern int show_available_funcs(const char *module, struct strfilter *filter); -- -+extern int show_available_funcs(const char *module, struct strfilter *filter, -+ bool user); - - /* Maximum index number of event-name postfix */ - #define MAX_EVENT_INDEX 1024 -diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c -index ab9867b..0ef529e 100644 ---- a/tools/perf/util/symbol.c -+++ b/tools/perf/util/symbol.c -@@ -2783,3 +2783,14 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type, - - return ret; - } -+ -+struct map *dso__new_map(const char *name) -+{ -+ struct map *map = NULL; -+ struct dso *dso = dso__new(name); -+ -+ if (dso) -+ map = map__new2(0, dso, MAP__FUNCTION); -+ -+ return map; -+} -diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h -index ac49ef2..9e7742c 100644 ---- a/tools/perf/util/symbol.h -+++ b/tools/perf/util/symbol.h -@@ -237,6 +237,7 @@ void dso__set_long_name(struct dso *dso, char *name); - void dso__set_build_id(struct dso *dso, void *build_id); - void dso__read_running_kernel_build_id(struct dso *dso, - struct machine *machine); -+struct map *dso__new_map(const char *name); - struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, - u64 addr); - struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, diff --git a/uprobes-task_work_add-generic-process-context-callbacks.patch b/uprobes-task_work_add-generic-process-context-callbacks.patch deleted file mode 100644 index 6f56b4261..000000000 --- a/uprobes-task_work_add-generic-process-context-callbacks.patch +++ /dev/null @@ -1,249 +0,0 @@ -FYI. This patch is upstream since linux-3.5. Backported in order to -bring SystemTap functionality back after the switch to linux-3.4 that -doesn't have utrace. :) - -The split-out series is available in the git repository at: - - git://fedorapeople.org/home/fedora/aarapov/public_git/kernel-uprobes.git - -Oleg Nesterov (1): - task_work_add: generic process-context callbacks - -Signed-off-by: Anton Arapov ---- - include/linux/sched.h | 2 ++ - include/linux/task_work.h | 33 ++++++++++++++++++ - include/linux/tracehook.h | 11 ++++++ - kernel/Makefile | 2 +- - kernel/exit.c | 5 ++- - kernel/fork.c | 1 + - kernel/task_work.c | 84 +++++++++++++++++++++++++++++++++++++++++++++ - 7 files changed, 136 insertions(+), 2 deletions(-) - create mode 100644 include/linux/task_work.h - create mode 100644 kernel/task_work.c - -diff --git a/include/linux/sched.h b/include/linux/sched.h -index 6869c60..e011a11 100644 ---- a/include/linux/sched.h -+++ b/include/linux/sched.h -@@ -1445,6 +1445,8 @@ struct task_struct { - int (*notifier)(void *priv); - void *notifier_data; - sigset_t *notifier_mask; -+ struct hlist_head task_works; -+ - struct audit_context *audit_context; - #ifdef CONFIG_AUDITSYSCALL - uid_t loginuid; -diff --git a/include/linux/task_work.h b/include/linux/task_work.h -new file mode 100644 -index 0000000..294d5d5 ---- /dev/null -+++ b/include/linux/task_work.h -@@ -0,0 +1,33 @@ -+#ifndef _LINUX_TASK_WORK_H -+#define _LINUX_TASK_WORK_H -+ -+#include -+#include -+ -+struct task_work; -+typedef void (*task_work_func_t)(struct task_work *); -+ -+struct task_work { -+ struct hlist_node hlist; -+ task_work_func_t func; -+ void *data; -+}; -+ -+static inline void -+init_task_work(struct task_work *twork, task_work_func_t func, void *data) -+{ -+ twork->func = func; -+ twork->data = data; -+} -+ -+int task_work_add(struct task_struct *task, struct task_work *twork, bool); -+struct task_work *task_work_cancel(struct task_struct *, task_work_func_t); -+void task_work_run(void); -+ -+static inline void exit_task_work(struct task_struct *task) -+{ -+ if (unlikely(!hlist_empty(&task->task_works))) -+ task_work_run(); -+} -+ -+#endif /* _LINUX_TASK_WORK_H */ -diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h -index 51bd91d..48c597d 100644 ---- a/include/linux/tracehook.h -+++ b/include/linux/tracehook.h -@@ -49,6 +49,7 @@ - #include - #include - #include -+#include - struct linux_binprm; - - /* -@@ -165,8 +166,10 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info, - */ - static inline void set_notify_resume(struct task_struct *task) - { -+#ifdef TIF_NOTIFY_RESUME - if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_RESUME)) - kick_process(task); -+#endif - } - - /** -@@ -184,6 +187,14 @@ static inline void set_notify_resume(struct task_struct *task) - */ - static inline void tracehook_notify_resume(struct pt_regs *regs) - { -+ /* -+ * The caller just cleared TIF_NOTIFY_RESUME. This barrier -+ * pairs with task_work_add()->set_notify_resume() after -+ * hlist_add_head(task->task_works); -+ */ -+ smp_mb__after_clear_bit(); -+ if (unlikely(!hlist_empty(¤t->task_works))) -+ task_work_run(); - } - #endif /* TIF_NOTIFY_RESUME */ - -diff --git a/kernel/Makefile b/kernel/Makefile -index cb41b95..2479528 100644 ---- a/kernel/Makefile -+++ b/kernel/Makefile -@@ -5,7 +5,7 @@ - obj-y = fork.o exec_domain.o panic.o printk.o \ - cpu.o exit.o itimer.o time.o softirq.o resource.o \ - sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \ -- signal.o sys.o kmod.o workqueue.o pid.o \ -+ signal.o sys.o kmod.o workqueue.o pid.o task_work.o \ - rcupdate.o extable.o params.o posix-timers.o \ - kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ -diff --git a/kernel/exit.c b/kernel/exit.c -index d8bd3b42..b82c38e 100644 ---- a/kernel/exit.c -+++ b/kernel/exit.c -@@ -946,11 +946,14 @@ void do_exit(long code) - exit_signals(tsk); /* sets PF_EXITING */ - /* - * tsk->flags are checked in the futex code to protect against -- * an exiting task cleaning up the robust pi futexes. -+ * an exiting task cleaning up the robust pi futexes, and in -+ * task_work_add() to avoid the race with exit_task_work(). - */ - smp_mb(); - raw_spin_unlock_wait(&tsk->pi_lock); - -+ exit_task_work(tsk); -+ - exit_irq_thread(); - - if (unlikely(in_atomic())) -diff --git a/kernel/fork.c b/kernel/fork.c -index 5b87e9f..76a961d 100644 ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -1391,6 +1391,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, - */ - p->group_leader = p; - INIT_LIST_HEAD(&p->thread_group); -+ INIT_HLIST_HEAD(&p->task_works); - - /* Now that the task is set up, run cgroup callbacks if - * necessary. We need to run them before the task is visible -diff --git a/kernel/task_work.c b/kernel/task_work.c -new file mode 100644 -index 0000000..82d1c79 ---- /dev/null -+++ b/kernel/task_work.c -@@ -0,0 +1,84 @@ -+#include -+#include -+#include -+ -+int -+task_work_add(struct task_struct *task, struct task_work *twork, bool notify) -+{ -+ unsigned long flags; -+ int err = -ESRCH; -+ -+#ifndef TIF_NOTIFY_RESUME -+ if (notify) -+ return -ENOTSUPP; -+#endif -+ /* -+ * We must not insert the new work if the task has already passed -+ * exit_task_work(). We rely on do_exit()->raw_spin_unlock_wait() -+ * and check PF_EXITING under pi_lock. -+ */ -+ raw_spin_lock_irqsave(&task->pi_lock, flags); -+ if (likely(!(task->flags & PF_EXITING))) { -+ hlist_add_head(&twork->hlist, &task->task_works); -+ err = 0; -+ } -+ raw_spin_unlock_irqrestore(&task->pi_lock, flags); -+ -+ /* test_and_set_bit() implies mb(), see tracehook_notify_resume(). */ -+ if (likely(!err) && notify) -+ set_notify_resume(task); -+ return err; -+} -+ -+struct task_work * -+task_work_cancel(struct task_struct *task, task_work_func_t func) -+{ -+ unsigned long flags; -+ struct task_work *twork; -+ struct hlist_node *pos; -+ -+ raw_spin_lock_irqsave(&task->pi_lock, flags); -+ hlist_for_each_entry(twork, pos, &task->task_works, hlist) { -+ if (twork->func == func) { -+ hlist_del(&twork->hlist); -+ goto found; -+ } -+ } -+ twork = NULL; -+ found: -+ raw_spin_unlock_irqrestore(&task->pi_lock, flags); -+ -+ return twork; -+} -+ -+void task_work_run(void) -+{ -+ struct task_struct *task = current; -+ struct hlist_head task_works; -+ struct hlist_node *pos; -+ -+ raw_spin_lock_irq(&task->pi_lock); -+ hlist_move_list(&task->task_works, &task_works); -+ raw_spin_unlock_irq(&task->pi_lock); -+ -+ if (unlikely(hlist_empty(&task_works))) -+ return; -+ /* -+ * We use hlist to save the space in task_struct, but we want fifo. -+ * Find the last entry, the list should be short, then process them -+ * in reverse order. -+ */ -+ for (pos = task_works.first; pos->next; pos = pos->next) -+ ; -+ -+ for (;;) { -+ struct hlist_node **pprev = pos->pprev; -+ struct task_work *twork = container_of(pos, struct task_work, -+ hlist); -+ twork->func(twork); -+ -+ if (pprev == &task_works.first) -+ break; -+ pos = container_of(pprev, struct hlist_node, next); -+ } -+} diff --git a/vgaarb-vga_default_device.patch b/vgaarb-vga_default_device.patch deleted file mode 100644 index 5929a5f84..000000000 --- a/vgaarb-vga_default_device.patch +++ /dev/null @@ -1,474 +0,0 @@ -From 1a39b310e920bb7098067d96411b31e459ae8f32 Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Mon, 16 Apr 2012 16:26:02 -0400 -Subject: [PATCH] vgaarb: Add support for setting the default video device - (v2) - -The default VGA device is a somewhat fluid concept on platforms with -multiple GPUs. Add support for setting it so switching code can update -things appropriately, and make sure that the sysfs code returns the right -device if it's changed. - -v2: Updated to fix builds when __ARCH_HAS_VGA_DEFAULT_DEVICE is false. - -Signed-off-by: Matthew Garrett -Acked-by: H. Peter Anvin -Acked-by: benh@kernel.crashing.org -Cc: airlied@redhat.com -Signed-off-by: Dave Airlie ---- - drivers/gpu/vga/vgaarb.c | 7 +++++++ - drivers/pci/pci-sysfs.c | 5 +++++ - include/linux/vgaarb.h | 2 ++ - 3 files changed, 14 insertions(+) - -diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c -index 111d956..e223b96 100644 ---- a/drivers/gpu/vga/vgaarb.c -+++ b/drivers/gpu/vga/vgaarb.c -@@ -136,6 +136,11 @@ struct pci_dev *vga_default_device(void) - { - return vga_default; - } -+ -+void vga_set_default_device(struct pci_dev *pdev) -+{ -+ vga_default = pdev; -+} - #endif - - static inline void vga_irq_set_state(struct vga_device *vgadev, bool state) -@@ -605,10 +610,12 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev) - goto bail; - } - -+#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE - if (vga_default == pdev) { - pci_dev_put(vga_default); - vga_default = NULL; - } -+#endif - - if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM)) - vga_decode_count--; -diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c -index a55e248..86c63fe 100644 ---- a/drivers/pci/pci-sysfs.c -+++ b/drivers/pci/pci-sysfs.c -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - #include "pci.h" - - static int sysfs_initialized; /* = 0 */ -@@ -417,6 +418,10 @@ static ssize_t - boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) - { - struct pci_dev *pdev = to_pci_dev(dev); -+ struct pci_dev *vga_dev = vga_default_device(); -+ -+ if (vga_dev) -+ return sprintf(buf, "%u\n", (pdev == vga_dev)); - - return sprintf(buf, "%u\n", - !!(pdev->resource[PCI_ROM_RESOURCE].flags & -diff --git a/include/linux/vgaarb.h b/include/linux/vgaarb.h -index 9c3120d..759a25ba 100644 ---- a/include/linux/vgaarb.h -+++ b/include/linux/vgaarb.h -@@ -31,6 +31,7 @@ - #ifndef LINUX_VGA_H - #define LINUX_VGA_H - -+#include