Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 48544cc2 authored by Masakazu Mokuno's avatar Masakazu Mokuno Committed by Jeff Garzik
Browse files

ps3: tx descriptor handling cleanup



gelic: TX descriptor handling cleanup

        - Emitted return value of NETDEV_TX_LOCKED when DMA map or kick
          failure.
          Now it would free the skb, update drop packet statistics
          and return OK. Requested from Jeff Garzik.
        - Enable tx queue if number of free descriptors are more than 2
        - Fixed descriptor leak if dma map for second descriptor failed
        - Stopped calling xmit handler from interrupt handler in order
          to recheck tx queue.  Instead, call appropriate helper functions.

Signed-off-by: default avatarMasakazu Mokuno <mokuno@sm.sony.co.jp>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent ea6992aa
Loading
Loading
Loading
Loading
+71 −53
Original line number Diff line number Diff line
@@ -408,22 +408,25 @@ static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop)
			break;

		case GELIC_NET_DESCR_COMPLETE:
			if (tx_chain->tail->skb) {
				card->netdev_stats.tx_packets++;
				card->netdev_stats.tx_bytes +=
					tx_chain->tail->skb->len;
			}
			break;

		case GELIC_NET_DESCR_CARDOWNED:
			/* pending tx request */
		default:
			/* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */
			if (!stop)
				goto out;
		}
		gelic_net_release_tx_descr(card, tx_chain->tail);
		release = 1;
		release ++;
	}
out:
	if (!stop && release)
	if (!stop && (2 < release))
		netif_wake_queue(card->netdev);
}

@@ -660,19 +663,21 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
{
	dma_addr_t buf[2];
	unsigned int vlan_len;
	struct gelic_net_descr *sec_descr = descr->next;

	if (skb->len < GELIC_NET_VLAN_POS)
		return -EINVAL;

	memcpy(&descr->vlan, skb->data, GELIC_NET_VLAN_POS);
	vlan_len = GELIC_NET_VLAN_POS;
	memcpy(&descr->vlan, skb->data, vlan_len);
	if (card->vlan_index != -1) {
		/* internal vlan tag used */
		descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/
		descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]);
		vlan_len = GELIC_NET_VLAN_POS + VLAN_HLEN; /* VLAN_HLEN=4 */
	} else
		vlan_len = GELIC_NET_VLAN_POS; /* no vlan tag */
		vlan_len += VLAN_HLEN; /* added for above two lines */
	}

	/* first descr */
	/* map data area */
	buf[0] = dma_map_single(ctodev(card), &descr->vlan,
			     vlan_len, DMA_TO_DEVICE);

@@ -683,20 +688,6 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
		return -ENOMEM;
	}

	descr->buf_addr = buf[0];
	descr->buf_size = vlan_len;
	descr->skb = skb; /* not used */
	descr->data_status = 0;
	gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */

	/* second descr */
	card->tx_chain.head = card->tx_chain.head->next;
	descr->next_descr_addr = descr->next->bus_addr;
	descr = descr->next;
	if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_NOT_IN_USE)
		/* XXX will be removed */
		dev_err(ctodev(card), "descr is not free!\n");

	buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS,
			     skb->len - GELIC_NET_VLAN_POS,
			     DMA_TO_DEVICE);
@@ -711,13 +702,24 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
		return -ENOMEM;
	}

	descr->buf_addr = buf[1];
	descr->buf_size = skb->len - GELIC_NET_VLAN_POS;
	descr->skb = skb;
	/* first descr */
	descr->buf_addr = buf[0];
	descr->buf_size = vlan_len;
	descr->skb = NULL; /* not used */
	descr->data_status = 0;
	descr->next_descr_addr = 0; /* terminate hw descr */
	gelic_net_set_txdescr_cmdstat(descr, skb, 0);
	descr->next_descr_addr = descr->next->bus_addr;
	gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */

	/* second descr */
	sec_descr->buf_addr = buf[1];
	sec_descr->buf_size = skb->len - GELIC_NET_VLAN_POS;
	sec_descr->skb = skb;
	sec_descr->data_status = 0;
	sec_descr->next_descr_addr = 0; /* terminate hw descr */
	gelic_net_set_txdescr_cmdstat(sec_descr, skb, 0);

	/* bump free descriptor pointer */
	card->tx_chain.head = sec_descr->next;
	return 0;
}

