* [net-2.6 PATCH 4/5] e1000e: separate mutex usage between NVM and PHY/CSR register for ICHx/PCH
From: Jeff Kirsher @ 2009-10-26 21:23 UTC (permalink / raw)
To: davem; +Cc: netdev, gospo, Bruce Allan, Jeff Kirsher
In-Reply-To: <20091026212242.9682.25442.stgit@localhost.localdomain>
From: Bruce Allan <bruce.w.allan@intel.com>
Accesses to NVM and PHY/CSR registers on ICHx/PCH-based parts are protected
from concurrent accesses with a mutex that is acquired when the access is
initiated and released when the access has completed. However, the two
types of accesses should not be protected by the same mutex because the
driver may have to access the NVM while already holding the mutex over
several consecutive PHY/CSR accesses which would result in livelock.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/e1000e/ich8lan.c | 89 +++++++++++++++++++++++++++---------------
1 files changed, 58 insertions(+), 31 deletions(-)
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index 2451dc8..aaaaf2c 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -578,12 +578,39 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
static DEFINE_MUTEX(nvm_mutex);
/**
+ * e1000_acquire_nvm_ich8lan - Acquire NVM mutex
+ * @hw: pointer to the HW structure
+ *
+ * Acquires the mutex for performing NVM operations.
+ **/
+static s32 e1000_acquire_nvm_ich8lan(struct e1000_hw *hw)
+{
+ mutex_lock(&nvm_mutex);
+
+ return 0;
+}
+
+/**
+ * e1000_release_nvm_ich8lan - Release NVM mutex
+ * @hw: pointer to the HW structure
+ *
+ * Releases the mutex used while performing NVM operations.
+ **/
+static void e1000_release_nvm_ich8lan(struct e1000_hw *hw)
+{
+ mutex_unlock(&nvm_mutex);
+
+ return;
+}
+
+static DEFINE_MUTEX(swflag_mutex);
+
+/**
* e1000_acquire_swflag_ich8lan - Acquire software control flag
* @hw: pointer to the HW structure
*
- * Acquires the software control flag for performing NVM and PHY
- * operations. This is a function pointer entry point only called by
- * read/write routines for the PHY and NVM parts.
+ * Acquires the software control flag for performing PHY and select
+ * MAC CSR accesses.
**/
static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
{
@@ -592,7 +619,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
might_sleep();
- mutex_lock(&nvm_mutex);
+ mutex_lock(&swflag_mutex);
while (timeout) {
extcnf_ctrl = er32(EXTCNF_CTRL);
@@ -633,7 +660,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
out:
if (ret_val)
- mutex_unlock(&nvm_mutex);
+ mutex_unlock(&swflag_mutex);
return ret_val;
}
@@ -642,9 +669,8 @@ out:
* e1000_release_swflag_ich8lan - Release software control flag
* @hw: pointer to the HW structure
*
- * Releases the software control flag for performing NVM and PHY operations.
- * This is a function pointer entry point only called by read/write
- * routines for the PHY and NVM parts.
+ * Releases the software control flag for performing PHY and select
+ * MAC CSR accesses.
**/
static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
{
@@ -654,7 +680,9 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
ew32(EXTCNF_CTRL, extcnf_ctrl);
- mutex_unlock(&nvm_mutex);
+ mutex_unlock(&swflag_mutex);
+
+ return;
}
/**
@@ -1360,12 +1388,11 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
(words == 0)) {
hw_dbg(hw, "nvm parameter(s) out of bounds\n");
- return -E1000_ERR_NVM;
+ ret_val = -E1000_ERR_NVM;
+ goto out;
}
- ret_val = e1000_acquire_swflag_ich8lan(hw);
- if (ret_val)
- goto out;
+ nvm->ops.acquire_nvm(hw);
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
if (ret_val) {
@@ -1391,7 +1418,7 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
}
}
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
out:
if (ret_val)
@@ -1649,11 +1676,15 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
return -E1000_ERR_NVM;
}
+ nvm->ops.acquire_nvm(hw);
+
for (i = 0; i < words; i++) {
dev_spec->shadow_ram[offset+i].modified = 1;
dev_spec->shadow_ram[offset+i].value = data[i];
}
+ nvm->ops.release_nvm(hw);
+
return 0;
}
@@ -1683,9 +1714,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
if (nvm->type != e1000_nvm_flash_sw)
goto out;
- ret_val = e1000_acquire_swflag_ich8lan(hw);
- if (ret_val)
- goto out;
+ nvm->ops.acquire_nvm(hw);
/*
* We're writing to the opposite bank so if we're on bank 1,
@@ -1703,7 +1732,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
old_bank_offset = 0;
ret_val = e1000_erase_flash_bank_ich8lan(hw, 1);
if (ret_val) {
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
goto out;
}
} else {
@@ -1711,7 +1740,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
new_bank_offset = 0;
ret_val = e1000_erase_flash_bank_ich8lan(hw, 0);
if (ret_val) {
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
goto out;
}
}
@@ -1769,7 +1798,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
if (ret_val) {
/* Possibly read-only, see e1000e_write_protect_nvm_ich8lan() */
hw_dbg(hw, "Flash commit failed.\n");
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
goto out;
}
@@ -1782,7 +1811,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data);
if (ret_val) {
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
goto out;
}
data &= 0xBFFF;
@@ -1790,7 +1819,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
act_offset * 2 + 1,
(u8)(data >> 8));
if (ret_val) {
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
goto out;
}
@@ -1803,7 +1832,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
act_offset = (old_bank_offset + E1000_ICH_NVM_SIG_WORD) * 2 + 1;
ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0);
if (ret_val) {
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
goto out;
}
@@ -1813,7 +1842,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
dev_spec->shadow_ram[i].value = 0xFFFF;
}
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
/*
* Reload the EEPROM, or else modifications will not appear
@@ -1877,14 +1906,12 @@ static s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw)
**/
void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw)
{
+ struct e1000_nvm_info *nvm = &hw->nvm;
union ich8_flash_protected_range pr0;
union ich8_hws_flash_status hsfsts;
u32 gfpreg;
- s32 ret_val;
- ret_val = e1000_acquire_swflag_ich8lan(hw);
- if (ret_val)
- return;
+ nvm->ops.acquire_nvm(hw);
gfpreg = er32flash(ICH_FLASH_GFPREG);
@@ -1905,7 +1932,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw)
hsfsts.hsf_status.flockdn = true;
ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval);
- e1000_release_swflag_ich8lan(hw);
+ nvm->ops.release_nvm(hw);
}
/**
@@ -3162,9 +3189,9 @@ static struct e1000_phy_operations ich8_phy_ops = {
};
static struct e1000_nvm_operations ich8_nvm_ops = {
- .acquire_nvm = e1000_acquire_swflag_ich8lan,
+ .acquire_nvm = e1000_acquire_nvm_ich8lan,
.read_nvm = e1000_read_nvm_ich8lan,
- .release_nvm = e1000_release_swflag_ich8lan,
+ .release_nvm = e1000_release_nvm_ich8lan,
.update_nvm = e1000_update_nvm_checksum_ich8lan,
.valid_led_default = e1000_valid_led_default_ich8lan,
.validate_nvm = e1000_validate_nvm_checksum_ich8lan,
^ permalink raw reply related
* [net-2.6 PATCH 5/5] e1000e: allow for swflag to be held over consecutive PHY accesses
From: Jeff Kirsher @ 2009-10-26 21:24 UTC (permalink / raw)
To: davem; +Cc: netdev, gospo, Bruce Allan, Jeff Kirsher
In-Reply-To: <20091026212242.9682.25442.stgit@localhost.localdomain>
From: Bruce Allan <bruce.w.allan@intel.com>
PCH-based parts (82577/82578) and some ICH8-based parts (82566) need to
hold the swflag (sw/fw/hw hardware semaphore) over consecutive PHY accesses
in order to perform sw-driven PHY configuration during initialization to
workaround known hardware issues (see follow-on patch). This patch
provides new PHY read/write functions (and function pointers) that will
allow accessing the PHY registers assuming the swflag has already been
acquired. The actual PHY register access code has moved into helper
functions that are called with a flag indicating whether or not the swflag
has already been acquired and acquires/releases it if not.
The functions called from within the updated PHY access functions had to be
updated to assume the swflag was already acquired, and other functions that
called those functions were also updated to acquire/release the swflag.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/e1000e/e1000.h | 12 +
drivers/net/e1000e/hw.h | 2
drivers/net/e1000e/ich8lan.c | 4
drivers/net/e1000e/phy.c | 469 +++++++++++++++++++++++++++++-------------
4 files changed, 346 insertions(+), 141 deletions(-)
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h
index 981936c..405a144 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/e1000e/e1000.h
@@ -519,9 +519,13 @@ extern s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw);
extern s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw);
extern s32 e1000e_get_phy_info_igp(struct e1000_hw *hw);
extern s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data);
+extern s32 e1000e_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset,
+ u16 *data);
extern s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw);
extern s32 e1000e_set_d3_lplu_state(struct e1000_hw *hw, bool active);
extern s32 e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data);
+extern s32 e1000e_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset,
+ u16 data);
extern s32 e1000e_phy_sw_reset(struct e1000_hw *hw);
extern s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw);
extern s32 e1000e_get_cfg_done(struct e1000_hw *hw);
@@ -538,7 +542,11 @@ extern s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data);
extern s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data);
extern void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl);
extern s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data);
+extern s32 e1000e_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset,
+ u16 data);
extern s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data);
+extern s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset,
+ u16 *data);
extern s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
u32 usec_interval, bool *success);
extern s32 e1000e_phy_reset_dsp(struct e1000_hw *hw);
@@ -546,7 +554,11 @@ extern s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
extern s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
extern s32 e1000e_check_downshift(struct e1000_hw *hw);
extern s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data);
+extern s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset,
+ u16 *data);
extern s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data);
+extern s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset,
+ u16 data);
extern s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow);
extern s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw);
extern s32 e1000_copper_link_setup_82577(struct e1000_hw *hw);
diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h
index fd44d9f..7b05cf4 100644
--- a/drivers/net/e1000e/hw.h
+++ b/drivers/net/e1000e/hw.h
@@ -764,11 +764,13 @@ struct e1000_phy_operations {
s32 (*get_cable_length)(struct e1000_hw *);
s32 (*get_phy_info)(struct e1000_hw *);
s32 (*read_phy_reg)(struct e1000_hw *, u32, u16 *);
+ s32 (*read_phy_reg_locked)(struct e1000_hw *, u32, u16 *);
void (*release_phy)(struct e1000_hw *);
s32 (*reset_phy)(struct e1000_hw *);
s32 (*set_d0_lplu_state)(struct e1000_hw *, bool);
s32 (*set_d3_lplu_state)(struct e1000_hw *, bool);
s32 (*write_phy_reg)(struct e1000_hw *, u32, u16);
+ s32 (*write_phy_reg_locked)(struct e1000_hw *, u32, u16);
s32 (*cfg_on_link_up)(struct e1000_hw *);
};
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index aaaaf2c..b6388b9 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -250,9 +250,11 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan;
phy->ops.read_phy_reg = e1000_read_phy_reg_hv;
+ phy->ops.read_phy_reg_locked = e1000_read_phy_reg_hv_locked;
phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan;
phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan;
phy->ops.write_phy_reg = e1000_write_phy_reg_hv;
+ phy->ops.write_phy_reg_locked = e1000_write_phy_reg_hv_locked;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
phy->id = e1000_phy_unknown;
@@ -313,6 +315,8 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
case IGP03E1000_E_PHY_ID:
phy->type = e1000_phy_igp_3;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+ phy->ops.read_phy_reg_locked = e1000e_read_phy_reg_igp_locked;
+ phy->ops.write_phy_reg_locked = e1000e_write_phy_reg_igp_locked;
break;
case IFE_E_PHY_ID:
case IFE_PLUS_E_PHY_ID:
diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c
index 994401f..f9d33ab 100644
--- a/drivers/net/e1000e/phy.c
+++ b/drivers/net/e1000e/phy.c
@@ -164,16 +164,25 @@ s32 e1000e_get_phy_id(struct e1000_hw *hw)
* MDIC mode. No harm in trying again in this case since
* the PHY ID is unknown at this point anyway
*/
+ ret_val = phy->ops.acquire_phy(hw);
+ if (ret_val)
+ goto out;
ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
if (ret_val)
goto out;
+ phy->ops.release_phy(hw);
retry_count++;
}
out:
/* Revert to MDIO fast mode, if applicable */
- if (retry_count)
+ if (retry_count) {
+ ret_val = phy->ops.acquire_phy(hw);
+ if (ret_val)
+ return ret_val;
ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
+ phy->ops.release_phy(hw);
+ }
return ret_val;
}
@@ -354,94 +363,173 @@ s32 e1000e_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data)
}
/**
- * e1000e_read_phy_reg_igp - Read igp PHY register
+ * __e1000e_read_phy_reg_igp - Read igp PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
* @data: pointer to the read data
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then reads the PHY register at offset
- * and storing the retrieved information in data. Release any acquired
+ * and stores the retrieved information in data. Release any acquired
* semaphores before exiting.
**/
-s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data)
+static s32 __e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data,
+ bool locked)
{
- s32 ret_val;
+ s32 ret_val = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- return ret_val;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire_phy))
+ goto out;
+
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ goto out;
+ }
if (offset > MAX_PHY_MULTI_PAGE_REG) {
ret_val = e1000e_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT,
(u16)offset);
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
- return ret_val;
- }
+ if (ret_val)
+ goto release;
}
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
- data);
-
- hw->phy.ops.release_phy(hw);
+ data);
+release:
+ if (!locked)
+ hw->phy.ops.release_phy(hw);
+out:
return ret_val;
}
/**
+ * e1000e_read_phy_reg_igp - Read igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquires semaphore then reads the PHY register at offset and stores the
+ * retrieved information in data.
+ * Release the acquired semaphore before exiting.
+ **/
+s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000e_read_phy_reg_igp(hw, offset, data, false);
+}
+
+/**
+ * e1000e_read_phy_reg_igp_locked - Read igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset and stores the retrieved information
+ * in data. Assumes semaphore already acquired.
+ **/
+s32 e1000e_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000e_read_phy_reg_igp(hw, offset, data, true);
+}
+
+/**
* e1000e_write_phy_reg_igp - Write igp PHY register
* @hw: pointer to the HW structure
* @offset: register offset to write to
* @data: data to write at register offset
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then writes the data to PHY register
* at the offset. Release any acquired semaphores before exiting.
**/
-s32 e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
+static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data,
+ bool locked)
{
- s32 ret_val;
+ s32 ret_val = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- return ret_val;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire_phy))
+ goto out;
+
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ goto out;
+ }
if (offset > MAX_PHY_MULTI_PAGE_REG) {
ret_val = e1000e_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT,
(u16)offset);
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
- return ret_val;
- }
+ if (ret_val)
+ goto release;
}
ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release_phy(hw);
+release:
+ if (!locked)
+ hw->phy.ops.release_phy(hw);
+out:
return ret_val;
}
/**
- * e1000e_read_kmrn_reg - Read kumeran register
+ * e1000e_write_phy_reg_igp - Write igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore then writes the data to PHY register
+ * at the offset. Release any acquired semaphores before exiting.
+ **/
+s32 e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000e_write_phy_reg_igp(hw, offset, data, false);
+}
+
+/**
+ * e1000e_write_phy_reg_igp_locked - Write igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset.
+ * Assumes semaphore already acquired.
+ **/
+s32 e1000e_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000e_write_phy_reg_igp(hw, offset, data, true);
+}
+
+/**
+ * __e1000_read_kmrn_reg - Read kumeran register
* @hw: pointer to the HW structure
* @offset: register offset to be read
* @data: pointer to the read data
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary. Then reads the PHY register at offset
* using the kumeran interface. The information retrieved is stored in data.
* Release any acquired semaphores before exiting.
**/
-s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data)
+static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data,
+ bool locked)
{
u32 kmrnctrlsta;
- s32 ret_val;
+ s32 ret_val = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- return ret_val;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire_phy))
+ goto out;
+
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ goto out;
+ }
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
@@ -452,41 +540,111 @@ s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data)
kmrnctrlsta = er32(KMRNCTRLSTA);
*data = (u16)kmrnctrlsta;
- hw->phy.ops.release_phy(hw);
+ if (!locked)
+ hw->phy.ops.release_phy(hw);
+out:
return ret_val;
}
/**
- * e1000e_write_kmrn_reg - Write kumeran register
+ * e1000e_read_kmrn_reg - Read kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquires semaphore then reads the PHY register at offset using the
+ * kumeran interface. The information retrieved is stored in data.
+ * Release the acquired semaphore before exiting.
+ **/
+s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_kmrn_reg(hw, offset, data, false);
+}
+
+/**
+ * e1000_read_kmrn_reg_locked - Read kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset using the kumeran interface. The
+ * information retrieved is stored in data.
+ * Assumes semaphore already acquired.
+ **/
+s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_kmrn_reg(hw, offset, data, true);
+}
+
+/**
+ * __e1000_write_kmrn_reg - Write kumeran register
* @hw: pointer to the HW structure
* @offset: register offset to write to
* @data: data to write at register offset
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary. Then write the data to PHY register
* at the offset using the kumeran interface. Release any acquired semaphores
* before exiting.
**/
-s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data)
+static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data,
+ bool locked)
{
u32 kmrnctrlsta;
- s32 ret_val;
+ s32 ret_val = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- return ret_val;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire_phy))
+ goto out;
+
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ goto out;
+ }
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | data;
ew32(KMRNCTRLSTA, kmrnctrlsta);
udelay(2);
- hw->phy.ops.release_phy(hw);
+ if (!locked)
+ hw->phy.ops.release_phy(hw);
+
+out:
return ret_val;
}
/**
+ * e1000e_write_kmrn_reg - Write kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore then writes the data to the PHY register at the offset
+ * using the kumeran interface. Release the acquired semaphore before exiting.
+ **/
+s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_kmrn_reg(hw, offset, data, false);
+}
+
+/**
+ * e1000_write_kmrn_reg_locked - Write kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Write the data to PHY register at the offset using the kumeran interface.
+ * Assumes semaphore already acquired.
+ **/
+s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_kmrn_reg(hw, offset, data, true);
+}
+
+/**
* e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link
* @hw: pointer to the HW structure
*
@@ -2105,6 +2263,10 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
u32 page = offset >> IGP_PAGE_SHIFT;
u32 page_shift = 0;
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
@@ -2112,10 +2274,6 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
goto out;
}
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset);
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2135,18 +2293,15 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
/* Page is shifted left, PHY expects (page x 32) */
ret_val = e1000e_write_phy_reg_mdic(hw, page_select,
(page << page_shift));
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
+ if (ret_val)
goto out;
- }
}
ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release_phy(hw);
-
out:
+ hw->phy.ops.release_phy(hw);
return ret_val;
}
@@ -2167,6 +2322,10 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
u32 page = offset >> IGP_PAGE_SHIFT;
u32 page_shift = 0;
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
@@ -2174,10 +2333,6 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
goto out;
}
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset);
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2197,17 +2352,14 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
/* Page is shifted left, PHY expects (page x 32) */
ret_val = e1000e_write_phy_reg_mdic(hw, page_select,
(page << page_shift));
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
+ if (ret_val)
goto out;
- }
}
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release_phy(hw);
-
out:
+ hw->phy.ops.release_phy(hw);
return ret_val;
}
@@ -2226,17 +2378,17 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
s32 ret_val;
u16 page = (u16)(offset >> IGP_PAGE_SHIFT);
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
true);
- return ret_val;
+ goto out;
}
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- return ret_val;
-
hw->phy.addr = 1;
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2245,16 +2397,14 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
ret_val = e1000e_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT,
page);
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
- return ret_val;
- }
+ if (ret_val)
+ goto out;
}
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
+out:
hw->phy.ops.release_phy(hw);
-
return ret_val;
}
@@ -2272,17 +2422,17 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
s32 ret_val;
u16 page = (u16)(offset >> IGP_PAGE_SHIFT);
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
false);
- return ret_val;
+ goto out;
}
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- return ret_val;
-
hw->phy.addr = 1;
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2290,17 +2440,15 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
ret_val = e1000e_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT,
page);
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
- return ret_val;
- }
+ if (ret_val)
+ goto out;
}
ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
+out:
hw->phy.ops.release_phy(hw);
-
return ret_val;
}
@@ -2320,6 +2468,8 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
* 3) Write the address using the address opcode (0x11)
* 4) Read or write the data using the data opcode (0x12)
* 5) Restore 769_17.2 to its original value
+ *
+ * Assumes semaphore already acquired.
**/
static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
u16 *data, bool read)
@@ -2327,20 +2477,12 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
s32 ret_val;
u16 reg = BM_PHY_REG_NUM(offset);
u16 phy_reg = 0;
- u8 phy_acquired = 1;
-
/* Gig must be disabled for MDIO accesses to page 800 */
if ((hw->mac.type == e1000_pchlan) &&
(!(er32(PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE)))
hw_dbg(hw, "Attempting to access page 800 while gig enabled\n");
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val) {
- phy_acquired = 0;
- goto out;
- }
-
/* All operations in this function are phy address 1 */
hw->phy.addr = 1;
@@ -2397,8 +2539,6 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
out:
- if (phy_acquired == 1)
- hw->phy.ops.release_phy(hw);
return ret_val;
}
@@ -2439,52 +2579,63 @@ static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active)
return 0;
}
+/**
+ * e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode
+ * @hw: pointer to the HW structure
+ * @slow: true for slow mode, false for normal mode
+ *
+ * Assumes semaphore already acquired.
+ **/
s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow)
{
s32 ret_val = 0;
u16 data = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- return ret_val;
-
/* Set MDIO mode - page 769, register 16: 0x2580==slow, 0x2180==fast */
hw->phy.addr = 1;
ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
(BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
- return ret_val;
- }
+ if (ret_val)
+ goto out;
+
ret_val = e1000e_write_phy_reg_mdic(hw, BM_CS_CTRL1,
(0x2180 | (slow << 10)));
+ if (ret_val)
+ goto out;
/* dummy read when reverting to fast mode - throw away result */
if (!slow)
- e1000e_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data);
-
- hw->phy.ops.release_phy(hw);
+ ret_val = e1000e_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data);
+out:
return ret_val;
}
/**
- * e1000_read_phy_reg_hv - Read HV PHY register
+ * __e1000_read_phy_reg_hv - Read HV PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
* @data: pointer to the read data
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then reads the PHY register at offset
- * and storing the retrieved information in data. Release any acquired
+ * and stores the retrieved information in data. Release any acquired
* semaphore before exiting.
**/
-s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
+static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
+ bool locked)
{
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
bool in_slow_mode = false;
+ if (!locked) {
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ return ret_val;
+ }
+
/* Workaround failure in MDIO access while cable is disconnected */
if ((hw->phy.type == e1000_phy_82577) &&
!(er32(STATUS) & E1000_STATUS_LU)) {
@@ -2508,10 +2659,6 @@ s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
goto out;
}
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
if (page == HV_INTC_FC_PAGE_START)
@@ -2529,42 +2676,76 @@ s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
ret_val = e1000e_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT,
(page << IGP_PAGE_SHIFT));
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
- goto out;
- }
hw->phy.addr = phy_addr;
}
}
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
data);
- hw->phy.ops.release_phy(hw);
-
out:
/* Revert to MDIO fast mode, if applicable */
if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
+ if (!locked)
+ hw->phy.ops.release_phy(hw);
+
return ret_val;
}
/**
- * e1000_write_phy_reg_hv - Write HV PHY register
+ * e1000_read_phy_reg_hv - Read HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquires semaphore then reads the PHY register at offset and stores
+ * the retrieved information in data. Release the acquired semaphore
+ * before exiting.
+ **/
+s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_phy_reg_hv(hw, offset, data, false);
+}
+
+/**
+ * e1000_read_phy_reg_hv_locked - Read HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset and stores the retrieved information
+ * in data. Assumes semaphore already acquired.
+ **/
+s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_phy_reg_hv(hw, offset, data, true);
+}
+
+/**
+ * __e1000_write_phy_reg_hv - Write HV PHY register
* @hw: pointer to the HW structure
* @offset: register offset to write to
* @data: data to write at register offset
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then writes the data to PHY register
* at the offset. Release any acquired semaphores before exiting.
**/
-s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
+static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
+ bool locked)
{
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
bool in_slow_mode = false;
+ if (!locked) {
+ ret_val = hw->phy.ops.acquire_phy(hw);
+ if (ret_val)
+ return ret_val;
+ }
+
/* Workaround failure in MDIO access while cable is disconnected */
if ((hw->phy.type == e1000_phy_82577) &&
!(er32(STATUS) & E1000_STATUS_LU)) {
@@ -2588,10 +2769,6 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
goto out;
}
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
if (page == HV_INTC_FC_PAGE_START)
@@ -2607,15 +2784,10 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
((MAX_PHY_REG_ADDRESS & reg) == 0) &&
(data & (1 << 11))) {
u16 data2 = 0x7EFF;
- hw->phy.ops.release_phy(hw);
ret_val = e1000_access_phy_debug_regs_hv(hw, (1 << 6) | 0x3,
&data2, false);
if (ret_val)
goto out;
-
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val)
- goto out;
}
if (reg > MAX_PHY_MULTI_PAGE_REG) {
@@ -2630,27 +2802,53 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
ret_val = e1000e_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT,
(page << IGP_PAGE_SHIFT));
- if (ret_val) {
- hw->phy.ops.release_phy(hw);
- goto out;
- }
hw->phy.addr = phy_addr;
}
}
ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
data);
- hw->phy.ops.release_phy(hw);
out:
/* Revert to MDIO fast mode, if applicable */
if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
+ if (!locked)
+ hw->phy.ops.release_phy(hw);
+
return ret_val;
}
/**
+ * e1000_write_phy_reg_hv - Write HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore then writes the data to PHY register at the offset.
+ * Release the acquired semaphores before exiting.
+ **/
+s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_phy_reg_hv(hw, offset, data, false);
+}
+
+/**
+ * e1000_write_phy_reg_hv_locked - Write HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset. Assumes semaphore
+ * already acquired.
+ **/
+s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_phy_reg_hv(hw, offset, data, true);
+}
+
+/**
* e1000_get_phy_addr_for_hv_page - Get PHY adrress based on page
* @page: page to be accessed
**/
@@ -2671,10 +2869,9 @@ static u32 e1000_get_phy_addr_for_hv_page(u32 page)
* @data: pointer to the data to be read or written
* @read: determines if operation is read or written
*
- * Acquires semaphore, if necessary, then reads the PHY register at offset
- * and storing the retreived information in data. Release any acquired
- * semaphores before exiting. Note that the procedure to read these regs
- * uses the address port and data port to read/write.
+ * Reads the PHY register at offset and stores the retreived information
+ * in data. Assumes semaphore already acquired. Note that the procedure
+ * to read these regs uses the address port and data port to read/write.
**/
static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
u16 *data, bool read)
@@ -2682,20 +2879,12 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
s32 ret_val;
u32 addr_reg = 0;
u32 data_reg = 0;
- u8 phy_acquired = 1;
/* This takes care of the difference with desktop vs mobile phy */
addr_reg = (hw->phy.type == e1000_phy_82578) ?
I82578_ADDR_REG : I82577_ADDR_REG;
data_reg = addr_reg + 1;
- ret_val = hw->phy.ops.acquire_phy(hw);
- if (ret_val) {
- hw_dbg(hw, "Could not acquire PHY\n");
- phy_acquired = 0;
- goto out;
- }
-
/* All operations in this function are phy address 2 */
hw->phy.addr = 2;
@@ -2718,8 +2907,6 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
}
out:
- if (phy_acquired == 1)
- hw->phy.ops.release_phy(hw);
return ret_val;
}
^ permalink raw reply related
* [net-2.6 PATCH 1/3] igb: fix memory leak when setting ring size while interface is down
From: Jeff Kirsher @ 2009-10-26 21:31 UTC (permalink / raw)
To: davem; +Cc: netdev, gospo, Alexander Duyck, Jeff Kirsher
From: Alexander Duyck <alexander.h.duyck@intel.com>
Changing ring sizes while the interface was down was causing a double
allocation of the receive and transmit rings. This issue is amplified when
there are multiple rings enabled. To prevent this we need to add an
additional check which will just update the ring counts when the interface
is not up and skip the allocation steps.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/igb/igb_ethtool.c | 35 ++++++++++++++++++++++-------------
1 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c
index d004c35..aab3d97 100644
--- a/drivers/net/igb/igb_ethtool.c
+++ b/drivers/net/igb/igb_ethtool.c
@@ -731,7 +731,7 @@ static int igb_set_ringparam(struct net_device *netdev,
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct igb_ring *temp_ring;
- int i, err;
+ int i, err = 0;
u32 new_rx_count, new_tx_count;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
@@ -751,18 +751,30 @@ static int igb_set_ringparam(struct net_device *netdev,
return 0;
}
+ while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
+ msleep(1);
+
+ if (!netif_running(adapter->netdev)) {
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ adapter->tx_ring[i].count = new_tx_count;
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ adapter->rx_ring[i].count = new_rx_count;
+ adapter->tx_ring_count = new_tx_count;
+ adapter->rx_ring_count = new_rx_count;
+ goto clear_reset;
+ }
+
if (adapter->num_tx_queues > adapter->num_rx_queues)
temp_ring = vmalloc(adapter->num_tx_queues * sizeof(struct igb_ring));
else
temp_ring = vmalloc(adapter->num_rx_queues * sizeof(struct igb_ring));
- if (!temp_ring)
- return -ENOMEM;
- while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
- msleep(1);
+ if (!temp_ring) {
+ err = -ENOMEM;
+ goto clear_reset;
+ }
- if (netif_running(adapter->netdev))
- igb_down(adapter);
+ igb_down(adapter);
/*
* We can't just free everything and then setup again,
@@ -819,14 +831,11 @@ static int igb_set_ringparam(struct net_device *netdev,
adapter->rx_ring_count = new_rx_count;
}
-
- err = 0;
err_setup:
- if (netif_running(adapter->netdev))
- igb_up(adapter);
-
- clear_bit(__IGB_RESETTING, &adapter->state);
+ igb_up(adapter);
vfree(temp_ring);
+clear_reset:
+ clear_bit(__IGB_RESETTING, &adapter->state);
return err;
}
^ permalink raw reply related
* [net-2.6 PATCH 2/3] ixgbe: fix memory leak when resizing rings while interface is down
From: Jeff Kirsher @ 2009-10-26 21:32 UTC (permalink / raw)
To: davem; +Cc: netdev, gospo, Alexander Duyck, Jeff Kirsher
In-Reply-To: <20091026213147.9993.9778.stgit@localhost.localdomain>
From: Alexander Duyck <alexander.h.duyck@intel.com>
This patch resolves a memory leak that occurs when you resize the rings via
the ethtool -G option while the interface is down.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ixgbe/ixgbe_ethtool.c | 22 +++++++++++++---------
1 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
index fa314cb..856c18c 100644
--- a/drivers/net/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ixgbe/ixgbe_ethtool.c
@@ -798,7 +798,7 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_ring *temp_tx_ring, *temp_rx_ring;
- int i, err;
+ int i, err = 0;
u32 new_rx_count, new_tx_count;
bool need_update = false;
@@ -822,6 +822,16 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
msleep(1);
+ if (!netif_running(adapter->netdev)) {
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ adapter->tx_ring[i].count = new_tx_count;
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ adapter->rx_ring[i].count = new_rx_count;
+ adapter->tx_ring_count = new_tx_count;
+ adapter->rx_ring_count = new_rx_count;
+ goto err_setup;
+ }
+
temp_tx_ring = kcalloc(adapter->num_tx_queues,
sizeof(struct ixgbe_ring), GFP_KERNEL);
if (!temp_tx_ring) {
@@ -879,8 +889,7 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
/* if rings need to be updated, here's the place to do it in one shot */
if (need_update) {
- if (netif_running(netdev))
- ixgbe_down(adapter);
+ ixgbe_down(adapter);
/* tx */
if (new_tx_count != adapter->tx_ring_count) {
@@ -897,13 +906,8 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
temp_rx_ring = NULL;
adapter->rx_ring_count = new_rx_count;
}
- }
-
- /* success! */
- err = 0;
- if (netif_running(netdev))
ixgbe_up(adapter);
-
+ }
err_setup:
clear_bit(__IXGBE_RESETTING, &adapter->state);
return err;
^ permalink raw reply related
* [net-2.6 PATCH 3/3] igbvf: fix memory leak when ring size changed while interface down
From: Jeff Kirsher @ 2009-10-26 21:32 UTC (permalink / raw)
To: davem; +Cc: netdev, gospo, Alexander Duyck, Jeff Kirsher
In-Reply-To: <20091026213147.9993.9778.stgit@localhost.localdomain>
From: Alexander Duyck <alexander.h.duyck@intel.com>
This patch resolves a memory leak which occurs while changing the ring size
while the interface is down.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/igbvf/ethtool.c | 30 +++++++++++++++++-------------
1 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/drivers/net/igbvf/ethtool.c b/drivers/net/igbvf/ethtool.c
index ee17a09..c68265b 100644
--- a/drivers/net/igbvf/ethtool.c
+++ b/drivers/net/igbvf/ethtool.c
@@ -279,7 +279,7 @@ static int igbvf_set_ringparam(struct net_device *netdev,
{
struct igbvf_adapter *adapter = netdev_priv(netdev);
struct igbvf_ring *temp_ring;
- int err;
+ int err = 0;
u32 new_rx_count, new_tx_count;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
@@ -299,15 +299,22 @@ static int igbvf_set_ringparam(struct net_device *netdev,
return 0;
}
- temp_ring = vmalloc(sizeof(struct igbvf_ring));
- if (!temp_ring)
- return -ENOMEM;
-
while (test_and_set_bit(__IGBVF_RESETTING, &adapter->state))
msleep(1);
- if (netif_running(adapter->netdev))
- igbvf_down(adapter);
+ if (!netif_running(adapter->netdev)) {
+ adapter->tx_ring->count = new_tx_count;
+ adapter->rx_ring->count = new_rx_count;
+ goto clear_reset;
+ }
+
+ temp_ring = vmalloc(sizeof(struct igbvf_ring));
+ if (!temp_ring) {
+ err = -ENOMEM;
+ goto clear_reset;
+ }
+
+ igbvf_down(adapter);
/*
* We can't just free everything and then setup again,
@@ -339,14 +346,11 @@ static int igbvf_set_ringparam(struct net_device *netdev,
memcpy(adapter->rx_ring, temp_ring,sizeof(struct igbvf_ring));
}
-
- err = 0;
err_setup:
- if (netif_running(adapter->netdev))
- igbvf_up(adapter);
-
- clear_bit(__IGBVF_RESETTING, &adapter->state);
+ igbvf_up(adapter);
vfree(temp_ring);
+clear_reset:
+ clear_bit(__IGBVF_RESETTING, &adapter->state);
return err;
}
^ permalink raw reply related
* [PATCH -next] netxen: fix builds for SYSFS=n or MODULES=n
From: Randy Dunlap @ 2009-10-26 22:09 UTC (permalink / raw)
To: Stephen Rothwell, netdev; +Cc: linux-next, LKML, davem, Dhananjay Phadke
In-Reply-To: <20091026172104.205f15af.sfr@canb.auug.org.au>
From: Randy Dunlap <randy.dunlap@oracle.com>
When CONFIG_MODULES=n:
drivers/net/netxen/netxen_nic_main.c:2751: error: dereferencing pointer to incomplete type
drivers/net/netxen/netxen_nic_main.c:2764: error: dereferencing pointer to incomplete type
Also needs addition of <linux/sysfs.h> for sysfs function prototypes or
stubs when CONFIG_SYSFS=n.
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
---
drivers/net/netxen/netxen_nic_main.c | 9 +++++++++
1 file changed, 9 insertions(+)
--- linux-next-20091026.orig/drivers/net/netxen/netxen_nic_main.c
+++ linux-next-20091026/drivers/net/netxen/netxen_nic_main.c
@@ -34,6 +34,7 @@
#include <net/ip.h>
#include <linux/ipv6.h>
#include <linux/inetdevice.h>
+#include <linux/sysfs.h>
MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver");
MODULE_LICENSE("GPL");
@@ -2500,6 +2501,7 @@ static struct bin_attribute bin_attr_mem
.write = netxen_sysfs_write_mem,
};
+#ifdef CONFIG_MODULES
static ssize_t
netxen_store_auto_fw_reset(struct module_attribute *mattr,
struct module *mod, const char *buf, size_t count)
@@ -2534,6 +2536,7 @@ static struct module_attribute mod_attr_
.show = netxen_show_auto_fw_reset,
.store = netxen_store_auto_fw_reset,
};
+#endif
static void
netxen_create_sysfs_entries(struct netxen_adapter *adapter)
@@ -2739,7 +2742,9 @@ static struct pci_driver netxen_driver =
static int __init netxen_init_module(void)
{
+#ifdef CONFIG_MODULES
struct module *mod = THIS_MODULE;
+#endif
printk(KERN_INFO "%s\n", netxen_nic_driver_string);
@@ -2748,9 +2753,11 @@ static int __init netxen_init_module(voi
register_inetaddr_notifier(&netxen_inetaddr_cb);
#endif
+#ifdef CONFIG_MODULES
if (sysfs_create_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr))
printk(KERN_ERR "%s: Failed to create auto_fw_reset "
"sysfs entry.", netxen_nic_driver_name);
+#endif
return pci_register_driver(&netxen_driver);
}
@@ -2759,9 +2766,11 @@ module_init(netxen_init_module);
static void __exit netxen_exit_module(void)
{
+#ifdef CONFIG_MODULES
struct module *mod = THIS_MODULE;
sysfs_remove_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr);
+#endif
pci_unregister_driver(&netxen_driver);
^ permalink raw reply
* Re: [PATCH 0/5] Candidate fix for increased number of GFP_ATOMIC failures V2
From: Frans Pop @ 2009-10-26 22:17 UTC (permalink / raw)
To: Mel Gorman
Cc: Jiri Kosina, Sven Geggus, Karol Lewandowski, Tobias Oetiker,
Rafael J. Wysocki, David Miller, Reinette Chatre, Kalle Valo,
David Rientjes, KOSAKI Motohiro, Mohamed Abbas, Jens Axboe,
John W. Linville, Pekka Enberg, Bartlomiej Zolnierkiewicz,
Greg Kroah-Hartman, Stephan von Krawczynski, Kernel Testers List,
netdev, linux-kernel, linux-mm@kvack.org
In-Reply-To: <1256221356-26049-1-git-send-email-mel@csn.ul.ie>
On Thursday 22 October 2009, Mel Gorman wrote:
> Test 1: Verify your problem occurs on 2.6.32-rc5 if you can
I've tested against 2.6.31.1 as it's easier for me to compare behaviors
with that than with .32. All patches applied without problems against .31.
I've also tested 2.6.31.1 with SLAB instead of SLUB, but that does not seem
to make a significant difference for my test.
> Test 2: Apply the following two patches and test again
> 1/5 page allocator: Always wake kswapd when restarting an allocation
> attempt after direct reclaim failed
> 2/5 page allocator: Do not allow interrupts to use ALLOC_HARDER
Does not look to make any difference. Possibly causes more variation in the
duration of the test (increases timing effects)?
> Test 3: If you are getting allocation failures, try with the following
> patch
> 3/5 vmscan: Force kswapd to take notice faster when high-order
> watermarks are being hit
Applied on top of patches 1-2. Does not look to make any difference.
> Test 4: If you are still getting failures, apply the following
> 4/5 page allocator: Pre-emptively wake kswapd when high-order
> watermarks are hit
Applied on top of patches 1-3. Does not look to make any difference.
> Test 5: If things are still screwed, apply the following
> 5/5 Revert 373c0a7e, 8aa7e847: Fix congestion_wait() sync/async vs
> read/write confusion
Applied on top of patches 1-4. Despite Jens' scepticism is this still the
patch that makes the most significant difference in my test.
The reading of commits in gitk is much more fluent and music skips are a
lot less severe. But most important is that there is no long total freeze
of the system halfway during the reading of commits and gitk loads
fastest. It also gives by far the most consistent results.
The likelyhood of SKB allocation errors during the test is a lot smaller.
See also http://lkml.org/lkml/2009/10/26/455.
Detailed test results follow. I've done 2 test runs with each kernel (3 for
the last).
The columns below give the following info:
- time at which all commits have been read by gitk
- time at which gitk fills in "branch", "follows" and "precedes" data for
the current commit
- time at which there's no longer any disk activity, i.e. when gitk is
fully loaded and all swapping is done
- total number of SKB allocation errors during the test
A "freeze" during the reading of commits is indicated by an "f" (short
freeze) or "F" (long "hard" freeze). An "S" shows when there were SKB
allocation errors.
end commits show branch done SKB errs
1) vanilla .31.1
run 1: 1:20 fFS 2:10 S 2:30 44 a)
run 2: 1:35 FS 1:45 2:10 13
2) .31.1 + patches 1-2
run1: 2:30 fFS 2:45 3:00 58
run2: 1:15 fS 2:00 2:20 2 a)
3) .31.1 + patches 1-3
run1: 1:00 fS 1:15 1:45 1 *)
run2: 3:00 fFS 3:15 3:30 33
*) unexpected; fortunate timing?
4) .31.1 + patches 1-4
run1: 1:10 ffS 1:55 S 2:20 35 a)
run2: 3:05 fFS 3:15 3:25 36
5) .31.1 + patches 1-5
run1: 1:00 1:15 1:35 0
run2: 0:50 1:15 S 1:45 45 *)
run3: 1:00 1:15 1:45 0
*) unexpected; unfortunate timing?
a) fast in 1st phase; slow in 2nd and 3rd
Note that without the congestion_wait() reverts occurrence of SKB errors,
the long freezes and time it takes for gitk to load seem roughly related;
with the reverts total time is not affected even with many SKB errors.
Cheers,
FJP
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH net-next] bnx2x: Do Tx handling in a separate tasklet.
From: David Miller @ 2009-10-26 22:28 UTC (permalink / raw)
To: vladz; +Cc: eilong, netdev
In-Reply-To: <8628FE4E7912BF47A96AE7DD7BAC0AADCB2CFF1C42@SJEXCHCCR02.corp.ad.broadcom.com>
From: "Vladislav Zolotarov" <vladz@broadcom.com>
Date: Mon, 26 Oct 2009 07:42:27 -0700
> The separation of Tx and Rx interrupt handling gives us the
> possibility to properly affinitize the Rx (heavy CPU consuming task)
> and Tx (low CPU consuming task) and to ensure that Tx work is done
> not long after the Tx interrupt without interference of Rx work thus
> letting the user benefit from Tx coalescing configuration in order
> to achieve the best performance in each specific scenario. This is
> most important in heavy load scenarios with mixed traffic (UDP + TCP
> for instance). If we didn't separate Tx and Rx interrupt handling Tx
> coalescing configuration was not worth much.
There are other issues:
1) Actually, it makes sense to do TX and RX work together, since TX
packet liberation makes fresh CPU local packets available for
responses generated by RX packet reception.
2) TX packet liberation is not low CPU consumption, it has to perform
many atomic instructions, reference socket state, enter the SLAB
allocator, potentially liberate netfilter state, etc.
Using NAPI also moves the TX freeing into softirq context.
If you do it from a hardirq you are making it more expensive. From
hardirq the free just puts the SKB on a list, schedules a softirq,
then does the real SKB free work from the softirq.
This needless SKB list management and softirq scheduling you'll
avoid if you do things from softirqs, and thus using NAPI makes
sense here.
^ permalink raw reply
* Re: [PATCH] TI DaVinci EMAC: Minor macro related updates
From: Jean-Christophe PLAGNIOL-VILLARD @ 2009-10-26 22:07 UTC (permalink / raw)
To: Chaithrika U S; +Cc: netdev, davem, davinci-linux-open-source
In-Reply-To: <1254428719-13960-1-git-send-email-chaithrika@ti.com>
On 16:25 Thu 01 Oct , Chaithrika U S wrote:
> Use BIT for macro definitions wherever possible, remove
> unused and redundant macros.
>
> Signed-off-by: Chaithrika U S <chaithrika@ti.com>
> ---
> Applies to Linus' kernel tree
do you plan to send a new version soon?
as the current DaVinci EMAC does not build on the v2.6.32-rc5
Best Regards,
J.
^ permalink raw reply
* [PATCH] dcache: better name hash function
From: Stephen Hemminger <shemminger@vyatta.com>, Al Viro @ 2009-10-26 22:36 UTC (permalink / raw)
To: Andrew Morton, Linus Torvalds; +Cc: Octavian Purdila, netdev, linux-kernel
In-Reply-To: <20091025214357.666350d2@nehalam>
Some experiments by Octavian with large numbers of network devices identified
that name_hash does not evenly distribute values causing performance
penalties. The name hashing function is used by dcache et. all
so let's just choose a better one.
Additional standalone tests for 10,000,000 consecutive names
using lots of different algorithms shows fnv as the winner.
It is faster and has almost ideal dispersion.
string10 is slightly faster, but only works for names like ppp0, ppp1,...
Algorithm Time Ratio Max StdDev
string10 0.238201 1.00 2444 0.02
fnv32 0.240595 1.00 2576 1.05
fnv64 0.241224 1.00 2556 0.69
SuperFastHash 0.272872 1.00 2871 2.15
string_hash17 0.295160 1.00 2484 0.40
jhash_string 0.300925 1.00 2606 1.00
crc 1.606741 1.00 2474 0.29
md5_string 2.424771 1.00 2644 0.99
djb2 0.275424 1.15 3821 19.04
string_hash31 0.264806 1.21 4097 22.78
sdbm 0.371136 2.87 13016 67.54
elf 0.371279 3.59 9990 79.50
pjw 0.401172 3.59 9990 79.50
full_name_hash 0.285851 13.09 35174 171.81
kr_hash 0.245068 124.84 468448 549.89
fletcher 0.267664 124.84 468448 549.89
adler32 0.640668 124.84 468448 549.89
xor 0.220545 213.82 583189 720.85
lastchar 0.194604 409.57 1000000 998.78
Time is seconds.
Ratio is how many probes required to lookup all values versus
an ideal hash.
Max is longest chain
Reported-by: Octavian Purdila <opurdila@ixiacom.com>
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
--- a/include/linux/dcache.h 2009-10-26 14:58:45.220347300 -0700
+++ b/include/linux/dcache.h 2009-10-26 15:12:15.004160122 -0700
@@ -45,15 +45,28 @@ struct dentry_stat_t {
};
extern struct dentry_stat_t dentry_stat;
-/* Name hashing routines. Initial hash value */
-/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
-#define init_name_hash() 0
+/*
+ * Fowler / Noll / Vo (FNV) Hash
+ * see: http://www.isthe.com/chongo/tech/comp/fnv/
+ */
+#ifdef CONFIG_64BIT
+#define FNV_PRIME 1099511628211ull
+#define FNV1_INIT 14695981039346656037ull
+#else
+#define FNV_PRIME 16777619u
+#define FNV1_INIT 2166136261u
+#endif
+
+#define init_name_hash() FNV1_INIT
-/* partial hash update function. Assume roughly 4 bits per character */
+/* partial hash update function. */
static inline unsigned long
-partial_name_hash(unsigned long c, unsigned long prevhash)
+partial_name_hash(unsigned char c, unsigned long prevhash)
{
- return (prevhash + (c << 4) + (c >> 4)) * 11;
+ prevhash ^= c;
+ prevhash *= FNV_PRIME;
+
+ return prevhash;
}
/*
^ permalink raw reply
* Re: [PATCH]NET/KS8695: add support NAPI for Rx
From: David Miller @ 2009-10-26 22:43 UTC (permalink / raw)
To: bhutchings; +Cc: figo1802, dsilvers, netdev, vince, ben
In-Reply-To: <1256575746.2783.37.camel@achroite>
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Mon, 26 Oct 2009 16:49:06 +0000
>> @@ -152,6 +156,10 @@ struct ks8695_priv {
>> enum ks8695_dtype dtype;
>> void __iomem *io_regs;
>>
>> + #ifdef KS8695NET_NAPI
>> + struct napi_struct napi;
>> + #endif
>> +
>
> NAPI is well-established and there should be no need to make it
> optional. So far as I'm aware, all other drivers that had it as an
> option now use it unconditionally.
I absolutely refuse to apply this patch with the CPP conditional
present, if you convert to NAPI make it unconditional.
^ permalink raw reply
* Re: [PATCH v1 1/7] gianfar: Add per queue structure support
From: David Miller @ 2009-10-26 23:07 UTC (permalink / raw)
To: sandeep.kumar; +Cc: netdev
In-Reply-To: <1256574433500-git-send-email-sandeep.kumar@freescale.com>
This patch set still doesn't apply cleanly to net-next-2.6,
in particular patch #4 fails to apply:
Applying: fsl_pq_mdio: Add Suport for etsec2.0 devices.
error: patch failed: drivers/net/fsl_pq_mdio.c:405
error: drivers/net/fsl_pq_mdio.c: patch does not apply
Patch failed at 0004 fsl_pq_mdio: Add Suport for etsec2.0 devices.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
Please respin this, and also please CC: on all of your patch
postings as that helps me sort things.
Thanks.
^ permalink raw reply
* Re: [net-2.6 PATCH 1/3] igb: fix memory leak when setting ring size while interface is down
From: David Miller @ 2009-10-26 23:09 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, alexander.h.duyck
In-Reply-To: <20091026213147.9993.9778.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:31:47 -0700
> From: Alexander Duyck <alexander.h.duyck@intel.com>
>
> Changing ring sizes while the interface was down was causing a double
> allocation of the receive and transmit rings. This issue is amplified when
> there are multiple rings enabled. To prevent this we need to add an
> additional check which will just update the ring counts when the interface
> is not up and skip the allocation steps.
>
> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [net-2.6 PATCH 2/3] ixgbe: fix memory leak when resizing rings while interface is down
From: David Miller @ 2009-10-26 23:09 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, alexander.h.duyck
In-Reply-To: <20091026213205.9993.32014.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:32:05 -0700
> From: Alexander Duyck <alexander.h.duyck@intel.com>
>
> This patch resolves a memory leak that occurs when you resize the rings via
> the ethtool -G option while the interface is down.
>
> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [net-2.6 PATCH 3/3] igbvf: fix memory leak when ring size changed while interface down
From: David Miller @ 2009-10-26 23:09 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, alexander.h.duyck
In-Reply-To: <20091026213225.9993.25681.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:32:25 -0700
> From: Alexander Duyck <alexander.h.duyck@intel.com>
>
> This patch resolves a memory leak which occurs while changing the ring size
> while the interface is down.
>
> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [net-2.6 PATCH 1/5] e1000e: clear PHY wakeup bit after LCD reset on 82577/82578
From: David Miller @ 2009-10-26 23:17 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, bruce.w.allan
In-Reply-To: <20091026212242.9682.25442.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:22:47 -0700
> From: Bruce Allan <bruce.w.allan@intel.com>
>
> Performing a dummy read of the PHY Wakeup Control (WUC) register clears the
> wakeup enable bit set by an PHY reset. If this bit remains set, link
> problems may occur.
>
> Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [net-2.6 PATCH 2/5] e1000e: increase swflag acquisition timeout for ICHx/PCH
From: David Miller @ 2009-10-26 23:17 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, bruce.w.allan
In-Reply-To: <20091026212306.9682.73519.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:23:06 -0700
> From: Bruce Allan <bruce.w.allan@intel.com>
>
> In some conditions (e.g. when AMT is enabled on the system), it is possible
> to take an extended period of time to for the driver to acquire the sw/fw/hw
> hardware semaphore used to protect against concurrent access of a shared
> resource (e.g. PHY registers). This could cause PHY registers to not get
> configured properly resulting in link issues.
>
> Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [net-2.6 PATCH 3/5] e1000e: 82577/82578 requires a different method to configure LPLU
From: David Miller @ 2009-10-26 23:17 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, bruce.w.allan
In-Reply-To: <20091026212325.9682.22270.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:23:25 -0700
> From: Bruce Allan <bruce.w.allan@intel.com>
>
> Unlike previous ICHx-based parts, the PCH-based parts (82577/82578) require
> LPLU (Low Power Link Up, or "reverse auto-negotiation") to be configured in
> the PHY rather than the MAC.
>
> Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [net-2.6 PATCH 4/5] e1000e: separate mutex usage between NVM and PHY/CSR register for ICHx/PCH
From: David Miller @ 2009-10-26 23:17 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, bruce.w.allan
In-Reply-To: <20091026212343.9682.56885.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:23:43 -0700
> From: Bruce Allan <bruce.w.allan@intel.com>
>
> Accesses to NVM and PHY/CSR registers on ICHx/PCH-based parts are protected
> from concurrent accesses with a mutex that is acquired when the access is
> initiated and released when the access has completed. However, the two
> types of accesses should not be protected by the same mutex because the
> driver may have to access the NVM while already holding the mutex over
> several consecutive PHY/CSR accesses which would result in livelock.
>
> Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [net-2.6 PATCH 5/5] e1000e: allow for swflag to be held over consecutive PHY accesses
From: David Miller @ 2009-10-26 23:17 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, gospo, bruce.w.allan
In-Reply-To: <20091026212401.9682.51970.stgit@localhost.localdomain>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Mon, 26 Oct 2009 14:24:02 -0700
> From: Bruce Allan <bruce.w.allan@intel.com>
>
> PCH-based parts (82577/82578) and some ICH8-based parts (82566) need to
> hold the swflag (sw/fw/hw hardware semaphore) over consecutive PHY accesses
> in order to perform sw-driven PHY configuration during initialization to
> workaround known hardware issues (see follow-on patch). This patch
> provides new PHY read/write functions (and function pointers) that will
> allow accessing the PHY registers assuming the swflag has already been
> acquired. The actual PHY register access code has moved into helper
> functions that are called with a flag indicating whether or not the swflag
> has already been acquired and acquires/releases it if not.
>
> The functions called from within the updated PHY access functions had to be
> updated to assume the swflag was already acquired, and other functions that
> called those functions were also updated to acquire/release the swflag.
>
> Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Applied.
^ permalink raw reply
* Re: [PATCH] PPPoE: Fix flush/close races.
From: David Miller @ 2009-10-26 23:23 UTC (permalink / raw)
To: denys; +Cc: gorcunov, mostrows, linux-ppp, netdev, eric.dumazet
In-Reply-To: <200910262205.38108.denys@visp.net.lb>
From: Denys Fedoryschenko <denys@visp.net.lb>
Date: Mon, 26 Oct 2009 22:05:37 +0200
> On Monday 26 October 2009 21:59:33 Cyrill Gorcunov wrote:
>> [Michal Ostrowski - Mon, Oct 26, 2009 at 02:51:52PM -0500]
>>
>>
>> Thanks a lot Michal!
>>
>> I think we should add as well
>>
>> Reported-by: Denys Fedoryschenko <denys@visp.net.lb>
>> Tested-by: Denys Fedoryschenko <denys@visp.net.lb>
>>
>> -- Cyrill
>
> Yes, till now everything working perfectly. Confirming :-)
Applied, thanks everyone.
^ permalink raw reply
* Re: [PATCH 0/5] Candidate fix for increased number of GFP_ATOMIC failures V2
From: Frans Pop @ 2009-10-26 23:45 UTC (permalink / raw)
To: Mel Gorman
Cc: Jiri Kosina, Sven Geggus, Karol Lewandowski, Tobias Oetiker,
Rafael J. Wysocki, David Miller, Reinette Chatre, Kalle Valo,
David Rientjes, KOSAKI Motohiro, Mohamed Abbas, Jens Axboe,
John W. Linville, Pekka Enberg, Bartlomiej Zolnierkiewicz,
Greg Kroah-Hartman, Stephan von Krawczynski, Kernel Testers List,
netdev, linux-kernel, linux-mm@kvack.org
In-Reply-To: <200910262317.55960.elendil@planet.nl>
On Monday 26 October 2009, Frans Pop wrote:
> Detailed test results follow. I've done 2 test runs with each kernel (3
> for the last).
Forgot to mention that each run was after a reboot, so they are not
interdependant.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH] sh_eth: Add asm/cacheflush.h
From: Nobuhiro Iwamatsu @ 2009-10-26 23:49 UTC (permalink / raw)
To: netdev; +Cc: Linux-sh, Paul Mundt
Add include asm/cacheflush.h, because declaration of __flush_purge_region
moved to asm/cacheflush.h.
Signed-off-by: Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
---
drivers/net/sh_eth.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c
index f49d080..528b912 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/sh_eth.c
@@ -30,6 +30,7 @@
#include <linux/phy.h>
#include <linux/cache.h>
#include <linux/io.h>
+#include <asm/cacheflush.h>
#include "sh_eth.h"
--
1.6.4.3
^ permalink raw reply related
* Re: [PATCH 6/9] ser_gigaset: checkpatch cleanup
From: Tilman Schmidt @ 2009-10-26 23:59 UTC (permalink / raw)
To: Joe Perches
Cc: David Miller, Karsten Keil, Hansjoerg Lipp, netdev, linux-kernel,
isdn4linux, i4ldeveloper
In-Reply-To: <1256518486.14711.13.camel@Joe-Laptop.home>
[-- Attachment #1: Type: text/plain, Size: 2021 bytes --]
Am 26.10.2009 01:54 schrieb Joe Perches:
> On Sun, 2009-10-25 at 20:30 +0100, Tilman Schmidt wrote:
>> Duly uglified as demanded by checkpatch.pl.
>> diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
>> index 3071a52..ac3409e 100644
>> --- a/drivers/isdn/gigaset/ser-gigaset.c
>> +++ b/drivers/isdn/gigaset/ser-gigaset.c
>> @@ -164,9 +164,15 @@ static void gigaset_modem_fill(unsigned long data)
>> {
>> struct cardstate *cs = (struct cardstate *) data;
>> struct bc_state *bcs;
>> + struct sk_buff *nextskb;
>> int sent = 0;
>>
>> - if (!cs || !(bcs = cs->bcs)) {
>> + if (!cs) {
>> + gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
>> + return;
>> + }
>> + bcs = cs->bcs;
>> + if (!bcs) {
>> gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
>> return;
>> }
>
> perhaps:
> if (!cs || !cs->bcs) {
> gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
> return;
> }
> bcs = cs->bcs;
That would evaluate cs->bcs twice, and is also, in my experience,
significantly more prone to easily overlooked typos which result in
checking a different pointer in the if statement than the one that's
actually used in the subsequent assignment.
>> @@ -404,16 +412,20 @@ static void gigaset_device_release(struct device *dev)
>> static int gigaset_initcshw(struct cardstate *cs)
>> {
>> int rc;
>> + struct ser_cardstate *scs;
>>
>> - if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) {
>> + scs = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL);
>> + if (!scs) {
>> pr_err("out of memory\n");
>> return 0;
>> }
>> + cs->hw.ser = scs;
>
> Why not no temporary and just:
>
> cs->hw.ser = kzalloc...
> if (!cs->hw.ser)
For the same reasons as above.
Thanks,
Tilman
--
Tilman Schmidt E-Mail: tilman@imap.cc
Bonn, Germany
Diese Nachricht besteht zu 100% aus wiederverwerteten Bits.
Ungeöffnet mindestens haltbar bis: (siehe Rückseite)
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 254 bytes --]
^ permalink raw reply
* [RFC PATCH] fib_hash: improve route deletion scaling on interface drop with lots of interfaces
From: Benjamin LaHaise @ 2009-10-27 0:03 UTC (permalink / raw)
To: netdev
Hi folks,
Below is a patch to improve the scaling of interface destruction in
fib_hash. The general idea is to tie the fib_alias structure into a
list off of net_device and walk that list during a fib_flush() caused
by an interface drop. This makes the resulting flush only have to walk
the number of routes attached to an interface rather than the number of
routes attached to all interfaces at the expense of a couple of additional
pointers in struct fib_alias.
This patch is against Linus' tree. I'll post against net-next after a
bit more testing and feedback. With 20,000 interfaces & routes, interface
deletion time improves from 53s to 40s. Note that this is with other changes
applied to improve sysfs and procfs scaling, as otherwise those are the
bottleneck. Next up in the network code is rt_cache_flush(). Comments?
-ben
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 812a5f3..982045b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -856,6 +856,7 @@ struct net_device
/* delayed register/unregister */
struct list_head todo_list;
+ struct list_head fib_list;
/* device index hash chain */
struct hlist_node index_hlist;
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index ef91fe9..0c32193 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -149,7 +149,7 @@ struct fib_table {
int (*tb_delete)(struct fib_table *, struct fib_config *);
int (*tb_dump)(struct fib_table *table, struct sk_buff *skb,
struct netlink_callback *cb);
- int (*tb_flush)(struct fib_table *table);
+ int (*tb_flush)(struct fib_table *table, struct net_device *dev);
void (*tb_select_default)(struct fib_table *table,
const struct flowi *flp, struct fib_result *res);
diff --git a/net/core/dev.c b/net/core/dev.c
index b8f74cf..9f6f736 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5173,6 +5173,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
netdev_init_queues(dev);
INIT_LIST_HEAD(&dev->napi_list);
+ INIT_LIST_HEAD(&dev->fib_list);
dev->priv_flags = IFF_XMIT_DST_RELEASE;
setup(dev);
strcpy(dev->name, name);
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index e2f9505..0283b1f 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -128,18 +128,19 @@ void fib_select_default(struct net *net,
tb->tb_select_default(tb, flp, res);
}
-static void fib_flush(struct net *net)
+static void fib_flush(struct net_device *dev)
{
int flushed = 0;
struct fib_table *tb;
struct hlist_node *node;
struct hlist_head *head;
unsigned int h;
+ struct net *net = dev_net(dev);
for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
head = &net->ipv4.fib_table_hash[h];
hlist_for_each_entry(tb, node, head, tb_hlist)
- flushed += tb->tb_flush(tb);
+ flushed += tb->tb_flush(tb, dev);
}
if (flushed)
@@ -805,7 +806,7 @@ static void fib_del_ifaddr(struct in_ifaddr *ifa)
for stray nexthop entries, then ignite fib_flush.
*/
if (fib_sync_down_addr(dev_net(dev), ifa->ifa_local))
- fib_flush(dev_net(dev));
+ fib_flush(dev);
}
}
#undef LOCAL_OK
@@ -895,7 +896,7 @@ static void nl_fib_lookup_exit(struct net *net)
static void fib_disable_ip(struct net_device *dev, int force)
{
if (fib_sync_down_dev(dev, force))
- fib_flush(dev_net(dev));
+ fib_flush(dev);
rt_cache_flush(dev_net(dev), 0);
arp_ifdown(dev);
}
@@ -1009,7 +1010,7 @@ static void __net_exit ip_fib_net_exit(struct net *net)
head = &net->ipv4.fib_table_hash[i];
hlist_for_each_entry_safe(tb, node, tmp, head, tb_hlist) {
hlist_del(node);
- tb->tb_flush(tb);
+ tb->tb_flush(tb, NULL);
kfree(tb);
}
}
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
index ecd3945..d08ba2f 100644
--- a/net/ipv4/fib_hash.c
+++ b/net/ipv4/fib_hash.c
@@ -377,6 +377,7 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
u8 tos = cfg->fc_tos;
__be32 key;
int err;
+ struct net_device *dev;
if (cfg->fc_dst_len > 32)
return -EINVAL;
@@ -516,6 +517,10 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
new_fa->fa_type = cfg->fc_type;
new_fa->fa_scope = cfg->fc_scope;
new_fa->fa_state = 0;
+ new_fa->fa_fib_node = f;
+ new_fa->fa_fz = fz;
+
+ dev = fi->fib_dev;
/*
* Insert new entry to the list.
@@ -527,6 +532,7 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
list_add_tail(&new_fa->fa_list,
(fa ? &fa->fa_list : &f->fn_alias));
fib_hash_genid++;
+ list_add_tail(&new_fa->fa_dev_list, &dev->fib_list);
write_unlock_bh(&fib_hash_lock);
if (new_f)
@@ -605,6 +611,7 @@ static int fn_hash_delete(struct fib_table *tb, struct fib_config *cfg)
kill_fn = 0;
write_lock_bh(&fib_hash_lock);
list_del(&fa->fa_list);
+ list_del(&fa->fa_dev_list);
if (list_empty(&f->fn_alias)) {
hlist_del(&f->fn_hash);
kill_fn = 1;
@@ -643,6 +650,7 @@ static int fn_flush_list(struct fn_zone *fz, int idx)
if (fi && (fi->fib_flags&RTNH_F_DEAD)) {
write_lock_bh(&fib_hash_lock);
list_del(&fa->fa_list);
+ list_del(&fa->fa_dev_list);
if (list_empty(&f->fn_alias)) {
hlist_del(&f->fn_hash);
kill_f = 1;
@@ -662,17 +670,69 @@ static int fn_flush_list(struct fn_zone *fz, int idx)
return found;
}
-static int fn_hash_flush(struct fib_table *tb)
+static int fn_flush_alias(struct fn_hash *table, struct fib_alias *fa)
+{
+ int kill_f = 0;
+ struct fib_info *fi = fa->fa_info;
+ int found = 0;
+
+ if (!fi)
+ BUG();
+
+ if (fi && (fi->fib_flags & RTNH_F_DEAD)) {
+ struct fib_node *f = fa->fa_fib_node;
+ struct fn_zone *fz = fa->fa_fz;
+
+ write_lock_bh(&fib_hash_lock);
+ list_del(&fa->fa_list);
+ list_del(&fa->fa_dev_list);
+ if (list_empty(&f->fn_alias)) {
+ hlist_del(&f->fn_hash);
+ kill_f = 1;
+ }
+ fib_hash_genid++;
+ write_unlock_bh(&fib_hash_lock);
+
+ fn_free_alias(fa, f);
+ found++;
+
+ if (kill_f)
+ fn_free_node(f);
+ fz->fz_nent--;
+ }
+
+ return found;
+}
+
+static int fn_flush_dev(struct fn_hash *table, struct net_device *dev)
+{
+ int found = 0;
+ struct list_head *pos, *next;
+
+ list_for_each_safe(pos, next, &dev->fib_list) {
+ struct fib_alias *fa =
+ container_of(pos, struct fib_alias, fa_dev_list);
+ found += fn_flush_alias(table, fa);
+ }
+
+ return found;
+}
+
+static int fn_hash_flush(struct fib_table *tb, struct net_device *dev)
{
struct fn_hash *table = (struct fn_hash *) tb->tb_data;
struct fn_zone *fz;
int found = 0;
- for (fz = table->fn_zone_list; fz; fz = fz->fz_next) {
- int i;
+ if (dev) {
+ found = fn_flush_dev(table, dev);
+ } else {
+ for (fz = table->fn_zone_list; fz; fz = fz->fz_next) {
+ int i;
- for (i = fz->fz_divisor - 1; i >= 0; i--)
- found += fn_flush_list(fz, i);
+ for (i = fz->fz_divisor - 1; i >= 0; i--)
+ found += fn_flush_list(fz, i);
+ }
}
return found;
}
diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h
index 637b133..9f2fad1 100644
--- a/net/ipv4/fib_lookup.h
+++ b/net/ipv4/fib_lookup.h
@@ -5,9 +5,17 @@
#include <linux/list.h>
#include <net/ip_fib.h>
+struct fib_node;
+struct fn_zone;
+
struct fib_alias {
struct list_head fa_list;
+ struct list_head fa_dev_list;
struct fib_info *fa_info;
+#ifdef CONFIG_IP_FIB_HASH
+ struct fib_node *fa_fib_node;
+ struct fn_zone *fa_fz;
+#endif
u8 fa_tos;
u8 fa_type;
u8 fa_scope;
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 291bdf5..4805772 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1786,7 +1786,7 @@ static struct leaf *trie_leafindex(struct trie *t, int index)
/*
* Caller must hold RTNL.
*/
-static int fn_trie_flush(struct fib_table *tb)
+static int fn_trie_flush(struct fib_table *tb, struct net_device *dev)
{
struct trie *t = (struct trie *) tb->tb_data;
struct leaf *l, *ll = NULL;
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox