devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Timur Tabi <timur@codeaurora.org>
To: Lino Sanfilippo <LinoSanfilippo@gmx.de>,
	netdev@vger.kernel.org, devicetree@vger.kernel.org,
	linux-arm-msm@vger.kernel.org, sdharia@codeaurora.org,
	shankerd@codeaurora.org, vikrams@codeaurora.org,
	cov@codeaurora.org, gavidov@codeaurora.org, robh+dt@kernel.org,
	andrew@lunn.ch, bjorn.andersson@linaro.org, mlangsdo@redhat.com,
	jcm@redhat.com, agross@codeaurora.org, davem@davemloft.net,
	f.fainelli@gmail.com
Subject: Re: [PATCH] [v7] net: emac: emac gigabit ethernet controller driver
Date: Tue, 9 Aug 2016 13:25:05 -0500	[thread overview]
Message-ID: <57AA2001.2010904@codeaurora.org> (raw)
In-Reply-To: <9ebb6cb7-c793-cd76-5283-c9a659d0398f@gmx.de>

Lino Sanfilippo wrote:

>> +/* Fill up transmit descriptors */
>> +static void emac_tx_fill_tpd(struct emac_adapter *adpt,
>> +			     struct emac_tx_queue *tx_q, struct sk_buff *skb,
>> +			     struct emac_tpd *tpd)
>> +{
>> +	u16 nr_frags = skb_shinfo(skb)->nr_frags;
>> +	unsigned int len = skb_headlen(skb);
>> +	struct emac_buffer *tpbuf = NULL;
>> +	unsigned int mapped_len = 0;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	/* if Large Segment Offload is (in TCP Segmentation Offload struct) */
>> +	if (TPD_LSO(tpd)) {
>> +		mapped_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
>> +
>> +		tpbuf = GET_TPD_BUFFER(tx_q, tx_q->tpd.produce_idx);
>> +		tpbuf->length = mapped_len;
>> +		tpbuf->dma_addr = dma_map_single(adpt->netdev->dev.parent,
>> +						 skb->data, mapped_len,
>> +						 DMA_TO_DEVICE);
>> +		ret = dma_mapping_error(adpt->netdev->dev.parent,
>> +					tpbuf->dma_addr);
>> +		if (ret) {
>> +			dev_kfree_skb(skb);
>> +			return;
>> +		}
>> +
>> +		TPD_BUFFER_ADDR_L_SET(tpd, lower_32_bits(tpbuf->dma_addr));
>> +		TPD_BUFFER_ADDR_H_SET(tpd, upper_32_bits(tpbuf->dma_addr));
>
> You should also take big endian systems into account. This means that if the multi-byte values
> in the descriptors require little-endian you have to convert from host byte order to le and
> vice versa. You can use cpu_to_le32() and friends for this.

I used to work on PowerPC, so I respect making things work for both 
endians.  However, even I think that this is overkill for my driver. 
First, there's no way this driver will ever be used on a big-endian 
system.  Second, I'm pretty sure there are lots of places that would 
need cpu_to_le32() in order to make this driver big-endian compatible. 
It would be a huge mess.

#define TPD_BUFFER_ADDR_H_SET(tpd, val)	BITS_SET((tpd)->word[3], 18, 30, 
val)

This macros sets specific bits based on the definition of the register. 
  I could change it to this:

#define TPD_BUFFER_ADDR_H_SET(tpd, val) BITS_SET((tpd)->word[3], 18, 30, 
cpu_to_le32(val))

But I honestly don't see what good that will do.  There are still 
thousands of other places that assume little-endian.

