mirror of
https://github.com/armbian/build.git
synced 2025-08-12 06:06:58 +02:00
1308 lines
44 KiB
Diff
1308 lines
44 KiB
Diff
diff --git a/Makefile b/Makefile
|
|
index a12f1af9b03b1..c0a1daa41dc04 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,7 +1,7 @@
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
VERSION = 5
|
|
PATCHLEVEL = 4
|
|
-SUBLEVEL = 244
|
|
+SUBLEVEL = 245
|
|
EXTRAVERSION =
|
|
NAME = Kleptomaniac Octopus
|
|
|
|
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
|
|
index 4b96742f0fe57..1c39cd12b755a 100644
|
|
--- a/drivers/android/binder.c
|
|
+++ b/drivers/android/binder.c
|
|
@@ -2270,24 +2270,23 @@ static void binder_deferred_fd_close(int fd)
|
|
static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|
struct binder_thread *thread,
|
|
struct binder_buffer *buffer,
|
|
- binder_size_t failed_at,
|
|
+ binder_size_t off_end_offset,
|
|
bool is_failure)
|
|
{
|
|
int debug_id = buffer->debug_id;
|
|
- binder_size_t off_start_offset, buffer_offset, off_end_offset;
|
|
+ binder_size_t off_start_offset, buffer_offset;
|
|
|
|
binder_debug(BINDER_DEBUG_TRANSACTION,
|
|
"%d buffer release %d, size %zd-%zd, failed at %llx\n",
|
|
proc->pid, buffer->debug_id,
|
|
buffer->data_size, buffer->offsets_size,
|
|
- (unsigned long long)failed_at);
|
|
+ (unsigned long long)off_end_offset);
|
|
|
|
if (buffer->target_node)
|
|
binder_dec_node(buffer->target_node, 1, 0);
|
|
|
|
off_start_offset = ALIGN(buffer->data_size, sizeof(void *));
|
|
- off_end_offset = is_failure && failed_at ? failed_at :
|
|
- off_start_offset + buffer->offsets_size;
|
|
+
|
|
for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
|
|
buffer_offset += sizeof(binder_size_t)) {
|
|
struct binder_object_header *hdr;
|
|
@@ -2447,6 +2446,21 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|
}
|
|
}
|
|
|
|
+/* Clean up all the objects in the buffer */
|
|
+static inline void binder_release_entire_buffer(struct binder_proc *proc,
|
|
+ struct binder_thread *thread,
|
|
+ struct binder_buffer *buffer,
|
|
+ bool is_failure)
|
|
+{
|
|
+ binder_size_t off_end_offset;
|
|
+
|
|
+ off_end_offset = ALIGN(buffer->data_size, sizeof(void *));
|
|
+ off_end_offset += buffer->offsets_size;
|
|
+
|
|
+ binder_transaction_buffer_release(proc, thread, buffer,
|
|
+ off_end_offset, is_failure);
|
|
+}
|
|
+
|
|
static int binder_translate_binder(struct flat_binder_object *fp,
|
|
struct binder_transaction *t,
|
|
struct binder_thread *thread)
|
|
@@ -3930,7 +3944,7 @@ binder_free_buf(struct binder_proc *proc,
|
|
binder_node_inner_unlock(buf_node);
|
|
}
|
|
trace_binder_transaction_buffer_release(buffer);
|
|
- binder_transaction_buffer_release(proc, thread, buffer, 0, is_failure);
|
|
+ binder_release_entire_buffer(proc, thread, buffer, is_failure);
|
|
binder_alloc_free_buf(&proc->alloc, buffer);
|
|
}
|
|
|
|
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
|
|
index abd066e952286..438be215bbd45 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
|
|
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
|
|
@@ -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;
|
|
|
|
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h
|
|
index 939d5bf1581b5..94313c18bb647 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h
|
|
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h
|
|
@@ -6,6 +6,8 @@
|
|
|
|
#include <linux/mlx5/driver.h>
|
|
|
|
+#define MLX5_DEVCOM_PORTS_SUPPORTED 2
|
|
+
|
|
enum mlx5_devcom_components {
|
|
MLX5_DEVCOM_ESW_OFFLOADS,
|
|
|
|
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
|
|
index 1c9a1b94f6e28..4824385fe2c79 100644
|
|
--- a/drivers/net/usb/cdc_ncm.c
|
|
+++ b/drivers/net/usb/cdc_ncm.c
|
|
@@ -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 */
|
|
- 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)
|
|
+ if (ctx->is_ndp16)
|
|
+ min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16);
|
|
+ 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,10 +316,17 @@ 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) {
|
|
- ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
|
|
- if (!ctx->delayed_ndp16)
|
|
- return -ENOMEM;
|
|
+ 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 */
|
|
@@ -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,14 +538,27 @@ static int cdc_ncm_init(struct usbnet *dev)
|
|
*/
|
|
if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) &
|
|
USB_CDC_NCM_NTB32_SUPPORTED) {
|
|
- 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 (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) {
|
|
+ ctx->is_ndp16 = 1;
|
|
dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");
|
|
+ }
|
|
}
|
|
|
|
/* set initial device values */
|
|
@@ -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 */
|
|
- ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16);
|
|
+ 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;
|
|
}
|
|
|
|
- kfree(ctx->delayed_ndp16);
|
|
+ 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) {
|
|
- ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
|
|
- if (!ctx->delayed_ndp16)
|
|
- goto error2;
|
|
+ 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];
|
|
- struct usb_cdc_ncm_nth16 *nth16;
|
|
- struct usb_cdc_ncm_ndp16 *ndp16;
|
|
+ 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--;
|
|
}
|
|
- /* 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++);
|
|
+ if (ctx->is_ndp16) {
|
|
+ /* fill out the initial 16-bit NTB header */
|
|
+ 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);
|
|
- index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;
|
|
+ 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 */
|
|
+ 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;
|
|
|
|
- /* 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.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;
|
|
- 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);
|
|
- skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size);
|
|
+ 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);
|
|
+ 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. */
|
|
+ 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);
|
|
|
|
- /* Zero out delayed NDP - signature checking will naturally fail. */
|
|
- ndp16 = memset(ctx->delayed_ndp16, 0, 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);
|
|
- skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
|
|
+
|
|
+ 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 @@ error:
|
|
}
|
|
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 @@ error:
|
|
}
|
|
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;
|
|
- struct usb_cdc_ncm_ndp16 *ndp16;
|
|
- struct usb_cdc_ncm_dpe16 *dpe16;
|
|
+ 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;
|
|
|
|
- ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
|
|
+ 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:
|
|
- nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset);
|
|
- if (nframes < 0)
|
|
- goto error;
|
|
+ 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)) {
|
|
- netif_dbg(dev, rx_err, dev->net,
|
|
- "invalid DPT16 signature <%#010x>\n",
|
|
- le32_to_cpu(ndp16->dwSignature));
|
|
- goto err_ndp;
|
|
+ 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(ndp.ndp16->dwSignature));
|
|
+ goto err_ndp;
|
|
+ }
|
|
+ dpe.dpe16 = ndp.ndp16->dpe16;
|
|
+ } else {
|
|
+ nframes = cdc_ncm_rx_verify_ndp32(skb_in, ndpoffset);
|
|
+ if (nframes < 0)
|
|
+ goto error;
|
|
+
|
|
+ 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;
|
|
}
|
|
- dpe16 = ndp16->dpe16;
|
|
|
|
- for (x = 0; x < nframes; x++, dpe16++) {
|
|
- offset = le16_to_cpu(dpe16->wDatagramIndex);
|
|
- len = le16_to_cpu(dpe16->wDatagramLength);
|
|
+ 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 @@ next_ndp:
|
|
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;
|
|
|
|
diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c
|
|
index e15a472c6a54c..099d848270042 100644
|
|
--- a/drivers/net/usb/huawei_cdc_ncm.c
|
|
+++ b/drivers/net/usb/huawei_cdc_ncm.c
|
|
@@ -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;
|
|
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
|
|
index 64d87dccea82c..f912284b2e551 100644
|
|
--- a/drivers/power/supply/bq24190_charger.c
|
|
+++ b/drivers/power/supply/bq24190_charger.c
|
|
@@ -1211,8 +1211,19 @@ static void bq24190_input_current_limit_work(struct work_struct *work)
|
|
struct bq24190_dev_info *bdi =
|
|
container_of(work, struct bq24190_dev_info,
|
|
input_current_limit_work.work);
|
|
+ union power_supply_propval val;
|
|
+ int ret;
|
|
|
|
- power_supply_set_input_current_limit_from_supplier(bdi->charger);
|
|
+ ret = power_supply_get_property_from_supplier(bdi->charger,
|
|
+ POWER_SUPPLY_PROP_CURRENT_MAX,
|
|
+ &val);
|
|
+ if (ret)
|
|
+ return;
|
|
+
|
|
+ bq24190_charger_set_property(bdi->charger,
|
|
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
|
+ &val);
|
|
+ power_supply_changed(bdi->charger);
|
|
}
|
|
|
|
/* Sync the input-current-limit with our parent supply (if we have one) */
|
|
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
|
|
index b45cbd40294b9..ffdd646d7ebc0 100644
|
|
--- a/drivers/power/supply/bq27xxx_battery.c
|
|
+++ b/drivers/power/supply/bq27xxx_battery.c
|
|
@@ -1862,8 +1862,8 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)
|
|
{
|
|
struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
|
|
|
|
- cancel_delayed_work_sync(&di->work);
|
|
- schedule_delayed_work(&di->work, 0);
|
|
+ /* After charger plug in/out wait 0.5s for things to stabilize */
|
|
+ mod_delayed_work(system_wq, &di->work, HZ / 2);
|
|
}
|
|
|
|
int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
|
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
|
|
index fd24254d90142..3fdc988595b3a 100644
|
|
--- a/drivers/power/supply/power_supply_core.c
|
|
+++ b/drivers/power/supply/power_supply_core.c
|
|
@@ -375,46 +375,49 @@ int power_supply_is_system_supplied(void)
|
|
}
|
|
EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
|
|
|
|
-static int __power_supply_get_supplier_max_current(struct device *dev,
|
|
- void *data)
|
|
+struct psy_get_supplier_prop_data {
|
|
+ struct power_supply *psy;
|
|
+ enum power_supply_property psp;
|
|
+ union power_supply_propval *val;
|
|
+};
|
|
+
|
|
+static int __power_supply_get_supplier_property(struct device *dev, void *_data)
|
|
{
|
|
- union power_supply_propval ret = {0,};
|
|
struct power_supply *epsy = dev_get_drvdata(dev);
|
|
- struct power_supply *psy = data;
|
|
+ struct psy_get_supplier_prop_data *data = _data;
|
|
|
|
- if (__power_supply_is_supplied_by(epsy, psy))
|
|
- if (!epsy->desc->get_property(epsy,
|
|
- POWER_SUPPLY_PROP_CURRENT_MAX,
|
|
- &ret))
|
|
- return ret.intval;
|
|
+ if (__power_supply_is_supplied_by(epsy, data->psy))
|
|
+ if (!epsy->desc->get_property(epsy, data->psp, data->val))
|
|
+ return 1; /* Success */
|
|
|
|
- return 0;
|
|
+ return 0; /* Continue iterating */
|
|
}
|
|
|
|
-int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy)
|
|
+int power_supply_get_property_from_supplier(struct power_supply *psy,
|
|
+ enum power_supply_property psp,
|
|
+ union power_supply_propval *val)
|
|
{
|
|
- union power_supply_propval val = {0,};
|
|
- int curr;
|
|
-
|
|
- if (!psy->desc->set_property)
|
|
- return -EINVAL;
|
|
+ struct psy_get_supplier_prop_data data = {
|
|
+ .psy = psy,
|
|
+ .psp = psp,
|
|
+ .val = val,
|
|
+ };
|
|
+ int ret;
|
|
|
|
/*
|
|
* This function is not intended for use with a supply with multiple
|
|
- * suppliers, we simply pick the first supply to report a non 0
|
|
- * max-current.
|
|
+ * suppliers, we simply pick the first supply to report the psp.
|
|
*/
|
|
- curr = class_for_each_device(power_supply_class, NULL, psy,
|
|
- __power_supply_get_supplier_max_current);
|
|
- if (curr <= 0)
|
|
- return (curr == 0) ? -ENODEV : curr;
|
|
-
|
|
- val.intval = curr;
|
|
+ ret = class_for_each_device(power_supply_class, NULL, &data,
|
|
+ __power_supply_get_supplier_property);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ if (ret == 0)
|
|
+ return -ENODEV;
|
|
|
|
- return psy->desc->set_property(psy,
|
|
- POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
|
+ return 0;
|
|
}
|
|
-EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
|
|
+EXPORT_SYMBOL_GPL(power_supply_get_property_from_supplier);
|
|
|
|
int power_supply_set_battery_charged(struct power_supply *psy)
|
|
{
|
|
diff --git a/fs/io_uring.c b/fs/io_uring.c
|
|
index e8df6345a812d..3683ddeb625ae 100644
|
|
--- a/fs/io_uring.c
|
|
+++ b/fs/io_uring.c
|
|
@@ -551,7 +551,8 @@ static void io_kill_timeout(struct io_kiocb *req)
|
|
atomic_inc(&req->ctx->cq_timeouts);
|
|
list_del(&req->list);
|
|
io_cqring_fill_event(req->ctx, req->user_data, 0);
|
|
- __io_free_req(req);
|
|
+ if (refcount_dec_and_test(&req->refs))
|
|
+ __io_free_req(req);
|
|
}
|
|
}
|
|
|
|
@@ -2079,12 +2080,12 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
|
req->sequence -= span;
|
|
add:
|
|
list_add(&req->list, entry);
|
|
- spin_unlock_irq(&ctx->completion_lock);
|
|
|
|
hrtimer_init(&req->timeout.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
req->timeout.timer.function = io_timeout_fn;
|
|
hrtimer_start(&req->timeout.timer, timespec64_to_ktime(ts),
|
|
HRTIMER_MODE_REL);
|
|
+ spin_unlock_irq(&ctx->completion_lock);
|
|
return 0;
|
|
}
|
|
|
|
@@ -3738,9 +3739,6 @@ static void io_cancel_async_work(struct io_ring_ctx *ctx,
|
|
{
|
|
struct io_kiocb *req;
|
|
|
|
- if (list_empty(&ctx->task_list))
|
|
- return;
|
|
-
|
|
spin_lock_irq(&ctx->task_lock);
|
|
|
|
list_for_each_entry(req, &ctx->task_list, task_list) {
|
|
diff --git a/include/linux/fs.h b/include/linux/fs.h
|
|
index e003afcea3f3e..4b1553f570f2c 100644
|
|
--- a/include/linux/fs.h
|
|
+++ b/include/linux/fs.h
|
|
@@ -1360,28 +1360,28 @@ extern int send_sigurg(struct fown_struct *fown);
|
|
* sb->s_flags. Note that these mirror the equivalent MS_* flags where
|
|
* represented in both.
|
|
*/
|
|
-#define SB_RDONLY 1 /* Mount read-only */
|
|
-#define SB_NOSUID 2 /* Ignore suid and sgid bits */
|
|
-#define SB_NODEV 4 /* Disallow access to device special files */
|
|
-#define SB_NOEXEC 8 /* Disallow program execution */
|
|
-#define SB_SYNCHRONOUS 16 /* Writes are synced at once */
|
|
-#define SB_MANDLOCK 64 /* Allow mandatory locks on an FS */
|
|
-#define SB_DIRSYNC 128 /* Directory modifications are synchronous */
|
|
-#define SB_NOATIME 1024 /* Do not update access times. */
|
|
-#define SB_NODIRATIME 2048 /* Do not update directory access times */
|
|
-#define SB_SILENT 32768
|
|
-#define SB_POSIXACL (1<<16) /* VFS does not apply the umask */
|
|
-#define SB_KERNMOUNT (1<<22) /* this is a kern_mount call */
|
|
-#define SB_I_VERSION (1<<23) /* Update inode I_version field */
|
|
-#define SB_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */
|
|
+#define SB_RDONLY BIT(0) /* Mount read-only */
|
|
+#define SB_NOSUID BIT(1) /* Ignore suid and sgid bits */
|
|
+#define SB_NODEV BIT(2) /* Disallow access to device special files */
|
|
+#define SB_NOEXEC BIT(3) /* Disallow program execution */
|
|
+#define SB_SYNCHRONOUS BIT(4) /* Writes are synced at once */
|
|
+#define SB_MANDLOCK BIT(6) /* Allow mandatory locks on an FS */
|
|
+#define SB_DIRSYNC BIT(7) /* Directory modifications are synchronous */
|
|
+#define SB_NOATIME BIT(10) /* Do not update access times. */
|
|
+#define SB_NODIRATIME BIT(11) /* Do not update directory access times */
|
|
+#define SB_SILENT BIT(15)
|
|
+#define SB_POSIXACL BIT(16) /* VFS does not apply the umask */
|
|
+#define SB_KERNMOUNT BIT(22) /* this is a kern_mount call */
|
|
+#define SB_I_VERSION BIT(23) /* Update inode I_version field */
|
|
+#define SB_LAZYTIME BIT(25) /* Update the on-disk [acm]times lazily */
|
|
|
|
/* These sb flags are internal to the kernel */
|
|
-#define SB_SUBMOUNT (1<<26)
|
|
-#define SB_FORCE (1<<27)
|
|
-#define SB_NOSEC (1<<28)
|
|
-#define SB_BORN (1<<29)
|
|
-#define SB_ACTIVE (1<<30)
|
|
-#define SB_NOUSER (1<<31)
|
|
+#define SB_SUBMOUNT BIT(26)
|
|
+#define SB_FORCE BIT(27)
|
|
+#define SB_NOSEC BIT(28)
|
|
+#define SB_BORN BIT(29)
|
|
+#define SB_ACTIVE BIT(30)
|
|
+#define SB_NOUSER BIT(31)
|
|
|
|
/*
|
|
* Umount options
|
|
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
|
|
index 28413f737e7d0..f4aad9554de2e 100644
|
|
--- a/include/linux/power_supply.h
|
|
+++ b/include/linux/power_supply.h
|
|
@@ -383,8 +383,9 @@ extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
|
|
int ocv, int temp);
|
|
extern void power_supply_changed(struct power_supply *psy);
|
|
extern int power_supply_am_i_supplied(struct power_supply *psy);
|
|
-extern int power_supply_set_input_current_limit_from_supplier(
|
|
- struct power_supply *psy);
|
|
+int power_supply_get_property_from_supplier(struct power_supply *psy,
|
|
+ enum power_supply_property psp,
|
|
+ union power_supply_propval *val);
|
|
extern int power_supply_set_battery_charged(struct power_supply *psy);
|
|
|
|
#ifdef CONFIG_POWER_SUPPLY
|
|
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
|
|
index 1646c06989df7..0ce4377545f82 100644
|
|
--- a/include/linux/usb/cdc_ncm.h
|
|
+++ b/include/linux/usb/cdc_ncm.h
|
|
@@ -46,9 +46,12 @@
|
|
#define CDC_NCM_DATA_ALTSETTING_NCM 1
|
|
#define CDC_NCM_DATA_ALTSETTING_MBIM 2
|
|
|
|
-/* CDC NCM subclass 3.2.1 */
|
|
+/* CDC NCM subclass 3.3.1 */
|
|
#define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10
|
|
|
|
+/* CDC NCM subclass 3.3.2 */
|
|
+#define USB_CDC_NCM_NDP32_LENGTH_MIN 0x20
|
|
+
|
|
/* Maximum NTB length */
|
|
#define CDC_NCM_NTB_MAX_SIZE_TX 32768 /* bytes */
|
|
#define CDC_NCM_NTB_MAX_SIZE_RX 32768 /* bytes */
|
|
@@ -84,7 +87,7 @@
|
|
/* Driver flags */
|
|
#define CDC_NCM_FLAG_NDP_TO_END 0x02 /* NDP is placed at end of frame */
|
|
#define CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE 0x04 /* Avoid altsetting toggle during init */
|
|
-#define CDC_NCM_FLAG_RESET_NTB16 0x08 /* set NDP16 one more time after altsetting switch */
|
|
+#define CDC_NCM_FLAG_PREFER_NTB32 0x08 /* prefer NDP32 over NDP16 */
|
|
|
|
#define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \
|
|
(x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
|
|
@@ -113,7 +116,11 @@ struct cdc_ncm_ctx {
|
|
|
|
u32 timer_interval;
|
|
u32 max_ndp_size;
|
|
- struct usb_cdc_ncm_ndp16 *delayed_ndp16;
|
|
+ u8 is_ndp16;
|
|
+ union {
|
|
+ struct usb_cdc_ncm_ndp16 *delayed_ndp16;
|
|
+ struct usb_cdc_ncm_ndp32 *delayed_ndp32;
|
|
+ };
|
|
|
|
u32 tx_timer_pending;
|
|
u32 tx_curr_frame_num;
|
|
@@ -150,6 +157,8 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
|
|
struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
|
|
int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);
|
|
int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset);
|
|
+int cdc_ncm_rx_verify_nth32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);
|
|
+int cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset);
|
|
struct sk_buff *
|
|
cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags);
|
|
int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in);
|
|
diff --git a/include/net/ip.h b/include/net/ip.h
|
|
index db841ab388c0e..0ebe5385efcae 100644
|
|
--- a/include/net/ip.h
|
|
+++ b/include/net/ip.h
|
|
@@ -73,6 +73,7 @@ struct ipcm_cookie {
|
|
__be32 addr;
|
|
int oif;
|
|
struct ip_options_rcu *opt;
|
|
+ __u8 protocol;
|
|
__u8 ttl;
|
|
__s16 tos;
|
|
char priority;
|
|
@@ -93,6 +94,7 @@ static inline void ipcm_init_sk(struct ipcm_cookie *ipcm,
|
|
ipcm->sockc.tsflags = inet->sk.sk_tsflags;
|
|
ipcm->oif = inet->sk.sk_bound_dev_if;
|
|
ipcm->addr = inet->inet_saddr;
|
|
+ ipcm->protocol = inet->inet_num;
|
|
}
|
|
|
|
#define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))
|
|
diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h
|
|
index 60e1241d4b77b..f5ac290c22509 100644
|
|
--- a/include/uapi/linux/in.h
|
|
+++ b/include/uapi/linux/in.h
|
|
@@ -154,6 +154,8 @@ struct in_addr {
|
|
#define MCAST_MSFILTER 48
|
|
#define IP_MULTICAST_ALL 49
|
|
#define IP_UNICAST_IF 50
|
|
+#define IP_LOCAL_PORT_RANGE 51
|
|
+#define IP_PROTOCOL 52
|
|
|
|
#define MCAST_EXCLUDE 0
|
|
#define MCAST_INCLUDE 1
|
|
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
|
|
index 4f8f5204ae7a0..45f6ce1f380e0 100644
|
|
--- a/net/bluetooth/hci_sock.c
|
|
+++ b/net/bluetooth/hci_sock.c
|
|
@@ -973,6 +973,34 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
|
|
|
|
BT_DBG("cmd %x arg %lx", cmd, arg);
|
|
|
|
+ /* Make sure the cmd is valid before doing anything */
|
|
+ switch (cmd) {
|
|
+ case HCIGETDEVLIST:
|
|
+ case HCIGETDEVINFO:
|
|
+ case HCIGETCONNLIST:
|
|
+ case HCIDEVUP:
|
|
+ case HCIDEVDOWN:
|
|
+ case HCIDEVRESET:
|
|
+ case HCIDEVRESTAT:
|
|
+ case HCISETSCAN:
|
|
+ case HCISETAUTH:
|
|
+ case HCISETENCRYPT:
|
|
+ case HCISETPTYPE:
|
|
+ case HCISETLINKPOL:
|
|
+ case HCISETLINKMODE:
|
|
+ case HCISETACLMTU:
|
|
+ case HCISETSCOMTU:
|
|
+ case HCIINQUIRY:
|
|
+ case HCISETRAW:
|
|
+ case HCIGETCONNINFO:
|
|
+ case HCIGETAUTHINFO:
|
|
+ case HCIBLOCKADDR:
|
|
+ case HCIUNBLOCKADDR:
|
|
+ break;
|
|
+ default:
|
|
+ return -ENOIOCTLCMD;
|
|
+ }
|
|
+
|
|
lock_sock(sk);
|
|
|
|
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
|
|
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
|
|
index aa3fd61818c47..0c528f642eb86 100644
|
|
--- a/net/ipv4/ip_sockglue.c
|
|
+++ b/net/ipv4/ip_sockglue.c
|
|
@@ -316,7 +316,14 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
|
|
ipc->tos = val;
|
|
ipc->priority = rt_tos2priority(ipc->tos);
|
|
break;
|
|
-
|
|
+ case IP_PROTOCOL:
|
|
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
|
|
+ return -EINVAL;
|
|
+ val = *(int *)CMSG_DATA(cmsg);
|
|
+ if (val < 1 || val > 255)
|
|
+ return -EINVAL;
|
|
+ ipc->protocol = val;
|
|
+ break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
@@ -1524,6 +1531,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
|
|
case IP_MINTTL:
|
|
val = inet->min_ttl;
|
|
break;
|
|
+ case IP_PROTOCOL:
|
|
+ val = inet_sk(sk)->inet_num;
|
|
+ break;
|
|
default:
|
|
release_sock(sk);
|
|
return -ENOPROTOOPT;
|
|
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
|
|
index ddc24e57dc555..1044f4985922f 100644
|
|
--- a/net/ipv4/raw.c
|
|
+++ b/net/ipv4/raw.c
|
|
@@ -558,6 +558,9 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
}
|
|
|
|
ipcm_init_sk(&ipc, inet);
|
|
+ /* Keep backward compat */
|
|
+ if (hdrincl)
|
|
+ ipc.protocol = IPPROTO_RAW;
|
|
|
|
if (msg->msg_controllen) {
|
|
err = ip_cmsg_send(sk, msg, &ipc, false);
|
|
@@ -625,7 +628,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
|
|
flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos,
|
|
RT_SCOPE_UNIVERSE,
|
|
- hdrincl ? IPPROTO_RAW : sk->sk_protocol,
|
|
+ hdrincl ? ipc.protocol : sk->sk_protocol,
|
|
inet_sk_flowi_flags(sk) |
|
|
(hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
|
|
daddr, saddr, 0, 0, sk->sk_uid);
|
|
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
|
|
index bf052d68e1f5a..731485b18de31 100644
|
|
--- a/net/ipv6/raw.c
|
|
+++ b/net/ipv6/raw.c
|
|
@@ -828,7 +828,8 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
|
|
if (!proto)
|
|
proto = inet->inet_num;
|
|
- else if (proto != inet->inet_num)
|
|
+ else if (proto != inet->inet_num &&
|
|
+ inet->inet_num != IPPROTO_RAW)
|
|
return -EINVAL;
|
|
|
|
if (proto > 255)
|
|
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
|
index 4747daf901e71..754f473ce3de4 100644
|
|
--- a/net/netfilter/nf_conntrack_netlink.c
|
|
+++ b/net/netfilter/nf_conntrack_netlink.c
|
|
@@ -1229,9 +1229,6 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
|
|
|
|
static int ctnetlink_flush_iterate(struct nf_conn *ct, void *data)
|
|
{
|
|
- if (test_bit(IPS_OFFLOAD_BIT, &ct->status))
|
|
- return 0;
|
|
-
|
|
return ctnetlink_filter_match(ct, data);
|
|
}
|
|
|
|
@@ -1294,11 +1291,6 @@ static int ctnetlink_del_conntrack(struct net *net, struct sock *ctnl,
|
|
|
|
ct = nf_ct_tuplehash_to_ctrack(h);
|
|
|
|
- if (test_bit(IPS_OFFLOAD_BIT, &ct->status)) {
|
|
- nf_ct_put(ct);
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
if (cda[CTA_ID]) {
|
|
__be32 id = nla_get_be32(cda[CTA_ID]);
|
|
|