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

Commit 153b8ef6 authored by Mayank Rana's avatar Mayank Rana
Browse files

USB: gadget: rndis: Optimize tx path for better performance



Current rndis header addition and aggregation steps:
1. Increase skb headroom by size of rndis header.
2. Copy skb data with newly created skb having more headroom.
3. Create and add rndis header with it.
4. Again copy skb data with usb request buffer for aggregation purpose.

New optimized steps :
1. Create and copy rndis header directly with usb request buffer
2. Copy skb data with usb request buffer

Above new steps avoids cpu intensive skb operations which reduces CPU
usage and provide better throughput.

This change also assigns completion handle with usb request at the time
of usb request creation instead of assigning everytime before queueing
usb request.

CRs-Fixed: 551363
Change-Id: Ifc64d6a8573f6a24e28c73ef9a19b62649e171cd
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent a5befd3f
Loading
Loading
Loading
Loading
+29 −7
Original line number Diff line number Diff line
@@ -405,14 +405,36 @@ static struct sk_buff *rndis_add_header(struct gether *port,
					struct sk_buff *skb)
{
	struct sk_buff *skb2;

	skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
	struct rndis_packet_msg_type *header = NULL;
	struct f_rndis *rndis = func_to_rndis(&port->func);

	if (rndis->port.multi_pkt_xfer) {
		if (port->header) {
			header = port->header;
			memset(header, 0, sizeof(*header));
			header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
			header->MessageLength = cpu_to_le32(skb->len +
							sizeof(*header));
			header->DataOffset = cpu_to_le32(36);
			header->DataLength = cpu_to_le32(skb->len);
			pr_debug("MessageLength:%d DataLength:%d\n",
						header->MessageLength,
						header->DataLength);
			return skb;
		} else {
			pr_err("RNDIS header is NULL.\n");
			return NULL;
		}
	} else {
		skb2 = skb_realloc_headroom(skb,
				sizeof(struct rndis_packet_msg_type));
		if (skb2)
			rndis_add_hdr(skb2);

		dev_kfree_skb_any(skb);
		return skb2;
	}
}

static void rndis_response_available(void *_rndis)
{
+58 −23
Original line number Diff line number Diff line
@@ -222,6 +222,7 @@ static void defer_kevent(struct eth_dev *dev, int flag)
}

static void rx_complete(struct usb_ep *ep, struct usb_request *req);
static void tx_complete(struct usb_ep *ep, struct usb_request *req);

static int
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
@@ -288,7 +289,6 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)

	req->buf = skb->data;
	req->length = size;
	req->complete = rx_complete;
	req->context = skb;

	retval = usb_ep_queue(out, req, gfp_flags);
@@ -381,6 +381,7 @@ static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
{
	unsigned		i;
	struct usb_request	*req;
	bool			usb_in;

	if (!n)
		return -ENOMEM;
@@ -391,10 +392,22 @@ static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
		if (i-- == 0)
			goto extra;
	}

	if (ep->desc->bEndpointAddress & USB_DIR_IN)
		usb_in = true;
	else
		usb_in = false;

	while (i--) {
		req = usb_ep_alloc_request(ep, GFP_ATOMIC);
		if (!req)
			return list_empty(list) ? -ENOMEM : 0;
		/* update completion handler */
		if (usb_in)
			req->complete = tx_complete;
		else
			req->complete = rx_complete;

		list_add(&req->list, list);
	}
	return 0;
@@ -538,7 +551,7 @@ static void eth_work(struct work_struct *work)

static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct sk_buff	*skb = req->context;
	struct sk_buff	*skb;
	struct eth_dev	*dev;
	struct net_device *net;
	struct usb_request *new_req;
@@ -612,7 +625,6 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
				}

				new_req->length = length;
				new_req->complete = tx_complete;
				retval = usb_ep_queue(in, new_req, GFP_ATOMIC);
				switch (retval) {
				default:
@@ -644,6 +656,7 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
			spin_unlock(&dev->req_lock);
		}
	} else {
		skb = req->context;
		spin_unlock(&dev->req_lock);
		dev_kfree_skb_any(skb);
	}
