Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acebb475 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge 5.4.245 into android11-5.4-lts



Changes in 5.4.245
	cdc_ncm: Implement the 32-bit version of NCM Transfer Block
	net: cdc_ncm: Deal with too low values of dwNtbOutMaxSize
	power: supply: bq27xxx: After charger plug in/out wait 0.5s for things to stabilize
	power: supply: core: Refactor power_supply_set_input_current_limit_from_supplier()
	power: supply: bq24190: Call power_supply_changed() after updating input current
	fs: fix undefined behavior in bit shift for SB_NOUSER
	net/mlx5: devcom only supports 2 ports
	net/mlx5: Devcom, serialize devcom registration
	cdc_ncm: Fix the build warning
	io_uring: always grab lock in io_cancel_async_work()
	io_uring: don't drop completion lock before timer is fully initialized
	io_uring: have io_kill_timeout() honor the request references
	bluetooth: Add cmd validity checks at the start of hci_sock_ioctl()
	binder: fix UAF caused by faulty buffer cleanup
	ipv{4,6}/raw: fix output xfrm lookup wrt protocol
	netfilter: ctnetlink: Support offloaded conntrack entry deletion
	Linux 5.4.245

Change-Id: I25e786ed304f80b6ccb3896a8b8d2f16384f0cd6
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parents b5d7df0c cf0b1e54
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
VERSION = 5
PATCHLEVEL = 4
SUBLEVEL = 244
SUBLEVEL = 245
EXTRAVERSION =
NAME = Kleptomaniac Octopus

+23 −12
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@

#include <linux/mlx5/vport.h>
#include "lib/devcom.h"
#include "mlx5_core.h"

static LIST_HEAD(devcom_list);

@@ -14,7 +15,7 @@ static LIST_HEAD(devcom_list);
struct mlx5_devcom_component {
	struct {
		void *data;
	} device[MLX5_MAX_PORTS];
	} device[MLX5_DEVCOM_PORTS_SUPPORTED];

	mlx5_devcom_event_handler_t handler;
	struct rw_semaphore sem;
@@ -25,7 +26,7 @@ struct mlx5_devcom_list {
	struct list_head list;

	struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS];
	struct mlx5_core_dev *devs[MLX5_MAX_PORTS];
	struct mlx5_core_dev *devs[MLX5_DEVCOM_PORTS_SUPPORTED];
};

struct mlx5_devcom {
@@ -74,13 +75,16 @@ struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)

	if (!mlx5_core_is_pf(dev))
		return NULL;
	if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED)
		return NULL;

	mlx5_dev_list_lock();
	sguid0 = mlx5_query_nic_system_image_guid(dev);
	list_for_each_entry(iter, &devcom_list, list) {
		struct mlx5_core_dev *tmp_dev = NULL;

		idx = -1;
		for (i = 0; i < MLX5_MAX_PORTS; i++) {
		for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) {
			if (iter->devs[i])
				tmp_dev = iter->devs[i];
			else
@@ -100,8 +104,10 @@ struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)

	if (!priv) {
		priv = mlx5_devcom_list_alloc();
		if (!priv)
			return ERR_PTR(-ENOMEM);
		if (!priv) {
			devcom = ERR_PTR(-ENOMEM);
			goto out;
		}

		idx = 0;
		new_priv = true;
@@ -112,12 +118,14 @@ struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)
	if (!devcom) {
		if (new_priv)
			kfree(priv);
		return ERR_PTR(-ENOMEM);
		devcom = ERR_PTR(-ENOMEM);
		goto out;
	}

	if (new_priv)
		list_add(&priv->list, &devcom_list);

out:
	mlx5_dev_list_unlock();
	return devcom;
}

@@ -130,20 +138,23 @@ void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom)
	if (IS_ERR_OR_NULL(devcom))
		return;

	mlx5_dev_list_lock();
	priv = devcom->priv;
	priv->devs[devcom->idx] = NULL;

	kfree(devcom);

	for (i = 0; i < MLX5_MAX_PORTS; i++)
	for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
		if (priv->devs[i])
			break;

	if (i != MLX5_MAX_PORTS)
		return;
	if (i != MLX5_DEVCOM_PORTS_SUPPORTED)
		goto out;

	list_del(&priv->list);
	kfree(priv);