>> +		TPD_BUF_LEN_SET(tpd, tpbuf->length);
>> +		emac_tx_tpd_create(adpt, tx_q, tpd);
>> +	}
>> +
>> +	if (mapped_len < len) {
>> +		tpbuf = GET_TPD_BUFFER(tx_q, tx_q->tpd.produce_idx);
>> +		tpbuf->length = len - mapped_len;
>> +		tpbuf->dma_addr = dma_map_single(adpt->netdev->dev.parent,
>> +						 skb->data + mapped_len,
>> +						 tpbuf->length, DMA_TO_DEVICE);
>> +		ret = dma_mapping_error(adpt->netdev->dev.parent,
>> +					tpbuf->dma_addr);
>> +		if (ret) {
>> +			dev_kfree_skb(skb);
>> +			return;
>> +		}
>> +
>> +		TPD_BUFFER_ADDR_L_SET(tpd, lower_32_bits(tpbuf->dma_addr));
>> +		TPD_BUFFER_ADDR_H_SET(tpd, upper_32_bits(tpbuf->dma_addr));
>> +		TPD_BUF_LEN_SET(tpd, tpbuf->length);
>> +		emac_tx_tpd_create(adpt, tx_q, tpd);
>> +	}
>> +
>> +	for (i = 0; i < nr_frags; i++) {
>> +		struct skb_frag_struct *frag;
>> +
>> +		frag = &skb_shinfo(skb)->frags[i];
>> +
>> +		tpbuf = GET_TPD_BUFFER(tx_q, tx_q->tpd.produce_idx);
>> +		tpbuf->length = frag->size;
>> +		tpbuf->dma_addr = dma_map_page(adpt->netdev->dev.parent,
>> +					       frag->page.p, frag->page_offset,
>> +					       tpbuf->length, DMA_TO_DEVICE);
>> +		ret = dma_mapping_error(adpt->netdev->dev.parent,
>> +					tpbuf->dma_addr);
>> +		if (ret) {
>> +			dev_kfree_skb(skb);
>> +			return;
>> +		}
>
> In case of error you need to undo all mappings that you have done so far.

Ok.

>
>> +
>> +		TPD_BUFFER_ADDR_L_SET(tpd, lower_32_bits(tpbuf->dma_addr));
>> +		TPD_BUFFER_ADDR_H_SET(tpd, upper_32_bits(tpbuf->dma_addr));
>> +		TPD_BUF_LEN_SET(tpd, tpbuf->length);
>> +		emac_tx_tpd_create(adpt, tx_q, tpd);
>> +	}
>> +
>> +	/* The last tpd */
>> +	emac_tx_tpd_mark_last(adpt, tx_q);
>
> Use a wmb() here to make sure that all writes to the descriptors in dma memory
> are completed before you update the producer register (see memory-barriers.txt
> for the reason why this is needed)

Ok.

