usb: Add support for rndis uplink aggregation

RNDIS protocol supports data aggregation on uplink and can help
reduce mips by reducing number of interrupts on device. Throughput
also improved by 20-30%. Aggregation is disabled by setting
aggregation packet size to 1. To help better UL throughput, set
as ul aggregation support to 3 rndis packets by default. It can be
configured via module parameter: rndis_ul_max_pkt_per_xfer.

Change-Id: I0b62a21a5c3ceb6b04933d0d6da33301dbafe493
Signed-off-by: Vamsi Krishna <vskrishn@codeaurora.org>
Signed-off-by: Xerox Lin <xerox_lin@htc.com>
This commit is contained in:
xerox_lin 2014-08-14 14:48:44 +08:00 committed by John Stultz
parent c996cdd384
commit 70d6557c59
5 changed files with 111 additions and 18 deletions

View file

@ -70,6 +70,16 @@
* - MS-Windows drivers sometimes emit undocumented requests.
*/
static bool rndis_multipacket_dl_disable;
module_param(rndis_multipacket_dl_disable, bool, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(rndis_multipacket_dl_disable,
"Disable RNDIS Multi-packet support in DownLink");
static unsigned int rndis_ul_max_pkt_per_xfer = 3;
module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer,
"Maximum packets per transfer for UL aggregation");
struct f_rndis {
struct gether port;
u8 ctrl_id, data_id;
@ -792,6 +802,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->params, rndis->ethaddr);
rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer);
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->params, rndis->vendorID,
@ -978,6 +989,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
rndis->port.wrap = rndis_add_header;
rndis->port.unwrap = rndis_rm_hdr;
rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer;
rndis->port.func.name = "rndis";
/* descriptors are per-instance copies */

View file

@ -42,6 +42,16 @@
#include "rndis.h"
int rndis_ul_max_pkt_per_xfer_rcvd;
module_param(rndis_ul_max_pkt_per_xfer_rcvd, int, S_IRUGO);
MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer_rcvd,
"Max num of REMOTE_NDIS_PACKET_MSGs received in a single transfer");
int rndis_ul_max_xfer_size_rcvd;
module_param(rndis_ul_max_xfer_size_rcvd, int, S_IRUGO);
MODULE_PARM_DESC(rndis_ul_max_xfer_size_rcvd,
"Max size of bus transfer received");
/* The driver for your USB chip needs to support ep0 OUT to work with
* RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
@ -579,12 +589,12 @@ static int rndis_init_response(struct rndis_params *params,
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(params->max_pkt_per_xfer);
resp->MaxTransferSize = cpu_to_le32(params->max_pkt_per_xfer *
(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);
@ -964,6 +974,8 @@ int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
params->dev = dev;
params->filter = cdc_filter;
rndis_ul_max_xfer_size_rcvd = 0;
rndis_ul_max_pkt_per_xfer_rcvd = 0;
return 0;
}
EXPORT_SYMBOL_GPL(rndis_set_param_dev);
@ -996,6 +1008,13 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed)
}
EXPORT_SYMBOL_GPL(rndis_set_param_medium);
void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer)
{
pr_debug("%s:\n", __func__);
rndis_per_dev_params[configNr].max_pkt_per_xfer = max_pkt_per_xfer;
}
void rndis_add_hdr(struct sk_buff *skb)
{
struct rndis_packet_msg_type *header;
@ -1068,23 +1087,73 @@ int rndis_rm_hdr(struct gether *port,
struct sk_buff *skb,
struct sk_buff_head *list)
{
/* tmp points to a struct rndis_packet_msg_type */
__le32 *tmp = (void *)skb->data;
int num_pkts = 1;
/* MessageType, MessageLength */
if (cpu_to_le32(RNDIS_MSG_PACKET)
!= get_unaligned(tmp++)) {
dev_kfree_skb_any(skb);
return -EINVAL;
}
tmp++;
if (skb->len > rndis_ul_max_xfer_size_rcvd)
rndis_ul_max_xfer_size_rcvd = skb->len;
/* DataOffset, DataLength */
if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
dev_kfree_skb_any(skb);
return -EOVERFLOW;
while (skb->len) {
struct rndis_packet_msg_type *hdr;
struct sk_buff *skb2;
u32 msg_len, data_offset, data_len;
/* some rndis hosts send extra byte to avoid zlp, ignore it */
if (skb->len == 1) {
dev_kfree_skb_any(skb);
return 0;
}
if (skb->len < sizeof *hdr) {
pr_err("invalid rndis pkt: skblen:%u hdr_len:%u",
skb->len, sizeof *hdr);
dev_kfree_skb_any(skb);
return -EINVAL;
}
hdr = (void *)skb->data;
msg_len = le32_to_cpu(hdr->MessageLength);
data_offset = le32_to_cpu(hdr->DataOffset);
data_len = le32_to_cpu(hdr->DataLength);
if (skb->len < msg_len ||
((data_offset + data_len + 8) > msg_len)) {
pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n",
le32_to_cpu(hdr->MessageType),
msg_len, data_offset, data_len, skb->len);
dev_kfree_skb_any(skb);
return -EOVERFLOW;
}
if (le32_to_cpu(hdr->MessageType) != RNDIS_MSG_PACKET) {
pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n",
le32_to_cpu(hdr->MessageType),
msg_len, data_offset, data_len, skb->len);
dev_kfree_skb_any(skb);
return -EINVAL;
}
skb_pull(skb, data_offset + 8);
if (msg_len == skb->len) {
skb_trim(skb, data_len);
break;
}
skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2) {
pr_err("%s:skb clone failed\n", __func__);
dev_kfree_skb_any(skb);
return -ENOMEM;
}
skb_pull(skb, msg_len - sizeof *hdr);
skb_trim(skb2, data_len);
skb_queue_tail(list, skb2);
num_pkts++;
}
skb_trim(skb, get_unaligned_le32(tmp++));
if (num_pkts > rndis_ul_max_pkt_per_xfer_rcvd)
rndis_ul_max_pkt_per_xfer_rcvd = num_pkts;
skb_queue_tail(list, skb);
return 0;