out:
	mlx5_dev_list_unlock();
}

void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
@@ -192,7 +203,7 @@ int mlx5_devcom_send_event(struct mlx5_devcom *devcom,

	comp = &devcom->priv->components[id];
	down_write(&comp->sem);
	for (i = 0; i < MLX5_MAX_PORTS; i++)
	for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
		if (i != devcom->idx && comp->device[i].data) {
			err = comp->handler(event, comp->device[i].data,
					    event_data);
@@ -240,7 +251,7 @@ void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
		return NULL;
	}

	for (i = 0; i < MLX5_MAX_PORTS; i++)
	for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
		if (i != devcom->idx)
			break;

+2 −0
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@

#include <linux/mlx5/driver.h>

#define MLX5_DEVCOM_PORTS_SUPPORTED 2

enum mlx5_devcom_components {
	MLX5_DEVCOM_ESW_OFFLOADS,

+339 −96
Original line number Diff line number Diff line
@@ -175,10 +175,17 @@ static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx)
	u32 val, max, min;

	/* clamp new_tx to sane values */
	if (ctx->is_ndp16)
		min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16);
	max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
	if (max == 0)
	else
		min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth32);

	if (le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) == 0)
		max = CDC_NCM_NTB_MAX_SIZE_TX; /* dwNtbOutMaxSize not set */
	else
		max = clamp_t(u32, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize),
			      USB_CDC_NCM_NTB_MIN_OUT_SIZE,
			      CDC_NCM_NTB_MAX_SIZE_TX);

	/* some devices set dwNtbOutMaxSize too low for the above default */
	min = min(min, max);
@@ -309,11 +316,18 @@ static ssize_t ndp_to_end_store(struct device *d, struct device_attribute *attr
	if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
		return len;

	if (enable && !ctx->delayed_ndp16) {
	if (enable) {
		if (ctx->is_ndp16 && !ctx->delayed_ndp16) {
			ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
			if (!ctx->delayed_ndp16)
				return -ENOMEM;
		}
		if (!ctx->is_ndp16 && !ctx->delayed_ndp32) {
			ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
			if (!ctx->delayed_ndp32)
				return -ENOMEM;
		}
	}

	/* flush pending data before changing flag */
	netif_tx_lock_bh(dev->net);
@@ -514,6 +528,9 @@ static int cdc_ncm_init(struct usbnet *dev)
			dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n");
	}

	/* use ndp16 by default */
	ctx->is_ndp16 = 1;

	/* set NTB format, if both formats are supported.
	 *
	 * "The host shall only send this command while the NCM Data
@@ -521,15 +538,28 @@ static int cdc_ncm_init(struct usbnet *dev)
	 */
	if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) &
						USB_CDC_NCM_NTB32_SUPPORTED) {
		if (ctx->drvflags & CDC_NCM_FLAG_PREFER_NTB32) {
			ctx->is_ndp16 = 0;
			dev_dbg(&dev->intf->dev, "Setting NTB format to 32-bit\n");
			err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
					       USB_TYPE_CLASS | USB_DIR_OUT
					       | USB_RECIP_INTERFACE,
					       USB_CDC_NCM_NTB32_FORMAT,
					       iface_no, NULL, 0);
		} else {
			ctx->is_ndp16 = 1;
			dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n");
			err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
					       USB_TYPE_CLASS | USB_DIR_OUT
					       | USB_RECIP_INTERFACE,
					       USB_CDC_NCM_NTB16_FORMAT,
					       iface_no, NULL, 0);
		if (err < 0)
		}
		if (err < 0) {
			ctx->is_ndp16 = 1;
			dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");
		}
	}

	/* set initial device values */
	ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);
@@ -551,7 +581,10 @@ static int cdc_ncm_init(struct usbnet *dev)
		ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX;

	/* set up maximum NDP size */
	if (ctx->is_ndp16)
		ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16);
	else
		ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp32) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe32);

	/* initial coalescing timer interval */
	ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC;
