All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net v2 1/5] r8152: Hold the rtnl_lock for all of reset
@ 2023-11-28 21:38 Douglas Anderson
  2023-11-28 21:38 ` [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops Douglas Anderson
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Douglas Anderson @ 2023-11-28 21:38 UTC (permalink / raw)
  To: Jakub Kicinski, Hayes Wang, David S . Miller
  Cc: Laura Nao, Edward Hill, Alan Stern, Grant Grundler, linux-usb,
	Simon Horman, Douglas Anderson, Bjørn Mork, Eric Dumazet,
	Paolo Abeni, linux-kernel, netdev

As of commit d9962b0d4202 ("r8152: Block future register access if
register access fails") there is a race condition that can happen
between the USB device reset thread and napi_enable() (not) getting
called during rtl8152_open(). Specifically:
* While rtl8152_open() is running we get a register access error
  that's _not_ -ENODEV and queue up a USB reset.
* rtl8152_open() exits before calling napi_enable() due to any reason
  (including usb_submit_urb() returning an error).

In that case:
* Since the USB reset is perform in a separate thread asynchronously,
  it can run at anytime USB device lock is not held - even before
  rtl8152_open() has exited with an error and caused __dev_open() to
  clear the __LINK_STATE_START bit.
* The rtl8152_pre_reset() will notice that the netif_running() returns
  true (since __LINK_STATE_START wasn't cleared) so it won't exit
  early.
* rtl8152_pre_reset() will then hang in napi_disable() because
  napi_enable() was never called.

We can fix the race by making sure that the r8152 reset routines don't
run at the same time as we're opening the device. Specifically we need
the reset routines in their entirety rely on the return value of
netif_running(). The only way to reliably depend on that is for them
to hold the rntl_lock() mutex for the duration of reset.

Grabbing the rntl_lock() mutex for the duration of reset seems like a
long time, but reset is not expected to be common and the rtnl_lock()
mutex is already held for long durations since the core grabs it
around the open/close calls.

Fixes: d9962b0d4202 ("r8152: Block future register access if register access fails")
Reviewed-by: Grant Grundler <grundler@chromium.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
In response to v1 Paolo questioned the wisdom of grabbing the
rtnl_lock in the USB pre_reset() and releasing it in the USB
post_reset() [1]. While his concern is a legitimate one because this
looks a bit fragile, I'm still of the belief that the current patch is
the best solution.

This patch has been tested with lockdep and I saw no splats about
it. I've also read through the usb core code twice and I don't see any
way that post_reset() won't be called if pre_reset() was called,
assuming that the pre_reset() doesn't return an error (we never return
an error from pre_reset()).

If folks have some example of something that's broken by the current
rtnl_lock strategy used by this patch (or if folks feel very strongly
that it needs to be changed) then I can spin another version. ...but
as per my reply to Paolo [2] I think that does have some minor
downsides.

[1] https://lore.kernel.org/r/f8c1979e2c71d871998aec0126dd87adb5e76cce.camel@redhat.com
[2] https://lore.kernel.org/r/CAD=FV=VqZq33eLiFPNiZCJmewQ1hxECmUnwbjVbvdJiDkQMAJA@mail.gmail.com

Changes in v2:
- Added "after the cut" notes about rtnl lock strategy.

 drivers/net/usb/r8152.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 2c5c1e91ded6..d6edf0254599 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -8397,6 +8397,8 @@ static int rtl8152_pre_reset(struct usb_interface *intf)
 	struct r8152 *tp = usb_get_intfdata(intf);
 	struct net_device *netdev;
 
+	rtnl_lock();
+
 	if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
 		return 0;
 
@@ -8428,20 +8430,17 @@ static int rtl8152_post_reset(struct usb_interface *intf)
 	struct sockaddr sa;
 
 	if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
-		return 0;
+		goto exit;
 
 	rtl_set_accessible(tp);
 
 	/* reset the MAC address in case of policy change */
-	if (determine_ethernet_addr(tp, &sa) >= 0) {
-		rtnl_lock();
+	if (determine_ethernet_addr(tp, &sa) >= 0)
 		dev_set_mac_address (tp->netdev, &sa, NULL);
-		rtnl_unlock();
-	}
 
 	netdev = tp->netdev;
 	if (!netif_running(netdev))
-		return 0;
+		goto exit;
 
 	set_bit(WORK_ENABLE, &tp->flags);
 	if (netif_carrier_ok(netdev)) {
@@ -8460,6 +8459,8 @@ static int rtl8152_post_reset(struct usb_interface *intf)
 	if (!list_empty(&tp->rx_done))
 		napi_schedule(&tp->napi);
 
+exit:
+	rtnl_unlock();
 	return 0;
 }
 
-- 
2.43.0.rc1.413.gea7ed67945-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops
  2023-11-28 21:38 [PATCH net v2 1/5] r8152: Hold the rtnl_lock for all of reset Douglas Anderson
@ 2023-11-28 21:38 ` Douglas Anderson
  2023-11-29 12:47   ` Hayes Wang
  2023-11-28 21:38 ` [PATCH net v2 3/5] r8152: Add RTL8152_INACCESSIBLE to r8156b_wait_loading_flash() Douglas Anderson
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Douglas Anderson @ 2023-11-28 21:38 UTC (permalink / raw)
  To: Jakub Kicinski, Hayes Wang, David S . Miller
  Cc: Laura Nao, Edward Hill, Alan Stern, Grant Grundler, linux-usb,
	Simon Horman, Douglas Anderson, Bjørn Mork, Eric Dumazet,
	Paolo Abeni, linux-kernel, netdev

