esp32s3/wifi: Fix bug related to IOB off-loading with SMP

For now, IOB off-loading in the wireless driver was removed because
it is not compatible with SMP-enabled devices, which is valid for
ESP32-S3. The performance gain by keeping the IOB off-loading in
the wireless drivers is not exceeded by keeping the flat buffer
approach and enabling `CONFIG_SMP=y`.
This commit is contained in:
Tiago Medicci Serrano 2024-06-18 17:05:51 -03:00 committed by Xiang Xiao
parent e913828955
commit f594859d99
2 changed files with 351 additions and 90 deletions

View file

@ -1544,6 +1544,10 @@ config ESP32S3_WIFI_RXBA_AMPDU_WZ
int "Wi-Fi RX BA AMPDU windown size"
default 6
config ESP32S3_WLAN_PKTBUF_NUM
int "WLAN netcard packet buffer number per netcard"
default 16
config ESP_WIFI_GCMP_SUPPORT
bool "WiFi GCMP Support(GCMP128 and GCMP256)"
default n
@ -2398,7 +2402,7 @@ config ESP32S3_KERNEL_IMAGE_SIZE
config ESP32S3_KERNEL_RAM_SIZE
hex "Kernel Allocated RAM"
default 0x29000
default 0x30000
range 0x10000 0x54700
depends on ESP32S3_APP_FORMAT_LEGACY
---help---

View file