@@ -730,7 +732,7 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
static int gelic_net_kick_txdma(struct gelic_net_card *card,
				struct gelic_net_descr *descr)
{
	int status = -ENXIO;
	int status = 0;
	int count = 10;

	if (card->tx_dma_progress)
@@ -764,47 +766,62 @@ static int gelic_net_kick_txdma(struct gelic_net_card *card,
static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
	struct gelic_net_card *card = netdev_priv(netdev);
	struct gelic_net_descr *descr = NULL;
	struct gelic_net_descr *descr;
	int result;
	unsigned long flags;

	spin_lock_irqsave(&card->tx_dma_lock, flags);

	gelic_net_release_tx_chain(card, 0);
	if (!skb)
		goto kick;

	descr = gelic_net_get_next_tx_descr(card);
	if (!descr) {
		/*
		 * no more descriptors free
		 */
		netif_stop_queue(netdev);
		spin_unlock_irqrestore(&card->tx_dma_lock, flags);
		return NETDEV_TX_BUSY;
	}
	result = gelic_net_prepare_tx_descr_v(card, descr, skb);

	if (result)
		goto error;

	card->tx_chain.head = card->tx_chain.head->next;

	if (descr->prev)
	result = gelic_net_prepare_tx_descr_v(card, descr, skb);
	if (result) {
		/*
		 * DMA map failed.  As chanses are that failure
		 * would continue, just release skb and return
		 */
		card->netdev_stats.tx_dropped++;
		dev_kfree_skb_any(skb);
		spin_unlock_irqrestore(&card->tx_dma_lock, flags);
		return NETDEV_TX_OK;
	}
	/*
	 * link this prepared descriptor to previous one
	 * to achieve high performance
	 */
	descr->prev->next_descr_addr = descr->bus_addr;
kick:
	/*
	 * as hardware descriptor is modified in the above lines,
	 * ensure that the hardware sees it
	 */
	wmb();
	if (gelic_net_kick_txdma(card, card->tx_chain.tail))
		goto error;

	if (gelic_net_kick_txdma(card, descr)) {
		/*
		 * kick failed.
		 * release descriptors which were just prepared
		 */
		card->netdev_stats.tx_dropped++;
		gelic_net_release_tx_descr(card, descr);
		gelic_net_release_tx_descr(card, descr->next);
		card->tx_chain.tail = descr->next->next;
		dev_info(ctodev(card), "%s: kick failure\n", __func__);
	} else {
		/* OK, DMA started/reserved */
		netdev->trans_start = jiffies;
	spin_unlock_irqrestore(&card->tx_dma_lock, flags);
	return NETDEV_TX_OK;
	}

error:
	card->netdev_stats.tx_dropped++;
	spin_unlock_irqrestore(&card->tx_dma_lock, flags);
	return NETDEV_TX_LOCKED;
	return NETDEV_TX_OK;
}

/**
@@ -1025,9 +1042,10 @@ static irqreturn_t gelic_net_interrupt(int irq, void *ptr)
	if (status & GELIC_NET_TXINT) {
		spin_lock_irqsave(&card->tx_dma_lock, flags);
		card->tx_dma_progress = 0;
		gelic_net_release_tx_chain(card, 0);
		/* kick outstanding tx descriptor if any */
		gelic_net_kick_txdma(card, card->tx_chain.tail);
		spin_unlock_irqrestore(&card->tx_dma_lock, flags);
		/* start pending DMA */
		gelic_net_xmit(NULL, netdev);
	}
	return IRQ_HANDLED;
}