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

Commit 7ec4c2f8 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: gadget: u_ether: add scatter/gather support"

parents 28323d09 d062ba1f
Loading
Loading
Loading
Loading
+25 −2
Original line number Diff line number Diff line
@@ -407,11 +407,11 @@ static struct sk_buff *rndis_add_header(struct gether *port,
	struct sk_buff *skb2;
	struct rndis_packet_msg_type *header = NULL;
	struct f_rndis *rndis = func_to_rndis(&port->func);
	struct usb_composite_dev *cdev = port->func.config->cdev;

	if (rndis->port.multi_pkt_xfer) {
	if (rndis->port.multi_pkt_xfer || cdev->gadget->sg_supported) {
		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));
@@ -422,6 +422,7 @@ static struct sk_buff *rndis_add_header(struct gether *port,
						header->DataLength);
			return skb;
		} else {
			dev_kfree_skb_any(skb);
			pr_err("RNDIS header is NULL.\n");
			return NULL;
		}
@@ -506,6 +507,7 @@ static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
	}
}

#define MAX_PKTS_PER_XFER	10
static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct f_rndis			*rndis = req->context;
@@ -528,6 +530,27 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
	buf = (rndis_init_msg_type *)req->buf;

	if (buf->MessageType == RNDIS_MSG_INIT) {
		if (cdev->gadget->sg_supported) {
			rndis->port.dl_max_xfer_size = buf->MaxTransferSize;
			gether_update_dl_max_xfer_size(&rndis->port,
					rndis->port.dl_max_xfer_size);

			/* if SG is enabled multiple packets can be put
			 * together too quickly. However, module param
			 * is not honored.
			 */
			rndis->port.dl_max_pkts_per_xfer = MAX_PKTS_PER_XFER;

			/* rndis spec does not specify max packets per xfer
			 * but just to be consistent update it with 10
			 * packets
			 */
			gether_update_dl_max_pkts_per_xfer(&rndis->port,
						MAX_PKTS_PER_XFER);

			return;
		}

		if (buf->MaxTransferSize > 2048)
			rndis->port.multi_pkt_xfer = 1;
		else
+320 −28
Original line number Diff line number Diff line
@@ -52,7 +52,22 @@
#define UETH__VERSION	"29-May-2008"

static struct workqueue_struct	*uether_wq;
static struct workqueue_struct	*uether_tx_wq;

static int tx_start_threshold = 1500;
module_param(tx_start_threshold, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(tx_start_threshold,
	"Threashold to start stopped network queue");

static int tx_stop_threshold = 2000;
module_param(tx_stop_threshold, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(tx_stop_threshold,
	"Threashold to stop network queue");

/* this refers to max number sgs per transfer
 * which includes headers/data packets
 */
#define DL_MAX_PKTS_PER_XFER	20
struct eth_dev {
	/* lock is held while accessing port_usb
	 */
@@ -70,12 +85,14 @@ struct eth_dev {
	int			no_tx_req_used;
	int			tx_skb_hold_count;
	u32			tx_req_bufsize;
	struct sk_buff_head	tx_skb_q;

	struct sk_buff_head	rx_frames;

	unsigned		header_len;
	unsigned int		ul_max_pkts_per_xfer;
	unsigned int		dl_max_pkts_per_xfer;
	uint32_t		dl_max_xfer_size;
	bool			rx_trigger_enabled;
	struct sk_buff		*(*wrap)(struct gether *, struct sk_buff *skb);
	int			(*unwrap)(struct gether *,
@@ -84,6 +101,7 @@ struct eth_dev {

	struct work_struct	work;
	struct work_struct	rx_work;
	struct work_struct	tx_work;

	unsigned long		todo;
	unsigned long		flags;
@@ -92,21 +110,34 @@ struct eth_dev {

	bool			zlp;
	u8			host_mac[ETH_ALEN];

	/* stats */
	unsigned long		tx_throttle;
	unsigned int		tx_aggr_cnt[DL_MAX_PKTS_PER_XFER];
	unsigned int		loop_brk_cnt;
	struct dentry		*uether_dent;
	struct dentry		*uether_dfile;
};

static void uether_debugfs_init(struct eth_dev *dev);
/* when sg is enabled, sg_ctx is used to track skb each usb request will
 * xfer
 */
struct sg_ctx {
	struct sk_buff_head	skbs;
};


static void uether_debugfs_init(struct eth_dev *dev, const char *n);
static void uether_debugfs_exit(struct eth_dev *dev);

/*-------------------------------------------------------------------------*/


#define RX_EXTRA	20	/* bytes guarding against rx overflows */

#define DEFAULT_QLEN	2	/* double buffering by default */

static unsigned qmult = 10;
static unsigned qmult = 20;
module_param(qmult, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");

@@ -384,11 +415,14 @@ clean:
		queue_work(uether_wq, &dev->rx_work);
}

static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
static int prealloc(struct list_head *list,
		struct usb_ep *ep, unsigned n,
		bool sg_supported, int hlen)
{
	unsigned		i;
	struct usb_request	*req;
	bool			usb_in;
	struct sg_ctx		*sg_ctx;

	if (!n)
		return -ENOMEM;
@@ -410,11 +444,26 @@ static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
		if (!req)
			return list_empty(list) ? -ENOMEM : 0;
		/* update completion handler */
		if (usb_in)
		if (usb_in) {
			req->complete = tx_complete;
		else
			if (!sg_supported)
				goto add_list;
			req->sg = kmalloc(
					DL_MAX_PKTS_PER_XFER *
						sizeof(struct scatterlist),
					GFP_ATOMIC);
			if (!req->sg)
				goto extra;
			sg_ctx = kmalloc(sizeof(*sg_ctx), GFP_ATOMIC);
			if (!sg_ctx)
				goto extra;
			req->context = sg_ctx;
			req->buf = kzalloc(DL_MAX_PKTS_PER_XFER * hlen,
						GFP_ATOMIC);
		} else {
			req->complete = rx_complete;

		}
add_list:
		list_add(&req->list, list);
	}
	return 0;
@@ -426,6 +475,13 @@ extra:

		next = req->list.next;
		list_del(&req->list);

		if (sg_supported) {
			kfree(req->sg);
			kfree(req->context);
			kfree(req->buf);
		}

		usb_ep_free_request(ep, req);

		if (next == list)
@@ -441,10 +497,14 @@ static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
	int	status;

	spin_lock(&dev->req_lock);
	status = prealloc(&dev->tx_reqs, link->in_ep, n);
	status = prealloc(&dev->tx_reqs, link->in_ep, n,
				dev->gadget->sg_supported,
				dev->header_len);
	if (status < 0)
		goto fail;
	status = prealloc(&dev->rx_reqs, link->out_ep, n);
	status = prealloc(&dev->rx_reqs, link->out_ep, n,
				dev->gadget->sg_supported,
				dev->header_len);
	if (status < 0)
		goto fail;
	goto done;
@@ -593,6 +653,25 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
	case -ESHUTDOWN:		/* disconnect etc */
		break;
	case 0:
		if (req->num_sgs) {
			struct sg_ctx *sg_ctx = req->context;
			int n = skb_queue_len(&sg_ctx->skbs);

			dev->net->stats.tx_bytes += req->length;
			dev->net->stats.tx_packets += n;
			dev->tx_aggr_cnt[n-1]++;

			skb_queue_purge(&sg_ctx->skbs);

			spin_lock(&dev->req_lock);
			list_add_tail(&req->list, &dev->tx_reqs);
			spin_unlock(&dev->req_lock);

			queue_work(uether_tx_wq, &dev->tx_work);

			return;
		}

		if (!req->zero)
			dev->net->stats.tx_bytes += req->length-1;
		else
@@ -726,6 +805,147 @@ free_buf:
	return -ENOMEM;
}

static void process_tx_w(struct work_struct *w)
{
	struct eth_dev		*dev = container_of(w, struct eth_dev, tx_work);
	struct net_device	*net = NULL;
	struct sk_buff		*skb = NULL;
	struct sg_ctx		*sg_ctx;
	struct usb_request	*req;
	struct usb_ep		*in = NULL;
	int			ret, count, hlen = 0, hdr_offset;
	uint32_t		max_size = 0;
	uint32_t		max_num_pkts = 1;
	unsigned long		flags;
	bool			header_on = false;
	int			req_cnt = 0;

	spin_lock_irqsave(&dev->lock, flags);
	if (dev->port_usb) {
		in = dev->port_usb->in_ep;
		max_size = dev->dl_max_xfer_size;
		max_num_pkts = dev->dl_max_pkts_per_xfer;
		if (!max_num_pkts)
			max_num_pkts = 1;
		hlen = dev->header_len;
		net = dev->net;
	}
	spin_unlock_irqrestore(&dev->lock, flags);

	spin_lock_irqsave(&dev->req_lock, flags);
	while (in && !list_empty(&dev->tx_reqs) &&
			(skb = skb_dequeue(&dev->tx_skb_q))) {
		req = list_first_entry(&dev->tx_reqs, struct usb_request,
				list);
		list_del(&req->list);
		spin_unlock_irqrestore(&dev->req_lock, flags);

		req->num_sgs = 0;
		req->zero = 1;
		req->length = 0;
		sg_ctx = req->context;
		skb_queue_head_init(&sg_ctx->skbs);
		sg_init_table(req->sg, DL_MAX_PKTS_PER_XFER);

		hdr_offset = 0;
		count = 1;
		do {
			/* spinlock can be avoided if buffer can passed
			 * wrap callback argument. However, it requires
			 * changes to all existing clients
			 */
			spin_lock_irqsave(&dev->lock, flags);
			if (!dev->port_usb) {
				spin_unlock_irqrestore(&dev->lock, flags);
				skb_queue_purge(&sg_ctx->skbs);
				kfree(req->sg);
				kfree(req->context);
				kfree(req->buf);
				usb_ep_free_request(in, req);

				return;
			}

			if (hlen && dev->wrap) {
				dev->port_usb->header = req->buf + hdr_offset;
				skb = dev->wrap(dev->port_usb, skb);
				header_on = true;
			}
			spin_unlock_irqrestore(&dev->lock, flags);

			if (header_on) {
				sg_set_buf(&req->sg[req->num_sgs],
					req->buf + hdr_offset, hlen);
				req->num_sgs++;
				hdr_offset += hlen;
				req->length += hlen;
			}

			/* skb processing */
			sg_set_buf(&req->sg[req->num_sgs], skb->data, skb->len);
			req->num_sgs++;

			req->length += skb->len;
			skb_queue_tail(&sg_ctx->skbs, skb);

			skb = skb_dequeue(&dev->tx_skb_q);
			if (!skb)
				break;
			if ((req->length + skb->len + hlen) >= max_size ||
					count >= max_num_pkts) {
				skb_queue_head(&dev->tx_skb_q, skb);
				break;
			}
			count++;
		} while (true);

		sg_mark_end(&req->sg[req->num_sgs - 1]);

		in = NULL;
		spin_lock_irqsave(&dev->lock, flags);
		if (dev->port_usb)
			in = dev->port_usb->in_ep;
		spin_unlock_irqrestore(&dev->lock, flags);

		if (!in) {
			__skb_queue_purge(&sg_ctx->skbs);
			kfree(req->sg);
			kfree(req->context);
			kfree(req->buf);
			usb_ep_free_request(in, req);

			return;
		}

		ret = usb_ep_queue(in, req, GFP_KERNEL);
		spin_lock_irqsave(&dev->req_lock, flags);
		switch (ret) {
		default:
			dev->net->stats.tx_dropped +=
				skb_queue_len(&sg_ctx->skbs);

			__skb_queue_purge(&sg_ctx->skbs);
			list_add_tail(&req->list, &dev->tx_reqs);
			break;
		case 0:
			net->trans_start = jiffies;
		}

		/* break the loop after processing 10 packets
		 * otherwise wd may kick in
		 */
		if (ret || ++req_cnt > 10) {
			dev->loop_brk_cnt++;
			break;
		}

		if (dev->tx_skb_q.qlen <  tx_start_threshold)
			netif_start_queue(net);

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

static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
					struct net_device *net)
{
@@ -774,6 +994,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
		/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
	}

	if (dev->gadget->sg_supported) {
		skb_queue_tail(&dev->tx_skb_q, skb);
		if (dev->tx_skb_q.qlen > tx_stop_threshold) {
			dev->tx_throttle++;
			netif_stop_queue(net);
		}

		queue_work(uether_tx_wq, &dev->tx_work);
		return NETDEV_TX_OK;
	}

	/*
	 * No buffer copies needed, unless the network stack did it
	 * or the hardware can't use skb buffers or there's not enough
@@ -1244,10 +1475,12 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
	spin_lock_init(&dev->req_lock);
	INIT_WORK(&dev->work, eth_work);
	INIT_WORK(&dev->rx_work, process_rx_w);
	INIT_WORK(&dev->tx_work, process_tx_w);
	INIT_LIST_HEAD(&dev->tx_reqs);
	INIT_LIST_HEAD(&dev->rx_reqs);

	skb_queue_head_init(&dev->rx_frames);
	skb_queue_head_init(&dev->tx_skb_q);

	/* network device setup */
	dev->net = net;
@@ -1288,7 +1521,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
		 *  - tx queueing enabled if open *and* carrier is "on"
		 */
		netif_carrier_off(net);
		uether_debugfs_init(dev);
		uether_debugfs_init(dev, netname);
	}

	return dev;
@@ -1311,6 +1544,29 @@ void gether_cleanup(struct eth_dev *dev)
	free_netdev(dev->net);
}

void gether_update_dl_max_xfer_size(struct gether *link, uint32_t s)
{
	struct eth_dev		*dev = link->ioport;
	unsigned long flags;

	spin_lock_irqsave(&dev->lock, flags);
	dev->dl_max_xfer_size = s;
	spin_unlock_irqrestore(&dev->lock, flags);
}

void gether_update_dl_max_pkts_per_xfer(struct gether *link, uint32_t n)
{
	struct eth_dev		*dev = link->ioport;
	unsigned long flags;

	if (n > DL_MAX_PKTS_PER_XFER)
		n = DL_MAX_PKTS_PER_XFER;

	spin_lock_irqsave(&dev->lock, flags);
	dev->dl_max_pkts_per_xfer = n;
	spin_unlock_irqrestore(&dev->lock, flags);
}

/**
 * gether_connect - notify network layer that USB link is active
 * @link: the USB link, set up with endpoints, descriptors matching
@@ -1336,6 +1592,10 @@ struct net_device *gether_connect(struct gether *link)
	if (!dev)
		return ERR_PTR(-EINVAL);

	/* if scatter/gather or sg is supported then headers can be part of
	 * req->buf which is allocated later
	 */
	if (!dev->gadget->sg_supported) {
		link->header = kzalloc(sizeof(struct rndis_packet_msg_type),
						GFP_ATOMIC);
		if (!link->header) {
@@ -1343,6 +1603,7 @@ struct net_device *gether_connect(struct gether *link)
			result = -ENOMEM;
			goto fail;
		}
	}

	link->in_ep->driver_data = dev;
	result = usb_ep_enable(link->in_ep);
@@ -1360,6 +1621,14 @@ struct net_device *gether_connect(struct gether *link)
		goto fail1;
	}

	dev->header_len = link->header_len;
	dev->unwrap = link->unwrap;
	dev->wrap = link->wrap;
	dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer;
	dev->dl_max_pkts_per_xfer = link->dl_max_pkts_per_xfer;
	dev->dl_max_xfer_size = link->dl_max_xfer_size;
	dev->rx_trigger_enabled = link->rx_trigger_enabled;

	if (result == 0)
		result = alloc_requests(dev, link, qlen(dev->gadget));

@@ -1368,13 +1637,6 @@ struct net_device *gether_connect(struct gether *link)
		dev->zlp = link->is_zlp_ok;
		DBG(dev, "qlen %d\n", qlen(dev->gadget));

		dev->header_len = link->header_len;
		dev->unwrap = link->unwrap;
		dev->wrap = link->wrap;
		dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer;
		dev->dl_max_pkts_per_xfer = link->dl_max_pkts_per_xfer;
		dev->rx_trigger_enabled = link->rx_trigger_enabled;

		spin_lock(&dev->lock);
		dev->tx_skb_hold_count = 0;
		dev->no_tx_req_used = 0;
@@ -1453,17 +1715,28 @@ void gether_disconnect(struct gether *link)
		list_del(&req->list);

		spin_unlock(&dev->req_lock);
		if (link->multi_pkt_xfer) {
		if (link->multi_pkt_xfer ||
				dev->gadget->sg_supported) {
			kfree(req->buf);
			req->buf = NULL;
		}
		if (dev->gadget->sg_supported) {
			kfree(req->context);
			kfree(req->sg);
		}

		usb_ep_free_request(link->in_ep, req);
		spin_lock(&dev->req_lock);
	}

	/* Free rndis header buffer memory */
	if (!dev->gadget->sg_supported)
		kfree(link->header);
	link->header = NULL;
	spin_unlock(&dev->req_lock);

	skb_queue_purge(&dev->tx_skb_q);

	link->in_ep->driver_data = NULL;
	link->in_ep->desc = NULL;

@@ -1519,9 +1792,18 @@ static int uether_stat_show(struct seq_file *s, void *unused)
{
	struct eth_dev *dev = s->private;
	int ret = 0;
	int i;

	if (dev) {
		seq_printf(s, "tx_throttle = %lu\n aggr count:",
					dev->tx_throttle);
		for (i = 0; i < DL_MAX_PKTS_PER_XFER; i++)
			seq_printf(s, "%u\t", dev->tx_aggr_cnt[i]);

		seq_printf(s, "\nloop_brk_cnt = %u",
					dev->loop_brk_cnt);
	}

	if (dev)
		seq_printf(s, "tx_throttle = %lu\n", dev->tx_throttle);
	return ret;
}

@@ -1550,12 +1832,12 @@ const struct file_operations uether_stats_ops = {
	.write = uether_stat_reset,
};

static void uether_debugfs_init(struct eth_dev *dev)
static void uether_debugfs_init(struct eth_dev *dev, const char *name)
{
	struct dentry *uether_dent;
	struct dentry *uether_dfile;

	uether_dent = debugfs_create_dir("uether_rndis", 0);
	uether_dent = debugfs_create_dir(name, 0);
	if (IS_ERR(uether_dent))
		return;
	dev->uether_dent = uether_dent;
@@ -1582,12 +1864,22 @@ static int __init gether_init(void)
		pr_err("%s: Unable to create workqueue: uether\n", __func__);
		return -ENOMEM;
	}

	uether_tx_wq = alloc_workqueue("uether_tx",
				WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
	if (!uether_tx_wq) {
		destroy_workqueue(uether_wq);
		pr_err("%s: Unable to create workqueue: uether\n", __func__);
		return -ENOMEM;
	}

	return 0;
}
module_init(gether_init);

static void __exit gether_exit(void)
{
	destroy_workqueue(uether_tx_wq);
	destroy_workqueue(uether_wq);

}
+4 −1
Original line number Diff line number Diff line
@@ -56,7 +56,8 @@ struct gether {
	u32				fixed_in_len;

	unsigned			ul_max_pkts_per_xfer;
	unsigned			dl_max_pkts_per_xfer;
	uint32_t			dl_max_pkts_per_xfer;
	uint32_t			dl_max_xfer_size;
	bool				multi_pkt_xfer;
	bool				rx_trigger_enabled;
	bool				rx_triggered;
@@ -107,6 +108,8 @@ void gether_cleanup(struct eth_dev *dev);
struct net_device *gether_connect(struct gether *);
void gether_disconnect(struct gether *);
int gether_up(struct gether *);
void gether_update_dl_max_pkts_per_xfer(struct gether *link, uint32_t n);
void gether_update_dl_max_xfer_size(struct gether *link, uint32_t s);

/* Some controllers can't support CDC Ethernet (ECM) ... */
static inline bool can_support_ecm(struct usb_gadget *gadget)