Skip to content

Commit c78b7c5

Browse files
bmorkdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ff06ab1 commit c78b7c5

File tree

1 file changed

+102
-145
lines changed

1 file changed

+102
-145
lines changed

drivers/net/usb/cdc_ncm.c

Lines changed: 102 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,11 @@
8888
(sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \
8989
(CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
9090

91-
struct cdc_ncm_data {
92-
struct usb_cdc_ncm_nth16 nth16;
93-
struct usb_cdc_ncm_ndp16 ndp16;
94-
struct usb_cdc_ncm_dpe16 dpe16[CDC_NCM_DPT_DATAGRAMS_MAX + 1];
95-
};
91+
#define CDC_NCM_NDP_SIZE \
92+
(sizeof(struct usb_cdc_ncm_ndp16) + \
93+
(CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
9694

9795
struct cdc_ncm_ctx {
98-
struct cdc_ncm_data tx_ncm;
9996
struct usb_cdc_ncm_ntb_parameters ncm_parm;
10097
struct hrtimer tx_timer;
10198
struct tasklet_struct bh;
@@ -117,13 +114,12 @@ struct cdc_ncm_ctx {
117114

118115
struct sk_buff *tx_curr_skb;
119116
struct sk_buff *tx_rem_skb;
117+
__le32 tx_rem_sign;
120118

121119
spinlock_t mtx;
122120
atomic_t stop;
123121

124122
u32 tx_timer_pending;
125-
u32 tx_curr_offset;
126-
u32 tx_curr_last_offset;
127123
u32 tx_curr_frame_num;
128124
u32 rx_speed;
129125
u32 tx_speed;
@@ -658,51 +654,75 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
658654
return ret;
659655
}
660656

661-
static void cdc_ncm_zero_fill(u8 *ptr, u32 first, u32 end, u32 max)
657+
static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max)
662658
{
663-
if (first >= max)
664-
return;
665-
if (first >= end)
666-
return;
667-
if (end > max)
668-
end = max;
669-
memset(ptr + first, 0, end - first);
659+
size_t align = ALIGN(skb->len, modulus) - skb->len + remainder;
660+
661+
if (skb->len + align > max)
662+
align = max - skb->len;
663+
if (align && skb_tailroom(skb) >= align)
664+
memset(skb_put(skb, align), 0, align);
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+
*/
670+
static 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;
670702
}
671703

672704
static struct sk_buff *
673-
cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
705+
cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
674706
{
707+
struct usb_cdc_ncm_nth16 *nth16;
708+
struct usb_cdc_ncm_ndp16 *ndp16;
675709
struct sk_buff *skb_out;
676-
u32 rem;
677-
u32 offset;
678-
u32 last_offset;
679-
u16 n = 0, index;
710+
u16 n = 0, index, ndplen;
680711
u8 ready2send = 0;
681712

682713
/* if there is a remaining skb, it gets priority */
683-
if (skb != NULL)
714+
if (skb != NULL) {
684715
swap(skb, ctx->tx_rem_skb);
685-
else
716+
swap(sign, ctx->tx_rem_sign);
717+
} else {
686718
ready2send = 1;
687-
688-
/*
689-
* +----------------+
690-
* | skb_out |
691-
* +----------------+
692-
* ^ offset
693-
* ^ last_offset
694-
*/
719+
}
695720

696721
/* check if we are resuming an OUT skb */
697-
if (ctx->tx_curr_skb != NULL) {
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;
722+
skb_out = ctx->tx_curr_skb;
703723

704-
} else {
705-
/* reset variables */
724+
/* allocate a new OUT skb */
725+
if (!skb_out) {
706726
skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC);
707727
if (skb_out == NULL) {
708728
if (skb != NULL) {
@@ -711,43 +731,36 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
711731
}
712732
goto exit_no_skb;
713733
}
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++);
714739

715-
/* make room for NTH and NDP */
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;
740+
/* count total number of frames in this NTB */
729741
ctx->tx_curr_frame_num = 0;
730742
}
731743

732-
for (; n < ctx->tx_max_datagrams; n++) {
733-
/* check if end of transmit buffer is reached */
734-
if (offset >= ctx->tx_max) {
735-
ready2send = 1;
736-
break;
737-
}
738-
/* compute maximum buffer size */
739-
rem = ctx->tx_max - offset;
740-
744+
for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
745+
/* send any remaining skb first */
741746
if (skb == NULL) {
742747
skb = ctx->tx_rem_skb;
748+
sign = ctx->tx_rem_sign;
743749
ctx->tx_rem_skb = NULL;
744750

745751
/* check for end of skb */
746752
if (skb == NULL)
747753
break;
748754
}
749755

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) {
751764
if (n == 0) {
752765
/* won't fit, MTU problem? */
753766
dev_kfree_skb_any(skb);
@@ -760,31 +773,30 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
760773
ctx->netdev->stats.tx_dropped++;
761774
}
762775
ctx->tx_rem_skb = skb;
776+
ctx->tx_rem_sign = sign;
763777
skb = NULL;
764778
ready2send = 1;
765779
}
766780
break;
767781
}
768782

769-
memcpy(((u8 *)skb_out->data) + offset, skb->data, skb->len);
770-
771-
ctx->tx_ncm.dpe16[n].wDatagramLength = cpu_to_le16(skb->len);
772-
ctx->tx_ncm.dpe16[n].wDatagramIndex = cpu_to_le16(offset);
773-
774-
/* update offset */
775-
offset += skb->len;
783+
/* calculate frame number withing this NDP */
784+
ndplen = le16_to_cpu(ndp16->wLength);
785+
index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;
776786

777-
/* store last valid offset before alignment */
778-
last_offset = offset;
779-
780-
/* align offset correctly */
781-
offset = ALIGN(offset, ctx->tx_modulus) + ctx->tx_remainder;
782-
783-
/* zero padding */
784-
cdc_ncm_zero_fill(skb_out->data, last_offset, offset,
785-
ctx->tx_max);
787+
/* OK, add this skb */
788+
ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
789+
ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
790+
ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
791+
memcpy(skb_put(skb_out, skb->len), skb->data, skb->len);
786792
dev_kfree_skb_any(skb);
787793
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+
}
788800
}
789801

790802
/* free up any dangling skb */
@@ -800,16 +812,12 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
800812
/* wait for more frames */
801813
/* push variables */
802814
ctx->tx_curr_skb = skb_out;
803-
ctx->tx_curr_offset = offset;
804-
ctx->tx_curr_last_offset = last_offset;
805815
goto exit_no_skb;
806816

807817
} else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) {
808818
/* wait for more frames */
809819
/* push variables */
810820
ctx->tx_curr_skb = skb_out;
811-
ctx->tx_curr_offset = offset;
812-
ctx->tx_curr_last_offset = last_offset;
813821
/* set the pending count */
814822
if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
815823
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)
820828
/* variables will be reset at next call */
821829
}
822830

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-
830831
/*
831832
* If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes,
832833
* we send buffers as it is. If we get more data, it would be more
833834
* efficient for USB HS mobile device with DMA engine to receive a full
834835
* size NTB, than canceling DMA transfer and receiving a short packet.
835836
*/
836-
if (offset > CDC_NCM_MIN_TX_PKT)
837-
offset = ctx->tx_max;
838-
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-
}
837+
if (skb_out->len > CDC_NCM_MIN_TX_PKT)
838+
/* final zero padding */
839+
memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len);
854840

855-
/* zero the rest of the DPEs plus the last NULL entry */
856-
for (; n <= CDC_NCM_DPT_DATAGRAMS_MAX; n++) {
857-
ctx->tx_ncm.dpe16[n].wDatagramLength = 0;
858-
ctx->tx_ncm.dpe16[n].wDatagramIndex = 0;
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));
841+
/* do we need to prevent a ZLP? */
842+
if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) &&
843+
(skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out))
844+
*skb_put(skb_out, 1) = 0; /* force short packet */
889845

890-
/* set frame length */
891-
skb_put(skb_out, last_offset);
846+
/* set final frame length */
847+
nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
848+
nth16->wBlockLength = cpu_to_le16(skb_out->len);
892849

893850
/* return skb */
894851
ctx->tx_curr_skb = NULL;
@@ -955,7 +912,7 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
955912
goto error;
956913

957914
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));
959916
spin_unlock_bh(&ctx->mtx);
960917
return skb_out;
961918

0 commit comments

Comments
 (0)