@ -75,14 +75,39 @@
* Total size : 1514
*/
#define WLAN_BUF_SIZE (CONFIG_NET_ETH_PKTSIZE + \
CONFIG_NET_LL_GUARDSIZE + \
CONFIG_NET_GUARDSIZE)
#define WLAN_BUF_SIZE (CONFIG_NET_ETH_PKTSIZE)
/* WLAN packet buffer number */
#define WLAN_PKTBUF_NUM (CONFIG_ESP32S3_WLAN_PKTBUF_NUM)
/* Receive threshold which allows the receive function to trigger a scheduler
* to activate the application if possible.
*/
#ifdef CONFIG_MM_IOB
# define IOBBUF_SIZE (CONFIG_IOB_NBUFFERS * CONFIG_IOB_BUFSIZE)
# if (IOBBUF_SIZE) > (WLAN_BUF_SIZE + 1)
# define WLAN_RX_THRESHOLD (IOBBUF_SIZE - WLAN_BUF_SIZE + 1)
# endif
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* WLAN packet buffer */
struct wlan_pktbuf
{
sq_entry_t entry; /* Queue entry */
/* Packet data buffer */
uint8_t buffer[WLAN_BUF_SIZE];
uint16_t len; /* Packet data length */
};
/* WLAN operations */
struct wlan_ops
@ -129,17 +154,23 @@ struct wlan_priv_s
struct net_driver_s dev;
/* Packet buffer cache */
struct wlan_pktbuf pktbuf[WLAN_PKTBUF_NUM];
/* RX packet queue */
struct iob_queue_s rxb;
sq_queue_t rxb;
/* TX ready packet queue */
struct iob_queue_s txb;
sq_queue_t txb;
/* Flat buffer swap */
/* Free packet buffer queue */
uint8_t flatbuf[WLAN_BUF_SIZE];
sq_queue_t freeb;
/* Device specific lock */
spinlock_t lock;
};
@ -204,8 +235,10 @@ static const struct wlan_ops g_softap_ops =
* Private Function Prototypes
****************************************************************************/
/* Common TX logic */
/* Common TX/RX logic */
static struct wlan_pktbuf *wlan_recvframe(struct wlan_priv_s *priv);
static struct wlan_pktbuf *wlan_txframe(struct wlan_priv_s *priv);
static void wlan_transmit(struct wlan_priv_s *priv);
static void wlan_rxpoll(void *arg);
static int wlan_txpoll(struct net_driver_s *dev);
@ -251,6 +284,102 @@ static int wlan_ioctl(struct net_driver_s *dev, int cmd,
* mutex/semaphore instead of disable interrupt, if necessary.
*/
/****************************************************************************
* Function: wlan_init_buffer
*
* Description:
* Initialize the free buffer list
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
****************************************************************************/
static inline void wlan_init_buffer(struct wlan_priv_s *priv)
{
irqstate_t flags;
int i;
flags = spin_lock_irqsave(&priv->lock);
priv->dev.d_buf = NULL;
priv->dev.d_len = 0;
sq_init(&priv->freeb);
sq_init(&priv->rxb);
sq_init(&priv->txb);
for (i = 0; i < WLAN_PKTBUF_NUM; i++)
{
sq_addlast(&priv->pktbuf[i].entry, &priv->freeb);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************
* Function: wlan_alloc_buffer
*
* Description:
* Allocate one buffer from the free buffer queue
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* Pointer to the allocated buffer on success; NULL on failure
*
****************************************************************************/
static inline struct wlan_pktbuf *wlan_alloc_buffer(struct wlan_priv_s *priv)
{
struct wlan_pktbuf *pktbuf = NULL;
sq_entry_t *entry;
irqstate_t flags = spin_lock_irqsave(&priv->lock);
entry = sq_remfirst(&priv->freeb);
if (entry)
{
pktbuf = container_of(entry, struct wlan_pktbuf, entry);
}
spin_unlock_irqrestore(&priv->lock, flags);
return pktbuf;
}
/****************************************************************************
* Function: wlan_free_buffer
*
* Description:
* Insert a free Rx buffer into the free queue
*
* Input Parameters:
* priv - Reference to the driver state structure
* buffer - A pointer to the packet buffer to be freed
*
* Returned Value:
* None
*
****************************************************************************/
static inline void wlan_free_buffer(struct wlan_priv_s *priv,
uint8_t *buffer)
{
struct wlan_pktbuf *pktbuf;
irqstate_t flags = spin_lock_irqsave(&priv->lock);
pktbuf = container_of(buffer, struct wlan_pktbuf, buffer);
sq_addlast(&pktbuf->entry, &priv->freeb);
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************
* Function: wlan_cache_txpkt_tail
*
@ -267,12 +396,43 @@ static int wlan_ioctl(struct net_driver_s *dev, int cmd,
static inline void wlan_cache_txpkt_tail(struct wlan_priv_s *priv)
{
if (priv->dev.d_iob)
{
iob_tryadd_queue(priv->dev.d_iob, &priv->txb);
}
struct wlan_pktbuf *pktbuf;
irqstate_t flags;
struct net_driver_s *dev = &priv->dev;
netdev_iob_clear(&priv->dev);
pktbuf = container_of(dev->d_buf, struct wlan_pktbuf, buffer);
pktbuf->len = dev->d_len;
flags = spin_lock_irqsave(&priv->lock);
sq_addlast(&pktbuf->entry, &priv->txb);
spin_unlock_irqrestore(&priv->lock, flags);
dev->d_buf = NULL;
dev->d_len = 0;
}
/****************************************************************************
* Function: wlan_add_txpkt_head
*
* Description:
* Add packet into head of TX ready queue.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
****************************************************************************/
static inline void wlan_add_txpkt_head(struct wlan_priv_s *priv,
struct wlan_pktbuf *pktbuf)
{
irqstate_t flags;
flags = spin_lock_irqsave(&priv->lock);
sq_addfirst(&pktbuf->entry, &priv->txb);
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************
@ -289,13 +449,56 @@ static inline void wlan_cache_txpkt_tail(struct wlan_priv_s *priv)
*
****************************************************************************/
static struct iob_s *wlan_recvframe(struct wlan_priv_s *priv)
static struct wlan_pktbuf *wlan_recvframe(struct wlan_priv_s *priv)
{
struct iob_s *iob;
irqstate_t flags;
sq_entry_t *entry;
struct wlan_pktbuf *pktbuf = NULL;
iob = iob_remove_queue(&priv->rxb);
flags = spin_lock_irqsave(&priv->lock);
return iob;
entry = sq_remfirst(&priv->rxb);
if (entry)
{
pktbuf = container_of(entry, struct wlan_pktbuf, entry);
}
spin_unlock_irqrestore(&priv->lock, flags);
return pktbuf;
}
/****************************************************************************
* Function: wlan_txframe
*
* Description:
* Try to receive TX buffer from TX ready buffer queue.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* TX packets buffer if success or NULL if no packet in queue.
*
****************************************************************************/
static struct wlan_pktbuf *wlan_txframe(struct wlan_priv_s *priv)
{
irqstate_t flags;
sq_entry_t *entry;
struct wlan_pktbuf *pktbuf = NULL;
flags = spin_lock_irqsave(&priv->lock);
entry = sq_remfirst(&priv->txb);
if (entry)
{
pktbuf = container_of(entry, struct wlan_pktbuf, entry);
}
spin_unlock_irqrestore(&priv->lock, flags);
return pktbuf;
}
/****************************************************************************
@ -315,19 +518,15 @@ static struct iob_s *wlan_recvframe(struct wlan_priv_s *priv)
static void wlan_transmit(struct wlan_priv_s *priv)
{
uint16_t llhdrlen = NET_LL_HDRLEN(&priv->dev);
unsigned int offset = CONFIG_NET_LL_GUARDSIZE - llhdrlen;
struct iob_s *iob;
struct wlan_pktbuf *pktbuf;
int ret;
while ((iob = iob_peek_queue(&priv->txb)) != NULL)
while ((pktbuf = wlan_txframe(priv)))
{
iob_copyout(priv->flatbuf + llhdrlen, iob, iob->io_pktlen, 0);
memcpy(priv->flatbuf, iob->io_data + offset, llhdrlen);
ret = priv->ops->send(priv->flatbuf, iob->io_pktlen + llhdrlen);
ret = priv->ops->send(pktbuf->buffer, pktbuf->len);
if (ret == -ENOMEM)
{
wlan_add_txpkt_head(priv, pktbuf);
wd_start(&priv->txtimeout, WLAN_TXTOUT,
wlan_txtimeout_expiry, (uint32_t)priv);
break;
@ -339,11 +538,7 @@ static void wlan_transmit(struct wlan_priv_s *priv)
nwarn("WARN: Failed to send pkt, ret: %d\n", ret);
}
iob_remove_queue(&priv->txb);
/* And free the I/O buffer chain */
iob_free_chain(iob);
wlan_free_buffer(priv, pktbuf->buffer);
}
}
}
@ -391,8 +586,7 @@ static void wlan_tx_done(struct wlan_priv_s *priv)
static int wlan_rx_done(struct wlan_priv_s *priv, void *buffer,
uint16_t len, void *eb)
{
struct net_driver_s *dev = &priv->dev;
struct iob_s *iob = NULL;
struct wlan_pktbuf *pktbuf;
irqstate_t flags;
int ret = 0;
@ -409,56 +603,37 @@ static int wlan_rx_done(struct wlan_priv_s *priv, void *buffer,
goto out;
}
if (len > iob_navail(false) * CONFIG_IOB_BUFSIZE)
pktbuf = wlan_alloc_buffer(priv);
if (!pktbuf)
{
ret = -ENOBUFS;
goto out;
}
iob = iob_tryalloc(false);
if (iob == NULL)
{
ret = -ENOBUFS;
goto out;
}
memcpy(pktbuf->buffer, buffer, len);
pktbuf->len = len;
iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE - NET_LL_HDRLEN(dev));
ret = iob_trycopyin(iob, buffer, len, 0, false);
if (ret != len)
{
ret = -ENOBUFS;
goto out;
}
flags = spin_lock_irqsave(&priv->lock);
ret = iob_tryadd_queue(iob, &priv->rxb);
spin_unlock_irqrestore(&priv->lock, flags);
if (ret < 0)
{
ret = -ENOBUFS;
goto out;
}
out:
if (eb != NULL)
if (eb)
{
esp_wifi_free_eb(eb);
}
if (ret != OK && iob != NULL)
{
iob_free_chain(iob);
}
flags = spin_lock_irqsave(&priv->lock);
sq_addlast(&pktbuf->entry, &priv->rxb);
spin_unlock_irqrestore(&priv->lock, flags);
if (work_available(&priv->rxwork))
{
work_queue(WLAN_WORK, &priv->rxwork, wlan_rxpoll, priv, 0);
}
wlan_txavail(&priv->dev);
return 0;
out:
if (eb)
{
esp_wifi_free_eb(eb);
}
return ret;
}
@ -480,27 +655,32 @@ out:
static void wlan_rxpoll(void *arg)
{
struct wlan_pktbuf *pktbuf;
struct eth_hdr_s *eth_hdr;
struct wlan_priv_s *priv = (struct wlan_priv_s *)arg;
struct net_driver_s *dev = &priv->dev;
struct eth_hdr_s *eth_hdr;
struct iob_s *iob;
#ifdef WLAN_RX_THRESHOLD
uint32_t rbytes = 0;
#endif
/* Try to send all cached TX packets for TX ack and so on */
wlan_transmit(priv);
/* Loop while while iob_remove_queue() successfully retrieves valid
/* Loop while while wlan_recvframe() successfully retrieves valid
* Ethernet frames.
*/
net_lock();
while ((iob = wlan_recvframe(priv)) != NULL)
while ((pktbuf = wlan_recvframe(priv)) != NULL)
{
dev->d_iob = iob;
dev->d_len = iob->io_pktlen;
dev->d_buf = pktbuf->buffer;
dev->d_len = pktbuf->len;
iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE);
#ifdef WLAN_RX_THRESHOLD
rbytes += pktbuf->len;
#endif
#ifdef CONFIG_NET_PKT
@ -511,9 +691,27 @@ static void wlan_rxpoll(void *arg)
pkt_input(&priv->dev);
#endif
eth_hdr = (struct eth_hdr_s *)
&dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE -
NET_LL_HDRLEN(dev)];
/* Check if the packet is a valid size for the network
* buffer configuration (this should not happen)
*/
if (dev->d_len > WLAN_BUF_SIZE)
{
nwarn("WARNING: DROPPED Too big: %d\n", dev->d_len);
/* Free dropped packet buffer */
if (dev->d_buf)
{
wlan_free_buffer(priv, dev->d_buf);
dev->d_buf = NULL;
dev->d_len = 0;
}
continue;
}
eth_hdr = (struct eth_hdr_s *)dev->d_buf;
/* We only accept IP packets of the configured type and ARP packets */
@ -588,7 +786,33 @@ static void wlan_rxpoll(void *arg)
ninfo("INFO: Dropped, Unknown type: %04x\n", eth_hdr->type);
}
netdev_iob_release(&priv->dev);
/* We are finished with the RX buffer. NOTE: If the buffer is
* re-used for transmission, the dev->d_buf field will have been
* nullified.
*/
if (dev->d_buf)
{
/* Free the receive packet buffer */
wlan_free_buffer(priv, dev->d_buf);
dev->d_buf = NULL;
dev->d_len = 0;
}
#ifdef WLAN_RX_THRESHOLD
/* If received total bytes is larger than receive threshold,
* then do "unlock" to try to active applicantion to receive
* data from low-level buffer of IP stack.
*/
if (rbytes >= WLAN_RX_THRESHOLD)
{
net_unlock();
rbytes = 0;
net_lock();
}
#endif
}
/* Try to send all cached TX packets */
@ -620,10 +844,25 @@ static void wlan_rxpoll(void *arg)
static int wlan_txpoll(struct net_driver_s *dev)
{
struct wlan_priv_s *priv = dev->d_private;
struct wlan_pktbuf *pktbuf;
struct wlan_priv_s *priv = (struct wlan_priv_s *)dev->d_private;
DEBUGASSERT(dev->d_buf != NULL);
wlan_cache_txpkt_tail(priv);
wlan_transmit(priv);
pktbuf = wlan_alloc_buffer(priv);
if (!pktbuf)
{
return -ENOMEM;
}
dev->d_buf = pktbuf->buffer;
dev->d_len = WLAN_BUF_SIZE;
/* If zero is returned, the polling will continue until
* all connections have been examined.
*/
return OK;
}
@ -650,10 +889,36 @@ static int wlan_txpoll(struct net_driver_s *dev)
static void wlan_dopoll(struct wlan_priv_s *priv)
{
struct net_driver_s *dev = &priv->dev;
struct wlan_pktbuf *pktbuf;
uint8_t *txbuf;
int ret;
pktbuf = wlan_alloc_buffer(priv);
if (!pktbuf)
{
return ;
}
dev->d_buf = pktbuf->buffer;
dev->d_len = WLAN_BUF_SIZE;
/* Try to let TCP/IP to send all packets to netcard driver */
while (devif_poll(dev, wlan_txpoll));
do
{
txbuf = dev->d_buf;
ret = devif_poll(dev, wlan_txpoll);
}
while ((ret == 0) &&
(dev->d_buf != txbuf));
if (dev->d_buf)
{
wlan_free_buffer(priv, dev->d_buf);
dev->d_buf = NULL;
dev->d_len = 0;
}
/* Try to send all cached TX packets */
@ -807,6 +1072,7 @@ static int wlan_ifup(struct net_driver_s *dev)
return OK;
}
wlan_init_buffer(priv);
ret = priv->ops->start();
if (ret < 0)
{
@ -815,12 +1081,6 @@ static int wlan_ifup(struct net_driver_s *dev)
return ret;
}
IOB_QINIT(&priv->rxb);
IOB_QINIT(&priv->txb);
priv->dev.d_buf = NULL;
priv->dev.d_len = 0;
priv->ifup = true;
if (g_callback_register_ref == 0)
{
@ -872,9 +1132,6 @@ static int wlan_ifdown(struct net_driver_s *dev)
priv->ifup = false;
iob_free_queue(&priv->rxb);
iob_free_queue(&priv->txb);
ret = priv->ops->stop();
if (ret < 0)
{