From c02d05d48db9dcb5601811703bfd8e563f94beac Mon Sep 17 00:00:00 2001 From: Eero Nurkkala Date: Tue, 28 Nov 2023 13:18:32 +0200 Subject: [PATCH] risc-v/mpfs: usb: fix fierce cpu polling if remote closes If the remote end just closes an endpoint and no longer handles it, the system is prone to intensive cpu polling via mpfs_write_tx_fifo() especially if the device side doesn't know a thing about what the remote did. Fix this by marking the EP as dead, which will skip all writes causing unnecessary polling. The EP is back in business if the remote end sends some data (rx) or the connection is re-established. Signed-off-by: Eero Nurkkala --- arch/risc-v/src/mpfs/hardware/mpfs_usb.h | 1 + arch/risc-v/src/mpfs/mpfs_usb.c | 68 +++++++++++++++++++++--- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/arch/risc-v/src/mpfs/hardware/mpfs_usb.h b/arch/risc-v/src/mpfs/hardware/mpfs_usb.h index 863044d8ddd9a..25264faf0e869 100644 --- a/arch/risc-v/src/mpfs/hardware/mpfs_usb.h +++ b/arch/risc-v/src/mpfs/hardware/mpfs_usb.h @@ -419,6 +419,7 @@ struct mpfs_ep_s struct mpfs_rqhead_s reqq; /* Read/write request queue */ struct mpfs_rqhead_s pendq; /* Write requests pending stall sent */ struct usbdev_epdesc_s *descb[2]; /* Pointers to this endpoint descriptors */ + uint32_t linkdead; /* Remote end has closed the connection */ volatile uint8_t epstate; /* State of the endpoint (see enum mpfs_epstate_e) */ uint8_t stalled:1; /* true: Endpoint is stalled */ uint8_t pending:1; /* true: IN Endpoint stall is pending */ diff --git a/arch/risc-v/src/mpfs/mpfs_usb.c b/arch/risc-v/src/mpfs/mpfs_usb.c index 8f0e11bc20e1c..cfb8d6264951d 100644 --- a/arch/risc-v/src/mpfs/mpfs_usb.c +++ b/arch/risc-v/src/mpfs/mpfs_usb.c @@ -133,6 +133,8 @@ #define MPFS_MIN_EP_FIFO_SIZE 8 #define MPFS_USB_REG_MAX 0x2000 +#define LINKDEAD_THRESHOLD 20 + /* Request queue operations *************************************************/ #define mpfs_rqempty(q) ((q)->head == NULL) @@ -229,6 +231,7 @@ static void mpfs_epset_reset(struct mpfs_usbdev_s *priv, uint16_t epset); static struct mpfs_usbdev_s g_usbd; static uint8_t g_clkrefs; +static bool g_linkdead; static const struct usbdev_epops_s g_epops = { @@ -772,11 +775,26 @@ static int mpfs_req_wrsetup(struct mpfs_usbdev_s *priv, if (nbytes > packetsize) { - ret = mpfs_write_tx_fifo(buf, packetsize, epno); - if (ret != OK) + if (privep->linkdead < LINKDEAD_THRESHOLD) { - privep->epstate = USB_EPSTATE_IDLE; - return ret; + ret = mpfs_write_tx_fifo(buf, packetsize, epno); + if (ret != OK) + { + privep->linkdead++; + privep->epstate = USB_EPSTATE_IDLE; + return ret; + } + else + { + privep->linkdead = 0; + } + } + else + { + /* We're in trouble, remote has likely closed */ + + g_linkdead = true; + return -EIO; } if (epno == EP0) @@ -797,11 +815,26 @@ static int mpfs_req_wrsetup(struct mpfs_usbdev_s *priv, } else { - ret = mpfs_write_tx_fifo(buf, nbytes, epno); - if (ret != OK) + if (privep->linkdead < LINKDEAD_THRESHOLD) { - privep->epstate = USB_EPSTATE_IDLE; - return ret; + ret = mpfs_write_tx_fifo(buf, nbytes, epno); + if (ret != OK) + { + privep->linkdead++; + privep->epstate = USB_EPSTATE_IDLE; + return ret; + } + else + { + privep->linkdead = 0; + } + } + else + { + /* We're in trouble, remote has likely closed */ + + g_linkdead = true; + return -EIO; } } @@ -2444,6 +2477,8 @@ static void mpfs_reset(struct mpfs_usbdev_s *priv) mpfs_req_cancel(privep, -ESHUTDOWN); + privep->linkdead = 0; + /* Reset endpoint status */ privep->stalled = false; @@ -3456,11 +3491,28 @@ static int mpfs_usb_interrupt(int irq, void *context, void *arg) { for (i = 0; i < MPFS_USB_NENDPOINTS; i++) { + /* Check if dead connections are back in business */ + + if (g_linkdead) + { + /* This releases all, which is a problem if only some + * endpoints are closed on the remote; whereas some + * are functioning; for example ACM and mass storage; + * now the functioning one likely marks the closed ones + * as no longer dead. + */ + + struct mpfs_ep_s *privep = &priv->eplist[i]; + privep->linkdead = 0; + } + if ((pending_rx_ep & (1 << i)) != 0) { mpfs_ep_rx_interrupt(priv, i); } } + + g_linkdead = false; } if ((isr & SUSPEND_IRQ_MASK) != 0)