Enabling SG allows enabling GSO (generic segmentation offload) feature
of linux networking layer. This increases TCP throughput with NCM
on Cortex-A15+USB3380 based device from 300 Mbit/s to 1.1 Gbit/s.

Signed-off-by: Jussi Kivilinna <jussi.kivili...@haltian.com>
---
 drivers/usb/gadget/Kconfig          |  2 ++
 drivers/usb/gadget/function/f_ncm.c | 47 +++++++++++++++++++++++++++++++------
 2 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 3c3f31c..3a8b874 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -277,6 +277,8 @@ config USB_CONFIGFS_NCM
        depends on NET
        select USB_U_ETHER
        select USB_F_NCM
+       select CRYPTO
+       select CRYPTO_CRC32
        help
          NCM is an advanced protocol for Ethernet encapsulation, allows
          grouping of several ethernet frames into one USB transfer and
diff --git a/drivers/usb/gadget/function/f_ncm.c 
b/drivers/usb/gadget/function/f_ncm.c
index 08cff49..1f4f724 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -20,6 +20,8 @@
 #include <linux/device.h>
 #include <linux/etherdevice.h>
 #include <linux/crc32.h>
+#include <crypto/hash.h>
+#include <linux/scatterlist.h>
 
 #include <linux/usb/cdc.h>
 
@@ -80,6 +82,10 @@ struct f_ncm {
        struct hrtimer                  task_timer;
 
        bool                            timer_stopping;
+
+       /* For scatter/gather crc32 */
+       struct crypto_ahash             *tfm;
+       struct scatterlist              sg[MAX_SKB_FRAGS + 1];
 };
 
 static inline struct f_ncm *func_to_ncm(struct usb_function *f)
@@ -1017,6 +1023,25 @@ static struct sk_buff *package_for_tx(struct f_ncm *ncm)
        return skb2;
 }
 
+static u32 skb_crc(struct f_ncm *ncm, struct sk_buff *skb)
+{
+       AHASH_REQUEST_ON_STACK(req, ncm->tfm);
+       __le32 crc = cpu_to_le32(~(u32)0);
+
+       sg_init_table(ncm->sg, MAX_SKB_FRAGS + 1);
+       skb_to_sgvec(skb, ncm->sg, 0, skb->len);
+
+       crypto_ahash_setkey(ncm->tfm, (void *)&crc, sizeof(crc));
+
+       ahash_request_set_tfm(req, ncm->tfm);
+       ahash_request_set_callback(req, 0, NULL, NULL);
+       ahash_request_set_crypt(req, ncm->sg, (void *)&crc, skb->len);
+
+       crypto_ahash_digest(req);
+
+       return ~le32_to_cpu(crc);
+}
+
 static struct sk_buff *ncm_wrap_ntb(struct gether *port,
                                    struct sk_buff *skb)
 {
@@ -1040,13 +1065,11 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
        if (skb) {
                /* Add the CRC if required up front */
                if (ncm->is_crc) {
-                       uint32_t        crc;
-                       __le16          *crc_pos;
+                       u32     crc;
+                       __le32  *crc_pos;
 
-                       crc = ~crc32_le(~0,
-                                       skb->data,
-                                       skb->len);
-                       crc_pos = (void *) skb_put(skb, sizeof(uint32_t));
+                       crc = skb_crc(ncm, skb);
+                       crc_pos = (void *) skb_put(skb, sizeof(u32));
                        put_unaligned_le32(crc, crc_pos);
                }
 
@@ -1130,7 +1153,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
                ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad);
                memset(ntb_data, 0, dgram_pad);
                ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
-               memcpy(ntb_data, skb->data, skb->len);
+               skb_copy_bits(skb, 0, ntb_data, skb->len);
                dev_kfree_skb_any(skb);
                skb = NULL;
 
@@ -1513,6 +1536,12 @@ static int ncm_bind(struct usb_configuration *c, struct 
usb_function *f)
        if (status)
                goto fail;
 
+       ncm->tfm = crypto_alloc_ahash("crc32", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(ncm->tfm)) {
+               usb_free_all_descriptors(f);
+               goto fail;
+       }
+
        /*
         * NOTE:  all that is done without knowing or caring about
         * the network link ... which is unavailable to this code
@@ -1607,6 +1636,8 @@ static struct usb_function_instance *ncm_alloc_inst(void)
                return ERR_CAST(net);
        }
 
+       opts->net->features |= NETIF_F_SG;
+
        config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
 
        return &opts->func_inst;
@@ -1634,6 +1665,8 @@ static void ncm_unbind(struct usb_configuration *c, 
struct usb_function *f)
        hrtimer_cancel(&ncm->task_timer);
        tasklet_kill(&ncm->tx_tasklet);
 
+       crypto_free_ahash(ncm->tfm);
+
        ncm_string_defs[0].id = 0;
        usb_free_all_descriptors(f);
 
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to