>> +/* Transmit the packet using specified transmit queue */
>> +int emac_mac_tx_buf_send(struct emac_adapter *adpt, struct emac_tx_queue *tx_q,
>> +			 struct sk_buff *skb)
>> +{
>> +	struct emac_tpd tpd;
>> +	u32 prod_idx;
>> +
>> +	memset(&tpd, 0, sizeof(tpd));
>> +
>> +	if (emac_tso_csum(adpt, tx_q, skb, &tpd) != 0) {
>> +		dev_kfree_skb_any(skb);
>> +		return NETDEV_TX_OK;
>> +	}
>> +
>> +	if (skb_vlan_tag_present(skb)) {
>> +		u16 tag;
>> +
>> +		EMAC_VLAN_TO_TAG(skb_vlan_tag_get(skb), tag);
>> +		TPD_CVLAN_TAG_SET(&tpd, tag);
>> +		TPD_INSTC_SET(&tpd, 1);
>> +	}
>> +
>> +	if (skb_network_offset(skb) != ETH_HLEN)
>> +		TPD_TYP_SET(&tpd, 1);
>> +
>> +	emac_tx_fill_tpd(adpt, tx_q, skb, &tpd);
>> +
>> +	netdev_sent_queue(adpt->netdev, skb->len);
>> +
>> +	if (emac_tpd_num_free_descs(tx_q) <= (MAX_SKB_FRAGS + 1))
>> +		netif_stop_queue(adpt->netdev);
>
> Is MAX_SKB_FRAGS + 1 really the max number of descriptors required by your driver?
> There seem to be further descriptors needed for TSO and checksumming.
> Please make sure that you really check against the _max_ possible number of
> descriptors for a transmission.

I need some help figuring that out.  Like I said, I didn't write this 
driver initially, so there are parts that I don't really understand.  I 
copied the above code from other drivers, but after studying your 
question, I think I understand what you're asking.  I just don't know 
how to fix it.

First of all, why do other drivers test MAX_SKB_FRAGS + 1?  Why the +1?

The driver originally used function emac_tx_has_enough_descs() to 
determine if there is enough room for the new packet.  Then I changed 
the code as you suggested, and now it guesses how many descriptors need 
to be free to support the next packet.

If I'm reading emac_tx_fill_tpd() correctly, there could be as many as 
(2 + skb_shinfo(skb)->nr_frags) descriptors for a given packet.  I don't 
know how big nr_frags could get, so I don't know how to calculate the 
number of descriptors I really need.  I'm assuming I need to do 
something like this:

unsigned int max_desc_per_skb = 2 + ????;
unsigned int num_desc_needed = (MAX_SKB_FRAGS + 1) * max_desc_per_skb;

if (emac_tpd_num_free_descs(tx_q) < num_desc_needed)
	netif_stop_queue(adpt->netdev);

>> +
>> +/* Change the Maximum Transfer Unit (MTU) */
>> +static int emac_change_mtu(struct net_device *netdev, int new_mtu)
>> +{
>> +	unsigned int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
>> +	struct emac_adapter *adpt = netdev_priv(netdev);
>> +
>> +	if ((max_frame < EMAC_MIN_ETH_FRAME_SIZE) ||
>> +	    (max_frame > EMAC_MAX_ETH_FRAME_SIZE)) {
>> +		netdev_err(adpt->netdev, "error: invalid MTU setting\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	netif_info(adpt, hw, adpt->netdev,
>> +		   "changing MTU from %d to %d\n", netdev->mtu,
>> +		   new_mtu);
>> +	netdev->mtu = new_mtu;
>> +	adpt->rxbuf_size = new_mtu > EMAC_DEF_RX_BUF_SIZE ?
>> +		ALIGN(max_frame, 8) : EMAC_DEF_RX_BUF_SIZE;
>> +
>> +	if (netif_running(netdev))
>> +		return emac_reinit_locked(adpt);
>
> This does still not look correct. The rxbuf_size is changed while the interface
> is still running. If the rx buffers are refilled now, the rx buffers size does
> not match the size that is configured in the mac, does it?
> You have to stop the rx path first, then change the rx buffer size and then
> restart the rx path.

Ok.  Thanks for catching that.  I didn't notice that adpt->rxbuf_size 
was used in emac_mac_rx_descs_refill().

However, I'm confused about one thing.  Almost every other driver just 
sets "netdev->mtu = new_mtu" and does nothing else.  I can't find any 
other driver that actually stops the RX path, reprograms the hardware, 
and then restarts the RX path.  I know this is a stupid question, but 
why is my driver doing that?

Can I get away with just calling netdev_update_features()?

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the
Code Aurora Forum, hosted by The Linux Foundation.

  reply	other threads:[~2016-08-09 18:25 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-03 20:12 [PATCH] [v7] net: emac: emac gigabit ethernet controller driver Timur Tabi
2016-08-04 17:55 ` Rob Herring
2016-08-04 18:18   ` Timur Tabi
2016-08-05 19:36   ` Timur Tabi
2016-08-15 20:06   ` Timur Tabi
2016-08-16 13:29     ` Rob Herring
2016-08-16 13:39       ` Timur Tabi
2016-08-16 21:20         ` Al Stone
2016-08-16 21:37           ` Timur Tabi
2016-08-17 19:25             ` Timur Tabi
     [not found]             ` <57B3878D.1000805-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-08-17 20:07               ` Florian Fainelli
2016-08-17 20:19                 ` Timur Tabi
     [not found]                   ` <57B4C6EE.3080903-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-08-17 22:32                     ` Timur Tabi
     [not found]                       ` <57B4E5F7.9040500-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-08-17 22:48                         ` Florian Fainelli
2016-08-18  3:27                           ` Timur Tabi
     [not found]                             ` <57B52B17.1080809-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-08-18  4:04                               ` Florian Fainelli
     [not found]                                 ` <5CCEFB33-8F93-40D7-BD32-ACDE1CBA586D-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-18  4:19                                   ` Timur Tabi
2016-08-18 16:09                                     ` Florian Fainelli
2016-08-18 17:56                                       ` Timur Tabi
     [not found] ` <1470255143-3979-1-git-send-email-timur-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-08-04 23:29   ` Lino Sanfilippo
2016-08-09 18:25     ` Timur Tabi [this message]
     [not found]       ` <57AA2001.2010904-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-08-09 18:39         ` Rob Herring
2016-08-09 19:34         ` Timur Tabi
2016-08-09 19:17       ` Florian Fainelli
     [not found]         ` <214dcbb7-0c3b-1e00-3e50-db513d77b10b-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-10  1:09           ` Timur Tabi
2016-08-10  1:25             ` Florian Fainelli
2016-08-10 16:38               ` Timur Tabi
2016-08-10 17:49                 ` Florian Fainelli
2016-08-11 14:22                   ` Timur Tabi
2016-08-11 15:10                     ` Timur Tabi
2016-08-11 16:03                       ` Timur Tabi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=57AA2001.2010904@codeaurora.org \
    --to=timur@codeaurora.org \
    --cc=LinoSanfilippo@gmx.de \
    --cc=agross@codeaurora.org \
    --cc=andrew@lunn.ch \
    --cc=bjorn.andersson@linaro.org \
    --cc=cov@codeaurora.org \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=f.fainelli@gmail.com \
    --cc=gavidov@codeaurora.org \
    --cc=jcm@redhat.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=mlangsdo@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    --cc=sdharia@codeaurora.org \
    --cc=shankerd@codeaurora.org \
    --cc=vikrams@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).