@@ -736,7 +769,10 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
		ctx->tx_curr_skb = NULL;
	}

	if (ctx->is_ndp16)
		kfree(ctx->delayed_ndp16);
	else
		kfree(ctx->delayed_ndp32);

	kfree(ctx);
}
@@ -774,10 +810,8 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
	u8 *buf;
	int len;
	int temp;
	int err;
	u8 iface_no;
	struct usb_cdc_parsed_header hdr;
	__le16 curr_ntb_format;

	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
@@ -881,32 +915,6 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
		goto error2;
	}

	/*
	 * Some Huawei devices have been observed to come out of reset in NDP32 mode.
	 * Let's check if this is the case, and set the device to NDP16 mode again if
	 * needed.
	*/
	if (ctx->drvflags & CDC_NCM_FLAG_RESET_NTB16) {
		err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_FORMAT,
				      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
				      0, iface_no, &curr_ntb_format, 2);
		if (err < 0) {
			goto error2;
		}

		if (curr_ntb_format == cpu_to_le16(USB_CDC_NCM_NTB32_FORMAT)) {
			dev_info(&intf->dev, "resetting NTB format to 16-bit");
			err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
					       USB_TYPE_CLASS | USB_DIR_OUT
					       | USB_RECIP_INTERFACE,
					       USB_CDC_NCM_NTB16_FORMAT,
					       iface_no, NULL, 0);

			if (err < 0)
				goto error2;
		}
	}

	cdc_ncm_find_endpoints(dev, ctx->data);
	cdc_ncm_find_endpoints(dev, ctx->control);
	if (!dev->in || !dev->out || !dev->status) {
@@ -931,9 +939,15 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_

	/* Allocate the delayed NDP if needed. */
	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
		if (ctx->is_ndp16) {
			ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
			if (!ctx->delayed_ndp16)
				goto error2;
		} else {
			ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
			if (!ctx->delayed_ndp32)
				goto error2;
		}
		dev_info(&intf->dev, "NDP will be placed at end of frame for this device.");
	}

@@ -1057,7 +1071,7 @@ static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remai
/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly
 * allocating a new one within skb
 */
static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
{
	struct usb_cdc_ncm_ndp16 *ndp16 = NULL;
	struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
@@ -1112,12 +1126,73 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
	return ndp16;
}

static struct usb_cdc_ncm_ndp32 *cdc_ncm_ndp32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
{
	struct usb_cdc_ncm_ndp32 *ndp32 = NULL;
	struct usb_cdc_ncm_nth32 *nth32 = (void *)skb->data;
	size_t ndpoffset = le32_to_cpu(nth32->dwNdpIndex);

	/* If NDP should be moved to the end of the NCM package, we can't follow the
	 * NTH32 header as we would normally do. NDP isn't written to the SKB yet, and
	 * the wNdpIndex field in the header is actually not consistent with reality. It will be later.
	 */
	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
		if (ctx->delayed_ndp32->dwSignature == sign)
			return ctx->delayed_ndp32;

		/* We can only push a single NDP to the end. Return
		 * NULL to send what we've already got and queue this
		 * skb for later.
		 */
		else if (ctx->delayed_ndp32->dwSignature)
			return NULL;
	}

	/* follow the chain of NDPs, looking for a match */
	while (ndpoffset) {
		ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb->data + ndpoffset);
		if  (ndp32->dwSignature == sign)
			return ndp32;
		ndpoffset = le32_to_cpu(ndp32->dwNextNdpIndex);
	}

	/* align new NDP */
	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
		cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);

	/* verify that there is room for the NDP and the datagram (reserve) */
	if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size)
		return NULL;

	/* link to it */
	if (ndp32)
		ndp32->dwNextNdpIndex = cpu_to_le32(skb->len);
	else
		nth32->dwNdpIndex = cpu_to_le32(skb->len);

	/* push a new empty NDP */
	if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
		ndp32 = skb_put_zero(skb, ctx->max_ndp_size);
	else
		ndp32 = ctx->delayed_ndp32;

	ndp32->dwSignature = sign;
	ndp32->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp32) + sizeof(struct usb_cdc_ncm_dpe32));
	return ndp32;
}

