The em driver calls em_get_software_flag() recursively, which causes the
semaphore to be unlocked too early. Make em_get_software_flag and
em_release_software_flag handle this correctly.  Freebsd does not do
this, but they have a mutex that probably allows them to detect
recursive calls to e1000_acquire_swflag_ich8lan().  Reworking the
openbsd driver to not recursively get the semaphore would be very
invasive.
---
 sys/dev/pci/if_em_hw.c | 14 ++++++++++++++
 sys/dev/pci/if_em_hw.h |  1 +
 2 files changed, 15 insertions(+)

diff --git sys/dev/pci/if_em_hw.c sys/dev/pci/if_em_hw.c
index 5bba83cbcd4..7709a4c5805 100644
--- sys/dev/pci/if_em_hw.c
+++ sys/dev/pci/if_em_hw.c
@@ -945,6 +945,8 @@ em_reset_hw(struct em_hw *hw)
                }
                em_get_software_flag(hw);
                E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST));
+               /* HW reset releases software_flag */
+               hw->sw_flag = 0;
                msec_delay(20);
 
                /* Ungate automatic PHY configuration on non-managed 82579 */
@@ -9611,6 +9613,10 @@ em_get_software_flag(struct em_hw *hw)
        DEBUGFUNC("em_get_software_flag");
 
        if (IS_ICH8(hw->mac_type)) {
+               if (hw->sw_flag) {
+                       hw->sw_flag++;
+                       return E1000_SUCCESS;
+               }
                while (timeout) {
                        extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL);
                        if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG))
@@ -9644,6 +9650,7 @@ em_get_software_flag(struct em_hw *hw)
                        return -E1000_ERR_CONFIG;
                }
        }
+       hw->sw_flag++;
        return E1000_SUCCESS;
 }
 
@@ -9663,6 +9670,13 @@ em_release_software_flag(struct em_hw *hw)
        DEBUGFUNC("em_release_software_flag");
 
        if (IS_ICH8(hw->mac_type)) {
+               if (hw->sw_flag <= 0) {
+                       printf("%s: not locked!\n", __func__);
+                       return;
+               }
+               hw->sw_flag--;
+               if (hw->sw_flag > 0)
+                       return;
                extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL);
                extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
                E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl);
diff --git sys/dev/pci/if_em_hw.h sys/dev/pci/if_em_hw.h
index 71dc91e5582..91993a6eb66 100644
--- sys/dev/pci/if_em_hw.h
+++ sys/dev/pci/if_em_hw.h
@@ -1634,6 +1634,7 @@ struct em_hw {
     uint8_t bus_func;
     uint16_t swfw;
     boolean_t eee_enable;
+    int sw_flag;
 };
 
 #define E1000_EEPROM_SWDPIN0   0x0001   /* SWDPIN 0 EEPROM Value */
-- 
2.13.0

Reply via email to