Previous commits added checks for RTL8152_INACCESSIBLE in the loops in
the driver. There are still a few more that keep tripping the driver
up in error cases and make things take longer than they should. Add
those in.

All the loops that are part of this commit existed in some form or
another since the r8152 driver was first introduced, though
RTL8152_INACCESSIBLE was known as RTL8152_UNPLUG before commit
715f67f33af4 ("r8152: Rename RTL8152_UNPLUG to RTL8152_INACCESSIBLE")

Fixes: ac718b69301c ("net/usb: new driver for RTL8152")
Reviewed-by: Grant Grundler <grundler@chromium.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---

Changes in v2:
- Added Fixes tag to RTL8152_INACCESSIBLE patches.
- Split RTL8152_INACCESSIBLE patches by the commit the loop came from.

 drivers/net/usb/r8152.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index d6edf0254599..5a9c5b790508 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -3000,6 +3000,8 @@ static void rtl8152_nic_reset(struct r8152 *tp)
 		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
 
 		for (i = 0; i < 1000; i++) {
+			if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+				return;
 			if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
 				break;
 			usleep_range(100, 400);
@@ -3329,6 +3331,8 @@ static void rtl_disable(struct r8152 *tp)
 	rxdy_gated_en(tp, true);
 
 	for (i = 0; i < 1000; i++) {
+		if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+			return;
 		ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
 		if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY)
 			break;
@@ -3336,6 +3340,8 @@ static void rtl_disable(struct r8152 *tp)
 	}
 
 	for (i = 0; i < 1000; i++) {
+		if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+			return;
 		if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY)
 			break;
 		usleep_range(1000, 2000);