@@ -672,7 +685,7 @@ static int alloc_tx_buffer(struct eth_dev *dev)
	list_for_each(act, &dev->tx_reqs) {
		req = container_of(act, struct usb_request, list);
		if (!req->buf)
			req->buf = kmalloc(dev->tx_req_bufsize,
			req->buf = kzalloc(dev->tx_req_bufsize,
						GFP_ATOMIC);
			if (!req->buf)
				goto free_buf;
@@ -770,28 +783,37 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
	 * or the hardware can't use skb buffers.
	 * or there's not enough space for extra headers we need
	 */
	if (dev->wrap) {
		unsigned long	flags;

	spin_lock_irqsave(&dev->lock, flags);
	if (dev->wrap) {
		if (dev->port_usb)
			skb = dev->wrap(dev->port_usb, skb);
		if (!skb) {
			spin_unlock_irqrestore(&dev->lock, flags);
		if (!skb)
			goto drop;
		}

	spin_lock_irqsave(&dev->req_lock, flags);
	dev->tx_skb_hold_count++;
	spin_unlock_irqrestore(&dev->req_lock, flags);
	}

	if (multi_pkt_xfer) {

		pr_debug("req->length:%d header_len:%u\n"
				"skb->len:%d skb->data_len:%d\n",
				req->length, dev->header_len,
				skb->len, skb->data_len);
		/* Add RNDIS Header */
		memcpy(req->buf + req->length, dev->port_usb->header,
						dev->header_len);
		/* Increment req length by header size */
		req->length += dev->header_len;
		spin_unlock_irqrestore(&dev->lock, flags);
		/* Copy received IP data from SKB */
		memcpy(req->buf + req->length, skb->data, skb->len);
		req->length = req->length + skb->len;
		/* Increment req length by skb data length */
		req->length += skb->len;
		length = req->length;
		dev_kfree_skb_any(skb);

		spin_lock_irqsave(&dev->req_lock, flags);
		dev->tx_skb_hold_count++;
		if (dev->tx_skb_hold_count < dev->dl_max_pkts_per_xfer) {
			if (dev->no_tx_req_used > TX_REQ_THRESHOLD) {
				list_add(&req->list, &dev->tx_reqs);
@@ -801,19 +823,15 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
		}

		dev->no_tx_req_used++;
		spin_unlock_irqrestore(&dev->req_lock, flags);

		spin_lock_irqsave(&dev->lock, flags);
		dev->tx_skb_hold_count = 0;
		spin_unlock_irqrestore(&dev->lock, flags);
		spin_unlock_irqrestore(&dev->req_lock, flags);
	} else {
		spin_unlock_irqrestore(&dev->lock, flags);
		length = skb->len;
		req->buf = skb->data;
		req->context = skb;
	}

	req->complete = tx_complete;

	/* NCM requires no zlp if transfer is dwNtbInMaxSize */
	if (dev->port_usb->is_fixed &&
	    length == dev->port_usb->fixed_in_len &&
@@ -866,7 +884,7 @@ drop:
		spin_lock_irqsave(&dev->req_lock, flags);
		if (list_empty(&dev->tx_reqs))
			netif_start_queue(net);
		list_add(&req->list, &dev->tx_reqs);
		list_add_tail(&req->list, &dev->tx_reqs);
		spin_unlock_irqrestore(&dev->req_lock, flags);
	}
success:
@@ -1224,6 +1242,14 @@ struct net_device *gether_connect(struct gether *link)
	if (!dev)
		return ERR_PTR(-EINVAL);

	link->header = kzalloc(sizeof(struct rndis_packet_msg_type),
							GFP_ATOMIC);
	if (!link->header) {
		pr_err("RNDIS header memory allocation failed.\n");
		result = -ENOMEM;
		goto fail;
	}

	link->in_ep->driver_data = dev;
	result = usb_ep_enable(link->in_ep);
	if (result != 0) {
@@ -1244,6 +1270,7 @@ struct net_device *gether_connect(struct gether *link)
		result = alloc_requests(dev, link, qlen(dev->gadget));

	if (result == 0) {

		dev->zlp = link->is_zlp_ok;
		DBG(dev, "qlen %d\n", qlen(dev->gadget));

@@ -1282,10 +1309,15 @@ struct net_device *gether_connect(struct gether *link)
fail1:
		(void) usb_ep_disable(link->in_ep);
	}
fail0:

	/* caller is responsible for cleanup on error */
	if (result < 0)
	if (result < 0) {
fail0:
		kfree(link->header);
fail:
		return ERR_PTR(result);
	}

	return dev->net;
}

@@ -1332,6 +1364,9 @@ void gether_disconnect(struct gether *link)
		usb_ep_free_request(link->in_ep, req);
		spin_lock(&dev->req_lock);
	}
	/* Free rndis header buffer memory */
	kfree(link->header);
	link->header = NULL;
	spin_unlock(&dev->req_lock);
	link->in_ep->driver_data = NULL;
	link->in_ep->desc = NULL;
+2 −0
Original line number Diff line number Diff line
@@ -69,6 +69,8 @@ struct gether {
	/* called on network open/close */
	void				(*open)(struct gether *);
	void				(*close)(struct gether *);
	struct rndis_packet_msg_type	*header;

};

#define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \