aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/cdc_ncm.c
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2012-10-22 06:56:34 -0400
committerDavid S. Miller <davem@davemloft.net>2012-10-23 02:40:11 -0400
commitc78b7c58665748aa775e86e0e49798b9f34a1dc9 (patch)
tree81c298292d1ca721ab130a16d6c13f1a305af587 /drivers/net/usb/cdc_ncm.c
parentff06ab13a4ccae4acb44a2d4e3ece367b616ab50 (diff)
net: cdc_ncm: refactoring for tx multiplexing
Adding multiplexed NDP support to cdc_ncm_fill_tx_frame, allowing transmissions of multiple independent sessions within the same NTB. Refactoring the code quite a bit to avoid having to store copies of multiple NDPs being prepared for tx. The old code would still reserve enough room for a maximum sized NDP in the skb so we might as well keep them in the skb while they are being prepared. Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb/cdc_ncm.c')
-rw-r--r--drivers/net/usb/cdc_ncm.c247
1 files changed, 102 insertions, 145 deletions
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 6688a15e044d..2dd27346cb39 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -88,14 +88,11 @@
88 (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \ 88 (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \
89 (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) 89 (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
90 90
91struct cdc_ncm_data { 91#define CDC_NCM_NDP_SIZE \
92 struct usb_cdc_ncm_nth16 nth16; 92 (sizeof(struct usb_cdc_ncm_ndp16) + \
93 struct usb_cdc_ncm_ndp16 ndp16; 93 (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
94 struct usb_cdc_ncm_dpe16 dpe16[CDC_NCM_DPT_DATAGRAMS_MAX + 1];
95};
96 94
97struct cdc_ncm_ctx { 95struct cdc_ncm_ctx {
98 struct cdc_ncm_data tx_ncm;
99 struct usb_cdc_ncm_ntb_parameters ncm_parm; 96 struct usb_cdc_ncm_ntb_parameters ncm_parm;
100 struct hrtimer tx_timer; 97 struct hrtimer tx_timer;
101 struct tasklet_struct bh; 98 struct tasklet_struct bh;
@@ -117,13 +114,12 @@ struct cdc_ncm_ctx {
117 114
118 struct sk_buff *tx_curr_skb; 115 struct sk_buff *tx_curr_skb;
119 struct sk_buff *tx_rem_skb; 116 struct sk_buff *tx_rem_skb;
117 __le32 tx_rem_sign;
120 118
121 spinlock_t mtx; 119 spinlock_t mtx;
122 atomic_t stop; 120 atomic_t stop;
123 121
124 u32 tx_timer_pending; 122 u32 tx_timer_pending;
125 u32 tx_curr_offset;
126 u32 tx_curr_last_offset;
127 u32 tx_curr_frame_num; 123 u32 tx_curr_frame_num;
128 u32 rx_speed; 124 u32 rx_speed;
129 u32 tx_speed; 125 u32 tx_speed;
@@ -658,51 +654,75 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
658 return ret; 654 return ret;
659} 655}
660 656
661static void cdc_ncm_zero_fill(u8 *ptr, u32 first, u32 end, u32 max) 657static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max)
662{ 658{
663 if (first >= max) 659 size_t align = ALIGN(skb->len, modulus) - skb->len + remainder;
664 return; 660
665 if (first >= end) 661 if (skb->len + align > max)
666 return; 662 align = max - skb->len;
667 if (end > max) 663 if (align && skb_tailroom(skb) >= align)
668 end = max; 664 memset(skb_put(skb, align), 0, align);
669 memset(ptr + first, 0, end - first); 665}
666
667/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly
668 * allocating a new one within skb
669 */
670static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
671{
672 struct usb_cdc_ncm_ndp16 *ndp16 = NULL;
673 struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
674 size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);
675
676 /* follow the chain of NDPs, looking for a match */
677 while (ndpoffset) {
678 ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
679 if (ndp16->dwSignature == sign)
680 return ndp16;
681 ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
682 }
683
684 /* align new NDP */
685 cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
686
687 /* verify that there is room for the NDP and the datagram (reserve) */
688 if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE)
689 return NULL;
690
691 /* link to it */
692 if (ndp16)
693 ndp16->wNextNdpIndex = cpu_to_le16(skb->len);
694 else
695 nth16->wNdpIndex = cpu_to_le16(skb->len);
696
697 /* push a new empty NDP */
698 ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE);
699 ndp16->dwSignature = sign;
700 ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
701 return ndp16;
670} 702}
671 703
672static struct sk_buff * 704static struct sk_buff *
673cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb) 705cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
674{ 706{
707 struct usb_cdc_ncm_nth16 *nth16;
708 struct usb_cdc_ncm_ndp16 *ndp16;
675 struct sk_buff *skb_out; 709 struct sk_buff *skb_out;
676 u32 rem; 710 u16 n = 0, index, ndplen;
677 u32 offset;
678 u32 last_offset;
679 u16 n = 0, index;
680 u8 ready2send = 0; 711 u8 ready2send = 0;
681 712
682 /* if there is a remaining skb, it gets priority */ 713 /* if there is a remaining skb, it gets priority */
683 if (skb != NULL) 714 if (skb != NULL) {
684 swap(skb, ctx->tx_rem_skb); 715 swap(skb, ctx->tx_rem_skb);
685 else 716 swap(sign, ctx->tx_rem_sign);
717 } else {
686 ready2send = 1; 718 ready2send = 1;
687 719 }
688 /*
689 * +----------------+
690 * | skb_out |
691 * +----------------+
692 * ^ offset
693 * ^ last_offset
694 */
695 720
696 /* check if we are resuming an OUT skb */ 721 /* check if we are resuming an OUT skb */
697 if (ctx->tx_curr_skb != NULL) { 722 skb_out = ctx->tx_curr_skb;
698 /* pop variables */
699 skb_out = ctx->tx_curr_skb;
700 offset = ctx->tx_curr_offset;
701 last_offset = ctx->tx_curr_last_offset;
702 n = ctx->tx_curr_frame_num;
703 723
704 } else { 724 /* allocate a new OUT skb */
705 /* reset variables */ 725 if (!skb_out) {
706 skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC); 726 skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC);
707 if (skb_out == NULL) { 727 if (skb_out == NULL) {
708 if (skb != NULL) { 728 if (skb != NULL) {
@@ -711,35 +731,21 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
711 } 731 }
712 goto exit_no_skb; 732 goto exit_no_skb;
713 } 733 }
734 /* fill out the initial 16-bit NTB header */
735 nth16 = (struct usb_cdc_ncm_nth16 *)memset(skb_put(skb_out, sizeof(struct usb_cdc_ncm_nth16)), 0, sizeof(struct usb_cdc_ncm_nth16));
736 nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
737 nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
738 nth16->wSequence = cpu_to_le16(ctx->tx_seq++);
714 739
715 /* make room for NTH and NDP */ 740 /* count total number of frames in this NTB */
716 offset = ALIGN(sizeof(struct usb_cdc_ncm_nth16),
717 ctx->tx_ndp_modulus) +
718 sizeof(struct usb_cdc_ncm_ndp16) +
719 (ctx->tx_max_datagrams + 1) *
720 sizeof(struct usb_cdc_ncm_dpe16);
721
722 /* store last valid offset before alignment */
723 last_offset = offset;
724 /* align first Datagram offset correctly */
725 offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder;
726 /* zero buffer till the first IP datagram */
727 cdc_ncm_zero_fill(skb_out->data, 0, offset, offset);
728 n = 0;
729 ctx->tx_curr_frame_num = 0; 741 ctx->tx_curr_frame_num = 0;
730 } 742 }
731 743
732 for (; n < ctx->tx_max_datagrams; n++) { 744 for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
733 /* check if end of transmit buffer is reached */ 745 /* send any remaining skb first */
734 if (offset >= ctx->tx_max) {
735 ready2send = 1;
736 break;
737 }
738 /* compute maximum buffer size */
739 rem = ctx->tx_max - offset;
740
741 if (skb == NULL) { 746 if (skb == NULL) {
742 skb = ctx->tx_rem_skb; 747 skb = ctx->tx_rem_skb;
748 sign = ctx->tx_rem_sign;
743 ctx->tx_rem_skb = NULL; 749 ctx->tx_rem_skb = NULL;
744 750
745 /* check for end of skb */ 751 /* check for end of skb */
@@ -747,7 +753,14 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
747 break; 753 break;
748 } 754 }
749 755
750 if (skb->len > rem) { 756 /* get the appropriate NDP for this skb */
757 ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
758
759 /* align beginning of next frame */
760 cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max);
761
762 /* check if we had enough room left for both NDP and frame */
763 if (!ndp16 || skb_out->len + skb->len > ctx->tx_max) {
751 if (n == 0) { 764 if (n == 0) {
752 /* won't fit, MTU problem? */ 765 /* won't fit, MTU problem? */
753 dev_kfree_skb_any(skb); 766 dev_kfree_skb_any(skb);
@@ -760,31 +773,30 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
760 ctx->netdev->stats.tx_dropped++; 773 ctx->netdev->stats.tx_dropped++;
761 } 774 }
762 ctx->tx_rem_skb = skb; 775 ctx->tx_rem_skb = skb;
776 ctx->tx_rem_sign = sign;
763 skb = NULL; 777 skb = NULL;
764 ready2send = 1; 778 ready2send = 1;
765 } 779 }
766 break; 780 break;
767 } 781 }
768 782
769 memcpy(((u8 *)skb_out->data) + offset, skb->data, skb->len); 783 /* calculate frame number withing this NDP */
770 784 ndplen = le16_to_cpu(ndp16->wLength);
771 ctx->tx_ncm.dpe16[n].wDatagramLength = cpu_to_le16(skb->len); 785 index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;
772 ctx->tx_ncm.dpe16[n].wDatagramIndex = cpu_to_le16(offset);
773
774 /* update offset */
775 offset += skb->len;
776 786
777 /* store last valid offset before alignment */ 787 /* OK, add this skb */
778 last_offset = offset; 788 ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
779 789 ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
780 /* align offset correctly */ 790 ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
781 offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder; 791 memcpy(skb_put(skb_out, skb->len), skb->data, skb->len);
782
783 /* zero padding */
784 cdc_ncm_zero_fill(skb_out->data, last_offset, offset,
785 ctx->tx_max);
786 dev_kfree_skb_any(skb); 792 dev_kfree_skb_any(skb);
787 skb = NULL; 793 skb = NULL;
794
795 /* send now if this NDP is full */
796 if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
797 ready2send = 1;
798 break;
799 }
788 } 800 }
789 801
790 /* free up any dangling skb */ 802 /* free up any dangling skb */
@@ -800,16 +812,12 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
800 /* wait for more frames */ 812 /* wait for more frames */
801 /* push variables */ 813 /* push variables */
802 ctx->tx_curr_skb = skb_out; 814 ctx->tx_curr_skb = skb_out;
803 ctx->tx_curr_offset = offset;
804 ctx->tx_curr_last_offset = last_offset;
805 goto exit_no_skb; 815 goto exit_no_skb;
806 816
807 } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) { 817 } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) {
808 /* wait for more frames */ 818 /* wait for more frames */
809 /* push variables */ 819 /* push variables */
810 ctx->tx_curr_skb = skb_out; 820 ctx->tx_curr_skb = skb_out;
811 ctx->tx_curr_offset = offset;
812 ctx->tx_curr_last_offset = last_offset;
813 /* set the pending count */ 821 /* set the pending count */
814 if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT) 822 if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
815 ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT; 823 ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT;
@@ -820,75 +828,24 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
820 /* variables will be reset at next call */ 828 /* variables will be reset at next call */
821 } 829 }
822 830
823 /* check for overflow */
824 if (last_offset > ctx->tx_max)
825 last_offset = ctx->tx_max;
826
827 /* revert offset */
828 offset = last_offset;
829
830 /* 831 /*
831 * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes, 832 * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes,
832 * we send buffers as it is. If we get more data, it would be more 833 * we send buffers as it is. If we get more data, it would be more
833 * efficient for USB HS mobile device with DMA engine to receive a full 834 * efficient for USB HS mobile device with DMA engine to receive a full
834 * size NTB, than canceling DMA transfer and receiving a short packet. 835 * size NTB, than canceling DMA transfer and receiving a short packet.
835 */ 836 */
836 if (offset > CDC_NCM_MIN_TX_PKT) 837 if (skb_out->len > CDC_NCM_MIN_TX_PKT)
837 offset = ctx->tx_max; 838 /* final zero padding */
838 839 memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len);
839 /* final zero padding */
840 cdc_ncm_zero_fill(skb_out->data, last_offset, offset, ctx->tx_max);
841
842 /* store last offset */
843 last_offset = offset;
844
845 if (((last_offset < ctx->tx_max) && ((last_offset %
846 le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) ||
847 (((last_offset == ctx->tx_max) && ((ctx->tx_max %
848 le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0)) &&
849 (ctx->tx_max < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)))) {
850 /* force short packet */
851 *(((u8 *)skb_out->data) + last_offset) = 0;
852 last_offset++;
853 }
854 840
855 /* zero the rest of the DPEs plus the last NULL entry */ 841 /* do we need to prevent a ZLP? */
856 for (; n <= CDC_NCM_DPT_DATAGRAMS_MAX; n++) { 842 if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) &&
857 ctx->tx_ncm.dpe16[n].wDatagramLength = 0; 843 (skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out))
858 ctx->tx_ncm.dpe16[n].wDatagramIndex = 0; 844 *skb_put(skb_out, 1) = 0; /* force short packet */
859 }
860
861 /* fill out 16-bit NTB header */
862 ctx->tx_ncm.nth16.dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
863 ctx->tx_ncm.nth16.wHeaderLength =
864 cpu_to_le16(sizeof(ctx->tx_ncm.nth16));
865 ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq);
866 ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset);
867 index = ALIGN(sizeof(struct usb_cdc_ncm_nth16), ctx->tx_ndp_modulus);
868 ctx->tx_ncm.nth16.wNdpIndex = cpu_to_le16(index);
869
870 memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16));
871 ctx->tx_seq++;
872
873 /* fill out 16-bit NDP table */
874 ctx->tx_ncm.ndp16.dwSignature =
875 cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN);
876 rem = sizeof(ctx->tx_ncm.ndp16) + ((ctx->tx_curr_frame_num + 1) *
877 sizeof(struct usb_cdc_ncm_dpe16));
878 ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem);
879 ctx->tx_ncm.ndp16.wNextNdpIndex = 0; /* reserved */
880
881 memcpy(((u8 *)skb_out->data) + index,
882 &(ctx->tx_ncm.ndp16),
883 sizeof(ctx->tx_ncm.ndp16));
884
885 memcpy(((u8 *)skb_out->data) + index + sizeof(ctx->tx_ncm.ndp16),
886 &(ctx->tx_ncm.dpe16),
887 (ctx->tx_curr_frame_num + 1) *
888 sizeof(struct usb_cdc_ncm_dpe16));
889 845
890 /* set frame length */ 846 /* set final frame length */
891 skb_put(skb_out, last_offset); 847 nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
848 nth16->wBlockLength = cpu_to_le16(skb_out->len);
892 849
893 /* return skb */ 850 /* return skb */
894 ctx->tx_curr_skb = NULL; 851 ctx->tx_curr_skb = NULL;
@@ -955,7 +912,7 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
955 goto error; 912 goto error;
956 913
957 spin_lock_bh(&ctx->mtx); 914 spin_lock_bh(&ctx->mtx);
958 skb_out = cdc_ncm_fill_tx_frame(ctx, skb); 915 skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
959 spin_unlock_bh(&ctx->mtx); 916 spin_unlock_bh(&ctx->mtx);
960 return skb_out; 917 return skb_out;
961 918