Thread (13 messages) flat view 13 messages, 3 authors, 2012-11-14
STALE5039d

Re: [PATCH v6 2/7] net: mvneta: driver for Marvell Armada 370/XP network unit

From: Francois Romieu <romieu@fr.zoreil.com>
Date: 2012-11-13 23:12:48
Also in: linux-arm-kernel

Thomas Petazzoni [off-list ref] :
[...]
+static int mvneta_mac_addr_set(struct mvneta_port *pp, unsigned char *addr,
+			       int queue)
+{
+	unsigned int mac_h;
+	unsigned int mac_l;
+
+	if (queue >= 1) {
+		netdev_err(pp->dev, "RX queue #%d is out of range\n", queue);
+		return -EINVAL;
+	}
(whence q <= 0)
+
+	if (queue != -1) {
+		mac_l = (addr[4] << 8) | (addr[5]);
+		mac_h = (addr[0] << 24) | (addr[1] << 16) |
+			(addr[2] << 8) | (addr[3] << 0);
+
+		mvreg_write(pp, MVNETA_MAC_ADDR_LOW, mac_l);
+		mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, mac_h);
+	}
What is it trying to achieve with the "queue" argument ?

[...]
+/* Refill processing */
+static int mvneta_rx_refill(struct mvneta_port *pp,
+			    struct mvneta_rx_desc *rx_desc)
+
+{
+	dma_addr_t phys_addr;
+	struct sk_buff *skb;
+
+	skb = netdev_alloc_skb(pp->dev, pp->pkt_size);
+	if (!skb)
+		return -ENOMEM;
Could netdev_alloc_skb_ip_align be an option ?

[...]
+static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
+		     struct mvneta_rx_queue *rxq)
+{
+	struct net_device *dev = pp->dev;
+	int rx_done, rx_filled;
+
+	/* Get number of received packets */
+	rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
+
+	if (rx_todo > rx_done)
+		rx_todo = rx_done;
+
+	rx_done = 0;
+	rx_filled = 0;
+
+	/* Fairness NAPI loop */
+	while (rx_done < rx_todo) {
+		struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq);
+		struct sk_buff *skb;
+		u32 rx_status;
+		int rx_bytes, err;
+
+		prefetch(rx_desc);
+		rx_done++;
+		rx_filled++;
+		rx_status = rx_desc->status;
+		skb = (struct sk_buff *)rx_desc->buf_cookie;
+
+		if (!mvneta_rxq_desc_is_first_last(rx_desc) ||
+		    (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
+			dev->stats.rx_errors++;
+			mvneta_rx_error(pp, rx_desc);
+			mvneta_rx_desc_fill(rx_desc, rx_desc->buf_phys_addr,
+					    (u32)skb);
+			continue;
+		}
+
+		dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
+				 rx_desc->data_size, DMA_FROM_DEVICE);
+
+		rx_bytes = rx_desc->data_size -
+			(MVNETA_ETH_CRC_SIZE + MVNETA_MH_SIZE);
+		u64_stats_update_begin(&pp->rx_stats.syncp);
+		pp->rx_stats.packets++;
+		pp->rx_stats.bytes += rx_bytes;
+		u64_stats_update_end(&pp->rx_stats.syncp);
+
+		/* Linux processing */
+		skb_reserve(skb, MVNETA_MH_SIZE);
+		skb_put(skb, rx_bytes);
(I suggested to use skb_reserve / skb_put instead of hand-crafted code but
I may have ignored the elephant in the living room)

I understand the skb_put, ok. What is the purpose of the skb_reserve ?

Are MVNETA_ETH_CRC_SIZE (4) and MVNETA_MH_SIZE (2) really different from
ETH_FCS_LEN and NET_IP_ALIGN ?

[...]
+static irqreturn_t mvneta_isr(int irq, void *dev_id)
+{
+	struct mvneta_port *pp = (struct mvneta_port *)dev_id;
+
+	/* Mask all interrupts */
+	mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+
+	/* Verify that the device not already on the polling list */
+	if (napi_schedule_prep(&pp->napi))
+		__napi_schedule(&pp->napi);
Hand-crafted napi_schedule.

[...]
+static int mvneta_open(struct net_device *dev)
+{
+	struct mvneta_port *pp = netdev_priv(dev);
+	int ret;
+
+	ret = mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
+	if (ret < 0) {
+		netdev_err(dev, "mvneta_mac_addr_set failed\n");
+		goto mac_addr_set_failure;
+	}
AFAIU mvneta_mac_addr_set can only fail when (module parameter) rxq_def is
not set correctly. It imho calls for a check in probe/remove and a removal
of the failure case in mvneta_mac_addr_set.
+
+	pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
+
+	ret = mvneta_setup_rxqs(pp);
+	if (ret)
+		goto rxqs_setup_failure;
+
+	ret = mvneta_setup_txqs(pp);
+	if (ret)
+		goto txqs_setup_failure;
+
+	/* Connect to port interrupt line */
+	ret = request_irq(pp->dev->irq, mvneta_isr, IRQF_DISABLED,
+			  MVNETA_DRIVER_NAME, pp);
include/linux/interrupt.h
[...]
 * IRQF_DISABLED - keep irqs disabled when calling the action handler.
 *                 DEPRECATED. This flag is a NOOP and scheduled to be removed

[...]
+static int __devinit mvneta_probe(struct platform_device *pdev)
+{
+	const struct mbus_dram_target_info *dram_target_info;
+	struct device_node *dn = pdev->dev.of_node;
+	struct device_node *phy_node;
+	u32 phy_addr, clk_rate_hz;
+	struct mvneta_port *pp;
+	struct net_device *dev;
+	const char *mac_addr;
+	int phy_mode;
+	int err;
+
+	dev = alloc_etherdev_mq(sizeof(struct mvneta_port), 8);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->irq = irq_of_parse_and_map(dn, 0);
+	if (dev->irq == 0) {
+		err = -EINVAL;
+		goto err_irq;
+	}
+
+	phy_node = of_parse_phandle(dn, "phy", 0);
+	if (!phy_node) {
+		dev_err(&pdev->dev, "no associated PHY\n");
+		err = -ENODEV;
+		goto err_node;
+	}
+
+	phy_mode = of_get_phy_mode(dn);
+	if (phy_mode < 0) {
+		dev_err(&pdev->dev, "incorrect phy-mode\n");
+		err = -EINVAL;
+		goto err_node;
+	}
+
+	if (of_property_read_u32(dn, "clock-frequency", &clk_rate_hz) != 0) {
+		dev_err(&pdev->dev, "could not read clock-frequency\n");
+		err = -EINVAL;
+		goto err_node;
+	}
+
+	mac_addr = of_get_mac_address(dn);
+
+	if (!mac_addr || !is_valid_ether_addr(mac_addr))
+		eth_hw_addr_random(dev);
+	else
+		memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
+
+	dev->tx_queue_len = MVNETA_MAX_TXD;
+	dev->watchdog_timeo = 5 * HZ;
+	dev->netdev_ops = &mvneta_netdev_ops;
+
+	SET_ETHTOOL_OPS(dev, &mvneta_eth_tool_ops);
+
+	pp = netdev_priv(dev);
+
+	pp->tx_done_timer.function = mvneta_tx_done_timer_callback;
+	init_timer(&pp->tx_done_timer);
+	clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags);
+
+	pp->weight = MVNETA_RX_POLL_WEIGHT;
+	pp->clk_rate_hz = clk_rate_hz;
+	pp->phy_node = phy_node;
+	pp->phy_interface = phy_mode;
+
+	pp->base = of_iomap(dn, 0);
+	if (pp->base == NULL) {
+		err = -ENOMEM;
+		goto err_node;
+	}
+
+	pp->tx_done_timer.data = (unsigned long)dev;
+
+	pp->tx_ring_size = MVNETA_MAX_TXD;
+	pp->rx_ring_size = MVNETA_MAX_RXD;
+
+	pp->dev = dev;
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	if (mvneta_init(pp, phy_addr)) {
+		dev_err(&pdev->dev, "can't init eth hal\n");
+		err = -ENODEV;
+		goto err_base;
+	}
The error code from mvneta_init() should be used.
+	mvneta_port_power_up(pp, phy_mode);
+
+	dram_target_info = mv_mbus_dram_info();
+	if (dram_target_info)
+		mvneta_conf_mbus_windows(pp, dram_target_info);
+
+	netif_napi_add(dev, &pp->napi, mvneta_poll, pp->weight);
+
+	if (register_netdev(dev)) {
+		dev_err(&pdev->dev, "failed to register\n");
+		err = ENOMEM;
+		goto err_base;
+	}
- The error code from register_netdev() should be used.
- Leak: allocations in mvneta_init() are not balanced.
- Nit: the "err_where_did_it_trigger_first" style of label shows its
  downside when compared to the "err_what_should_be_done" when
  it gets used several times.
+
+	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
+	dev->hw_features =  NETIF_F_SG | NETIF_F_IP_CSUM;
+	dev->priv_flags |= IFF_UNICAST_FLT;
+
+	dev_info(&pdev->dev, "%s, mac: %pM\n", dev->name,
+		 dev->dev_addr);
+
+	platform_set_drvdata(pdev, pp->dev);
+
+	return 0;
+err_base:
+	iounmap(pp->base);
+err_node:
+	irq_dispose_mapping(dev->irq);
+err_irq:
+	free_netdev(dev);
+	return err;
+}
+
+/* Device removal routine */
+static int __devexit mvneta_remove(struct platform_device *pdev)
+{
+	struct net_device  *dev = platform_get_drvdata(pdev);
+	struct mvneta_port *pp = netdev_priv(dev);
+
+	iounmap(pp->base);
+
+	unregister_netdev(dev);
+	irq_dispose_mapping(dev->irq);
+	free_netdev(dev);
+	mvneta_deinit(pp);
One may expect the same ordering as the mvneta_probe unroll sequence.

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