Merge branch 'net-ntb_netdev-fix-tx-completion-and-error-handling'

Koichiro Den says:

====================
net: ntb_netdev: Fix TX completion and error handling

This small series fixes several TX buffer ownership and queue handling
bugs in ntb_netdev and ntb_transport.

Patch 4 first appeared in my "NTB: Add direct TX/RX using PCI endpoint
DMA" series. Sashiko later reported the same pre-existing leak while
reviewing another series, so I moved the fix here. See:
https://lore.kernel.org/r/20260815032932.151F11F000E9@smtp.kernel.org/

The ntb_transport fixes affect buffer ownership and queue handling in
ntb_netdev, the only in-tree ntb_transport_client implementation, so the
patches need to go in together. I am targeting the net tree for the
series.
====================

Link: https://patch.msgid.link/20260817053519.4135287-1-den@valinux.co.jp
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-08-20 13:43:26 -07:00
2 changed files with 37 additions and 28 deletions

View File

@@ -199,8 +199,10 @@ static int __ntb_netdev_maybe_stop_tx(struct net_device *netdev,
static int ntb_netdev_maybe_stop_tx(struct net_device *ndev,
struct ntb_netdev_queue *q, int size)
{
if (__netif_subqueue_stopped(ndev, q->qid) ||
(ntb_transport_tx_free_entry(q->qp) >= size))
if (__netif_subqueue_stopped(ndev, q->qid))
return -EBUSY;
if (ntb_transport_tx_free_entry(q->qp) >= size)
return 0;
return __ntb_netdev_maybe_stop_tx(ndev, q, size);
@@ -256,21 +258,30 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
q = &dev->queues[qid];
ntb_netdev_maybe_stop_tx(ndev, q, tx_stop);
if (unlikely(ntb_netdev_maybe_stop_tx(ndev, q, tx_stop)))
return NETDEV_TX_BUSY;
rc = ntb_transport_tx_enqueue(q->qp, skb, skb->data, skb->len);
if (rc)
goto err;
if (rc) {
if (rc == -EAGAIN || rc == -EBUSY) {
netif_stop_subqueue(ndev, q->qid);
mod_timer(&q->tx_timer,
jiffies + usecs_to_jiffies(tx_time));
return NETDEV_TX_BUSY;
}
goto drop;
}
/* check for next submit */
ntb_netdev_maybe_stop_tx(ndev, q, tx_stop);
return NETDEV_TX_OK;
err:
drop:
dev_kfree_skb_any(skb);
ndev->stats.tx_dropped++;
ndev->stats.tx_errors++;
return NETDEV_TX_BUSY;
return NETDEV_TX_OK;
}
static void ntb_netdev_tx_timer(struct timer_list *t)

View File

@@ -1719,9 +1719,16 @@ static void ntb_transport_rxc_db(unsigned long data)
static void ntb_tx_copy_callback(void *data,
const struct dmaengine_result *res)
{
struct ntb_payload_header __iomem *hdr;
struct ntb_queue_entry *entry = data;
struct ntb_transport_qp *qp = entry->qp;
struct ntb_payload_header __iomem *hdr = entry->tx_hdr;
struct ntb_transport_qp *qp;
unsigned int len;
void *cb_data;
qp = entry->qp;
hdr = entry->tx_hdr;
cb_data = entry->cb_data;
len = entry->len;
/* we need to check DMA results if we are using DMA */
if (res) {
@@ -1768,15 +1775,13 @@ static void ntb_tx_copy_callback(void *data,
* "link down" or similar. Since no payload is being sent in these
* cases, there is nothing to add to the completion queue.
*/
if (entry->len > 0) {
qp->tx_bytes += entry->len;
if (qp->tx_handler)
qp->tx_handler(qp, qp->cb_data, entry->cb_data,
entry->len);
}
if (len > 0)
qp->tx_bytes += len;
ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q);
if (len > 0 && qp->tx_handler)
qp->tx_handler(qp, qp->cb_data, cb_data, len);
}
static void ntb_memcpy_tx_on_stack(struct ntb_queue_entry *entry, void __iomem *offset)
@@ -1950,15 +1955,6 @@ static int ntb_process_tx(struct ntb_transport_qp *qp,
return -EAGAIN;
}
if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) {
if (qp->tx_handler)
qp->tx_handler(qp, qp->cb_data, NULL, -EIO);
ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
&qp->tx_free_q);
return 0;
}
ntb_async_tx(qp, entry);
qp->tx_pkts++;
@@ -2348,9 +2344,11 @@ int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,
if (!qp || !len)
return -EINVAL;
/* If the qp link is down already, just ignore. */
if (!qp->link_is_up)
return 0;
return -ENOLINK;
if (len > qp->tx_max_frame - sizeof(struct ntb_payload_header))
return -EMSGSIZE;
entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
if (!entry) {