@@ -5499,6 +5505,8 @@ static void wait_oob_link_list_ready(struct r8152 *tp)
 	int i;
 
 	for (i = 0; i < 1000; i++) {
+		if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+			return;
 		ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
 		if (ocp_data & LINK_LIST_READY)
 			break;
-- 
2.43.0.rc1.413.gea7ed67945-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH net v2 3/5] r8152: Add RTL8152_INACCESSIBLE to r8156b_wait_loading_flash()
  2023-11-28 21:38 [PATCH net v2 1/5] r8152: Hold the rtnl_lock for all of reset Douglas Anderson
  2023-11-28 21:38 ` [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops Douglas Anderson
@ 2023-11-28 21:38 ` Douglas Anderson
  2023-11-28 21:38 ` [PATCH net v2 4/5] r8152: Add RTL8152_INACCESSIBLE to r8153_pre_firmware_1() Douglas Anderson
  2023-11-28 21:38 ` [PATCH net v2 5/5] r8152: Add RTL8152_INACCESSIBLE to r8153_aldps_en() Douglas Anderson
  3 siblings, 0 replies; 7+ messages in thread
From: Douglas Anderson @ 2023-11-28 21:38 UTC (permalink / raw)
  To: Jakub Kicinski, Hayes Wang, David S . Miller
  Cc: Laura Nao, Edward Hill, Alan Stern, Grant Grundler, linux-usb,
	Simon Horman, Douglas Anderson, Bjørn Mork, Eric Dumazet,
	Paolo Abeni, linux-kernel, netdev

Delay loops in r8152 should break out if RTL8152_INACCESSIBLE is set
so that they don't delay too long if the device becomes
inaccessible. Add the break to the loop in
r8156b_wait_loading_flash().

Fixes: 195aae321c82 ("r8152: support new chips")
Reviewed-by: Grant Grundler <grundler@chromium.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---

Changes in v2:
- Added Fixes tag to RTL8152_INACCESSIBLE patches.
- Split RTL8152_INACCESSIBLE patches by the commit the loop came from.

 drivers/net/usb/r8152.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 5a9c5b790508..b7bbebf09d85 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -5521,6 +5521,8 @@ static void r8156b_wait_loading_flash(struct r8152 *tp)
 		int i;
 
 		for (i = 0; i < 100; i++) {
+			if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+				return;
 			if (ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL) & GPHY_PATCH_DONE)
 				break;
 			usleep_range(1000, 2000);
-- 
2.43.0.rc1.413.gea7ed67945-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH net v2 4/5] r8152: Add RTL8152_INACCESSIBLE to r8153_pre_firmware_1()
  2023-11-28 21:38 [PATCH net v2 1/5] r8152: Hold the rtnl_lock for all of reset Douglas Anderson
  2023-11-28 21:38 ` [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops Douglas Anderson
  2023-11-28 21:38 ` [PATCH net v2 3/5] r8152: Add RTL8152_INACCESSIBLE to r8156b_wait_loading_flash() Douglas Anderson
@ 2023-11-28 21:38 ` Douglas Anderson
  2023-11-28 21:38 ` [PATCH net v2 5/5] r8152: Add RTL8152_INACCESSIBLE to r8153_aldps_en() Douglas Anderson
  3 siblings, 0 replies; 7+ messages in thread
From: Douglas Anderson @ 2023-11-28 21:38 UTC (permalink / raw)
  To: Jakub Kicinski, Hayes Wang, David S . Miller
  Cc: Laura Nao, Edward Hill, Alan Stern, Grant Grundler, linux-usb,
	Simon Horman, Douglas Anderson, Bjørn Mork, Eric Dumazet,
	Paolo Abeni, Prashant Malani, linux-kernel, netdev

Delay loops in r8152 should break out if RTL8152_INACCESSIBLE is set
so that they don't delay too long if the device becomes
inaccessible. Add the break to the loop in r8153_pre_firmware_1().

Fixes: 9370f2d05a2a ("r8152: support request_firmware for RTL8153")
Reviewed-by: Grant Grundler <grundler@chromium.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---

Changes in v2:
- Added Fixes tag to RTL8152_INACCESSIBLE patches.
- Split RTL8152_INACCESSIBLE patches by the commit the loop came from.

 drivers/net/usb/r8152.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index b7bbebf09d85..26db3f6b3aa1 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -5645,6 +5645,8 @@ static int r8153_pre_firmware_1(struct r8152 *tp)
 	for (i = 0; i < 104; i++) {
 		u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_WDT1_CTRL);
 