struct sk_buff *
cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
{
	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
	union {
		struct usb_cdc_ncm_nth16 *nth16;
		struct usb_cdc_ncm_nth32 *nth32;
	} nth;
	union {
		struct usb_cdc_ncm_ndp16 *ndp16;
		struct usb_cdc_ncm_ndp32 *ndp32;
	} ndp;
	struct sk_buff *skb_out;
	u16 n = 0, index, ndplen;
	u8 ready2send = 0;
@@ -1157,6 +1232,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
			 * further.
			 */
			if (skb_out == NULL) {
				/* If even the smallest allocation fails, abort. */
				if (ctx->tx_curr_size == USB_CDC_NCM_NTB_MIN_OUT_SIZE)
					goto alloc_failed;
				ctx->tx_low_mem_max_cnt = min(ctx->tx_low_mem_max_cnt + 1,
							      (unsigned)CDC_NCM_LOW_MEM_MAX_CNT);
				ctx->tx_low_mem_val = ctx->tx_low_mem_max_cnt;
@@ -1175,20 +1253,23 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
			skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC);

			/* No allocation possible so we will abort */
			if (skb_out == NULL) {
				if (skb != NULL) {
					dev_kfree_skb_any(skb);
					dev->net->stats.tx_dropped++;
				}
				goto exit_no_skb;
			}
			if (!skb_out)
				goto alloc_failed;
			ctx->tx_low_mem_val--;
		}
		if (ctx->is_ndp16) {
			/* fill out the initial 16-bit NTB header */
		nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16));
		nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
		nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
		nth16->wSequence = cpu_to_le16(ctx->tx_seq++);
			nth.nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16));
			nth.nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
			nth.nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
			nth.nth16->wSequence = cpu_to_le16(ctx->tx_seq++);
		} else {
			/* fill out the initial 32-bit NTB header */
			nth.nth32 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth32));
			nth.nth32->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH32_SIGN);
			nth.nth32->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth32));
			nth.nth32->wSequence = cpu_to_le16(ctx->tx_seq++);
		}

		/* count total number of frames in this NTB */
		ctx->tx_curr_frame_num = 0;
@@ -1210,13 +1291,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
		}

		/* get the appropriate NDP for this skb */
		ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
		if (ctx->is_ndp16)
			ndp.ndp16 = cdc_ncm_ndp16(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
		else
			ndp.ndp32 = cdc_ncm_ndp32(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);

		/* align beginning of next frame */
		cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_curr_size);

		/* check if we had enough room left for both NDP and frame */
		if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) {
		if ((ctx->is_ndp16 && !ndp.ndp16) || (!ctx->is_ndp16 && !ndp.ndp32) ||
		    skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) {
			if (n == 0) {
				/* won't fit, MTU problem? */
				dev_kfree_skb_any(skb);
@@ -1238,13 +1323,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
		}

		/* calculate frame number withing this NDP */
		ndplen = le16_to_cpu(ndp16->wLength);
		if (ctx->is_ndp16) {
			ndplen = le16_to_cpu(ndp.ndp16->wLength);
			index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;

			/* OK, add this skb */
		ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
		ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
		ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
			ndp.ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
			ndp.ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
			ndp.ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
		} else {
			ndplen = le16_to_cpu(ndp.ndp32->wLength);
			index = (ndplen - sizeof(struct usb_cdc_ncm_ndp32)) / sizeof(struct usb_cdc_ncm_dpe32) - 1;

			ndp.ndp32->dpe32[index].dwDatagramLength = cpu_to_le32(skb->len);
			ndp.ndp32->dpe32[index].dwDatagramIndex = cpu_to_le32(skb_out->len);
			ndp.ndp32->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe32));
		}
		skb_put_data(skb_out, skb->data, skb->len);
		ctx->tx_curr_frame_payload += skb->len;	/* count real tx payload data */
		dev_kfree_skb_any(skb);
