Commit d9f0713c authored by Loic Poulain's avatar Loic Poulain Committed by David S. Miller

net: mhi: Add support for non-linear MBIM skb processing

Currently, if skb is non-linear, due to MHI skb chaining, it is
linearized in MBIM RX handler prior MBIM decoding, causing extra
allocation and copy that can be as large as the maximum MBIM frame
size (32K).

This change introduces MBIM decoding for non-linear skb, allowing to
process 'large' non-linear MBIM packets without skb linearization.
The IP packets are simply extracted from the MBIM frame using the
skb_copy_bits helper.
Signed-off-by: default avatarLoic Poulain <loic.poulain@linaro.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 24ad92c8
...@@ -91,20 +91,11 @@ static int mbim_rx_verify_nth16(struct sk_buff *skb) ...@@ -91,20 +91,11 @@ static int mbim_rx_verify_nth16(struct sk_buff *skb)
return le16_to_cpu(nth16->wNdpIndex); return le16_to_cpu(nth16->wNdpIndex);
} }
static int mbim_rx_verify_ndp16(struct sk_buff *skb, int ndpoffset) static int mbim_rx_verify_ndp16(struct sk_buff *skb, struct usb_cdc_ncm_ndp16 *ndp16)
{ {
struct mhi_net_dev *dev = netdev_priv(skb->dev); struct mhi_net_dev *dev = netdev_priv(skb->dev);
struct usb_cdc_ncm_ndp16 *ndp16;
int ret; int ret;
if (ndpoffset + sizeof(struct usb_cdc_ncm_ndp16) > skb->len) {
netif_dbg(dev, rx_err, dev->ndev, "invalid NDP offset <%u>\n",
ndpoffset);
return -EINVAL;
}
ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
netif_dbg(dev, rx_err, dev->ndev, "invalid DPT16 length <%u>\n", netif_dbg(dev, rx_err, dev->ndev, "invalid DPT16 length <%u>\n",
le16_to_cpu(ndp16->wLength)); le16_to_cpu(ndp16->wLength));
...@@ -130,9 +121,6 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb) ...@@ -130,9 +121,6 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb)
struct net_device *ndev = mhi_netdev->ndev; struct net_device *ndev = mhi_netdev->ndev;
int ndpoffset; int ndpoffset;
if (skb_linearize(skb))
goto error;
/* Check NTB header and retrieve first NDP offset */ /* Check NTB header and retrieve first NDP offset */
ndpoffset = mbim_rx_verify_nth16(skb); ndpoffset = mbim_rx_verify_nth16(skb);
if (ndpoffset < 0) { if (ndpoffset < 0) {
...@@ -142,12 +130,19 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb) ...@@ -142,12 +130,19 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb)
/* Process each NDP */ /* Process each NDP */
while (1) { while (1) {
struct usb_cdc_ncm_ndp16 *ndp16; struct usb_cdc_ncm_ndp16 ndp16;
struct usb_cdc_ncm_dpe16 *dpe16; struct usb_cdc_ncm_dpe16 dpe16;
int nframes, n; int nframes, n, dpeoffset;
if (skb_copy_bits(skb, ndpoffset, &ndp16, sizeof(ndp16))) {
net_err_ratelimited("%s: Incorrect NDP offset (%u)\n",
ndev->name, ndpoffset);
__mbim_length_errors_inc(mhi_netdev);
goto error;
}
/* Check NDP header and retrieve number of datagrams */ /* Check NDP header and retrieve number of datagrams */
nframes = mbim_rx_verify_ndp16(skb, ndpoffset); nframes = mbim_rx_verify_ndp16(skb, &ndp16);
if (nframes < 0) { if (nframes < 0) {
net_err_ratelimited("%s: Incorrect NDP16\n", ndev->name); net_err_ratelimited("%s: Incorrect NDP16\n", ndev->name);
__mbim_length_errors_inc(mhi_netdev); __mbim_length_errors_inc(mhi_netdev);
...@@ -155,8 +150,7 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb) ...@@ -155,8 +150,7 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb)
} }
/* Only IP data type supported, no DSS in MHI context */ /* Only IP data type supported, no DSS in MHI context */
ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset); if ((ndp16.dwSignature & cpu_to_le32(MBIM_NDP16_SIGN_MASK))
if ((ndp16->dwSignature & cpu_to_le32(MBIM_NDP16_SIGN_MASK))
!= cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN)) { != cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN)) {
net_err_ratelimited("%s: Unsupported NDP type\n", ndev->name); net_err_ratelimited("%s: Unsupported NDP type\n", ndev->name);
__mbim_errors_inc(mhi_netdev); __mbim_errors_inc(mhi_netdev);
...@@ -164,19 +158,24 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb) ...@@ -164,19 +158,24 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb)
} }
/* Only primary IP session 0 (0x00) supported for now */ /* Only primary IP session 0 (0x00) supported for now */
if (ndp16->dwSignature & ~cpu_to_le32(MBIM_NDP16_SIGN_MASK)) { if (ndp16.dwSignature & ~cpu_to_le32(MBIM_NDP16_SIGN_MASK)) {
net_err_ratelimited("%s: bad packet session\n", ndev->name); net_err_ratelimited("%s: bad packet session\n", ndev->name);
__mbim_errors_inc(mhi_netdev); __mbim_errors_inc(mhi_netdev);
goto next_ndp; goto next_ndp;
} }
/* de-aggregate and deliver IP packets */ /* de-aggregate and deliver IP packets */
dpe16 = ndp16->dpe16; dpeoffset = ndpoffset + sizeof(struct usb_cdc_ncm_ndp16);
for (n = 0; n < nframes; n++, dpe16++) { for (n = 0; n < nframes; n++, dpeoffset += sizeof(dpe16)) {
u16 dgram_offset = le16_to_cpu(dpe16->wDatagramIndex); u16 dgram_offset, dgram_len;
u16 dgram_len = le16_to_cpu(dpe16->wDatagramLength);
struct sk_buff *skbn; struct sk_buff *skbn;
if (skb_copy_bits(skb, dpeoffset, &dpe16, sizeof(dpe16)))
break;
dgram_offset = le16_to_cpu(dpe16.wDatagramIndex);
dgram_len = le16_to_cpu(dpe16.wDatagramLength);
if (!dgram_offset || !dgram_len) if (!dgram_offset || !dgram_len)
break; /* null terminator */ break; /* null terminator */
...@@ -185,7 +184,7 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb) ...@@ -185,7 +184,7 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb)
continue; continue;
skb_put(skbn, dgram_len); skb_put(skbn, dgram_len);
memcpy(skbn->data, skb->data + dgram_offset, dgram_len); skb_copy_bits(skb, dgram_offset, skbn->data, dgram_len);
switch (skbn->data[0] & 0xf0) { switch (skbn->data[0] & 0xf0) {
case 0x40: case 0x40:
...@@ -206,7 +205,7 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb) ...@@ -206,7 +205,7 @@ static void mbim_rx(struct mhi_net_dev *mhi_netdev, struct sk_buff *skb)
} }
next_ndp: next_ndp:
/* Other NDP to process? */ /* Other NDP to process? */
ndpoffset = (int)le16_to_cpu(ndp16->wNextNdpIndex); ndpoffset = (int)le16_to_cpu(ndp16.wNextNdpIndex);
if (!ndpoffset) if (!ndpoffset)
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment