]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
usb: Add support for rndis uplink aggregation
authorxerox_lin <xerox_lin@htc.com>
Thu, 14 Aug 2014 06:48:44 +0000 (14:48 +0800)
committerDhiren Parmar <dparmar@nvidia.com>
Fri, 17 Oct 2014 13:51:44 +0000 (06:51 -0700)
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.

Bug 1445536

Change-Id: I0b62a21a5c3ceb6b04933d0d6da33301dbafe493
Signed-off-by: Vamsi Krishna <vskrishn@codeaurora.org>
Signed-off-by: Xerox Lin <xerox_lin@htc.com>
(cherry picked from commit a327898fa354ae18e9fe07d3e203304f4ef5c0d2)
Reviewed-on: http://git-master/r/554491
Reviewed-by: Rohith Seelaboyina <rseelaboyina@nvidia.com>
Tested-by: Rohith Seelaboyina <rseelaboyina@nvidia.com>
Reviewed-by: Rakesh Babu Bodla <rbodla@nvidia.com>
Reviewed-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
drivers/usb/gadget/f_rndis.c
drivers/usb/gadget/rndis.c
drivers/usb/gadget/rndis.h
drivers/usb/gadget/u_ether.c
drivers/usb/gadget/u_ether.h

index fcdee12863cf945411eccaeead8ffbbcb30406ad..8355bee87263e8c9324941c17312700428c9796e 100644 (file)
 #include "u_ether.h"
 #include "rndis.h"
 
-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");
-
 /*
  * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
  * been promoted instead of the standard CDC Ethernet.  The published RNDIS
@@ -73,6 +68,16 @@ MODULE_PARM_DESC(rndis_multipacket_dl_disable,
  *   - 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;
@@ -768,6 +773,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
 
        rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
        rndis_set_host_mac(rndis->config, 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->config, rndis->vendorID,
@@ -873,6 +879,7 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
        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";
        rndis->port.func.strings = rndis_strings;
index e3e4c822dba59dafe1c8824a989a0ef2b4bdd522..63c43d1a256aa7091fcecd761c8b4c4eb0e6035b 100644 (file)
@@ -61,6 +61,16 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging");
 
 #define RNDIS_MAX_CONFIGS      1
 
+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");
+
 
 static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
 
@@ -587,8 +597,8 @@ 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(TX_SKB_HOLD_THRESHOLD);
-       resp->MaxTransferSize = cpu_to_le32(TX_SKB_HOLD_THRESHOLD *
+       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)
@@ -912,6 +922,8 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
        rndis_per_dev_params[configNr].dev = dev;
        rndis_per_dev_params[configNr].filter = cdc_filter;
 
+       rndis_ul_max_xfer_size_rcvd = 0;
+       rndis_ul_max_pkt_per_xfer_rcvd = 0;
        return 0;
 }
 
@@ -938,6 +950,13 @@ int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
        return 0;
 }
 
+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;
@@ -1010,23 +1029,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;
+
+       while (skb->len) {
+               struct rndis_packet_msg_type *hdr;
+               struct sk_buff          *skb2;
+               u32             msg_len, data_offset, data_len;
 
-       /* DataOffset, DataLength */
-       if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
-               dev_kfree_skb_any(skb);
-               return -EOVERFLOW;
+               /* 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;
index 0647f2f34e898770dac9c6021f0207e2ce6e3c28..12045b31a311e299a6673577638fc3842211c0bc 100644 (file)
@@ -189,6 +189,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;
@@ -204,6 +205,7 @@ int  rndis_set_param_dev (u8 configNr, struct net_device *dev,
 int  rndis_set_param_vendor (u8 configNr, u32 vendorID,
                            const char *vendorDescr);
 int  rndis_set_param_medium (u8 configNr, 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);
index 5b501fb488c2f81c6aa69f1fecd03f1dfd907178..ccba28860ccc980ff6aa4138dabd51c93f5aed84 100644 (file)
@@ -72,6 +72,7 @@ struct eth_dev {
        struct sk_buff_head     rx_frames;
 
        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,
@@ -236,9 +237,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");
@@ -1061,6 +1066,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->tx_skb_hold_count = 0;
index 1010e1f6dde83c40c07d0694e29b8dfd7e90bb4f..706e11ead5987c799ace30f3d43f207a2ddfcc66 100644 (file)
@@ -55,6 +55,7 @@ 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;