Thread (11 messages) 11 messages, 3 authors, 2025-10-22

Re: [PATCH net-next v7 4/5] eea: create/destroy rx,tx queues for netdevice open and stop

From: Paolo Abeni <pabeni@redhat.com>
Date: 2025-10-21 08:28:55

On 10/16/25 1:06 PM, Xuan Zhuo wrote:
+/* resources: ring, buffers, irq */
+int eea_reset_hw_resources(struct eea_net *enet, struct eea_net_tmp *tmp)
+{
+	struct eea_net_tmp _tmp;
+	int err;
+
+	if (!tmp) {
+		enet_init_cfg(enet, &_tmp);
+		tmp = &_tmp;
As suggested on v5, you should:

		enet_init_cfg(enet, &status);
		eea_reset_hw_resources(enet, &status);

in the caller currently using a NULL argument.
+	}
+
+	if (!netif_running(enet->netdev)) {
+		enet->cfg = tmp->cfg;
+		return 0;
+	}
+
+	err = eea_alloc_rxtx_q_mem(tmp);
+	if (err) {
+		netdev_warn(enet->netdev,
+			    "eea reset: alloc q failed. stop reset. err %d\n",
+			    err);
+		return err;
+	}
+
+	eea_netdev_stop(enet->netdev);
+
+	enet_bind_new_q_and_cfg(enet, tmp);
+
+	err = eea_active_ring_and_irq(enet);
+	if (err) {
+		netdev_warn(enet->netdev,
+			    "eea reset: active new ring and irq failed. err %d\n",
+			    err);
+		return err;
+	}
+
+	err = eea_start_rxtx(enet->netdev);
+	if (err)
+		netdev_warn(enet->netdev,
+			    "eea reset: start queue failed. err %d\n", err);
Following-up on v5 discussion, I see this function is used to handle
scenario where the entire setup fails, but it's also used in the next
patch to do set_ring/set_channel operations. The latter should leave the
device in a working state even when the requested change is not
possible, so this function should need gracefully failures at least on
such invocations.

[...]
+/* ha handle code */
+static void eea_ha_handle_work(struct work_struct *work)
+{
+	struct eea_pci_device *ep_dev;
+	struct eea_device *edev;
+	struct pci_dev *pci_dev;
+	u16 reset;
+
+	ep_dev = container_of(work, struct eea_pci_device, ha_handle_work);
+	edev = &ep_dev->edev;
+
+	/* Ha interrupt is triggered, so there maybe some error, we may need to
+	 * reset the device or reset some queues.
+	 */
+	dev_warn(&ep_dev->pci_dev->dev, "recv ha interrupt.\n");
+
+	if (ep_dev->reset_pos) {
+		pci_read_config_word(ep_dev->pci_dev, ep_dev->reset_pos,
+				     &reset);
+		/* clear bit */
+		pci_write_config_word(ep_dev->pci_dev, ep_dev->reset_pos,
+				      0xFFFF);
+
+		if (reset & EEA_PCI_CAP_RESET_FLAG) {
+			dev_warn(&ep_dev->pci_dev->dev,
+				 "recv device reset request.\n");
+
+			pci_dev = ep_dev->pci_dev;
+
+			/* The pci remove callback may hold this lock. If the
+			 * pci remove callback is called, then we can ignore the
+			 * ha interrupt.
+			 */
+			if (mutex_trylock(&edev->ha_lock)) {
+				edev->ha_reset = true;
+
+				__eea_pci_remove(pci_dev, false);
+				__eea_pci_probe(pci_dev, ep_dev);
+
+				edev->ha_reset = false;
+				mutex_unlock(&edev->ha_lock);
+			} else {
+				dev_warn(&ep_dev->pci_dev->dev,
+					 "ha device reset: trylock failed.\n");
+			}
+			return;
Nesting here is quite high, possibly move the above in a separate helper.
quoted hunk ↗ jump to hunk
@@ -45,9 +52,17 @@ u16 eea_pci_dev_id(struct eea_device *edev);
 
 int eea_device_reset(struct eea_device *dev);
 void eea_device_ready(struct eea_device *dev);
+
Minor nit: either do not introduce this whitespace, or add it together
with the surronding chunk of code

[...]> +static void meta_align_offset(struct eea_net_rx *rx, struct
eea_rx_meta *meta)
+{
+	int h, b;
+
+	h = rx->headroom;
+	b = meta->offset + h;
+
+	b = ALIGN(b, 128);
Out of sheer curiosity, why the above align? Possibly a comment and a
macro instead of a magic number would be useful.
+static int eea_alloc_rx_hdr(struct eea_net_tmp *tmp, struct eea_net_rx *rx)
+{
+	struct page *hdr_page = NULL;
+	struct eea_rx_meta *meta;
+	u32 offset = 0, hdrsize;
+	struct device *dmadev;
+	dma_addr_t dma;
+	int i;
+
+	dmadev = tmp->edev->dma_dev;
+	hdrsize = tmp->cfg.split_hdr;
+
+	for (i = 0; i < tmp->cfg.rx_ring_depth; ++i) {
+		meta = &rx->meta[i];
+
+		if (!hdr_page || offset + hdrsize > PAGE_SIZE) {
+			hdr_page = dev_alloc_page();
+			if (!hdr_page)
+				return -ENOMEM;
Why you are not using the page pool for the headers?
+
+			dma = dma_map_page(dmadev, hdr_page, 0, PAGE_SIZE,
+					   DMA_FROM_DEVICE);
+
+			if (unlikely(dma_mapping_error(dmadev, dma))) {
+				put_page(hdr_page);
+				return -ENOMEM;
+			}
+
+			offset = 0;
+			meta->hdr_page = hdr_page;
+			meta->dma = dma;
+		}
+
+		meta->hdr_dma = dma + offset;
+		meta->hdr_addr = page_address(hdr_page) + offset;
+		offset += hdrsize;
+	}
+
+	return 0;
+}
+
+static void eea_rx_meta_dma_sync_for_cpu(struct eea_net_rx *rx,
+					 struct eea_rx_meta *meta, u32 len)
+{
+	dma_sync_single_for_cpu(rx->enet->edev->dma_dev,
+				meta->dma + meta->offset + meta->headroom,
+				len, DMA_FROM_DEVICE);
+}
+
+static int eea_harden_check_overflow(struct eea_rx_ctx *ctx,
+				     struct eea_net *enet)
+{
+	if (unlikely(ctx->len > ctx->meta->truesize - ctx->meta->room)) {
Give the above, it looks like the hypervisor could corrupt the guest
driver memory. If so, are any defensive, related, checks in the guests
really effective?
+		pr_debug("%s: rx error: len %u exceeds truesize %u\n",
+			 enet->netdev->name, ctx->len,
+			 ctx->meta->truesize - ctx->meta->room);
+		return -EINVAL;
+	}
+
+	return 0;
+}

+static bool eea_rx_post(struct eea_net *enet,
+			struct eea_net_rx *rx, gfp_t gfp)
It looks like this function is always called with gfp == GFP_ATOMIC. If
so, just drop the argument.

[...]> +static int eea_tx_post_skb(struct eea_net_tx *tx, struct sk_buff
*skb)
+{
+	const struct skb_shared_info *shinfo = skb_shinfo(skb);
+	u32 hlen = skb_headlen(skb);
+	struct eea_tx_meta *meta;
+	dma_addr_t addr;
+	int i, err;
+	u16 flags;
+
+	addr = dma_map_single(tx->dma_dev, skb->data, hlen, DMA_TO_DEVICE);
+	if (unlikely(dma_mapping_error(tx->dma_dev, addr)))
+		return -ENOMEM;
+
+	flags = skb->ip_summed == CHECKSUM_PARTIAL ? EEA_DESC_F_DO_CSUM : 0;
+
+	meta = eea_tx_desc_fill(tx, addr, hlen, !shinfo->nr_frags, skb, flags);
+
+	if (eea_fill_desc_from_skb(skb, tx->ering, meta->desc))
+		goto err;
+
+	for (i = 0; i < shinfo->nr_frags; i++) {
+		const skb_frag_t *frag = &shinfo->frags[i];
+		bool is_last = i == (shinfo->nr_frags - 1);
+
+		err = eea_tx_add_skb_frag(tx, meta, frag, is_last);
+		if (err)
+			goto err;
+	}
+
+	meta->num = shinfo->nr_frags + 1;
It looks like there is no memory barrier after filling the descriptor
and before commiting it. Whoever is processing this data could possibly
observe inconsistent/corrupted descriptors.

/P
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help