RNDIS: Add Data aggregation (multi packet) support
Add data aggregation support using RNDIS Multi Packet feature to achieve better UDP Downlink throughput. Max 3 RNDIS Packets aggregated into one RNDIS Packet with this implementation. With this change, seeing UDP Downlink throughput increase from 90 Mbps to above 100 Mbps when using Iperf and sending data more than 100 Mbps. CRs-Fixed: 352010 Change-Id: I7c353d21d60002b66c08181fd621f9ab8753cb0f Signed-off-by: Mayank Rana <mrana@codeaurora.org> Signed-off-by: Rajkumar Raghupathy <raghup@codeaurora.org>
This commit is contained in:
committed by
Stephen Boyd
parent
f890452802
commit
dfae37e7d2
@@ -449,6 +449,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
struct f_rndis *rndis = req->context;
|
||||
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
|
||||
int status;
|
||||
rndis_init_msg_type *buf;
|
||||
|
||||
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
|
||||
// spin_lock(&dev->lock);
|
||||
@@ -456,6 +457,19 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
if (status < 0)
|
||||
ERROR(cdev, "RNDIS command error %d, %d/%d\n",
|
||||
status, req->actual, req->length);
|
||||
|
||||
buf = (rndis_init_msg_type *)req->buf;
|
||||
|
||||
if (buf->MessageType == REMOTE_NDIS_INITIALIZE_MSG) {
|
||||
if (buf->MaxTransferSize > 2048)
|
||||
rndis->port.multi_pkt_xfer = 1;
|
||||
else
|
||||
rndis->port.multi_pkt_xfer = 0;
|
||||
DBG(cdev, "%s: MaxTransferSize: %d : Multi_pkt_txr: %s\n",
|
||||
__func__, buf->MaxTransferSize,
|
||||
rndis->port.multi_pkt_xfer ? "enabled" :
|
||||
"disabled");
|
||||
}
|
||||
// spin_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -585,12 +585,12 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
|
||||
resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
|
||||
resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
|
||||
resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
|
||||
resp->MaxPacketsPerTransfer = cpu_to_le32(1);
|
||||
resp->MaxTransferSize = cpu_to_le32(
|
||||
params->dev->mtu
|
||||
resp->MaxPacketsPerTransfer = cpu_to_le32(TX_SKB_HOLD_THRESHOLD);
|
||||
resp->MaxTransferSize = cpu_to_le32(TX_SKB_HOLD_THRESHOLD *
|
||||
(params->dev->mtu
|
||||
+ sizeof(struct ethhdr)
|
||||
+ sizeof(struct rndis_packet_msg_type)
|
||||
+ 22);
|
||||
+ 22));
|
||||
resp->PacketAlignmentFactor = cpu_to_le32(0);
|
||||
resp->AFListOffset = cpu_to_le32(0);
|
||||
resp->AFListSize = cpu_to_le32(0);
|
||||
|
||||
@@ -59,6 +59,11 @@ struct eth_dev {
|
||||
spinlock_t req_lock; /* guard {rx,tx}_reqs */
|
||||
struct list_head tx_reqs, rx_reqs;
|
||||
unsigned tx_qlen;
|
||||
/* Minimum number of TX USB request queued to UDC */
|
||||
#define TX_REQ_THRESHOLD 5
|
||||
int no_tx_req_used;
|
||||
int tx_skb_hold_count;
|
||||
u32 tx_req_bufsize;
|
||||
|
||||
struct sk_buff_head rx_frames;
|
||||
|
||||
@@ -86,7 +91,7 @@ struct eth_dev {
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
|
||||
static unsigned qmult = 5;
|
||||
static unsigned qmult = 10;
|
||||
module_param(qmult, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
|
||||
|
||||
@@ -457,6 +462,11 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct sk_buff *skb = req->context;
|
||||
struct eth_dev *dev = ep->driver_data;
|
||||
struct net_device *net = dev->net;
|
||||
struct usb_request *new_req;
|
||||
struct usb_ep *in;
|
||||
int length;
|
||||
int retval;
|
||||
|
||||
switch (req->status) {
|
||||
default:
|
||||
@@ -467,14 +477,73 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
case -ESHUTDOWN: /* disconnect etc */
|
||||
break;
|
||||
case 0:
|
||||
dev->net->stats.tx_bytes += skb->len;
|
||||
if (!req->zero)
|
||||
dev->net->stats.tx_bytes += req->length-1;
|
||||
else
|
||||
dev->net->stats.tx_bytes += req->length;
|
||||
}
|
||||
dev->net->stats.tx_packets++;
|
||||
|
||||
spin_lock(&dev->req_lock);
|
||||
list_add(&req->list, &dev->tx_reqs);
|
||||
spin_unlock(&dev->req_lock);
|
||||
dev_kfree_skb_any(skb);
|
||||
list_add_tail(&req->list, &dev->tx_reqs);
|
||||
|
||||
if (dev->port_usb->multi_pkt_xfer) {
|
||||
dev->no_tx_req_used--;
|
||||
req->length = 0;
|
||||
in = dev->port_usb->in_ep;
|
||||
|
||||
if (!list_empty(&dev->tx_reqs)) {
|
||||
new_req = container_of(dev->tx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_del(&new_req->list);
|
||||
spin_unlock(&dev->req_lock);
|
||||
if (new_req->length > 0) {
|
||||
length = new_req->length;
|
||||
|
||||
/* NCM requires no zlp if transfer is
|
||||
* dwNtbInMaxSize */
|
||||
if (dev->port_usb->is_fixed &&
|
||||
length == dev->port_usb->fixed_in_len &&
|
||||
(length % in->maxpacket) == 0)
|
||||
new_req->zero = 0;
|
||||
else
|
||||
new_req->zero = 1;
|
||||
|
||||
/* use zlp framing on tx for strict CDC-Ether
|
||||
* conformance, though any robust network rx
|
||||
* path ignores extra padding. and some hardware
|
||||
* doesn't like to write zlps.
|
||||
*/
|
||||
if (new_req->zero && !dev->zlp &&
|
||||
(length % in->maxpacket) == 0) {
|
||||
new_req->zero = 0;
|
||||
length++;
|
||||
}
|
||||
|
||||
new_req->length = length;
|
||||
retval = usb_ep_queue(in, new_req, GFP_ATOMIC);
|
||||
switch (retval) {
|
||||
default:
|
||||
DBG(dev, "tx queue err %d\n", retval);
|
||||
break;
|
||||
case 0:
|
||||
spin_lock(&dev->req_lock);
|
||||
dev->no_tx_req_used++;
|
||||
spin_unlock(&dev->req_lock);
|
||||
net->trans_start = jiffies;
|
||||
}
|
||||
} else {
|
||||
spin_lock(&dev->req_lock);
|
||||
list_add(&new_req->list, &dev->tx_reqs);
|
||||
spin_unlock(&dev->req_lock);
|
||||
}
|
||||
} else {
|
||||
spin_unlock(&dev->req_lock);
|
||||
}
|
||||
} else {
|
||||
spin_unlock(&dev->req_lock);
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
if (netif_carrier_ok(dev->net))
|
||||
netif_wake_queue(dev->net);
|
||||
@@ -485,6 +554,26 @@ static inline int is_promisc(u16 cdc_filter)
|
||||
return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
|
||||
}
|
||||
|
||||
static void alloc_tx_buffer(struct eth_dev *dev)
|
||||
{
|
||||
struct list_head *act;
|
||||
struct usb_request *req;
|
||||
|
||||
dev->tx_req_bufsize = (TX_SKB_HOLD_THRESHOLD *
|
||||
(dev->net->mtu
|
||||
+ sizeof(struct ethhdr)
|
||||
/* size of rndis_packet_msg_type */
|
||||
+ 44
|
||||
+ 22));
|
||||
|
||||
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,
|
||||
GFP_ATOMIC);
|
||||
}
|
||||
}
|
||||
|
||||
static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *net)
|
||||
{
|
||||
@@ -511,6 +600,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* Allocate memory for tx_reqs to support multi packet transfer */
|
||||
if (dev->port_usb->multi_pkt_xfer && !dev->tx_req_bufsize)
|
||||
alloc_tx_buffer(dev);
|
||||
|
||||
/* apply outgoing CDC or RNDIS filters */
|
||||
if (!is_promisc(cdc_filter)) {
|
||||
u8 *dest = skb->data;
|
||||
@@ -565,11 +658,39 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
if (!skb)
|
||||
goto drop;
|
||||
|
||||
length = skb->len;
|
||||
}
|
||||
req->buf = skb->data;
|
||||
req->context = skb;
|
||||
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
dev->tx_skb_hold_count++;
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
|
||||
if (dev->port_usb->multi_pkt_xfer) {
|
||||
memcpy(req->buf + req->length, skb->data, skb->len);
|
||||
req->length = req->length + skb->len;
|
||||
length = req->length;
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
if (dev->tx_skb_hold_count < TX_SKB_HOLD_THRESHOLD) {
|
||||
if (dev->no_tx_req_used > TX_REQ_THRESHOLD) {
|
||||
list_add(&req->list, &dev->tx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
goto success;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
length = skb->len;
|
||||
req->buf = skb->data;
|
||||
req->context = skb;
|
||||
}
|
||||
|
||||
req->complete = tx_complete;
|
||||
|
||||
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
|
||||
@@ -584,8 +705,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
* though any robust network rx path ignores extra padding.
|
||||
* and some hardware doesn't like to write zlps.
|
||||
*/
|
||||
if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
|
||||
if (req->zero && !dev->zlp && (length % in->maxpacket) == 0) {
|
||||
req->zero = 0;
|
||||
length++;
|
||||
}
|
||||
|
||||
req->length = length;
|
||||
|
||||
@@ -593,7 +716,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
if (gadget_is_dualspeed(dev->gadget) &&
|
||||
(dev->gadget->speed == USB_SPEED_HIGH)) {
|
||||
dev->tx_qlen++;
|
||||
if (dev->tx_qlen == qmult) {
|
||||
if (dev->tx_qlen == (qmult/2)) {
|
||||
req->no_interrupt = 0;
|
||||
dev->tx_qlen = 0;
|
||||
} else {
|
||||
@@ -613,7 +736,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!dev->port_usb->multi_pkt_xfer)
|
||||
dev_kfree_skb_any(skb);
|
||||
drop:
|
||||
dev->net->stats.tx_dropped++;
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
@@ -622,6 +746,7 @@ drop:
|
||||
list_add(&req->list, &dev->tx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
success:
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
@@ -924,6 +1049,9 @@ struct net_device *gether_connect(struct gether *link)
|
||||
dev->wrap = link->wrap;
|
||||
|
||||
spin_lock(&dev->lock);
|
||||
dev->tx_skb_hold_count = 0;
|
||||
dev->no_tx_req_used = 0;
|
||||
dev->tx_req_bufsize = 0;
|
||||
dev->port_usb = link;
|
||||
link->ioport = dev;
|
||||
if (netif_running(dev->net)) {
|
||||
@@ -989,6 +1117,8 @@ void gether_disconnect(struct gether *link)
|
||||
list_del(&req->list);
|
||||
|
||||
spin_unlock(&dev->req_lock);
|
||||
if (link->multi_pkt_xfer)
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(link->in_ep, req);
|
||||
spin_lock(&dev->req_lock);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ struct gether {
|
||||
bool is_fixed;
|
||||
u32 fixed_out_len;
|
||||
u32 fixed_in_len;
|
||||
/* Max number of SKB packets to be used to create Multi Packet RNDIS */
|
||||
#define TX_SKB_HOLD_THRESHOLD 3
|
||||
bool multi_pkt_xfer;
|
||||
struct sk_buff *(*wrap)(struct gether *port,
|
||||
struct sk_buff *skb);
|
||||
int (*unwrap)(struct gether *port,
|
||||
|
||||
Reference in New Issue
Block a user