kernel: fq_codel match flows_cnt to limit sizing
[openwrt/openwrt.git] / target / linux / generic / patches-4.1 / 190-cdc_ncm_add_support_for_moving_ndp_to_end_of_ncm_frame.patch
1 From 4a0e3e989d66bb7204b163d9cfaa7fa96d0f2023 Mon Sep 17 00:00:00 2001
2 From: Enrico Mioso <mrkiko.rs@gmail.com>
3 Date: Wed, 8 Jul 2015 13:05:57 +0200
4 Subject: [PATCH] cdc_ncm: Add support for moving NDP to end of NCM frame
5
6 NCM specs are not actually mandating a specific position in the frame for
7 the NDP (Network Datagram Pointer). However, some Huawei devices will
8 ignore our aggregates if it is not placed after the datagrams it points
9 to. Add support for doing just this, in a per-device configurable way.
10 While at it, update NCM subdrivers, disabling this functionality in all of
11 them, except in huawei_cdc_ncm where it is enabled instead.
12 We aren't making any distinction between different Huawei NCM devices,
13 based on what the vendor driver does. Standard NCM devices are left
14 unaffected: if they are compliant, they should be always usable, still
15 stay on the safe side.
16
17 This change has been tested and working with a Huawei E3131 device (which
18 works regardless of NDP position), a Huawei E3531 (also working both
19 ways) and an E3372 (which mandates NDP to be after indexed datagrams).
20
21 V1->V2:
22 - corrected wrong NDP acronym definition
23 - fixed possible NULL pointer dereference
24 - patch cleanup
25 V2->V3:
26 - Properly account for the NDP size when writing new packets to SKB
27
28 Signed-off-by: Enrico Mioso <mrkiko.rs@gmail.com>
29 Signed-off-by: David S. Miller <davem@davemloft.net>
30 ---
31 drivers/net/usb/cdc_mbim.c | 2 +-
32 drivers/net/usb/cdc_ncm.c | 61 ++++++++++++++++++++++++++++++++++++----
33 drivers/net/usb/huawei_cdc_ncm.c | 7 +++--
34 include/linux/usb/cdc_ncm.h | 7 ++++-
35 4 files changed, 67 insertions(+), 10 deletions(-)
36
37 --- a/drivers/net/usb/cdc_mbim.c
38 +++ b/drivers/net/usb/cdc_mbim.c
39 @@ -158,7 +158,7 @@ static int cdc_mbim_bind(struct usbnet *
40 if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
41 goto err;
42
43 - ret = cdc_ncm_bind_common(dev, intf, data_altsetting);
44 + ret = cdc_ncm_bind_common(dev, intf, data_altsetting, 0);
45 if (ret)
46 goto err;
47
48 --- a/drivers/net/usb/cdc_ncm.c
49 +++ b/drivers/net/usb/cdc_ncm.c
50 @@ -685,6 +685,8 @@ static void cdc_ncm_free(struct cdc_ncm_
51 ctx->tx_curr_skb = NULL;
52 }
53
54 + kfree(ctx->delayed_ndp16);
55 +
56 kfree(ctx);
57 }
58
59 @@ -715,7 +717,7 @@ static const struct net_device_ops cdc_n
60 .ndo_validate_addr = eth_validate_addr,
61 };
62
63 -int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting)
64 +int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags)
65 {
66 const struct usb_cdc_union_desc *union_desc = NULL;
67 struct cdc_ncm_ctx *ctx;
68 @@ -883,6 +885,17 @@ advance:
69 /* finish setting up the device specific data */
70 cdc_ncm_setup(dev);
71
72 + /* Device-specific flags */
73 + ctx->drvflags = drvflags;
74 +
75 + /* Allocate the delayed NDP if needed. */
76 + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
77 + ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
78 + if (!ctx->delayed_ndp16)
79 + goto error2;
80 + dev_info(&intf->dev, "NDP will be placed at end of frame for this device.");
81 + }
82 +
83 /* override ethtool_ops */
84 dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
85
86 @@ -985,8 +998,11 @@ static int cdc_ncm_bind(struct usbnet *d
87 if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
88 return -ENODEV;
89
90 - /* The NCM data altsetting is fixed */
91 - ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);
92 + /* The NCM data altsetting is fixed, so we hard-coded it.
93 + * Additionally, generic NCM devices are assumed to accept arbitrarily
94 + * placed NDP.
95 + */
96 + ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0);
97
98 /*
99 * We should get an event when network connection is "connected" or
100 @@ -1017,6 +1033,14 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm
101 struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
102 size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);
103
104 + /* If NDP should be moved to the end of the NCM package, we can't follow the
105 + * NTH16 header as we would normally do. NDP isn't written to the SKB yet, and
106 + * the wNdpIndex field in the header is actually not consistent with reality. It will be later.
107 + */
108 + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
109 + if (ctx->delayed_ndp16->dwSignature == sign)
110 + return ctx->delayed_ndp16;
111 +
112 /* follow the chain of NDPs, looking for a match */
113 while (ndpoffset) {
114 ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
115 @@ -1026,7 +1050,8 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm
116 }
117
118 /* align new NDP */
119 - cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
120 + if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
121 + cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
122
123 /* verify that there is room for the NDP and the datagram (reserve) */
124 if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size)
125 @@ -1039,7 +1064,11 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm
126 nth16->wNdpIndex = cpu_to_le16(skb->len);
127
128 /* push a new empty NDP */
129 - ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size);
130 + if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
131 + ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size);
132 + else
133 + ndp16 = ctx->delayed_ndp16;
134 +
135 ndp16->dwSignature = sign;
136 ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
137 return ndp16;
138 @@ -1054,6 +1083,15 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev
139 struct sk_buff *skb_out;
140 u16 n = 0, index, ndplen;
141 u8 ready2send = 0;
142 + u32 delayed_ndp_size;
143 +
144 + /* When our NDP gets written in cdc_ncm_ndp(), then skb_out->len gets updated
145 + * accordingly. Otherwise, we should check here.
146 + */
147 + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
148 + delayed_ndp_size = ctx->max_ndp_size;
149 + else
150 + delayed_ndp_size = 0;
151
152 /* if there is a remaining skb, it gets priority */
153 if (skb != NULL) {
154 @@ -1108,7 +1146,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev
155 cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max);
156
157 /* check if we had enough room left for both NDP and frame */
158 - if (!ndp16 || skb_out->len + skb->len > ctx->tx_max) {
159 + if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_max) {
160 if (n == 0) {
161 /* won't fit, MTU problem? */
162 dev_kfree_skb_any(skb);
163 @@ -1181,6 +1219,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev
164 /* variables will be reset at next call */
165 }
166
167 + /* If requested, put NDP at end of frame. */
168 + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
169 + nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
170 + cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_max);
171 + nth16->wNdpIndex = cpu_to_le16(skb_out->len);
172 + memcpy(skb_put(skb_out, ctx->max_ndp_size), ctx->delayed_ndp16, ctx->max_ndp_size);
173 +
174 + /* Zero out delayed NDP - signature checking will naturally fail. */
175 + ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size);
176 + }
177 +
178 /* If collected data size is less or equal ctx->min_tx_pkt
179 * bytes, we send buffers as it is. If we get more data, it
180 * would be more efficient for USB HS mobile device with DMA
181 --- a/drivers/net/usb/huawei_cdc_ncm.c
182 +++ b/drivers/net/usb/huawei_cdc_ncm.c
183 @@ -73,11 +73,14 @@ static int huawei_cdc_ncm_bind(struct us
184 struct usb_driver *subdriver = ERR_PTR(-ENODEV);
185 int ret = -ENODEV;
186 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
187 + int drvflags = 0;
188
189 /* altsetting should always be 1 for NCM devices - so we hard-coded
190 - * it here
191 + * it here. Some huawei devices will need the NDP part of the NCM package to
192 + * be at the end of the frame.
193 */
194 - ret = cdc_ncm_bind_common(usbnet_dev, intf, 1);
195 + drvflags |= CDC_NCM_FLAG_NDP_TO_END;
196 + ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags);
197 if (ret)
198 goto err;
199
200 --- a/include/linux/usb/cdc_ncm.h
201 +++ b/include/linux/usb/cdc_ncm.h
202 @@ -80,6 +80,9 @@
203 #define CDC_NCM_TIMER_INTERVAL_MIN 5UL
204 #define CDC_NCM_TIMER_INTERVAL_MAX (U32_MAX / NSEC_PER_USEC)
205
206 +/* Driver flags */
207 +#define CDC_NCM_FLAG_NDP_TO_END 0x02 /* NDP is placed at end of frame */
208 +
209 #define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \
210 (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
211 #define cdc_ncm_data_intf_is_mbim(x) ((x)->desc.bInterfaceProtocol == USB_CDC_MBIM_PROTO_NTB)
212 @@ -103,9 +106,11 @@ struct cdc_ncm_ctx {
213
214 spinlock_t mtx;
215 atomic_t stop;
216 + int drvflags;
217
218 u32 timer_interval;
219 u32 max_ndp_size;
220 + struct usb_cdc_ncm_ndp16 *delayed_ndp16;
221
222 u32 tx_timer_pending;
223 u32 tx_curr_frame_num;
224 @@ -134,7 +139,7 @@ struct cdc_ncm_ctx {
225
226 u8 cdc_ncm_select_altsetting(struct usb_interface *intf);
227 int cdc_ncm_change_mtu(struct net_device *net, int new_mtu);
228 -int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
229 +int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags);
230 void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
231 struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
232 int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);