@@ -1291,13 +1385,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)

	/* If requested, put NDP at end of frame. */
	if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
		nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
		if (ctx->is_ndp16) {
			nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
			cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size);
		nth16->wNdpIndex = cpu_to_le16(skb_out->len);
			nth.nth16->wNdpIndex = cpu_to_le16(skb_out->len);
			skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size);

			/* Zero out delayed NDP - signature checking will naturally fail. */
		ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size);
			ndp.ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size);
		} else {
			nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data;
			cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size);
			nth.nth32->dwNdpIndex = cpu_to_le32(skb_out->len);
			skb_put_data(skb_out, ctx->delayed_ndp32, ctx->max_ndp_size);

			ndp.ndp32 = memset(ctx->delayed_ndp32, 0, ctx->max_ndp_size);
		}
	}

	/* If collected data size is less or equal ctx->min_tx_pkt
@@ -1320,8 +1423,13 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
	}

	/* set final frame length */
	nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
	nth16->wBlockLength = cpu_to_le16(skb_out->len);
	if (ctx->is_ndp16) {
		nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
		nth.nth16->wBlockLength = cpu_to_le16(skb_out->len);
	} else {
		nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data;
		nth.nth32->dwBlockLength = cpu_to_le32(skb_out->len);
	}

	/* return skb */
	ctx->tx_curr_skb = NULL;
@@ -1339,6 +1447,11 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)

	return skb_out;

alloc_failed:
	if (skb) {
		dev_kfree_skb_any(skb);
		dev->net->stats.tx_dropped++;
	}
exit_no_skb:
	/* Start timer, if there is a remaining non-empty skb */
	if (ctx->tx_curr_skb != NULL && n > 0)
@@ -1404,7 +1517,12 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
		goto error;

	spin_lock_bh(&ctx->mtx);

	if (ctx->is_ndp16)
		skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
	else
		skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN));

	spin_unlock_bh(&ctx->mtx);
	return skb_out;

@@ -1465,6 +1583,54 @@ int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
}
EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16);

int cdc_ncm_rx_verify_nth32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
{
	struct usbnet *dev = netdev_priv(skb_in->dev);
	struct usb_cdc_ncm_nth32 *nth32;
	int len;
	int ret = -EINVAL;

	if (ctx == NULL)
		goto error;

	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth32) +
					sizeof(struct usb_cdc_ncm_ndp32))) {
		netif_dbg(dev, rx_err, dev->net, "frame too short\n");
		goto error;
	}

	nth32 = (struct usb_cdc_ncm_nth32 *)skb_in->data;

	if (nth32->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH32_SIGN)) {
		netif_dbg(dev, rx_err, dev->net,
			  "invalid NTH32 signature <%#010x>\n",
			  le32_to_cpu(nth32->dwSignature));
		goto error;
	}

	len = le32_to_cpu(nth32->dwBlockLength);
	if (len > ctx->rx_max) {
		netif_dbg(dev, rx_err, dev->net,
			  "unsupported NTB block length %u/%u\n", len,
			  ctx->rx_max);
		goto error;
	}

	if ((ctx->rx_seq + 1) != le16_to_cpu(nth32->wSequence) &&
	    (ctx->rx_seq || le16_to_cpu(nth32->wSequence)) &&
	    !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth32->wSequence))) {
		netif_dbg(dev, rx_err, dev->net,
			  "sequence number glitch prev=%d curr=%d\n",
			  ctx->rx_seq, le16_to_cpu(nth32->wSequence));
	}
	ctx->rx_seq = le16_to_cpu(nth32->wSequence);

	ret = le32_to_cpu(nth32->dwNdpIndex);
error:
	return ret;
}
EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth32);

/* verify NDP header and return number of datagrams, or negative error */
int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
{
@@ -1501,6 +1667,42 @@ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
}
EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16);

/* verify NDP header and return number of datagrams, or negative error */
int cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset)
{
	struct usbnet *dev = netdev_priv(skb_in->dev);
	struct usb_cdc_ncm_ndp32 *ndp32;
	int ret = -EINVAL;

	if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp32)) > skb_in->len) {
		netif_dbg(dev, rx_err, dev->net, "invalid NDP offset  <%u>\n",
			  ndpoffset);
		goto error;
	}
	ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset);

	if (le16_to_cpu(ndp32->wLength) < USB_CDC_NCM_NDP32_LENGTH_MIN) {
		netif_dbg(dev, rx_err, dev->net, "invalid DPT32 length <%u>\n",
			  le16_to_cpu(ndp32->wLength));
		goto error;
	}

	ret = ((le16_to_cpu(ndp32->wLength) -
					sizeof(struct usb_cdc_ncm_ndp32)) /
					sizeof(struct usb_cdc_ncm_dpe32));
	ret--; /* we process NDP entries except for the last one */

	if ((sizeof(struct usb_cdc_ncm_ndp32) +
	     ret * (sizeof(struct usb_cdc_ncm_dpe32))) > skb_in->len) {
		netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);
		ret = -EINVAL;
	}