+		if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+			return -ENODEV;
 		if (!(ocp_data & WTD1_EN))
 			break;
 		usleep_range(1000, 2000);
-- 
2.43.0.rc1.413.gea7ed67945-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH net v2 5/5] r8152: Add RTL8152_INACCESSIBLE to r8153_aldps_en()
  2023-11-28 21:38 [PATCH net v2 1/5] r8152: Hold the rtnl_lock for all of reset Douglas Anderson
                   ` (2 preceding siblings ...)
  2023-11-28 21:38 ` [PATCH net v2 4/5] r8152: Add RTL8152_INACCESSIBLE to r8153_pre_firmware_1() Douglas Anderson
@ 2023-11-28 21:38 ` Douglas Anderson
  3 siblings, 0 replies; 7+ messages in thread
From: Douglas Anderson @ 2023-11-28 21:38 UTC (permalink / raw)
  To: Jakub Kicinski, Hayes Wang, David S . Miller
  Cc: Laura Nao, Edward Hill, Alan Stern, Grant Grundler, linux-usb,
	Simon Horman, Douglas Anderson, Bjørn Mork, Eric Dumazet,
	Paolo Abeni, linux-kernel, netdev

Delay loops in r8152 should break out if RTL8152_INACCESSIBLE is set
so that they don't delay too long if the device becomes
inaccessible. Add the break to the loop in r8153_aldps_en().

Fixes: 4214cc550bf9 ("r8152: check if disabling ALDPS is finished")
Reviewed-by: Grant Grundler <grundler@chromium.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---

Changes in v2:
- Added Fixes tag to RTL8152_INACCESSIBLE patches.
- Split RTL8152_INACCESSIBLE patches by the commit the loop came from.

 drivers/net/usb/r8152.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 26db3f6b3aa1..aca7dd7b4090 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -5803,6 +5803,8 @@ static void r8153_aldps_en(struct r8152 *tp, bool enable)
 		data &= ~EN_ALDPS;
 		ocp_reg_write(tp, OCP_POWER_CFG, data);
 		for (i = 0; i < 20; i++) {
+			if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+				return;
 			usleep_range(1000, 2000);
 			if (ocp_read_word(tp, MCU_TYPE_PLA, 0xe000) & 0x0100)
 				break;
-- 
2.43.0.rc1.413.gea7ed67945-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* RE: [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops
  2023-11-28 21:38 ` [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops Douglas Anderson
@ 2023-11-29 12:47   ` Hayes Wang
  2023-11-29 21:26     ` Doug Anderson
  0 siblings, 1 reply; 7+ messages in thread
From: Hayes Wang @ 2023-11-29 12:47 UTC (permalink / raw)
  To: Douglas Anderson, Jakub Kicinski, David S . Miller
  Cc: Laura Nao, Edward Hill, Alan Stern, Grant Grundler, linux-usb,
	Simon Horman, Bjørn Mork, Eric Dumazet, Paolo Abeni,
	linux-kernel, netdev

Douglas Anderson <dianders@chromium.org>
> Sent: Wednesday, November 29, 2023 5:38 AM
[...]
> @@ -3000,6 +3000,8 @@ static void rtl8152_nic_reset(struct r8152 *tp)
>                 ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
> 
>                 for (i = 0; i < 1000; i++) {
> +                       if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
> +                               return;
>                         if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
>                                 break;
>                         usleep_range(100, 400);
> @@ -3329,6 +3331,8 @@ static void rtl_disable(struct r8152 *tp)
>         rxdy_gated_en(tp, true);
> 
>         for (i = 0; i < 1000; i++) {
> +               if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
> +                       return;

I think you have to replace return with break.
Otherwise, rtl_stop_rx() wouldn't be called.
And, you may free the memory which is using, when rtl8152_close () is called.

>                 ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
>                 if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY)
>                         break;
> @@ -3336,6 +3340,8 @@ static void rtl_disable(struct r8152 *tp)
>         }
> 
>         for (i = 0; i < 1000; i++) {
> +               if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
> +                       return;

Same as above.

>                 if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY)
>                         break;
>                 usleep_range(1000, 2000);
> @@ -5499,6 +5505,8 @@ static void wait_oob_link_list_ready(struct r8152 *tp)
>         int i;
> 
>         for (i = 0; i < 1000; i++) {
> +               if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
> +                       return;
>                 ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
>                 if (ocp_data & LINK_LIST_READY)
>                         break;
> --

Best Regards,
Hayes


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops
  2023-11-29 12:47   ` Hayes Wang
@ 2023-11-29 21:26     ` Doug Anderson
  0 siblings, 0 replies; 7+ messages in thread
From: Doug Anderson @ 2023-11-29 21:26 UTC (permalink / raw)
  To: Hayes Wang
  Cc: Jakub Kicinski, David S . Miller, Laura Nao, Edward Hill,
	Alan Stern, Grant Grundler, linux-usb, Simon Horman,
	Bjørn Mork, Eric Dumazet, Paolo Abeni, linux-kernel, netdev

Hi,

On Wed, Nov 29, 2023 at 4:47 AM Hayes Wang <hayeswang@realtek.com> wrote:
>
> Douglas Anderson <dianders@chromium.org>
> > Sent: Wednesday, November 29, 2023 5:38 AM
> [...]
> > @@ -3000,6 +3000,8 @@ static void rtl8152_nic_reset(struct r8152 *tp)
> >                 ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
> >
> >                 for (i = 0; i < 1000; i++) {
> > +                       if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
> > +                               return;
> >                         if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
> >                                 break;
> >                         usleep_range(100, 400);
> > @@ -3329,6 +3331,8 @@ static void rtl_disable(struct r8152 *tp)
> >         rxdy_gated_en(tp, true);
> >
> >         for (i = 0; i < 1000; i++) {
> > +               if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
> > +                       return;
>
> I think you have to replace return with break.
> Otherwise, rtl_stop_rx() wouldn't be called.
> And, you may free the memory which is using, when rtl8152_close () is called.
>
> >                 ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
> >                 if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY)
> >                         break;
> > @@ -3336,6 +3340,8 @@ static void rtl_disable(struct r8152 *tp)
> >         }
> >
> >         for (i = 0; i < 1000; i++) {
> > +               if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
> > +                       return;
>
> Same as above.

Good catch, thanks! I've changed all of the `return` statements in
this patch to `break` for consistency, though for the other ones it
doesn't really matter. For patch #3 I also changed the `return` to a
`break`, but for patches #4 and #5 the return was better so I left
those.

v3 posted now with this fix.

-Doug

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-11-29 21:27 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-28 21:38 [PATCH net v2 1/5] r8152: Hold the rtnl_lock for all of reset Douglas Anderson
2023-11-28 21:38 ` [PATCH net v2 2/5] r8152: Add RTL8152_INACCESSIBLE checks to more loops Douglas Anderson
2023-11-29 12:47   ` Hayes Wang
2023-11-29 21:26     ` Doug Anderson
2023-11-28 21:38 ` [PATCH net v2 3/5] r8152: Add RTL8152_INACCESSIBLE to r8156b_wait_loading_flash() Douglas Anderson
2023-11-28 21:38 ` [PATCH net v2 4/5] r8152: Add RTL8152_INACCESSIBLE to r8153_pre_firmware_1() Douglas Anderson
2023-11-28 21:38 ` [PATCH net v2 5/5] r8152: Add RTL8152_INACCESSIBLE to r8153_aldps_en() Douglas Anderson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.