Thread (13 messages) 13 messages, 5 authors, 2012-10-12

[PATCH v2 1/4] net: mvneta: driver for Marvell Armada 370/XP network unit

flat view
STALE5117d

From: romieu@fr.zoreil.com (Francois Romieu)
Date: 2012-10-11 21:26:29
Also in: netdev

Thomas Petazzoni [off-list ref] :
[...]
quoted hunk ↗ jump to hunk
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
new file mode 100644
index 0000000..4f7fe08
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvneta.c
[...]
quoted hunk ↗ jump to hunk
+struct mvneta_port {
+	/* Packet size in bytes */
+	int pkt_size;
+
+	/* Base virtual address of the Ethernet controller registers */
+	void __iomem *base;
+
+	/* Array of RX queues */
+	struct mvneta_rx_queue *rxqs;
+
+	/* Array of TX queues */
+	struct mvneta_tx_queue *txqs;
+
+	/* Timer */
+	struct timer_list tx_done_timer;
+
+	/* Back pointer to the Linux network interface device */
+	struct net_device *dev;
You can avoid five comments.
quoted hunk ↗ jump to hunk
+
+	u32 cause_rx_tx[CONFIG_NR_CPUS];
+	struct napi_struct napi;
+
+	/* Flags */
+	unsigned long flags;
+#define MVNETA_F_TX_DONE_TIMER_BIT  0
+
+	/* Napi weight */
+	int weight;
+
+	/* Core clock [Hz] */
+	unsigned int clk_rate;
clk_rate_hz

[...]
quoted hunk ↗ jump to hunk
+/* Get System Network Statistics */
+struct rtnl_link_stats64 *mvneta_get_stats64(struct net_device *dev,
+					     struct rtnl_link_stats64 *stats)
+{
+	struct mvneta_port *pp = netdev_priv(dev);
+	unsigned int start;
+
+	memset(stats, 0, sizeof(struct rtnl_link_stats64));
+
+	do {
+		start = u64_stats_fetch_begin_bh(&pp->rx_stats.syncp);
+		stats->rx_packets = pp->rx_stats.packets;
+		stats->rx_bytes	= pp->rx_stats.bytes;
+	} while (u64_stats_fetch_retry_bh(&pp->rx_stats.syncp, start));
+
+
+	do {
+		start = u64_stats_fetch_begin_bh(&pp->tx_stats.syncp);
+		stats->tx_packets = pp->tx_stats.packets;
+		stats->tx_bytes	= pp->tx_stats.bytes;
+	} while (u64_stats_fetch_retry_bh(&pp->tx_stats.syncp, start));
+
+	stats->rx_errors	= dev->stats.rx_errors;
+	stats->rx_dropped	= dev->stats.rx_dropped;
+
+	stats->tx_dropped	= dev->stats.tx_dropped;
+
+	return stats;
+
+}
Excess empty line.

[...]
quoted hunk ↗ jump to hunk
+static void mvneta_rxq_desc_num_update(struct mvneta_port *pp,
+				       struct mvneta_rx_queue *rxq,
+				       int rx_done, int rx_filled)
+{
+	u32 val;
+
+	if ((rx_done <= 0xff) && (rx_filled <= 0xff)) {
+		val = rx_done |
+		  (rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT);
+		mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val);
+		return;
+	}
+
+	/* Only 255 descriptors can be added at once */
+	while ((rx_done > 0) || (rx_filled > 0)) {
+		if (rx_done <= 0xff) {
+			val = rx_done;
+			rx_done = 0;
+		} else {
+			val = 0xff;
+			rx_done -= 0xff;
+		}
+		if (rx_filled <= 0xff) {
+			val |= rx_filled
+				<< MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT;
			val |= rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT;
quoted hunk ↗ jump to hunk
+			rx_filled = 0;
+		} else {
+			val |= 0xff << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT;
+			rx_filled -= 0xff;
+		}
+		mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val);
+	}
+}
+
+/* Get pointer to next RX descriptor to be processed by SW */
+static struct mvneta_rx_desc *
+mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq)
+{
+	int rx_desc = rxq->next_desc_to_proc;
+
+	rxq->next_desc_to_proc =
+		MVNETA_QUEUE_NEXT_DESC(rxq, rx_desc);
	rxq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(rxq, rx_desc);
quoted hunk ↗ jump to hunk
+
+	return rxq->descs + rx_desc;
+}
+
+/* Change maximum receive size of the port. */
+static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size)
+{
+	u32 val;
+
+	val =  mvreg_read(pp, MVNETA_GMAC_CTRL_0);
+	val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK;
+	val |= (((max_rx_size - MVNETA_MH_SIZE) / 2)
+		    << MVNETA_GMAC_MAX_RX_SIZE_SHIFT);
Excess parenthesis.
Operator on start of line.

[...]
quoted hunk ↗ jump to hunk
+static struct mvneta_tx_desc *
+mvneta_txq_next_desc_get(struct mvneta_tx_queue *txq)
+{
+	int tx_desc = txq->next_desc_to_proc;
+	txq->next_desc_to_proc =
+		MVNETA_QUEUE_NEXT_DESC(txq, tx_desc);
	int tx_desc = txq->next_desc_to_proc;

	txq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(txq, tx_desc);

[...]
quoted hunk ↗ jump to hunk
+static void mvneta_port_up(struct mvneta_port *pp)
+{
+	int queue;
+	u32 q_map;
+
+	/* Enable all initialized TXs. */
+	mvneta_mib_counters_clear(pp);
+	q_map = 0;
+	for (queue = 0; queue < mvneta_txq_number; queue++) {
+		struct mvneta_tx_queue *txq = &pp->txqs[queue];
quoted hunk ↗ jump to hunk
+		if (txq->descs != NULL)
+			q_map |= (1 << queue);
+	}
+	mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
+
+	/* Enable all initialized RXQs. */
+	q_map = 0;
+	for (queue = 0; queue < mvneta_rxq_number; queue++) {
+		struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
quoted hunk ↗ jump to hunk
+		if (rxq->descs != NULL)
+			q_map |= (1 << queue);
+	}
+
+	mvreg_write(pp, MVNETA_RXQ_CMD, q_map);
+}
+
+/* Stop the Ethernet port activity */
+static void mvneta_port_down(struct mvneta_port *pp)
+{
+	u32 val;
+	int count;
+
+	/* Stop Rx port activity. Check port Rx activity. */
+	val = (mvreg_read(pp, MVNETA_RXQ_CMD))
+		& MVNETA_RXQ_ENABLE_MASK;
	val = mvreg_read(pp, MVNETA_RXQ_CMD) & MVNETA_RXQ_ENABLE_MASK;
quoted hunk ↗ jump to hunk
+	/* Issue stop command for active channels only */
+	if (val != 0)
+		mvreg_write(pp, MVNETA_RXQ_CMD,
+			    val << MVNETA_RXQ_DISABLE_SHIFT);
Too bad "val" isn't one character shorter.
quoted hunk ↗ jump to hunk
+
+	/* Wait for all Rx activity to terminate. */
+	count = 0;
+	do {
+		if (count >= MVNETA_RX_DISABLE_TIMEOUT_MSEC) {
+			netdev_warn(pp->dev,
+				    "TIMEOUT for RX stopped ! rx_queue_cmd: 0x08%x\n",
+				    val);
+			break;
+		}
+		mdelay(1);
+		count++;
+
+		val = mvreg_read(pp, MVNETA_RXQ_CMD);
+	} while (val & 0xff);
You can test "if (count++ >= MVNETA_RX_DISABLE_TIMEOUT_MSEC) {". Nobody
will notice.
quoted hunk ↗ jump to hunk
+
+	/* Stop Tx port activity. Check port Tx activity. Issue stop
+	   command for active channels only  */
+	val = (mvreg_read(pp, MVNETA_TXQ_CMD)) & MVNETA_TXQ_ENABLE_MASK;
+
+	if (val != 0)
+		mvreg_write(pp, MVNETA_TXQ_CMD,
+			    (val << MVNETA_TXQ_DISABLE_SHIFT));
+
+	/* Wait for all Tx activity to terminate. */
+	count = 0;
+	do {
+		if (count >= MVNETA_TX_DISABLE_TIMEOUT_MSEC) {
+			netdev_warn(pp->dev,
+				    "TIMEOUT for TX stopped tx_queue_cmd - 0x%08x\n",
+				    val);
+			break;
+		}
+		mdelay(1);
+		count++;
+
+		/* Check TX Command reg that all Txqs are stopped */
+		val = mvreg_read(pp, MVNETA_TXQ_CMD);
+
+	} while (val & 0xff);
+
+	/* Double check to verify that TX FIFO is empty */
+	count = 0;
+	do {
+		if (count >= MVNETA_TX_FIFO_EMPTY_TIMEOUT) {
+			netdev_warn(pp->dev,
+				    "TX FIFO empty timeout status=0x08%x",
+				    val);
				    "TX FIFO empty timeout status=0x08%x", val);
quoted hunk ↗ jump to hunk
+			break;
+		}
+		mdelay(1);
+		count++;
+
+		val = mvreg_read(pp, MVNETA_PORT_STATUS);
+	} while (!(val & MVNETA_TX_FIFO_EMPTY) &&
+		 (val & MVNETA_TX_IN_PRGRS));
+
+	udelay(200);
+}
+
+/* Enable the port by setting the port enable bit of the MAC control register */
+static void mvneta_port_enable(struct mvneta_port *pp)
+{
+	u32 val;
+
+	/* Enable port */
+	val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
+	val |= MVNETA_GMAC0_PORT_ENABLE;
+	mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
+}
+
+/* Disable the port and wait for about 200 usec before retuning */
+static void mvneta_port_disable(struct mvneta_port *pp)
+{
+	u32 val;
+
+	/* Reset the Enable bit in the Serial Control Register */
+	val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
+	val &= ~(MVNETA_GMAC0_PORT_ENABLE);
Excess parenthesis.
quoted hunk ↗ jump to hunk
+	mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
+
+	udelay(200);
+}
+
+/* Multicast tables methods */
+
+/* Set all entries in Unicast MAC Table; queue==-1 means reject all */
+static void mvneta_set_ucast_table(struct mvneta_port *pp, int queue)
+{
+	int offset;
+	u32 val;
+
+	if (queue == -1) {
+		val = 0;
+	} else {
+		val =	(((0x01 | (queue << 1)) << 0) |
+			((0x01 | (queue << 1)) << 8) |
+			((0x01 | (queue << 1)) << 16) |
+			((0x01 | (queue << 1)) << 24));
		val =  (0x01 | (queue << 1)) |
		      ((0x01 | (queue << 1)) << 8) |
		      ((0x01 | (queue << 1)) << 16) |
		      ((0x01 | (queue << 1)) << 24);

		val  = 0x01 | (queue << 1);
		val |= (val << 24) | (val << 16) | (val << 8);
quoted hunk ↗ jump to hunk
+	}
+
+	for (offset = 0; offset <= 0xc; offset += 4)
+		mvreg_write(pp, MVNETA_DA_FILT_UCAST_BASE + offset, val);
+}
+
+/* Set all entries in Special Multicast MAC Table; queue==-1 means reject all */
+static void mvneta_set_special_mcast_table(struct mvneta_port *pp, int queue)
+{
+	int offs;
+	u32 val;
+
+	if (queue == -1) {
+		val = 0;
+	} else {
+		val =	(((0x01 | (queue << 1)) << 0) |
+			((0x01 | (queue << 1)) << 8) |
+			((0x01 | (queue << 1)) << 16) |
+			((0x01 | (queue << 1)) << 24));
Bis.
quoted hunk ↗ jump to hunk
+	}
+
+	for (offs = 0; offs <= 0xfc; offs += 4)
+		mvreg_write(pp, (MVNETA_DA_FILT_SPEC_MCAST + offs), val);
+
+}
+
+/* Set all entries in Other Multicast MAC Table. queue==-1 means reject all */
+static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue)
+{
+	int offset;
+	u32 val;
+
+	if (queue == -1) {
+		memset(pp->mcast_count, 0, sizeof(pp->mcast_count));
+		val = 0;
+	} else {
+		memset(pp->mcast_count, 1, sizeof(pp->mcast_count));
+		val = (((0x01 | (queue << 1)) << 0) |
+			  ((0x01 | (queue << 1)) << 8) |
+			  ((0x01 | (queue << 1)) << 16) |
+			  ((0x01 | (queue << 1)) << 24));
Ter.

[...]
quoted hunk ↗ jump to hunk
+static void mvneta_rx_pkts_coal_set(struct mvneta_port *pp,
+				    struct mvneta_rx_queue *rxq, u32 value)
+{
+	mvreg_write(pp, MVNETA_RXQ_THRESHOLD_REG(rxq->id),
+		    (value | MVNETA_RXQ_NON_OCCUPIED(0)));
Excess parenthesis.
quoted hunk ↗ jump to hunk
+	rxq->pkts_coal = value;
+}
+
+/*
+ * Set the time delay in usec before
+ * RX interrupt will be generated by HW.
+ */
+static void mvneta_rx_time_coal_set(struct mvneta_port *pp,
+				    struct mvneta_rx_queue *rxq, u32 value)
+{
+	u32 val = (pp->clk_rate / 1000000) * value;
+
+	mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val);
+	rxq->time_coal = value;
+}
+
+/* Set threshold for TX_DONE pkts coalescing */
+static void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp,
+					 struct mvneta_tx_queue *txq, u32 value)
+{
+	u32 val;
+
+	val = mvreg_read(pp, MVNETA_TXQ_SIZE_REG(txq->id));
+
+	val &= ~MVNETA_TXQ_SENT_THRESH_ALL_MASK;
+	val |= MVNETA_TXQ_SENT_THRESH_MASK(value);
+
+	mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), val);
+
+	txq->done_pkts_coal = value;
+}
+
+/* Trigger tx done timer in MVNETA_TX_DONE_TIMER_PERIOD msecs */
+static void mvneta_add_tx_done_timer(struct mvneta_port *pp)
+{
+	if (test_and_set_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags) == 0) {
+		pp->tx_done_timer.expires = jiffies +
+			msecs_to_jiffies(MVNETA_TX_DONE_TIMER_PERIOD);
+		add_timer(&pp->tx_done_timer);
+	}
+}
+
+
+/* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */
+static void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc,
+				u32 phys_addr, u32 cookie)
+{
+	rx_desc->buf_cookie = cookie;
+	rx_desc->buf_phys_addr = phys_addr;
+}
+
+/* Decrement sent descriptors counter */
+static void mvneta_txq_sent_desc_dec(struct mvneta_port *pp,
+				     struct mvneta_tx_queue *txq,
+				     int sent_desc)
+{
+	u32 val;
+
+	/* Only 255 TX descriptors can be updated at once */
+	while (sent_desc > 0xff) {
+		val = (0xff << MVNETA_TXQ_DEC_SENT_SHIFT);
Parenthesis.
quoted hunk ↗ jump to hunk
+		mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val);
+		sent_desc = sent_desc - 0xff;
+	}
+
+	val = (sent_desc << MVNETA_TXQ_DEC_SENT_SHIFT);
Parenthesis.