error:
	return ret;
}
EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp32);

int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
{
	struct sk_buff *skb;
@@ -1509,34 +1711,66 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
	int nframes;
	int x;
	int offset;
	union {
		struct usb_cdc_ncm_ndp16 *ndp16;
		struct usb_cdc_ncm_ndp32 *ndp32;
	} ndp;
	union {
		struct usb_cdc_ncm_dpe16 *dpe16;
		struct usb_cdc_ncm_dpe32 *dpe32;
	} dpe;

	int ndpoffset;
	int loopcount = 50; /* arbitrary max preventing infinite loop */
	u32 payload = 0;

	if (ctx->is_ndp16)
		ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
	else
		ndpoffset = cdc_ncm_rx_verify_nth32(ctx, skb_in);

	if (ndpoffset < 0)
		goto error;

next_ndp:
	if (ctx->is_ndp16) {
		nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset);
		if (nframes < 0)
			goto error;

	ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
		ndp.ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);

	if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) {
		if (ndp.ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) {
			netif_dbg(dev, rx_err, dev->net,
				  "invalid DPT16 signature <%#010x>\n",
			  le32_to_cpu(ndp16->dwSignature));
				  le32_to_cpu(ndp.ndp16->dwSignature));
			goto err_ndp;
		}
	dpe16 = ndp16->dpe16;
		dpe.dpe16 = ndp.ndp16->dpe16;
	} else {
		nframes = cdc_ncm_rx_verify_ndp32(skb_in, ndpoffset);
		if (nframes < 0)
			goto error;

	for (x = 0; x < nframes; x++, dpe16++) {
		offset = le16_to_cpu(dpe16->wDatagramIndex);
		len = le16_to_cpu(dpe16->wDatagramLength);
		ndp.ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset);

		if (ndp.ndp32->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)) {
			netif_dbg(dev, rx_err, dev->net,
				  "invalid DPT32 signature <%#010x>\n",
				  le32_to_cpu(ndp.ndp32->dwSignature));
			goto err_ndp;
		}
		dpe.dpe32 = ndp.ndp32->dpe32;
	}

	for (x = 0; x < nframes; x++) {
		if (ctx->is_ndp16) {
			offset = le16_to_cpu(dpe.dpe16->wDatagramIndex);
			len = le16_to_cpu(dpe.dpe16->wDatagramLength);
		} else {
			offset = le32_to_cpu(dpe.dpe32->dwDatagramIndex);
			len = le32_to_cpu(dpe.dpe32->dwDatagramLength);
		}

		/*
		 * CDC NCM ch. 3.7
@@ -1567,10 +1801,19 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
			usbnet_skb_return(dev, skb);
			payload += len;	/* count payload bytes in this NTB */
		}

		if (ctx->is_ndp16)
			dpe.dpe16++;
		else
			dpe.dpe32++;
	}
err_ndp:
	/* are there more NDPs to process? */
	ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
	if (ctx->is_ndp16)
		ndpoffset = le16_to_cpu(ndp.ndp16->wNextNdpIndex);
	else
		ndpoffset = le32_to_cpu(ndp.ndp32->dwNextNdpIndex);

	if (ndpoffset && loopcount--)
		goto next_ndp;

+4 −4
Original line number Diff line number Diff line
@@ -77,11 +77,11 @@ static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev,
	 */
	drvflags |= CDC_NCM_FLAG_NDP_TO_END;

	/* Additionally, it has been reported that some Huawei E3372H devices, with
	 * firmware version 21.318.01.00.541, come out of reset in NTB32 format mode, hence
	 * needing to be set to the NTB16 one again.
	/* For many Huawei devices the NTB32 mode is the default and the best mode
	 * they work with. Huawei E5785 and E5885 devices refuse to work in NTB16 mode at all.
	 */
	drvflags |= CDC_NCM_FLAG_RESET_NTB16;
	drvflags |= CDC_NCM_FLAG_PREFER_NTB32;

	ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags);
	if (ret)
		goto err;
Loading