diff mbox series

[v4] e1000e: PCIm function state support

Message ID 20190804074026.25198-1-vitaly.lifshits@intel.com
State Changes Requested
Headers show
Series [v4] e1000e: PCIm function state support | expand

Commit Message

Vitaly Lifshits Aug. 4, 2019, 7:40 a.m. UTC
Due to commit: 5d8682588605 ("[misc] mei: me: allow runtime
pm for platform with D0i3")
when disconnecting the cable and reconnecting it the NIC
enters DMoff state. This caused wrong link indication
and duplex mismatch.

Checking PCIm function state and performing PHY reset after
exiting DMoff state in watchdog task, solves this issue.

Link: https://bugzilla.redhat.com/show_bug.cgi?id=1689436
Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
---

V2: Fix typos in commit message
V3: Fix minor typo
V4: Address community comments 
---
 drivers/net/ethernet/intel/e1000e/defines.h |  4 ++++
 drivers/net/ethernet/intel/e1000e/netdev.c  | 20 +++++++++++++++++++-
 2 files changed, 23 insertions(+), 1 deletion(-)

Comments

Sasha Neftin Aug. 4, 2019, 10:44 a.m. UTC | #1
On 8/4/2019 10:40, Vitaly Lifshits wrote:
> Due to commit: 5d8682588605 ("[misc] mei: me: allow runtime
> pm for platform with D0i3")
> when disconnecting the cable and reconnecting it the NIC
> enters DMoff state. This caused wrong link indication
> and duplex mismatch.
> 
> Checking PCIm function state and performing PHY reset after
> exiting DMoff state in watchdog task, solves this issue.
> 
> Link: https://bugzilla.redhat.com/show_bug.cgi?id=1689436
> Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
> ---
> 
> V2: Fix typos in commit message
> V3: Fix minor typo
> V4: Address community comments
> ---
>   drivers/net/ethernet/intel/e1000e/defines.h |  4 ++++
>   drivers/net/ethernet/intel/e1000e/netdev.c  | 20 +++++++++++++++++++-
>   2 files changed, 23 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
> index fd550dee4982..4cff73cbd032 100644
> --- a/drivers/net/ethernet/intel/e1000e/defines.h
> +++ b/drivers/net/ethernet/intel/e1000e/defines.h
> @@ -222,6 +222,10 @@
>   #define E1000_STATUS_PHYRA      0x00000400      /* PHY Reset Asserted */
>   #define E1000_STATUS_GIO_MASTER_ENABLE	0x00080000	/* Master Req status */
>   
> +/* PCIm function state */
> +#define E1000_STATUS_PCIM_STATE	0x40000000
> +#define PCIM_DMOFF_EXIT_TIMEOUT 100
> +
>   #define HALF_DUPLEX 1
>   #define FULL_DUPLEX 2
>   
> diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
> index b5fed6177ad6..2d29c0d0be1b 100644
> --- a/drivers/net/ethernet/intel/e1000e/netdev.c
> +++ b/drivers/net/ethernet/intel/e1000e/netdev.c
> @@ -5162,7 +5162,7 @@ static void e1000_watchdog_task(struct work_struct *work)
>   	struct e1000_phy_info *phy = &adapter->hw.phy;
>   	struct e1000_ring *tx_ring = adapter->tx_ring;
>   	struct e1000_hw *hw = &adapter->hw;
> -	u32 link, tctl;
> +	u32 link, tctl, pcim_state, tries = 0;
>   
>   	if (test_bit(__E1000_DOWN, &adapter->state))
>   		return;
> @@ -5187,6 +5187,24 @@ static void e1000_watchdog_task(struct work_struct *work)
>   			/* Cancel scheduled suspend requests. */
>   			pm_runtime_resume(netdev->dev.parent);
>   
> +			/* Checking if MAC is in DMoff state*/
> +			pcim_state = er32(STATUS);
> +			while (pcim_state & E1000_STATUS_PCIM_STATE) {
> +				if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {
> +					e_dbg("Error in exiting dmoff\n");
> +					e_err("PCIm DMoff timeout expired\n");
> +					break;
> +				}
> +				usleep_range(10000, 20000);
> +				pcim_state = er32(STATUS);
> +
> +				/* If MAC entered DMoff state, PHY reset is
> +				 * needed after exiting it
> +				 */
> +				if (!(pcim_state & E1000_STATUS_PCIM_STATE))
> +					e1000_phy_hw_reset(&adapter->hw);
> +			}
> +
>   			/* update snapshot of PHY registers on LSC */
>   			e1000_phy_read_status(adapter);
>   			mac->ops.get_link_up_info(&adapter->hw,
> 
Acked-by: Sasha Neftin <sasha.neftin@intel.com>
Paul Menzel Aug. 4, 2019, 1:44 p.m. UTC | #2
Dear Vitaly,


Thank you for the updated version.

On 04.08.19 09:40, Vitaly Lifshits wrote:
> Due to commit: 5d8682588605 ("[misc] mei: me: allow runtime
> pm for platform with D0i3")
> when disconnecting the cable and reconnecting it the NIC
> enters DMoff state. This caused wrong link indication
> and duplex mismatch.

*and* should fit on the line above.

> Checking PCIm function state and performing PHY reset after
> exiting DMoff state in watchdog task, solves this issue.
> 
> Link: https://bugzilla.redhat.com/show_bug.cgi?id=1689436
> Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
> ---
> 
> V2: Fix typos in commit message
> V3: Fix minor typo
> V4: Address community comments
> ---
>   drivers/net/ethernet/intel/e1000e/defines.h |  4 ++++
>   drivers/net/ethernet/intel/e1000e/netdev.c  | 20 +++++++++++++++++++-
>   2 files changed, 23 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
> index fd550dee4982..4cff73cbd032 100644
> --- a/drivers/net/ethernet/intel/e1000e/defines.h
> +++ b/drivers/net/ethernet/intel/e1000e/defines.h
> @@ -222,6 +222,10 @@
>   #define E1000_STATUS_PHYRA      0x00000400      /* PHY Reset Asserted */
>   #define E1000_STATUS_GIO_MASTER_ENABLE	0x00080000	/* Master Req status */
>   
> +/* PCIm function state */
> +#define E1000_STATUS_PCIM_STATE	0x40000000
> +#define PCIM_DMOFF_EXIT_TIMEOUT 100

Align the values?

> +
>   #define HALF_DUPLEX 1
>   #define FULL_DUPLEX 2
>   
> diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
> index b5fed6177ad6..2d29c0d0be1b 100644
> --- a/drivers/net/ethernet/intel/e1000e/netdev.c
> +++ b/drivers/net/ethernet/intel/e1000e/netdev.c
> @@ -5162,7 +5162,7 @@ static void e1000_watchdog_task(struct work_struct *work)
>   	struct e1000_phy_info *phy = &adapter->hw.phy;
>   	struct e1000_ring *tx_ring = adapter->tx_ring;
>   	struct e1000_hw *hw = &adapter->hw;
> -	u32 link, tctl;
> +	u32 link, tctl, pcim_state, tries = 0;
>   
>   	if (test_bit(__E1000_DOWN, &adapter->state))
>   		return;
> @@ -5187,6 +5187,24 @@ static void e1000_watchdog_task(struct work_struct *work)
>   			/* Cancel scheduled suspend requests. */
>   			pm_runtime_resume(netdev->dev.parent);
>   
> +			/* Checking if MAC is in DMoff state*/

s/Checking/Check/

> +			pcim_state = er32(STATUS);
> +			while (pcim_state & E1000_STATUS_PCIM_STATE) {
> +				if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {
> +					e_dbg("Error in exiting dmoff\n");
> +					e_err("PCIm DMoff timeout expired\n");
> +					break;
> +				}
> +				usleep_range(10000, 20000);
> +				pcim_state = er32(STATUS);
> +
> +				/* If MAC entered DMoff state, PHY reset is
> +				 * needed after exiting it
> +				 */
> +				if (!(pcim_state & E1000_STATUS_PCIM_STATE))
> +					e1000_phy_hw_reset(&adapter->hw);

I still believe, the if statement should be moved *outside* the loop.

> +			}
> +
>   			/* update snapshot of PHY registers on LSC */
>   			e1000_phy_read_status(adapter);
>   			mac->ops.get_link_up_info(&adapter->hw,


Kind regards,

Paul
Vitaly Lifshits Aug. 7, 2019, 7:47 a.m. UTC | #3
Dear Paul,

Thank you again for your comments.
I sent a new version for my patch (V5).

On 8/4/2019 16:44, Paul Menzel wrote:
> Dear Vitaly,
>
>
> Thank you for the updated version.
>
> On 04.08.19 09:40, Vitaly Lifshits wrote:
>> Due to commit: 5d8682588605 ("[misc] mei: me: allow runtime
>> pm for platform with D0i3")
>> when disconnecting the cable and reconnecting it the NIC
>> enters DMoff state. This caused wrong link indication
>> and duplex mismatch.
>
> *and* should fit on the line above.
>
>> Checking PCIm function state and performing PHY reset after
>> exiting DMoff state in watchdog task, solves this issue.
>>
>> Link: https://bugzilla.redhat.com/show_bug.cgi?id=1689436
>> Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
>> ---
>>
>> V2: Fix typos in commit message
>> V3: Fix minor typo
>> V4: Address community comments
>> ---
>>   drivers/net/ethernet/intel/e1000e/defines.h |  4 ++++
>>   drivers/net/ethernet/intel/e1000e/netdev.c  | 20 +++++++++++++++++++-
>>   2 files changed, 23 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/ethernet/intel/e1000e/defines.h 
>> b/drivers/net/ethernet/intel/e1000e/defines.h
>> index fd550dee4982..4cff73cbd032 100644
>> --- a/drivers/net/ethernet/intel/e1000e/defines.h
>> +++ b/drivers/net/ethernet/intel/e1000e/defines.h
>> @@ -222,6 +222,10 @@
>>   #define E1000_STATUS_PHYRA      0x00000400      /* PHY Reset 
>> Asserted */
>>   #define E1000_STATUS_GIO_MASTER_ENABLE    0x00080000    /* Master 
>> Req status */
>>   +/* PCIm function state */
>> +#define E1000_STATUS_PCIM_STATE    0x40000000
>> +#define PCIM_DMOFF_EXIT_TIMEOUT 100
>
> Align the values?
>
>> +
>>   #define HALF_DUPLEX 1
>>   #define FULL_DUPLEX 2
>>   diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c 
>> b/drivers/net/ethernet/intel/e1000e/netdev.c
>> index b5fed6177ad6..2d29c0d0be1b 100644
>> --- a/drivers/net/ethernet/intel/e1000e/netdev.c
>> +++ b/drivers/net/ethernet/intel/e1000e/netdev.c
>> @@ -5162,7 +5162,7 @@ static void e1000_watchdog_task(struct 
>> work_struct *work)
>>       struct e1000_phy_info *phy = &adapter->hw.phy;
>>       struct e1000_ring *tx_ring = adapter->tx_ring;
>>       struct e1000_hw *hw = &adapter->hw;
>> -    u32 link, tctl;
>> +    u32 link, tctl, pcim_state, tries = 0;
>>         if (test_bit(__E1000_DOWN, &adapter->state))
>>           return;
>> @@ -5187,6 +5187,24 @@ static void e1000_watchdog_task(struct 
>> work_struct *work)
>>               /* Cancel scheduled suspend requests. */
>>               pm_runtime_resume(netdev->dev.parent);
>>   +            /* Checking if MAC is in DMoff state*/
>
> s/Checking/Check/
>
>> +            pcim_state = er32(STATUS);
>> +            while (pcim_state & E1000_STATUS_PCIM_STATE) {
>> +                if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {
>> +                    e_dbg("Error in exiting dmoff\n");
>> +                    e_err("PCIm DMoff timeout expired\n");
>> +                    break;
>> +                }
>> +                usleep_range(10000, 20000);
>> +                pcim_state = er32(STATUS);
>> +
>> +                /* If MAC entered DMoff state, PHY reset is
>> +                 * needed after exiting it
>> +                 */
>> +                if (!(pcim_state & E1000_STATUS_PCIM_STATE))
>> +                    e1000_phy_hw_reset(&adapter->hw);
>
> I still believe, the if statement should be moved *outside* the loop.

The if statement has to stay in the loop since the PHY reset is needed 
only if the MAC entered DMoff state.

>
>> +            }
>> +
>>               /* update snapshot of PHY registers on LSC */
>>               e1000_phy_read_status(adapter);
>>               mac->ops.get_link_up_info(&adapter->hw,
>
>
> Kind regards,
>
> Paul

Thanks,

Vitaly
Paul Menzel Aug. 7, 2019, 2:51 p.m. UTC | #4
Dear Vitaly,


On 07.08.19 09:47, Lifshits, Vitaly wrote:

> Thank you again for your comments.
> I sent a new version for my patch (V5).

Thank you very much for incorporating them.

> On 8/4/2019 16:44, Paul Menzel wrote:

>> On 04.08.19 09:40, Vitaly Lifshits wrote:

[…]

>>> +            pcim_state = er32(STATUS);
>>> +            while (pcim_state & E1000_STATUS_PCIM_STATE) {
>>> +                if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {

By the way, can’t there be the unit appended to the macro name
`PCIM_DMOFF_EXIT_TIMEOUT`?

>>> +                    e_dbg("Error in exiting dmoff\n");
>>> +                    e_err("PCIm DMoff timeout expired\n");

It’s not a hold-up, but why do you print two messages? I’d just print the
error message.

    e_err("Failed to exit PCIm DMoff: Time-out (%i iterations) expired", PCIM_DMOFF_EXIT_TIMEOUT);

Or: e_err("Exiting PCIm DMoff timed out after %i iterations.", PCIM_DMOFF_EXIT_TIMEOUT);

>>> +                    break;
>>> +                }
>>> +                usleep_range(10000, 20000);
>>> +                pcim_state = er32(STATUS);
>>> +
>>> +                /* If MAC entered DMoff state, PHY reset is
>>> +                 * needed after exiting it
>>> +                 */
>>> +                if (!(pcim_state & E1000_STATUS_PCIM_STATE))
>>> +                    e1000_phy_hw_reset(&adapter->hw);
>>
>> I still believe, the if statement should be moved *outside* the loop.
> 
> The if statement has to stay in the loop since the PHY reset is
> needed only if the MAC entered DMoff state.

Thank you. Now I finally saw it. It’s about, when the loop is never
entered. I associated a while loop in my head normally with the assumption,
that the condition is true in the beginning.

For the record, the code below is more intuitive for me.

```
if (pcim_state & E1000_STATUS_PCIM_STATE) {
/* MAC in DMoff state */
	do {
		if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {
			e_dbg("Error in exiting dmoff\n");
			e_err("PCIm DMoff timeout expired\n");
			break;
		}
		usleep_range(10000, 20000);
		pcim_state = er32(STATUS);
	} while (pcim_state & E1000_STATUS_PCIM_STATE);

	e1000_phy_hw_reset(&adapter->hw);
}
```

>>> +            }
>>> +
>>>               /* update snapshot of PHY registers on LSC */
>>>               e1000_phy_read_status(adapter);
>>>               mac->ops.get_link_up_info(&adapter->hw,

Anyway, I do not want to hold up anything.


Kind regards,

Paul
Paul Menzel Aug. 8, 2019, 9:03 a.m. UTC | #5
Dear Vitaly,


On 07.08.19 16:51, Paul Menzel wrote:

> On 07.08.19 09:47, Lifshits, Vitaly wrote:

>> On 8/4/2019 16:44, Paul Menzel wrote:
> 
>>> On 04.08.19 09:40, Vitaly Lifshits wrote:
> 
> […]
> 
>>>> +            pcim_state = er32(STATUS);
>>>> +            while (pcim_state & E1000_STATUS_PCIM_STATE) {
>>>> +                if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {
> 
> By the way, can’t there be the unit appended to the macro name
> `PCIM_DMOFF_EXIT_TIMEOUT`?
> 
>>>> +                    e_dbg("Error in exiting dmoff\n");
>>>> +                    e_err("PCIm DMoff timeout expired\n");
> 
> It’s not a hold-up, but why do you print two messages? I’d just print the
> error message.
> 
>     e_err("Failed to exit PCIm DMoff: Time-out (%i iterations) expired", PCIM_DMOFF_EXIT_TIMEOUT);
> 
> Or: e_err("Exiting PCIm DMoff timed out after %i iterations.", PCIM_DMOFF_EXIT_TIMEOUT);
> 
>>>> +                    break;
>>>> +                }
>>>> +                usleep_range(10000, 20000);
>>>> +                pcim_state = er32(STATUS);
>>>> +
>>>> +                /* If MAC entered DMoff state, PHY reset is
>>>> +                 * needed after exiting it
>>>> +                 */
>>>> +                if (!(pcim_state & E1000_STATUS_PCIM_STATE))
>>>> +                    e1000_phy_hw_reset(&adapter->hw);
>>>
>>> I still believe, the if statement should be moved *outside* the loop.
>>
>> The if statement has to stay in the loop since the PHY reset is
>> needed only if the MAC entered DMoff state.
> 
> Thank you. Now I finally saw it. It’s about, when the loop is never
> entered. I associated a while loop in my head normally with the assumption,
> that the condition is true in the beginning.
> 
> For the record, the code below is more intuitive for me.
> 
> ```
> if (pcim_state & E1000_STATUS_PCIM_STATE) {
> /* MAC in DMoff state */
> 	do {
> 		if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {
> 			e_dbg("Error in exiting dmoff\n");
> 			e_err("PCIm DMoff timeout expired\n");
> 			break;
> 		}
> 		usleep_range(10000, 20000);
> 		pcim_state = er32(STATUS);
> 	} while (pcim_state & E1000_STATUS_PCIM_STATE);
> 
> 	e1000_phy_hw_reset(&adapter->hw);
> }
> ```

Sleeping a night over this, this is still not equivalent to your code, as
this would reset after timing out, which your code does not. So, the if
condition has to be added for that call.


```
[…]
unsigned int tries = 0; /* u32 not needed */
[…]

if (pcim_state & E1000_STATUS_PCIM_STATE) {
/* MAC in DMoff state */
	do {
		usleep_range(10000, 20000);
		pcim_state = er32(STATUS);
	} while ((pcim_state & E1000_STATUS_PCIM_STATE) && (++tries < PCIM_DMOFF_EXIT_TIMEOUT));

	if (tries == PCIM_DMOFF_EXIT_TIMEOUT)
		e_err("Exiting PCIm DMoff timed out after %i iterations.", PCIM_DMOFF_EXIT_TIMEOUT);
	else
		e1000_phy_hw_reset(&adapter->hw);
}
```

>>>> +            }
>>>> +
>>>>               /* update snapshot of PHY registers on LSC */
>>>>               e1000_phy_read_status(adapter);
>>>>               mac->ops.get_link_up_info(&adapter->hw,
> 
> Anyway, I do not want to hold up anything.


Kind regards,

Paul
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index fd550dee4982..4cff73cbd032 100644
--- a/drivers/net/ethernet/intel/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
@@ -222,6 +222,10 @@ 
 #define E1000_STATUS_PHYRA      0x00000400      /* PHY Reset Asserted */
 #define E1000_STATUS_GIO_MASTER_ENABLE	0x00080000	/* Master Req status */
 
+/* PCIm function state */
+#define E1000_STATUS_PCIM_STATE	0x40000000
+#define PCIM_DMOFF_EXIT_TIMEOUT 100
+
 #define HALF_DUPLEX 1
 #define FULL_DUPLEX 2
 
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index b5fed6177ad6..2d29c0d0be1b 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -5162,7 +5162,7 @@  static void e1000_watchdog_task(struct work_struct *work)
 	struct e1000_phy_info *phy = &adapter->hw.phy;
 	struct e1000_ring *tx_ring = adapter->tx_ring;
 	struct e1000_hw *hw = &adapter->hw;
-	u32 link, tctl;
+	u32 link, tctl, pcim_state, tries = 0;
 
 	if (test_bit(__E1000_DOWN, &adapter->state))
 		return;
@@ -5187,6 +5187,24 @@  static void e1000_watchdog_task(struct work_struct *work)
 			/* Cancel scheduled suspend requests. */
 			pm_runtime_resume(netdev->dev.parent);
 
+			/* Checking if MAC is in DMoff state*/
+			pcim_state = er32(STATUS);
+			while (pcim_state & E1000_STATUS_PCIM_STATE) {
+				if (tries++ == PCIM_DMOFF_EXIT_TIMEOUT) {
+					e_dbg("Error in exiting dmoff\n");
+					e_err("PCIm DMoff timeout expired\n");
+					break;
+				}
+				usleep_range(10000, 20000);
+				pcim_state = er32(STATUS);
+
+				/* If MAC entered DMoff state, PHY reset is
+				 * needed after exiting it
+				 */
+				if (!(pcim_state & E1000_STATUS_PCIM_STATE))
+					e1000_phy_hw_reset(&adapter->hw);
+			}
+
 			/* update snapshot of PHY registers on LSC */
 			e1000_phy_read_status(adapter);
 			mac->ops.get_link_up_info(&adapter->hw,