Skip to content

Commit

Permalink
cpu/sam0: improve ethernet driver resilience
Browse files Browse the repository at this point in the history
In case of network heavy traffic on the Ethernet, interrupts
fire faster than the netdev thread can process them and we
run out of buffers. With this commit, we now check if we
don't have buffers available, so we can flush everything and
restart reception properly even if we did drop a few in the
operation
  • Loading branch information
dylad committed Jul 27, 2021
1 parent 225c127 commit 950c71c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 14 deletions.
60 changes: 52 additions & 8 deletions cpu/sam0_common/periph/eth.c
Expand Up @@ -90,6 +90,19 @@ static uint8_t rx_idx;

static uint8_t rx_buf[ETH_RX_BUFFER_COUNT][ETH_RX_BUFFER_SIZE] __attribute__((aligned(GMAC_BUF_ALIGNMENT)));
static uint8_t tx_buf[ETH_TX_BUFFER_COUNT][ETH_TX_BUFFER_SIZE] __attribute__((aligned(GMAC_BUF_ALIGNMENT)));
extern sam0_eth_netdev_t _sam0_eth_dev;

/* Flush our reception buffers and reset reception internal mechanism,
this function may be call from ISR context */
void sam0_clear_rx_buffers(void)
{
for (int i=0; i<ETH_RX_BUFFER_COUNT; i++) {
rx_desc[i].address &= ~DESC_RX_ADDR_OWNSHP;
}
rx_idx = 0;
rx_curr = rx_desc;
GMAC->RBQB.reg = (uint32_t) rx_desc;
}

static void _init_desc_buf(void)
{
Expand Down Expand Up @@ -193,13 +206,33 @@ int sam0_eth_send(const struct iolist *iolist)
return len;
}

static int _try_receive(char* data, int max_len, int block)
static int _try_receive(char* data, unsigned max_len, int block)
{
(void)block;
unsigned rxlen = 0;
uint16_t idx = rx_idx;
/* Ensure we are at the beginning of the new frame */
while (!(rx_curr->address & DESC_RX_ADDR_OWNSHP) && (rx_curr->status & DESC_RX_STATUS_STA_FRAME)) {}
uint8_t tmp = ETH_RX_BUFFER_COUNT;

/* Check if the current rx descriptor contains the beginning of
a new frame, iterates over all our RX buffers if not.
If there is no new frame (because we may have flush our RX
buffers due to BNA interrupt), return an error to netdev so
we can move forward */
do {
if ((rx_curr->address & DESC_RX_ADDR_OWNSHP)
&& (rx_curr->status & DESC_RX_STATUS_STA_FRAME)) {
break;
}
else {
idx = (idx+1) % ETH_RX_BUFFER_COUNT;
rx_curr = &rx_desc[idx];
tmp--;
}
} while (tmp > 0);

if (tmp == 0) {
return -ENOBUFS;
}

for (unsigned cpt=0; cpt < ETH_RX_BUFFER_COUNT; cpt++) {
/* Get the length of the received frame */
Expand All @@ -210,6 +243,11 @@ static int _try_receive(char* data, int max_len, int block)
if (max_len) {
/* If buffer available, copy data into it */
if (data) {
/* If provided buffer is smaller than the received frame,
drop it as netdev request */
if (rxlen + len > max_len) {
return -ENOBUFS;
}
memcpy(&data[rxlen], rx_buf[idx], len);
}
/* Tell the GMAC IP that we don't need this frame anymore */
Expand All @@ -226,16 +264,22 @@ static int _try_receive(char* data, int max_len, int block)
idx = (idx + 1) % ETH_RX_BUFFER_COUNT;
rx_curr = &rx_desc[idx];

}
/* restore the previous index if packets were not released */
if (!max_len) {
}
/* restore the previous index if packets were not released */
if (!max_len) {
rx_curr = &rx_desc[rx_idx];
}
}
/* Point to the next buffer as GMAC IP will likely used it
to store the next frame */
else {
else {
rx_idx = (idx+1) % ETH_RX_BUFFER_COUNT;
rx_curr = &rx_desc[rx_idx];
}

/* If provided buffer is smaller than the received frame,
drop it as netdev request */
if (data != NULL && rxlen > max_len) {
return -ENOBUFS;
}

return rxlen;
Expand Down
18 changes: 12 additions & 6 deletions cpu/sam0_common/sam0_eth/eth-netdev.c
Expand Up @@ -40,6 +40,7 @@ extern int sam0_eth_send(const struct iolist *iolist);
extern int sam0_eth_receive_blocking(char *data, unsigned max_len);
extern void sam0_eth_set_mac(const eui48_t *mac);
extern void sam0_eth_get_mac(char *out);
extern void sam0_clear_rx_buffers(void);

/* SAM0 CPUs only have one GMAC IP, so it is safe to
statically defines one in this file */
Expand Down Expand Up @@ -140,24 +141,29 @@ void sam0_eth_setup(netdev_t* netdev)
netdev_register(netdev, NETDEV_SAM0_ETH, 0);
}

/* TODO: rework the whole isr management... */
void isr_gmac(void)
{
uint32_t isr;
uint32_t tsr;
uint32_t rsr;

isr = GMAC->ISR.reg;
tsr = GMAC->TSR.reg;
rsr = GMAC->RSR.reg;
(void)isr;

/* New frame received, signal it to netdev */
if (rsr & GMAC_RSR_REC) {
netdev_trigger_event_isr(_sam0_eth_dev.netdev);
}

GMAC->TSR.reg = tsr;
/* Buffers Not Available, this can occur if there is a heavy traffic
on the network. In this case, disable the GMAC reception, flush
our internal buffers and re-enable the reception. This will drop
a few packets but it allows the GMAC IP to remains functional */
if (rsr & GMAC_RSR_BNA) {
GMAC->NCR.reg &= ~GMAC_NCR_RXEN;
sam0_clear_rx_buffers();
GMAC->NCR.reg |= GMAC_NCR_RXEN;
}
GMAC->RSR.reg = rsr;
GMAC->ISR.reg = isr;

cortexm_isr_end();
}

0 comments on commit 950c71c

Please sign in to comment.