[...]
quoted hunk ↗ jump to hunk
+static u32 mvneta_txq_desc_csum(int l3_offs, int l3_proto,
+				int ip_hdr_len, int l4_proto)
+{
+	u32 command;
+
+	/* Fields: L3_offset, IP_hdrlen, L3_type, G_IPv4_chk,
+	   G_L4_chk, L4_type; required only for checksum
+	   calculation */
+	command =  (l3_offs    << MVNETA_TX_L3_OFF_SHIFT);
+	command |= (ip_hdr_len << MVNETA_TX_IP_HLEN_SHIFT);
Parenthesis.
quoted hunk ↗ jump to hunk
+
+	if (l3_proto == swab16(ETH_P_IP))
+		command |= MVNETA_TXD_IP_CSUM;
+	else
+		command |= MVNETA_TX_L3_IP6;
+
+	if (l4_proto == IPPROTO_TCP)
+		command |=  MVNETA_TX_L4_CSUM_FULL;
+	else if (l4_proto == IPPROTO_UDP)
+		command |= (MVNETA_TX_L4_UDP | MVNETA_TX_L4_CSUM_FULL);
+	else
+		command |= MVNETA_TX_L4_CSUM_NOT;
+
+	return command;
+}
+
+/* Display more error info */
+static void mvneta_rx_error(struct mvneta_port *pp,
+			    struct mvneta_rx_desc *rx_desc)
+{
+	u32 status = rx_desc->status;
+
+	if ((status & MVNETA_RXD_FIRST_LAST_DESC)
+	    != MVNETA_RXD_FIRST_LAST_DESC) {
	if ((status & MVNETA_RXD_FIRST_LAST_DESC) !=
	    MVNETA_RXD_FIRST_LAST_DESC) {

The length of the MVNETA prefix is a self inflicted pain.

(...]
quoted hunk ↗ jump to hunk
+static struct mvneta_tx_queue *mvneta_tx_done_policy(struct mvneta_port *pp,
+						     u32 cause)
+{
+	int queue;
quoted hunk ↗ jump to hunk
+	queue = fls(cause) - 1;
+	if (queue < 0 || queue >= mvneta_txq_number)
+		return NULL;
+	return &pp->txqs[queue];
	return (q < 0 || q >= mvneta_txq_number) ? NULL : &pp->txqs[q]; ?

(I admit it will obviously not resurrect an unicorn)
quoted hunk ↗ jump to hunk
+}
+
+/* Free tx queue skbuffs */
+static void mvneta_txq_bufs_free(struct mvneta_port *pp,
+				 struct mvneta_tx_queue *txq, int num)
+{
+	struct sk_buff *skb;
+	int i;
+	struct mvneta_tx_desc *tx_desc;
	int i;
quoted hunk ↗ jump to hunk
+	for (i = 0; i < num; i++) {
+		skb = txq->tx_skb[txq->txq_get_index];
+		tx_desc = txq->descs + txq->txq_get_index;
		struct mvneta_tx_desc *tx_desc = txq->descs + txq->txq_get_index;
		struct sk_buff *skb = txq->tx_skb[txq->txq_get_index];
quoted hunk ↗ jump to hunk
+
+		mvneta_txq_inc_get(txq);
+
+		if (!skb)
+			continue;
+		if (tx_desc) {
+			dma_unmap_single(pp->dev->dev.parent,
+					 tx_desc->buf_phys_addr,
+					 tx_desc->data_size,
+					 DMA_TO_DEVICE);
+			dev_kfree_skb_any(skb);
		if (!(skb && txd))
			continue;

		dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr,
				 tx_desc->data_size, DMA_TO_DEVICE);
quoted hunk ↗ jump to hunk
+		}
+	}
+}
+
+/* Handle end of transmission */
+static int mvneta_txq_done(struct mvneta_port *pp,
+			   struct mvneta_tx_queue *txq)
+{
+	int tx_done;
+
+	tx_done = mvneta_txq_sent_desc_proc(pp, txq);
+	if (tx_done == 0)
+		return tx_done;
+	mvneta_txq_bufs_free(pp, txq, tx_done);
+
+	txq->count -= tx_done;
+
+	return tx_done;
+}
+
+/* Refill processing */
+static int mvneta_rx_refill(struct mvneta_port *pp,
+			    struct mvneta_rx_desc *rx_desc)
+
+{
+	unsigned long phys_addr;
	dma_addr_t phys_addr;
quoted hunk ↗ jump to hunk
+	struct sk_buff *skb;
+
+	skb = netdev_alloc_skb(pp->dev, pp->pkt_size);
+	if (!skb)
+		return 1;
+
+	phys_addr = dma_map_single(pp->dev->dev.parent, skb->head,
+				   MVNETA_RX_BUF_SIZE(pp->pkt_size),
+				   DMA_FROM_DEVICE);
+	if (unlikely(dma_mapping_error(pp->dev->dev.parent,
+				       phys_addr))) {
+		dev_kfree_skb_irq(skb);
dev_kfree_skb in softirq context.

[...]
quoted hunk ↗ jump to hunk
+static struct mvneta_rx_queue *mvneta_rx_policy(struct mvneta_port *pp,
+						u32 cause)
+{
+	int queue = fls(cause >> 8) - 1;
quoted hunk ↗ jump to hunk
+	if (queue < 0 || queue >= mvneta_rxq_number)
+		return NULL;
+	return &pp->rxqs[queue];
+}
+
+/* Drop packets received by the RXQ and free buffers */
+static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
+				 struct mvneta_rx_queue *rxq)
+{
+	struct mvneta_rx_desc *rx_desc;
+	struct sk_buff *skb;
Variable scope.
quoted hunk ↗ jump to hunk
+	int rx_done, i;
+
+	rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
+	for (i = 0; i < rxq->size; i++) {
+		rx_desc = rxq->descs + i;
+		skb = (struct sk_buff *)rx_desc->buf_cookie;
+		dev_kfree_skb_any(skb);
+		dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
+				 rx_desc->data_size, DMA_FROM_DEVICE);
+
+
+	}
+	if (rx_done)
+		mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done);
+}
+
+
+/* Main rx processing */
+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, err;
+	struct mvneta_rx_desc *rx_desc;
Variable scope.
quoted hunk ↗ jump to hunk
+	u32 rx_status;
+	int rx_bytes;
+	struct sk_buff *skb;
+
+	/* 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) {
+		rx_desc = mvneta_rxq_next_desc_get(rxq);
+		prefetch(rx_desc);
+		rx_done++;
+		rx_filled++;
+		rx_status = rx_desc->status;
+		skb = (struct sk_buff *)rx_desc->buf_cookie;
+
+		if (((rx_status & MVNETA_RXD_FIRST_LAST_DESC)
+		     != MVNETA_RXD_FIRST_LAST_DESC)
+		    || (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
...
quoted hunk ↗ jump to hunk
+			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->data += MVNETA_MH_SIZE;
+		skb->tail += (rx_bytes + MVNETA_MH_SIZE);
skb_reserve + skb_put would be more idiomatic imho.
quoted hunk ↗ jump to hunk
+		skb->len = rx_bytes;
+
+		skb->protocol = eth_type_trans(skb, dev);
+
+		mvneta_rx_csum(pp, rx_desc, skb);
+
+		if (dev->features & NETIF_F_GRO)
+			napi_gro_receive(&pp->napi, skb);
+		else
+			netif_receive_skb(skb);
No test. Go for straight napi_gro_receive.
quoted hunk ↗ jump to hunk
+
+		/* Refill processing */
+		err = mvneta_rx_refill(pp, rx_desc);
+		if (err) {
+			netdev_err(pp->dev, "Linux processing - Can't refill\n");
+			rxq->missed++;
+			rx_filled--;
+		}
+	}
+
+	/* Update rxq management counters */
+	mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_filled);
+
+	return rx_done;
+}
+
+/* Handle tx fragmentation processing */
+static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
+				  struct mvneta_tx_queue *txq)
+{
+	int i, j;
+	struct mvneta_tx_desc *tx_desc;
+	skb_frag_t *frag;
	struct device *d = pp->dev->dev.parent;
	struct mvneta_tx_desc *tx_desc;
	int i, j;
quoted hunk ↗ jump to hunk
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		frag = &skb_shinfo(skb)->frags[i];
		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
		void *addr = page_address(frag->page.p) + frag->page_offset;
quoted hunk ↗ jump to hunk
+
+		tx_desc = mvneta_txq_next_desc_get(txq);
+		tx_desc->data_size = frag->size;
+
+		tx_desc->buf_phys_addr =
+			dma_map_single(pp->dev->dev.parent,
+				       page_address(frag->page.p) +
+				       frag->page_offset, tx_desc->data_size,
+				       DMA_TO_DEVICE);
		tx_desc->buf_phys_addr = dma_map_single(d, addr, frag->size,
							DMA_TO_DEVICE);

[...]
quoted hunk ↗ jump to hunk
+static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct mvneta_port *pp = netdev_priv(dev);
+	int frags = 0;
+	int res = NETDEV_TX_OK;
+	u32 tx_cmd;
+	struct mvneta_tx_queue *txq = &pp->txqs[mvneta_txq_def];
+	struct mvneta_tx_desc *tx_desc;
Longer first.
quoted hunk ↗ jump to hunk
+
+	if (!netif_running(dev))
+		goto out;
+
+	frags = skb_shinfo(skb)->nr_frags + 1;
+
+	/* Are there enough TX descriptors to send packet ? */
Never. You must disable queueing before it's too late.
quoted hunk ↗ jump to hunk
+	if ((txq->count + frags) >= txq->size) {
+		frags = 0;
+		res = NETDEV_TX_BUSY;
+		goto out;
+	}
+
+	/* Get a descriptor for the first part of the packet */
+	tx_desc = mvneta_txq_next_desc_get(txq);
+
+	tx_cmd = mvneta_skb_tx_csum(pp, skb);
+
+	tx_desc->data_size = skb_headlen(skb);
+
+	tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, skb->data,
+						tx_desc->data_size,
+						DMA_TO_DEVICE);
+	if (unlikely(dma_mapping_error(dev->dev.parent,
+				       tx_desc->buf_phys_addr))) {
+		mvneta_txq_desc_put(txq);
+		frags = 0;
+		res = NETDEV_TX_BUSY;
Neither. You drop the packet, update the stats and return ok.
quoted hunk ↗ jump to hunk
+		goto out;
+	}
+
+	if (frags == 1) {
+		/* First and Last descriptor */
+		tx_cmd |= MVNETA_TXD_FLZ_DESC;
+		tx_desc->command = tx_cmd;
+		txq->tx_skb[txq->txq_put_index] = skb;
+		mvneta_txq_inc_put(txq);
+	} else {
+		/* First but not Last */
+		tx_cmd |= MVNETA_TXD_F_DESC;
+		txq->tx_skb[txq->txq_put_index] = NULL;
+		mvneta_txq_inc_put(txq);
+		tx_desc->command = tx_cmd;
+		/* Continue with other skb fragments */
+		if (mvneta_tx_frag_process(pp, skb, txq)) {
+			dma_unmap_single(dev->dev.parent,
+					 tx_desc->buf_phys_addr,
+					 tx_desc->data_size,
+					 DMA_TO_DEVICE);
+			mvneta_txq_desc_put(txq);
+			frags = 0;
+			res = NETDEV_TX_BUSY;
Sic.

[...]
quoted hunk ↗ jump to hunk
+/* handle tx done - called from tx done timer callback */
+static u32 mvneta_tx_done_gbe(struct mvneta_port *pp, u32 cause_tx_done,
+			      int *tx_todo)
Why is it not done in napi context ?

-- 
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