View file

@ -190,6 +190,7 @@ typedef struct rndis_params
struct net_device *dev;
u32 vendorID;
u8 max_pkt_per_xfer;
const char *vendorDescr;
void (*resp_avail)(void *v);
void *v;
@ -206,6 +207,7 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
const char *vendorDescr);
int rndis_set_param_medium(struct rndis_params *params, u32 medium,
u32 speed);
void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer);
void rndis_add_hdr(struct sk_buff *skb);
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
struct sk_buff_head *list);

View file

@ -71,6 +71,7 @@ struct eth_dev {
unsigned qmult;
unsigned header_len;
unsigned ul_max_pkts_per_xfer;
struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
int (*unwrap)(struct gether *,
struct sk_buff *skb,
@ -230,9 +231,13 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
size += out->maxpacket - 1;
size -= size % out->maxpacket;
if (dev->ul_max_pkts_per_xfer)
size *= dev->ul_max_pkts_per_xfer;
if (dev->port_usb->is_fixed)
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
DBG(dev, "%s: size: %d\n", __func__, size);
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
if (skb == NULL) {
DBG(dev, "no rx skb\n");
@ -1077,6 +1082,7 @@ struct net_device *gether_connect(struct gether *link)
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;
spin_lock(&dev->lock);
dev->port_usb = link;

View file

@ -73,6 +73,10 @@ struct gether {
bool is_fixed;
u32 fixed_out_len;
u32 fixed_in_len;
unsigned ul_max_pkts_per_xfer;
/* Max number of SKB packets to be used to create Multi Packet RNDIS */
#define TX_SKB_HOLD_THRESHOLD 3
bool multi_pkt_xfer;
bool supports_multi_frame;
struct sk_buff *(*wrap)(struct gether *port,
struct sk_buff *skb);