[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <2026020623-gray-sandy-8577@gregkh>
Date: Fri, 6 Feb 2026 17:27:23 +0100
From: Greg Kroah-Hartman <gregkh@...uxfoundation.org>
To: linux-kernel@...r.kernel.org,
akpm@...ux-foundation.org,
torvalds@...ux-foundation.org,
stable@...r.kernel.org
Cc: lwn@....net,
jslaby@...e.cz,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>
Subject: Re: Linux 5.10.249
diff --git a/Documentation/netlink/specs/fou.yaml b/Documentation/netlink/specs/fou.yaml
new file mode 100644
index 000000000000..e5753a30a29a
--- /dev/null
+++ b/Documentation/netlink/specs/fou.yaml
@@ -0,0 +1,130 @@
+name: fou
+
+protocol: genetlink-legacy
+
+doc: |
+ Foo-over-UDP.
+
+c-family-name: fou-genl-name
+c-version-name: fou-genl-version
+max-by-define: true
+kernel-policy: global
+
+definitions:
+ -
+ type: enum
+ name: encap_type
+ name-prefix: fou-encap-
+ enum-name:
+ entries: [ unspec, direct, gue ]
+
+attribute-sets:
+ -
+ name: fou
+ name-prefix: fou-attr-
+ attributes:
+ -
+ name: unspec
+ type: unused
+ -
+ name: port
+ type: u16
+ byte-order: big-endian
+ -
+ name: af
+ type: u8
+ -
+ name: ipproto
+ type: u8
+ checks:
+ min: 1
+ -
+ name: type
+ type: u8
+ -
+ name: remcsum_nopartial
+ type: flag
+ -
+ name: local_v4
+ type: u32
+ -
+ name: local_v6
+ type: binary
+ checks:
+ min-len: 16
+ -
+ name: peer_v4
+ type: u32
+ -
+ name: peer_v6
+ type: binary
+ checks:
+ min-len: 16
+ -
+ name: peer_port
+ type: u16
+ byte-order: big-endian
+ -
+ name: ifindex
+ type: s32
+
+operations:
+ list:
+ -
+ name: unspec
+ doc: unused
+
+ -
+ name: add
+ doc: Add port.
+ attribute-set: fou
+
+ dont-validate: [ strict, dump ]
+ flags: [ admin-perm ]
+
+ do:
+ request: &all_attrs
+ attributes:
+ - port
+ - ipproto
+ - type
+ - remcsum_nopartial
+ - local_v4
+ - peer_v4
+ - local_v6
+ - peer_v6
+ - peer_port
+ - ifindex
+
+ -
+ name: del
+ doc: Delete port.
+ attribute-set: fou
+
+ dont-validate: [ strict, dump ]
+ flags: [ admin-perm ]
+
+ do:
+ request: &select_attrs
+ attributes:
+ - af
+ - ifindex
+ - port
+ - peer_port
+ - local_v4
+ - peer_v4
+ - local_v6
+ - peer_v6
+
+ -
+ name: get
+ doc: Get tunnel info.
+ attribute-set: fou
+ dont-validate: [ strict, dump ]
+
+ do:
+ request: *select_attrs
+ reply: *all_attrs
+
+ dump:
+ reply: *all_attrs
diff --git a/Makefile b/Makefile
index 451389daf57b..7ce2aac53688 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 5
PATCHLEVEL = 10
-SUBLEVEL = 248
+SUBLEVEL = 249
EXTRAVERSION =
NAME = Dare mighty things
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 0c3b8fa7e532..bcc40b7850cb 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1088,13 +1088,22 @@ static inline bool intel_pmu_has_bts_period(struct perf_event *event, u64 period
struct hw_perf_event *hwc = &event->hw;
unsigned int hw_event, bts_event;
- if (event->attr.freq)
+ /*
+ * Only use BTS for fixed rate period==1 events.
+ */
+ if (event->attr.freq || period != 1)
+ return false;
+
+ /*
+ * BTS doesn't virtualize.
+ */
+ if (event->attr.exclude_host)
return false;
hw_event = hwc->config & INTEL_ARCH_EVENT_MASK;
bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
- return hw_event == bts_event && period == 1;
+ return hw_event == bts_event;
}
static inline bool intel_pmu_has_bts(struct perf_event *event)
diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c
index 32005e229c03..861c9c001e1d 100644
--- a/arch/x86/kernel/cpu/resctrl/core.c
+++ b/arch/x86/kernel/cpu/resctrl/core.c
@@ -834,7 +834,8 @@ static __init bool get_mem_config(void)
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
return __get_mem_config_intel(&rdt_resources_all[RDT_RESOURCE_MBA]);
- else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
+ boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
return __rdt_get_mem_config_amd(&rdt_resources_all[RDT_RESOURCE_MBA]);
return false;
@@ -960,7 +961,8 @@ static __init void rdt_init_res_defs(void)
{
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
rdt_init_res_defs_intel();
- else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
+ boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
rdt_init_res_defs_amd();
}
@@ -991,8 +993,19 @@ void resctrl_cpu_detect(struct cpuinfo_x86 *c)
c->x86_cache_occ_scale = ebx;
c->x86_cache_mbm_width_offset = eax & 0xff;
- if (c->x86_vendor == X86_VENDOR_AMD && !c->x86_cache_mbm_width_offset)
- c->x86_cache_mbm_width_offset = MBM_CNTR_WIDTH_OFFSET_AMD;
+ if (!c->x86_cache_mbm_width_offset) {
+ switch (c->x86_vendor) {
+ case X86_VENDOR_AMD:
+ c->x86_cache_mbm_width_offset = MBM_CNTR_WIDTH_OFFSET_AMD;
+ break;
+ case X86_VENDOR_HYGON:
+ c->x86_cache_mbm_width_offset = MBM_CNTR_WIDTH_OFFSET_HYGON;
+ break;
+ default:
+ /* Leave c->x86_cache_mbm_width_offset as 0 */
+ break;
+ }
+ }
}
}
diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h
index f65d3c0dbc41..7de60f1dbdcb 100644
--- a/arch/x86/kernel/cpu/resctrl/internal.h
+++ b/arch/x86/kernel/cpu/resctrl/internal.h
@@ -39,6 +39,9 @@
#define MAX_MBA_BW_AMD 0x800
#define MBM_CNTR_WIDTH_OFFSET_AMD 20
+/* Hygon MBM counter width as an offset from MBM_CNTR_WIDTH_BASE */
+#define MBM_CNTR_WIDTH_OFFSET_HYGON 8
+
#define RMID_VAL_ERROR BIT_ULL(63)
#define RMID_VAL_UNAVAIL BIT_ULL(62)
/*
diff --git a/crypto/authencesn.c b/crypto/authencesn.c
index b60e61b1904c..6487b35851d5 100644
--- a/crypto/authencesn.c
+++ b/crypto/authencesn.c
@@ -191,6 +191,9 @@ static int crypto_authenc_esn_encrypt(struct aead_request *req)
struct scatterlist *src, *dst;
int err;
+ if (assoclen < 8)
+ return -EINVAL;
+
sg_init_table(areq_ctx->src, 2);
src = scatterwalk_ffwd(areq_ctx->src, req->src, assoclen);
dst = src;
@@ -284,6 +287,9 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req)
u32 tmp[2];
int err;
+ if (assoclen < 8)
+ return -EINVAL;
+
cryptlen -= authsize;
if (req->src != dst) {
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 82eb25ad1c72..e162c0c49787 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3036,6 +3036,7 @@ int device_add(struct device *dev)
device_pm_remove(dev);
dpm_sysfs_remove(dev);
DPMError:
+ dev->driver = NULL;
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 02c21fce457c..e86d069894c0 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -463,9 +463,11 @@ static void regmap_lock_hwlock_irq(void *__map)
static void regmap_lock_hwlock_irqsave(void *__map)
{
struct regmap *map = __map;
+ unsigned long flags = 0;
hwspin_lock_timeout_irqsave(map->hwlock, UINT_MAX,
- &map->spinlock_flags);
+ &flags);
+ map->spinlock_flags = flags;
}
static void regmap_unlock_hwlock(void *__map)
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 93bb58971dbe..436d82a7f587 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -684,6 +684,8 @@ static int hci_uart_register_dev(struct hci_uart *hu)
return err;
}
+ set_bit(HCI_UART_PROTO_INIT, &hu->flags);
+
if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
return 0;
@@ -711,8 +713,6 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
hu->proto = p;
- set_bit(HCI_UART_PROTO_INIT, &hu->flags);
-
err = hci_uart_register_dev(hu);
if (err) {
return err;
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 6a4f9697b574..0a4175b87979 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -1559,6 +1559,7 @@ static void atc_free_chan_resources(struct dma_chan *chan)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma *atdma = to_at_dma(chan->device);
+ struct at_dma_slave *atslave;
struct at_desc *desc, *_desc;
LIST_HEAD(list);
@@ -1579,8 +1580,12 @@ static void atc_free_chan_resources(struct dma_chan *chan)
/*
* Free atslave allocated in at_dma_xlate()
*/
- kfree(chan->private);
- chan->private = NULL;
+ atslave = chan->private;
+ if (atslave) {
+ put_device(atslave->dma_dev);
+ kfree(atslave);
+ chan->private = NULL;
+ }
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
}
diff --git a/drivers/dma/bcm-sba-raid.c b/drivers/dma/bcm-sba-raid.c
index 64239da02e74..108ddc30f29e 100644
--- a/drivers/dma/bcm-sba-raid.c
+++ b/drivers/dma/bcm-sba-raid.c
@@ -1707,7 +1707,7 @@ static int sba_probe(struct platform_device *pdev)
/* Prealloc channel resource */
ret = sba_prealloc_channel_resources(sba);
if (ret)
- goto fail_free_mchan;
+ goto fail_put_mbox;
/* Check availability of debugfs */
if (!debugfs_initialized())
@@ -1737,6 +1737,8 @@ static int sba_probe(struct platform_device *pdev)
fail_free_resources:
debugfs_remove_recursive(sba->root);
sba_freeup_channel_resources(sba);
+fail_put_mbox:
+ put_device(sba->mbox_dev);
fail_free_mchan:
mbox_free_channel(sba->mchan);
return ret;
@@ -1752,6 +1754,8 @@ static int sba_remove(struct platform_device *pdev)
sba_freeup_channel_resources(sba);
+ put_device(sba->mbox_dev);
+
mbox_free_channel(sba->mchan);
return 0;
diff --git a/drivers/dma/lpc18xx-dmamux.c b/drivers/dma/lpc18xx-dmamux.c
index df98cae8792b..96b51125cfa7 100644
--- a/drivers/dma/lpc18xx-dmamux.c
+++ b/drivers/dma/lpc18xx-dmamux.c
@@ -55,30 +55,31 @@ static void *lpc18xx_dmamux_reserve(struct of_phandle_args *dma_spec,
struct lpc18xx_dmamux_data *dmamux = platform_get_drvdata(pdev);
unsigned long flags;
unsigned mux;
+ int ret = -EINVAL;
if (dma_spec->args_count != 3) {
dev_err(&pdev->dev, "invalid number of dma mux args\n");
- return ERR_PTR(-EINVAL);
+ goto err_put_pdev;
}
mux = dma_spec->args[0];
if (mux >= dmamux->dma_master_requests) {
dev_err(&pdev->dev, "invalid mux number: %d\n",
dma_spec->args[0]);
- return ERR_PTR(-EINVAL);
+ goto err_put_pdev;
}
if (dma_spec->args[1] > LPC18XX_DMAMUX_MAX_VAL) {
dev_err(&pdev->dev, "invalid dma mux value: %d\n",
dma_spec->args[1]);
- return ERR_PTR(-EINVAL);
+ goto err_put_pdev;
}
/* The of_node_put() will be done in the core for the node */
dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0);
if (!dma_spec->np) {
dev_err(&pdev->dev, "can't get dma master\n");
- return ERR_PTR(-EINVAL);
+ goto err_put_pdev;
}
spin_lock_irqsave(&dmamux->lock, flags);
@@ -87,7 +88,8 @@ static void *lpc18xx_dmamux_reserve(struct of_phandle_args *dma_spec,
dev_err(&pdev->dev, "dma request %u busy with %u.%u\n",
mux, mux, dmamux->muxes[mux].value);
of_node_put(dma_spec->np);
- return ERR_PTR(-EBUSY);
+ ret = -EBUSY;
+ goto err_put_pdev;
}
dmamux->muxes[mux].busy = true;
@@ -104,7 +106,14 @@ static void *lpc18xx_dmamux_reserve(struct of_phandle_args *dma_spec,
dev_dbg(&pdev->dev, "mapping dmamux %u.%u to dma request %u\n", mux,
dmamux->muxes[mux].value, mux);
+ put_device(&pdev->dev);
+
return &dmamux->muxes[mux];
+
+err_put_pdev:
+ put_device(&pdev->dev);
+
+ return ERR_PTR(ret);
}
static int lpc18xx_dmamux_probe(struct platform_device *pdev)
diff --git a/drivers/dma/stm32-dmamux.c b/drivers/dma/stm32-dmamux.c
index f04bcffd3c24..f3dbbd1bf1bd 100644
--- a/drivers/dma/stm32-dmamux.c
+++ b/drivers/dma/stm32-dmamux.c
@@ -88,23 +88,25 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec,
struct stm32_dmamux_data *dmamux = platform_get_drvdata(pdev);
struct stm32_dmamux *mux;
u32 i, min, max;
- int ret;
+ int ret = -EINVAL;
unsigned long flags;
if (dma_spec->args_count != 3) {
dev_err(&pdev->dev, "invalid number of dma mux args\n");
- return ERR_PTR(-EINVAL);
+ goto err_put_pdev;
}
if (dma_spec->args[0] > dmamux->dmamux_requests) {
dev_err(&pdev->dev, "invalid mux request number: %d\n",
dma_spec->args[0]);
- return ERR_PTR(-EINVAL);
+ goto err_put_pdev;
}
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
- if (!mux)
- return ERR_PTR(-ENOMEM);
+ if (!mux) {
+ ret = -ENOMEM;
+ goto err_put_pdev;
+ }
spin_lock_irqsave(&dmamux->lock, flags);
mux->chan_id = find_first_zero_bit(dmamux->dma_inuse,
@@ -131,7 +133,6 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec,
dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", i - 1);
if (!dma_spec->np) {
dev_err(&pdev->dev, "can't get dma master\n");
- ret = -EINVAL;
goto error;
}
@@ -140,7 +141,7 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec,
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0) {
spin_unlock_irqrestore(&dmamux->lock, flags);
- goto error;
+ goto err_put_dma_spec_np;
}
spin_unlock_irqrestore(&dmamux->lock, flags);
@@ -158,13 +159,20 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec,
dev_dbg(&pdev->dev, "Mapping DMAMUX(%u) to DMA%u(%u)\n",
mux->request, mux->master, mux->chan_id);
+ put_device(&pdev->dev);
+
return mux;
+err_put_dma_spec_np:
+ of_node_put(dma_spec->np);
error:
clear_bit(mux->chan_id, dmamux->dma_inuse);
error_chan_id:
kfree(mux);
+err_put_pdev:
+ put_device(&pdev->dev);
+
return ERR_PTR(ret);
}
diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c
index d84010c2e4bf..bcd1deb08261 100644
--- a/drivers/dma/tegra210-adma.c
+++ b/drivers/dma/tegra210-adma.c
@@ -344,10 +344,17 @@ static void tegra_adma_stop(struct tegra_adma_chan *tdc)
return;
}
- kfree(tdc->desc);
+ vchan_terminate_vdesc(&tdc->desc->vd);
tdc->desc = NULL;
}
+static void tegra_adma_synchronize(struct dma_chan *dc)
+{
+ struct tegra_adma_chan *tdc = to_tegra_adma_chan(dc);
+
+ vchan_synchronize(&tdc->vc);
+}
+
static void tegra_adma_start(struct tegra_adma_chan *tdc)
{
struct virt_dma_desc *vd = vchan_next_desc(&tdc->vc);
@@ -893,6 +900,7 @@ static int tegra_adma_probe(struct platform_device *pdev)
tdma->dma_dev.device_config = tegra_adma_slave_config;
tdma->dma_dev.device_tx_status = tegra_adma_tx_status;
tdma->dma_dev.device_terminate_all = tegra_adma_terminate_all;
+ tdma->dma_dev.device_synchronize = tegra_adma_synchronize;
tdma->dma_dev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
tdma->dma_dev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
tdma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
diff --git a/drivers/dma/ti/dma-crossbar.c b/drivers/dma/ti/dma-crossbar.c
index 86ced7f2d771..a529a3a620ca 100644
--- a/drivers/dma/ti/dma-crossbar.c
+++ b/drivers/dma/ti/dma-crossbar.c
@@ -78,34 +78,35 @@ static void *ti_am335x_xbar_route_allocate(struct of_phandle_args *dma_spec,
{
struct platform_device *pdev = of_find_device_by_node(ofdma->of_node);
struct ti_am335x_xbar_data *xbar = platform_get_drvdata(pdev);
- struct ti_am335x_xbar_map *map;
+ struct ti_am335x_xbar_map *map = ERR_PTR(-EINVAL);
if (dma_spec->args_count != 3)
- return ERR_PTR(-EINVAL);
+ goto out_put_pdev;
if (dma_spec->args[2] >= xbar->xbar_events) {
dev_err(&pdev->dev, "Invalid XBAR event number: %d\n",
dma_spec->args[2]);
- return ERR_PTR(-EINVAL);
+ goto out_put_pdev;
}
if (dma_spec->args[0] >= xbar->dma_requests) {
dev_err(&pdev->dev, "Invalid DMA request line number: %d\n",
dma_spec->args[0]);
- return ERR_PTR(-EINVAL);
+ goto out_put_pdev;
}
/* The of_node_put() will be done in the core for the node */
dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0);
if (!dma_spec->np) {
dev_err(&pdev->dev, "Can't get DMA master\n");
- return ERR_PTR(-EINVAL);
+ goto out_put_pdev;
}
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map) {
of_node_put(dma_spec->np);
- return ERR_PTR(-ENOMEM);
+ map = ERR_PTR(-ENOMEM);
+ goto out_put_pdev;
}
map->dma_line = (u16)dma_spec->args[0];
@@ -119,6 +120,9 @@ static void *ti_am335x_xbar_route_allocate(struct of_phandle_args *dma_spec,
ti_am335x_xbar_write(xbar->iomem, map->dma_line, map->mux_val);
+out_put_pdev:
+ put_device(&pdev->dev);
+
return map;
}
@@ -287,6 +291,8 @@ static void *ti_dra7_xbar_route_allocate(struct of_phandle_args *dma_spec,
ti_dra7_xbar_write(xbar->iomem, map->xbar_out, map->xbar_in);
+ put_device(&pdev->dev);
+
return map;
}
diff --git a/drivers/dma/ti/k3-udma-private.c b/drivers/dma/ti/k3-udma-private.c
index dadab2feca08..f2c0c965e362 100644
--- a/drivers/dma/ti/k3-udma-private.c
+++ b/drivers/dma/ti/k3-udma-private.c
@@ -40,9 +40,9 @@ struct udma_dev *of_xudma_dev_get(struct device_node *np, const char *property)
}
ud = platform_get_drvdata(pdev);
+ put_device(&pdev->dev);
if (!ud) {
pr_debug("UDMA has not been probed\n");
- put_device(&pdev->dev);
return ERR_PTR(-EPROBE_DEFER);
}
diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c
index 268a08058714..6c6a34265b06 100644
--- a/drivers/dma/ti/omap-dma.c
+++ b/drivers/dma/ti/omap-dma.c
@@ -1803,6 +1803,8 @@ static int omap_dma_probe(struct platform_device *pdev)
if (rc) {
pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
rc);
+ if (od->ll123_supported)
+ dma_pool_destroy(od->desc_pool);
omap_dma_free(od);
return rc;
}
@@ -1818,6 +1820,8 @@ static int omap_dma_probe(struct platform_device *pdev)
if (rc) {
pr_warn("OMAP-DMA: failed to register DMA controller\n");
dma_async_device_unregister(&od->ddev);
+ if (od->ll123_supported)
+ dma_pool_destroy(od->desc_pool);
omap_dma_free(od);
}
}
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 3ecf0109af2b..12e9ba5b114d 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -128,6 +128,7 @@
#define XILINX_MCDMA_MAX_CHANS_PER_DEVICE 0x20
#define XILINX_DMA_MAX_CHANS_PER_DEVICE 0x2
#define XILINX_CDMA_MAX_CHANS_PER_DEVICE 0x1
+#define XILINX_DMA_DFAULT_ADDRWIDTH 0x20
#define XILINX_DMA_DMAXR_ALL_IRQ_MASK \
(XILINX_DMA_DMASR_FRM_CNT_IRQ | \
@@ -2996,7 +2997,7 @@ static int xilinx_dma_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct xilinx_dma_device *xdev;
struct device_node *child, *np = pdev->dev.of_node;
- u32 num_frames, addr_width, len_width;
+ u32 num_frames, addr_width = XILINX_DMA_DFAULT_ADDRWIDTH, len_width;
int i, err;
/* Allocate and initialize the DMA engine structure */
@@ -3065,7 +3066,9 @@ static int xilinx_dma_probe(struct platform_device *pdev)
err = of_property_read_u32(node, "xlnx,addrwidth", &addr_width);
if (err < 0)
- dev_warn(xdev->dev, "missing xlnx,addrwidth property\n");
+ dev_warn(xdev->dev,
+ "missing xlnx,addrwidth property, using default value %d\n",
+ XILINX_DMA_DFAULT_ADDRWIDTH);
if (addr_width > 32)
xdev->ext_addr = true;
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index a8988db6d423..e34f5c0f88d3 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -359,10 +359,11 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
layers[1].type = EDAC_MC_LAYER_CHANNEL;
layers[1].size = nr_channels;
layers[1].is_virt_csrow = false;
- mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
- sizeof(struct i3200_priv));
+
+ rc = -ENOMEM;
+ mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(struct i3200_priv));
if (!mci)
- return -ENOMEM;
+ goto unmap;
edac_dbg(3, "MC: init mci\n");
@@ -422,9 +423,9 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
return 0;
fail:
+ edac_mc_free(mci);
+unmap:
iounmap(window);
- if (mci)
- edac_mc_free(mci);
return rc;
}
diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c
index a65e2f78a402..54cc3e22eeb1 100644
--- a/drivers/edac/x38_edac.c
+++ b/drivers/edac/x38_edac.c
@@ -342,9 +342,12 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
layers[1].type = EDAC_MC_LAYER_CHANNEL;
layers[1].size = x38_channel_num;
layers[1].is_virt_csrow = false;
+
+
+ rc = -ENOMEM;
mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
if (!mci)
- return -ENOMEM;
+ goto unmap;
edac_dbg(3, "MC: init mci\n");
@@ -404,9 +407,9 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
return 0;
fail:
+ edac_mc_free(mci);
+unmap:
iounmap(window);
- if (mci)
- edac_mc_free(mci);
return rc;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 195b7e02dc4b..85b43d48f2dc 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -1832,6 +1832,14 @@ static int allocate_hiq_sdma_mqd(struct device_queue_manager *dqm)
return retval;
}
+static void deallocate_hiq_sdma_mqd(struct kfd_dev *dev,
+ struct kfd_mem_obj *mqd)
+{
+ WARN(!mqd, "No hiq sdma mqd trunk to free");
+
+ amdgpu_amdkfd_free_gtt_mem(dev->kgd, mqd->gtt_mem);
+}
+
struct device_queue_manager *device_queue_manager_init(struct kfd_dev *dev)
{
struct device_queue_manager *dqm;
@@ -1961,19 +1969,13 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_dev *dev)
if (!dqm->ops.initialize(dqm))
return dqm;
+ deallocate_hiq_sdma_mqd(dev, &dqm->hiq_sdma_mqd);
+
out_free:
kfree(dqm);
return NULL;
}
-static void deallocate_hiq_sdma_mqd(struct kfd_dev *dev,
- struct kfd_mem_obj *mqd)
-{
- WARN(!mqd, "No hiq sdma mqd trunk to free");
-
- amdgpu_amdkfd_free_gtt_mem(dev->kgd, mqd->gtt_mem);
-}
-
void device_queue_manager_uninit(struct device_queue_manager *dqm)
{
dqm->ops.uninitialize(dqm);
diff --git a/drivers/gpu/drm/amd/pm/powerplay/si_dpm.c b/drivers/gpu/drm/amd/pm/powerplay/si_dpm.c
index 6f0653c81f8f..ece892b16d9a 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/si_dpm.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/si_dpm.c
@@ -2242,8 +2242,6 @@ static int si_populate_smc_tdp_limits(struct amdgpu_device *adev,
if (scaling_factor == 0)
return -EINVAL;
- memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
-
ret = si_calculate_adjusted_tdp_limits(adev,
false, /* ??? */
adev->pm.dpm.tdp_adjustment,
@@ -2252,6 +2250,12 @@ static int si_populate_smc_tdp_limits(struct amdgpu_device *adev,
if (ret)
return ret;
+ if (adev->pdev->device == 0x6611 && adev->pdev->revision == 0x87) {
+ /* Workaround buggy powertune on Radeon 430 and 520. */
+ tdp_limit = 32;
+ near_tdp_limit = 28;
+ }
+
smc_table->dpm2Params.TDPLimit =
cpu_to_be32(si_scale_power_for_smc(tdp_limit, scaling_factor) * 1000);
smc_table->dpm2Params.NearTDPLimit =
@@ -2297,16 +2301,8 @@ static int si_populate_smc_tdp_limits_2(struct amdgpu_device *adev,
if (ni_pi->enable_power_containment) {
SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
- u32 scaling_factor = si_get_smc_power_scaling_factor(adev);
int ret;
- memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
-
- smc_table->dpm2Params.NearTDPLimit =
- cpu_to_be32(si_scale_power_for_smc(adev->pm.dpm.near_tdp_limit_adjusted, scaling_factor) * 1000);
- smc_table->dpm2Params.SafePowerLimit =
- cpu_to_be32(si_scale_power_for_smc((adev->pm.dpm.near_tdp_limit_adjusted * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
-
ret = amdgpu_si_copy_bytes_to_smc(adev,
(si_pi->state_table_start +
offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
@@ -3435,10 +3431,15 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev,
(adev->pdev->revision == 0x80) ||
(adev->pdev->revision == 0x81) ||
(adev->pdev->revision == 0x83) ||
- (adev->pdev->revision == 0x87) ||
+ (adev->pdev->revision == 0x87 &&
+ adev->pdev->device != 0x6611) ||
(adev->pdev->device == 0x6604) ||
(adev->pdev->device == 0x6605)) {
max_sclk = 75000;
+ } else if (adev->pdev->revision == 0x87 &&
+ adev->pdev->device == 0x6611) {
+ /* Radeon 430 and 520 */
+ max_sclk = 78000;
}
}
diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c
index 9fe6a4733106..e21b78eadea3 100644
--- a/drivers/gpu/drm/imx/imx-tve.c
+++ b/drivers/gpu/drm/imx/imx-tve.c
@@ -431,27 +431,31 @@ static int tve_clk_init(struct imx_tve *tve, void __iomem *base)
static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve)
{
+ struct drm_encoder *encoder = &tve->encoder;
+ struct drm_connector *connector = &tve->connector;
int encoder_type;
int ret;
encoder_type = tve->mode == TVE_MODE_VGA ?
DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC;
- ret = imx_drm_encoder_parse_of(drm, &tve->encoder, tve->dev->of_node);
+ memset(connector, 0, sizeof(*connector));
+ memset(encoder, 0, sizeof(*encoder));
+
+ ret = imx_drm_encoder_parse_of(drm, encoder, tve->dev->of_node);
if (ret)
return ret;
- drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs);
- drm_simple_encoder_init(drm, &tve->encoder, encoder_type);
+ drm_encoder_helper_add(encoder, &imx_tve_encoder_helper_funcs);
+ drm_simple_encoder_init(drm, encoder, encoder_type);
- drm_connector_helper_add(&tve->connector,
- &imx_tve_connector_helper_funcs);
- drm_connector_init_with_ddc(drm, &tve->connector,
+ drm_connector_helper_add(connector, &imx_tve_connector_helper_funcs);
+ drm_connector_init_with_ddc(drm, connector,
&imx_tve_connector_funcs,
DRM_MODE_CONNECTOR_VGA,
tve->ddc);
- drm_connector_attach_encoder(&tve->connector, &tve->encoder);
+ drm_connector_attach_encoder(connector, encoder);
return 0;
}
@@ -503,8 +507,26 @@ static int of_get_tve_mode(struct device_node *np)
static int imx_tve_bind(struct device *dev, struct device *master, void *data)
{
- struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = data;
+ struct imx_tve *tve = dev_get_drvdata(dev);
+
+ return imx_tve_register(drm, tve);
+}
+
+static const struct component_ops imx_tve_ops = {
+ .bind = imx_tve_bind,
+};
+
+static void imx_tve_put_device(void *_dev)
+{
+ struct device *dev = _dev;
+
+ put_device(dev);
+}
+
+static int imx_tve_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *ddc_node;
struct imx_tve *tve;
@@ -514,8 +536,9 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
int irq;
int ret;
- tve = dev_get_drvdata(dev);
- memset(tve, 0, sizeof(*tve));
+ tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL);
+ if (!tve)
+ return -ENOMEM;
tve->dev = dev;
@@ -523,6 +546,12 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
if (ddc_node) {
tve->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
+ if (tve->ddc) {
+ ret = devm_add_action_or_reset(dev, imx_tve_put_device,
+ &tve->ddc->dev);
+ if (ret)
+ return ret;
+ }
}
tve->mode = of_get_tve_mode(np);
@@ -622,28 +651,9 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;
- ret = imx_tve_register(drm, tve);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static const struct component_ops imx_tve_ops = {
- .bind = imx_tve_bind,
-};
-
-static int imx_tve_probe(struct platform_device *pdev)
-{
- struct imx_tve *tve;
-
- tve = devm_kzalloc(&pdev->dev, sizeof(*tve), GFP_KERNEL);
- if (!tve)
- return -ENOMEM;
-
platform_set_drvdata(pdev, tve);
- return component_add(&pdev->dev, &imx_tve_ops);
+ return component_add(dev, &imx_tve_ops);
}
static int imx_tve_remove(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c
index 54fbd6fe751d..ce59fe6b7a39 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c
@@ -84,6 +84,7 @@ curs507a_prepare(struct nv50_wndw *wndw, struct nv50_head_atom *asyh,
asyh->curs.handle = handle;
asyh->curs.offset = offset;
asyh->set.curs = asyh->curs.visible;
+ nv50_atom(asyh->state.state)->lock_core = true;
}
}
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 9cc9c950604a..d4bd72347245 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -1627,6 +1627,7 @@ static const struct panel_desc dataimage_scf0700c48ggu18 = {
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
};
static const struct display_timing dlc_dlc0700yzg_1_timing = {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
index f328aa5839a2..1957e8463030 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
@@ -1004,8 +1004,10 @@ int vmw_compat_shader_add(struct vmw_private *dev_priv,
ttm_bo_unreserve(&buf->base);
res = vmw_shader_alloc(dev_priv, buf, size, 0, shader_type);
- if (unlikely(ret != 0))
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
goto no_reserve;
+ }
ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_shader,
vmw_shader_key(user_key, shader_type),
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index e4811d37ca77..c0d56b7ca19e 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -104,10 +104,8 @@ static int uclogic_input_configured(struct hid_device *hdev,
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
struct uclogic_params *params = &drvdata->params;
- char *name;
const char *suffix = NULL;
struct hid_field *field;
- size_t len;
/* no report associated (HID_QUIRK_MULTI_INPUT not set) */
if (!hi->report)
@@ -146,12 +144,10 @@ static int uclogic_input_configured(struct hid_device *hdev,
}
if (suffix) {
- len = strlen(hdev->name) + 2 + strlen(suffix);
- name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL);
- if (name) {
- snprintf(name, len, "%s %s", hdev->name, suffix);
- hi->input->name = name;
- }
+ hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+ "%s %s", hdev->name, suffix);
+ if (!hi->input->name)
+ return -ENOMEM;
}
return 0;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index c3b104c72e49..03eecfd3692d 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -985,6 +985,7 @@ static int usbhid_parse(struct hid_device *hid)
struct usb_device *dev = interface_to_usbdev (intf);
struct hid_descriptor *hdesc;
struct hid_class_descriptor *hcdesc;
+ __u8 fixed_opt_descriptors_size;
u32 quirks = 0;
unsigned int rsize = 0;
char *rdesc;
@@ -1015,7 +1016,21 @@ static int usbhid_parse(struct hid_device *hid)
(hdesc->bNumDescriptors - 1) * sizeof(*hcdesc)) {
dbg_hid("hid descriptor invalid, bLen=%hhu bNum=%hhu\n",
hdesc->bLength, hdesc->bNumDescriptors);
- return -EINVAL;
+
+ /*
+ * Some devices may expose a wrong number of descriptors compared
+ * to the provided length.
+ * However, we ignore the optional hid class descriptors entirely
+ * so we can safely recompute the proper field.
+ */
+ if (hdesc->bLength >= sizeof(*hdesc)) {
+ fixed_opt_descriptors_size = hdesc->bLength - sizeof(*hdesc);
+
+ hid_warn(intf, "fixing wrong optional hid class descriptors count\n");
+ hdesc->bNumDescriptors = fixed_opt_descriptors_size / sizeof(*hcdesc) + 1;
+ } else {
+ return -EINVAL;
+ }
}
hid->version = le16_to_cpu(hdesc->bcdHID);
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 68af130712bc..786d24f7818a 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -810,9 +810,12 @@ static int intel_th_output_open(struct inode *inode, struct file *file)
int err;
dev = bus_find_device_by_devt(&intel_th_bus, inode->i_rdev);
- if (!dev || !dev->driver) {
+ if (!dev)
+ return -ENODEV;
+
+ if (!dev->driver) {
err = -ENODEV;
- goto out_no_device;
+ goto out_put_device;
}
thdrv = to_intel_th_driver(dev->driver);
@@ -836,12 +839,22 @@ static int intel_th_output_open(struct inode *inode, struct file *file)
out_put_device:
put_device(dev);
-out_no_device:
+
return err;
}
+static int intel_th_output_release(struct inode *inode, struct file *file)
+{
+ struct intel_th_device *thdev = file->private_data;
+
+ put_device(&thdev->dev);
+
+ return 0;
+}
+
static const struct file_operations intel_th_output_fops = {
.open = intel_th_output_open,
+ .release = intel_th_output_release,
.llseek = noop_llseek,
};
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 4fd74e2847f0..9643f60cf514 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -90,7 +90,7 @@
#define CHIPID_AD9434 0x6A
#define AD9434_DEF_OUTPUT_MODE 0x00
-#define AD9434_REG_VREF_MASK 0xC0
+#define AD9434_REG_VREF_MASK GENMASK(4, 0)
/*
* Analog Devices AD9467 16-Bit, 200/250 MSPS ADC
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index b806c1ab9b61..4a3d2d8841d2 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -1891,6 +1891,7 @@ static int at91_adc_remove(struct platform_device *pdev)
struct at91_adc_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
+ cancel_work_sync(&st->touch_st.workq);
at91_adc_dma_disable(pdev);
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index b6f09e5acb91..1a71d144059e 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -718,14 +718,7 @@ static const struct iio_chan_spec exynos_adc_iio_channels[] = {
ADC_CHANNEL(9, "adc9"),
};
-static int exynos_adc_remove_devices(struct device *dev, void *c)
-{
- struct platform_device *pdev = to_platform_device(dev);
-
- platform_device_unregister(pdev);
- return 0;
-}
static int exynos_adc_ts_open(struct input_dev *dev)
{
@@ -924,8 +917,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
return 0;
err_of_populate:
- device_for_each_child(&indio_dev->dev, NULL,
- exynos_adc_remove_devices);
+ of_platform_depopulate(&indio_dev->dev);
if (has_ts) {
input_unregister_device(info->input);
free_irq(info->tsirq, info);
@@ -954,8 +946,7 @@ static int exynos_adc_remove(struct platform_device *pdev)
free_irq(info->tsirq, info);
input_unregister_device(info->input);
}
- device_for_each_child(&indio_dev->dev, NULL,
- exynos_adc_remove_devices);
+ of_platform_depopulate(&indio_dev->dev);
iio_device_unregister(indio_dev);
free_irq(info->irq, info);
if (info->data->exit_hw)
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 148d9541f517..3f10bb872c79 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -402,6 +402,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
.num_channels = 4,
.regmap_type = AD5686_REGMAP,
},
+ [ID_AD5695R] = {
+ .channels = ad5685r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
[ID_AD5696] = {
.channels = ad5686_channels,
.num_channels = 4,
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index a2a5c3f5c42e..9195234e42e6 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -113,6 +113,17 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
},
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_NEVER)
},
+ {
+ /*
+ * ASUS Zenbook UX425QA_UM425QA
+ * Some Zenbooks report "Zenbook" with a lowercase b.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook UX425QA_UM425QA"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER)
+ },
{
/* ASUS ZenBook UX425UA */
.matches = {
@@ -1176,6 +1187,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "WUJIE Series-X5SP4NAG"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
+ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
/*
* A lot of modern Clevo barebones have touchpad and/or keyboard issues
* after suspend fixable with nomux + reset + noloop + nopnp. Luckily,
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index d8d3d0af988e..1934d1cc219b 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -609,7 +609,7 @@ static struct its_collection *its_build_mapd_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
- unsigned long itt_addr;
+ phys_addr_t itt_addr;
u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites);
itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt);
@@ -780,7 +780,7 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
struct its_cmd_desc *desc)
{
struct its_vpe *vpe = valid_vpe(its, desc->its_vmapp_cmd.vpe);
- unsigned long vpt_addr, vconf_addr;
+ phys_addr_t vpt_addr, vconf_addr;
u64 target;
bool alloc;
@@ -2408,10 +2408,10 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser,
baser->psz = psz;
tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz;
- pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n",
+ pr_info("ITS@%pa: allocated %d %s @%llx (%s, esz %d, psz %dK, shr %d)\n",
&its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / (int)tmp),
its_base_type_string[type],
- (unsigned long)virt_to_phys(base),
+ (u64)virt_to_phys(base),
indirect ? "indirect" : "flat", (int)esz,
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index abdf36ac3bee..74d6ed49dc36 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -109,14 +109,14 @@ mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
spin_unlock_irq(&dev->lock);
if (filep->f_flags & O_NONBLOCK)
return -EAGAIN;
- wait_event_interruptible(dev->wait, (dev->work ||
+ wait_event_interruptible(dev->wait, (READ_ONCE(dev->work) ||
!list_empty(list)));
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&dev->lock);
}
if (dev->work)
- dev->work = 0;
+ WRITE_ONCE(dev->work, 0);
if (!list_empty(list)) {
timer = list_first_entry(list, struct mISDNtimer, list);
list_del(&timer->list);
@@ -141,13 +141,16 @@ mISDN_poll(struct file *filep, poll_table *wait)
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
if (dev) {
+ u32 work;
+
poll_wait(filep, &dev->wait, wait);
mask = 0;
- if (dev->work || !list_empty(&dev->expired))
+ work = READ_ONCE(dev->work);
+ if (work || !list_empty(&dev->expired))
mask |= (EPOLLIN | EPOLLRDNORM);
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
- dev->work, list_empty(&dev->expired));
+ work, list_empty(&dev->expired));
}
return mask;
}
@@ -172,7 +175,7 @@ misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
struct mISDNtimer *timer;
if (!timeout) {
- dev->work = 1;
+ WRITE_ONCE(dev->work, 1);
wake_up_interruptible(&dev->wait);
id = 0;
} else {
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index d7c1f2263a57..1d189f9290c1 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -408,11 +408,6 @@ int led_classdev_register_ext(struct device *parent,
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
led_cdev->brightness_hw_changed = -1;
#endif
- /* add to the list of leds */
- down_write(&leds_list_lock);
- list_add_tail(&led_cdev->node, &leds_list);
- up_write(&leds_list_lock);
-
if (!led_cdev->max_brightness)
led_cdev->max_brightness = LED_FULL;
@@ -420,6 +415,11 @@ int led_classdev_register_ext(struct device *parent,
led_init_core(led_cdev);
+ /* add to the list of leds */
+ down_write(&leds_list_lock);
+ list_add_tail(&led_cdev->node, &leds_list);
+ up_write(&leds_list_lock);
+
#ifdef CONFIG_LEDS_TRIGGERS
led_trigger_set_default(led_cdev);
#endif
diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h
index df758033dc93..90249f74f374 100644
--- a/drivers/misc/mei/mei-trace.h
+++ b/drivers/misc/mei/mei-trace.h
@@ -21,18 +21,18 @@ TRACE_EVENT(mei_reg_read,
TP_ARGS(dev, reg, offs, val),
TP_STRUCT__entry(
__string(dev, dev_name(dev))
- __field(const char *, reg)
+ __string(reg, reg)
__field(u32, offs)
__field(u32, val)
),
TP_fast_assign(
__assign_str(dev, dev_name(dev))
- __entry->reg = reg;
+ __assign_str(reg, reg)
__entry->offs = offs;
__entry->val = val;
),
TP_printk("[%s] read %s:[%#x] = %#x",
- __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+ __get_str(dev), __get_str(reg), __entry->offs, __entry->val)
);
TRACE_EVENT(mei_reg_write,
@@ -40,18 +40,18 @@ TRACE_EVENT(mei_reg_write,
TP_ARGS(dev, reg, offs, val),
TP_STRUCT__entry(
__string(dev, dev_name(dev))
- __field(const char *, reg)
+ __string(reg, reg)
__field(u32, offs)
__field(u32, val)
),
TP_fast_assign(
__assign_str(dev, dev_name(dev))
- __entry->reg = reg;
+ __assign_str(reg, reg)
__entry->offs = offs;
__entry->val = val;
),
TP_printk("[%s] write %s[%#x] = %#x",
- __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+ __get_str(dev), __get_str(reg), __entry->offs, __entry->val)
);
TRACE_EVENT(mei_pci_cfg_read,
@@ -59,18 +59,18 @@ TRACE_EVENT(mei_pci_cfg_read,
TP_ARGS(dev, reg, offs, val),
TP_STRUCT__entry(
__string(dev, dev_name(dev))
- __field(const char *, reg)
+ __string(reg, reg)
__field(u32, offs)
__field(u32, val)
),
TP_fast_assign(
__assign_str(dev, dev_name(dev))
- __entry->reg = reg;
+ __assign_str(reg, reg)
__entry->offs = offs;
__entry->val = val;
),
TP_printk("[%s] pci cfg read %s:[%#x] = %#x",
- __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+ __get_str(dev), __get_str(reg), __entry->offs, __entry->val)
);
#endif /* _MEI_TRACE_H_ */
diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c
index 95e56eb2cdd0..31af216c47c2 100644
--- a/drivers/misc/uacce/uacce.c
+++ b/drivers/misc/uacce/uacce.c
@@ -37,20 +37,34 @@ static int uacce_start_queue(struct uacce_queue *q)
return 0;
}
-static int uacce_put_queue(struct uacce_queue *q)
+static int uacce_stop_queue(struct uacce_queue *q)
{
struct uacce_device *uacce = q->uacce;
- if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
+ if (q->state != UACCE_Q_STARTED)
+ return 0;
+
+ if (uacce->ops->stop_queue)
uacce->ops->stop_queue(q);
- if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) &&
- uacce->ops->put_queue)
+ q->state = UACCE_Q_INIT;
+
+ return 0;
+}
+
+static void uacce_put_queue(struct uacce_queue *q)
+{
+ struct uacce_device *uacce = q->uacce;
+
+ uacce_stop_queue(q);
+
+ if (q->state != UACCE_Q_INIT)
+ return;
+
+ if (uacce->ops->put_queue)
uacce->ops->put_queue(q);
q->state = UACCE_Q_ZOMBIE;
-
- return 0;
}
static long uacce_fops_unl_ioctl(struct file *filep,
@@ -77,7 +91,7 @@ static long uacce_fops_unl_ioctl(struct file *filep,
ret = uacce_start_queue(q);
break;
case UACCE_CMD_PUT_Q:
- ret = uacce_put_queue(q);
+ ret = uacce_stop_queue(q);
break;
default:
if (uacce->ops->ioctl)
@@ -208,8 +222,14 @@ static void uacce_vma_close(struct vm_area_struct *vma)
kfree(qfr);
}
+static int uacce_vma_mremap(struct vm_area_struct *area)
+{
+ return -EPERM;
+}
+
static const struct vm_operations_struct uacce_vm_ops = {
.close = uacce_vma_close,
+ .mremap = uacce_vma_mremap,
};
static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
@@ -482,6 +502,8 @@ EXPORT_SYMBOL_GPL(uacce_alloc);
*/
int uacce_register(struct uacce_device *uacce)
{
+ int ret;
+
if (!uacce)
return -ENODEV;
@@ -492,7 +514,11 @@ int uacce_register(struct uacce_device *uacce)
uacce->cdev->ops = &uacce_fops;
uacce->cdev->owner = THIS_MODULE;
- return cdev_device_add(uacce->cdev, &uacce->dev);
+ ret = cdev_device_add(uacce->cdev, &uacce->dev);
+ if (ret)
+ uacce->cdev = NULL;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(uacce_register);
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index b5cb83bb9b85..f0e9a7667994 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1277,6 +1277,46 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
return err;
}
+static int sdmmc_card_busy(struct mmc_host *mmc)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+ u8 stat;
+ u8 mask = SD_DAT3_STATUS | SD_DAT2_STATUS | SD_DAT1_STATUS
+ | SD_DAT0_STATUS;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+ SD_CLK_TOGGLE_EN);
+ if (err)
+ goto out;
+
+ mdelay(1);
+
+ err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
+ if (err)
+ goto out;
+
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+out:
+ mutex_unlock(&pcr->pcr_mutex);
+
+ if (err)
+ return err;
+
+ /* check if any pin between dat[0:3] is low */
+ if ((stat & mask) != mask)
+ return 1;
+ else
+ return 0;
+}
+
static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
@@ -1336,6 +1376,7 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
.get_ro = sdmmc_get_ro,
.get_cd = sdmmc_get_cd,
.start_signal_voltage_switch = sdmmc_switch_voltage,
+ .card_busy = sdmmc_card_busy,
.execute_tuning = sdmmc_execute_tuning,
};
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 08bc930afc4c..a6d552a83ca4 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3642,8 +3642,9 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23) {
memset(fk, 0, sizeof(*fk));
- return __skb_flow_dissect(NULL, skb, &flow_keys_bonding,
- fk, NULL, 0, 0, 0, 0);
+ return __skb_flow_dissect(dev_net(bond->dev), skb,
+ &flow_keys_bonding, fk, NULL, 0, 0,
+ 0, 0);
}
fk->ports.ports = 0;
diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c
index ff05b5230f0b..f8baf8283a65 100644
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@ -479,11 +479,17 @@ static void ems_usb_read_bulk_callback(struct urb *urb)
urb->transfer_buffer, RX_BUFFER_SIZE,
ems_usb_read_bulk_callback, dev);
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!retval)
+ return;
+
+ usb_unanchor_urb(urb);
if (retval == -ENODEV)
netif_device_detach(netdev);
- else if (retval)
+ else
netdev_err(netdev,
"failed resubmitting read bulk urb: %d\n", retval);
}
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
index c9ccce6c60b4..fb28e688300d 100644
--- a/drivers/net/can/usb/esd_usb2.c
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -441,13 +441,20 @@ static void esd_usb2_read_bulk_callback(struct urb *urb)
urb->transfer_buffer, RX_BUFFER_SIZE,
esd_usb2_read_bulk_callback, dev);
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!retval)
+ return;
+
+ usb_unanchor_urb(urb);
+
if (retval == -ENODEV) {
for (i = 0; i < dev->net_count; i++) {
if (dev->nets[i])
netif_device_detach(dev->nets[i]->netdev);
}
- } else if (retval) {
+ } else {
dev_err(dev->udev->dev.parent,
"failed resubmitting read bulk urb: %d\n", retval);
}
diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
index 602f0b3bbcdf..a7f7951bea2a 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
@@ -325,7 +325,14 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb)
urb->transfer_buffer, KVASER_USB_RX_BUFFER_SIZE,
kvaser_usb_read_bulk_callback, dev);
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!err)
+ return;
+
+ usb_unanchor_urb(urb);
+
if (err == -ENODEV) {
for (i = 0; i < dev->nchannels; i++) {
if (!dev->nets[i])
@@ -333,7 +340,7 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb)
netif_device_detach(dev->nets[i]->netdev);
}
- } else if (err) {
+ } else {
dev_err(&dev->intf->dev,
"Failed resubmitting read bulk urb: %d\n", err);
}
diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c
index 7179eb0fedb1..555a23d5134c 100644
--- a/drivers/net/can/usb/mcba_usb.c
+++ b/drivers/net/can/usb/mcba_usb.c
@@ -614,11 +614,17 @@ static void mcba_usb_read_bulk_callback(struct urb *urb)
urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
mcba_usb_read_bulk_callback, priv);
+ usb_anchor_urb(urb, &priv->rx_submitted);
+
retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!retval)
+ return;
+
+ usb_unanchor_urb(urb);
if (retval == -ENODEV)
netif_device_detach(netdev);
- else if (retval)
+ else
netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
retval);
}
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index 885c54c6f81a..eb9d17c70408 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -546,11 +546,17 @@ static void usb_8dev_read_bulk_callback(struct urb *urb)
urb->transfer_buffer, RX_BUFFER_SIZE,
usb_8dev_read_bulk_callback, priv);
+ usb_anchor_urb(urb, &priv->rx_submitted);
+
retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!retval)
+ return;
+
+ usb_unanchor_urb(urb);
if (retval == -ENODEV)
netif_device_detach(netdev);
- else if (retval)
+ else
netdev_err(netdev,
"failed resubmitting read bulk urb: %d\n", retval);
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 9cd6dac03363..3de7674a8467 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -2112,7 +2112,7 @@ static void xgbe_get_stats64(struct net_device *netdev,
s->multicast = pstats->rxmulticastframes_g;
s->rx_length_errors = pstats->rxlengtherror;
s->rx_crc_errors = pstats->rxcrcerror;
- s->rx_fifo_errors = pstats->rxfifooverflow;
+ s->rx_over_errors = pstats->rxfifooverflow;
s->tx_packets = pstats->txframecount_gb;
s->tx_bytes = pstats->txoctetcount_gb;
@@ -2568,9 +2568,6 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
goto read_again;
if (error || packet->errors) {
- if (packet->errors)
- netif_err(pdata, rx_err, netdev,
- "error in received packet\n");
dev_kfree_skb(skb);
goto next_packet;
}
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index d6984c179bae..a0f4bfe522d4 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -3796,6 +3796,7 @@ int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac)
{
int status;
bool pmac_valid = false;
+ u32 pmac_id;
eth_zero_addr(mac);
@@ -3808,7 +3809,7 @@ int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac)
adapter->if_handle, 0);
} else {
status = be_cmd_get_mac_from_list(adapter, mac, &pmac_valid,
- NULL, adapter->if_handle, 0);
+ &pmac_id, adapter->if_handle, 0);
}
return status;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 3d70c3a47d63..67d878eb8a7d 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -1038,7 +1038,7 @@ struct hclge_fd_tcam_config_3_cmd {
#define HCLGE_FD_AD_QID_M GENMASK(11, 2)
#define HCLGE_FD_AD_USE_COUNTER_B 12
#define HCLGE_FD_AD_COUNTER_NUM_S 13
-#define HCLGE_FD_AD_COUNTER_NUM_M GENMASK(20, 13)
+#define HCLGE_FD_AD_COUNTER_NUM_M GENMASK(19, 13)
#define HCLGE_FD_AD_NXT_STEP_B 20
#define HCLGE_FD_AD_NXT_KEY_S 21
#define HCLGE_FD_AD_NXT_KEY_M GENMASK(25, 21)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 99b5b956ed8f..fa51463bfd8b 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -5109,7 +5109,7 @@ static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc,
HCLGE_FD_AD_COUNTER_NUM_S, action->counter_id);
hnae3_set_bit(ad_data, HCLGE_FD_AD_NXT_STEP_B, action->use_next_stage);
hnae3_set_field(ad_data, HCLGE_FD_AD_NXT_KEY_M, HCLGE_FD_AD_NXT_KEY_S,
- action->counter_id);
+ action->next_input_key);
req->ad_data = cpu_to_le64(ad_data);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index a337a6826a84..b3ae457b4db6 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -5441,7 +5441,6 @@ void ice_update_vsi_stats(struct ice_vsi *vsi)
pf->stats.illegal_bytes +
pf->stats.rx_len_errors +
pf->stats.rx_undersize +
- pf->hw_csum_rx_error +
pf->stats.rx_jabber +
pf->stats.rx_fragments +
pf->stats.rx_oversize;
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c
index 3ad132739587..821cc5aa4a7b 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c
@@ -1383,7 +1383,7 @@ int mvpp2_ethtool_cls_rule_ins(struct mvpp2_port *port,
efs->rule.flow_type = mvpp2_cls_ethtool_flow_to_type(info->fs.flow_type);
if (efs->rule.flow_type < 0) {
ret = efs->rule.flow_type;
- goto clean_rule;
+ goto clean_eth_rule;
}
ret = mvpp2_cls_rfs_parse_rule(&efs->rule);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index f8e86f253563..3514564e2cc6 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -1273,8 +1273,8 @@ static int rvu_get_attach_blkaddr(struct rvu *rvu, int blktype,
return -ENODEV;
}
-static void rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
- int num_lfs, struct rsrc_attach *attach)
+static int rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
+ int num_lfs, struct rsrc_attach *attach)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
struct rvu_hwinfo *hw = rvu->hw;
@@ -1284,21 +1284,21 @@ static void rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
u64 cfg;
if (!num_lfs)
- return;
+ return -EINVAL;
blkaddr = rvu_get_attach_blkaddr(rvu, blktype, pcifunc, attach);
if (blkaddr < 0)
- return;
+ return -EFAULT;
block = &hw->block[blkaddr];
if (!block->lf.bmap)
- return;
+ return -ESRCH;
for (slot = 0; slot < num_lfs; slot++) {
/* Allocate the resource */
lf = rvu_alloc_rsrc(&block->lf);
if (lf < 0)
- return;
+ return -EFAULT;
cfg = (1ULL << 63) | (pcifunc << 8) | slot;
rvu_write64(rvu, blkaddr, block->lfcfg_reg |
@@ -1309,6 +1309,8 @@ static void rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
/* Set start MSIX vector for this LF within this PF/VF */
rvu_set_msix_offset(rvu, pfvf, block, lf);
}
+
+ return 0;
}
static int rvu_check_rsrc_availability(struct rvu *rvu,
@@ -1446,22 +1448,31 @@ int rvu_mbox_handler_attach_resources(struct rvu *rvu,
int err;
/* If first request, detach all existing attached resources */
- if (!attach->modify)
- rvu_detach_rsrcs(rvu, NULL, pcifunc);
+ if (!attach->modify) {
+ err = rvu_detach_rsrcs(rvu, NULL, pcifunc);
+ if (err)
+ return err;
+ }
mutex_lock(&rvu->rsrc_lock);
/* Check if the request can be accommodated */
err = rvu_check_rsrc_availability(rvu, attach, pcifunc);
if (err)
- goto exit;
+ goto fail1;
/* Now attach the requested resources */
- if (attach->npalf)
- rvu_attach_block(rvu, pcifunc, BLKTYPE_NPA, 1, attach);
+ if (attach->npalf) {
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_NPA, 1, attach);
+ if (err)
+ goto fail1;
+ }
- if (attach->nixlf)
- rvu_attach_block(rvu, pcifunc, BLKTYPE_NIX, 1, attach);
+ if (attach->nixlf) {
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_NIX, 1, attach);
+ if (err)
+ goto fail2;
+ }
if (attach->sso) {
/* RVU func doesn't know which exact LF or slot is attached
@@ -1471,33 +1482,64 @@ int rvu_mbox_handler_attach_resources(struct rvu *rvu,
*/
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_SSO);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_SSO,
- attach->sso, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_SSO,
+ attach->sso, attach);
+ if (err)
+ goto fail3;
}
if (attach->ssow) {
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_SSOW);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_SSOW,
- attach->ssow, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_SSOW,
+ attach->ssow, attach);
+ if (err)
+ goto fail4;
}
if (attach->timlfs) {
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_TIM);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_TIM,
- attach->timlfs, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_TIM,
+ attach->timlfs, attach);
+ if (err)
+ goto fail5;
}
if (attach->cptlfs) {
if (attach->modify &&
rvu_attach_from_same_block(rvu, BLKTYPE_CPT, attach))
rvu_detach_block(rvu, pcifunc, BLKTYPE_CPT);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_CPT,
- attach->cptlfs, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_CPT,
+ attach->cptlfs, attach);
+ if (err)
+ goto fail6;
}
-exit:
+ mutex_unlock(&rvu->rsrc_lock);
+ return 0;
+
+fail6:
+ if (attach->timlfs)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_TIM);
+
+fail5:
+ if (attach->ssow)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_SSOW);
+
+fail4:
+ if (attach->sso)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_SSO);
+
+fail3:
+ if (attach->nixlf)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_NIX);
+
+fail2:
+ if (attach->npalf)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_NPA);
+
+fail1:
mutex_unlock(&rvu->rsrc_lock);
return err;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index 866b1a2cc9a1..c5d20c09a1d7 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -562,13 +562,8 @@ static inline dma_addr_t otx2_dma_map_page(struct otx2_nic *pfvf,
size_t offset, size_t size,
enum dma_data_direction dir)
{
- dma_addr_t iova;
-
- iova = dma_map_page_attrs(pfvf->dev, page,
+ return dma_map_page_attrs(pfvf->dev, page,
offset, size, dir, DMA_ATTR_SKIP_CPU_SYNC);
- if (unlikely(dma_mapping_error(pfvf->dev, iova)))
- return (dma_addr_t)NULL;
- return iova;
}
static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index c3ff1fc577a7..9c5ccbaa160b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -3679,6 +3679,8 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
mlx5e_queue_update_stats(priv);
}
+ netdev_stats_to_stats64(stats, &dev->stats);
+
if (mlx5e_is_uplink_rep(priv)) {
struct mlx5e_vport_stats *vstats = &priv->stats.vport;
@@ -3695,19 +3697,21 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
mlx5e_fold_sw_stats64(priv, stats);
}
- stats->rx_missed_errors = priv->stats.qcnt.rx_out_of_buffer;
+ stats->rx_missed_errors += priv->stats.qcnt.rx_out_of_buffer;
+ stats->rx_dropped += PPORT_2863_GET(pstats, if_in_discards);
- stats->rx_length_errors =
+ stats->rx_length_errors +=
PPORT_802_3_GET(pstats, a_in_range_length_errors) +
PPORT_802_3_GET(pstats, a_out_of_range_length_field) +
- PPORT_802_3_GET(pstats, a_frame_too_long_errors);
- stats->rx_crc_errors =
+ PPORT_802_3_GET(pstats, a_frame_too_long_errors) +
+ VNIC_ENV_GET(&priv->stats.vnic, eth_wqe_too_small);
+ stats->rx_crc_errors +=
PPORT_802_3_GET(pstats, a_frame_check_sequence_errors);
- stats->rx_frame_errors = PPORT_802_3_GET(pstats, a_alignment_errors);
- stats->tx_aborted_errors = PPORT_2863_GET(pstats, if_out_discards);
- stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
- stats->rx_frame_errors;
- stats->tx_errors = stats->tx_aborted_errors + stats->tx_carrier_errors;
+ stats->rx_frame_errors += PPORT_802_3_GET(pstats, a_alignment_errors);
+ stats->tx_aborted_errors += PPORT_2863_GET(pstats, if_out_discards);
+ stats->rx_errors += stats->rx_length_errors + stats->rx_crc_errors +
+ stats->rx_frame_errors;
+ stats->tx_errors += stats->tx_aborted_errors + stats->tx_carrier_errors;
}
static void mlx5e_set_rx_mode(struct net_device *dev)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
index ff4f10d0f090..96d537bc0b8f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
@@ -489,17 +489,26 @@ static const struct counter_desc vnic_env_stats_dev_oob_desc[] = {
VNIC_ENV_OFF(vport_env.internal_rq_out_of_buffer) },
};
+static const struct counter_desc vnic_env_stats_drop_desc[] = {
+ { "rx_oversize_pkts_buffer",
+ VNIC_ENV_OFF(vport_env.eth_wqe_too_small) },
+};
+
#define NUM_VNIC_ENV_STEER_COUNTERS(dev) \
(MLX5_CAP_GEN(dev, nic_receive_steering_discard) ? \
ARRAY_SIZE(vnic_env_stats_steer_desc) : 0)
#define NUM_VNIC_ENV_DEV_OOB_COUNTERS(dev) \
(MLX5_CAP_GEN(dev, vnic_env_int_rq_oob) ? \
ARRAY_SIZE(vnic_env_stats_dev_oob_desc) : 0)
+#define NUM_VNIC_ENV_DROP_COUNTERS(dev) \
+ (MLX5_CAP_GEN(dev, eth_wqe_too_small) ? \
+ ARRAY_SIZE(vnic_env_stats_drop_desc) : 0)
static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(vnic_env)
{
return NUM_VNIC_ENV_STEER_COUNTERS(priv->mdev) +
- NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev);
+ NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev) +
+ NUM_VNIC_ENV_DROP_COUNTERS(priv->mdev);
}
static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(vnic_env)
@@ -513,6 +522,11 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(vnic_env)
for (i = 0; i < NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev); i++)
strcpy(data + (idx++) * ETH_GSTRING_LEN,
vnic_env_stats_dev_oob_desc[i].format);
+
+ for (i = 0; i < NUM_VNIC_ENV_DROP_COUNTERS(priv->mdev); i++)
+ strcpy(data + (idx++) * ETH_GSTRING_LEN,
+ vnic_env_stats_drop_desc[i].format);
+
return idx;
}
@@ -527,6 +541,11 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(vnic_env)
for (i = 0; i < NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev); i++)
data[idx++] = MLX5E_READ_CTR32_BE(priv->stats.vnic.query_vnic_env_out,
vnic_env_stats_dev_oob_desc, i);
+
+ for (i = 0; i < NUM_VNIC_ENV_DROP_COUNTERS(priv->mdev); i++)
+ data[idx++] = MLX5E_READ_CTR32_BE(priv->stats.vnic.query_vnic_env_out,
+ vnic_env_stats_drop_desc, i);
+
return idx;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
index 162daaadb0d8..8813989f3f10 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
@@ -239,6 +239,10 @@ struct mlx5e_qcounter_stats {
u32 rx_if_down_packets;
};
+#define VNIC_ENV_GET(vnic_env_stats, c) \
+ MLX5_GET(query_vnic_env_out, (vnic_env_stats)->query_vnic_env_out, \
+ vport_env.c)
+
struct mlx5e_vnic_env_stats {
__be64 query_vnic_env_out[MLX5_ST_SZ_QW(query_vnic_env_out)];
};
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
index 45570d0a58d2..02b2ab795854 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
@@ -185,7 +185,7 @@ int esw_acl_ingress_lgcy_setup(struct mlx5_eswitch *esw,
if (IS_ERR(vport->ingress.acl)) {
err = PTR_ERR(vport->ingress.acl);
vport->ingress.acl = NULL;
- return err;
+ goto out;
}
err = esw_acl_ingress_lgcy_groups_create(esw, vport);
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index ec90f75289db..4073e3852adb 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -1525,9 +1525,8 @@ static void rocker_world_port_post_fini(struct rocker_port *rocker_port)
{
struct rocker_world_ops *wops = rocker_port->rocker->wops;
- if (!wops->port_post_fini)
- return;
- wops->port_post_fini(rocker_port);
+ if (wops->port_post_fini)
+ wops->port_post_fini(rocker_port);
kfree(rocker_port->wpriv);
}
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
index 3837c897832e..befb61e00d07 100644
--- a/drivers/net/ipvlan/ipvlan.h
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -69,7 +69,6 @@ struct ipvl_dev {
DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
netdev_features_t sfeatures;
u32 msg_enable;
- spinlock_t addrs_lock;
};
struct ipvl_addr {
@@ -90,6 +89,7 @@ struct ipvl_port {
struct net_device *dev;
possible_net_t pnet;
struct hlist_head hlhead[IPVLAN_HASH_SIZE];
+ spinlock_t addrs_lock; /* guards hash-table and addrs */
struct list_head ipvlans;
u16 mode;
u16 flags;
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index a113a06c98a5..c1f57db3f185 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -104,17 +104,15 @@ void ipvlan_ht_addr_del(struct ipvl_addr *addr)
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
const void *iaddr, bool is_v6)
{
- struct ipvl_addr *addr, *ret = NULL;
+ struct ipvl_addr *addr;
- rcu_read_lock();
- list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) {
- if (addr_equal(is_v6, addr, iaddr)) {
- ret = addr;
- break;
- }
+ assert_spin_locked(&ipvlan->port->addrs_lock);
+
+ list_for_each_entry(addr, &ipvlan->addrs, anode) {
+ if (addr_equal(is_v6, addr, iaddr))
+ return addr;
}
- rcu_read_unlock();
- return ret;
+ return NULL;
}
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index f59ef2e2a614..964e1449a0c6 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -72,6 +72,7 @@ static int ipvlan_port_create(struct net_device *dev)
for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++)
INIT_HLIST_HEAD(&port->hlhead[idx]);
+ spin_lock_init(&port->addrs_lock);
skb_queue_head_init(&port->backlog);
INIT_WORK(&port->wq, ipvlan_process_multicast);
ida_init(&port->ida);
@@ -177,6 +178,7 @@ static void ipvlan_uninit(struct net_device *dev)
static int ipvlan_open(struct net_device *dev)
{
struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_port *port = ipvlan->port;
struct ipvl_addr *addr;
if (ipvlan->port->mode == IPVLAN_MODE_L3 ||
@@ -185,10 +187,10 @@ static int ipvlan_open(struct net_device *dev)
else
dev->flags &= ~IFF_NOARP;
- rcu_read_lock();
- list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
+ spin_lock_bh(&port->addrs_lock);
+ list_for_each_entry(addr, &ipvlan->addrs, anode)
ipvlan_ht_addr_add(ipvlan, addr);
- rcu_read_unlock();
+ spin_unlock_bh(&port->addrs_lock);
return 0;
}
@@ -202,10 +204,10 @@ static int ipvlan_stop(struct net_device *dev)
dev_uc_unsync(phy_dev, dev);
dev_mc_unsync(phy_dev, dev);
- rcu_read_lock();
- list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
+ spin_lock_bh(&ipvlan->port->addrs_lock);
+ list_for_each_entry(addr, &ipvlan->addrs, anode)
ipvlan_ht_addr_del(addr);
- rcu_read_unlock();
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return 0;
}
@@ -572,7 +574,6 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
if (!tb[IFLA_MTU])
ipvlan_adjust_mtu(ipvlan, phy_dev);
INIT_LIST_HEAD(&ipvlan->addrs);
- spin_lock_init(&ipvlan->addrs_lock);
/* TODO Probably put random address here to be presented to the
* world but keep using the physical-dev address for the outgoing
@@ -650,13 +651,13 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
struct ipvl_dev *ipvlan = netdev_priv(dev);
struct ipvl_addr *addr, *next;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
ipvlan_ht_addr_del(addr);
list_del_rcu(&addr->anode);
kfree_rcu(addr, rcu);
}
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
ida_simple_remove(&ipvlan->port->ida, dev->dev_id);
list_del_rcu(&ipvlan->pnode);
@@ -803,6 +804,8 @@ static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
{
struct ipvl_addr *addr;
+ assert_spin_locked(&ipvlan->port->addrs_lock);
+
addr = kzalloc(sizeof(struct ipvl_addr), GFP_ATOMIC);
if (!addr)
return -ENOMEM;
@@ -833,16 +836,16 @@ static void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
{
struct ipvl_addr *addr;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
addr = ipvlan_find_addr(ipvlan, iaddr, is_v6);
if (!addr) {
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return;
}
ipvlan_ht_addr_del(addr);
list_del_rcu(&addr->anode);
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
kfree_rcu(addr, rcu);
}
@@ -864,14 +867,14 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
{
int ret = -EINVAL;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true))
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv6=%pI6c addr for %s intf\n",
ip6_addr, ipvlan->dev->name);
else
ret = ipvlan_add_addr(ipvlan, ip6_addr, true);
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return ret;
}
@@ -910,21 +913,24 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused,
struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr;
struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev;
struct ipvl_dev *ipvlan = netdev_priv(dev);
+ int ret = NOTIFY_OK;
if (!ipvlan_is_valid_dev(dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_UP:
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) {
NL_SET_ERR_MSG(i6vi->extack,
"Address already assigned to an ipvlan device");
- return notifier_from_errno(-EADDRINUSE);
+ ret = notifier_from_errno(-EADDRINUSE);
}
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
break;
}
- return NOTIFY_OK;
+ return ret;
}
#endif
@@ -932,14 +938,14 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
{
int ret = -EINVAL;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false))
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv4=%pI4 on %s intf.\n",
ip4_addr, ipvlan->dev->name);
else
ret = ipvlan_add_addr(ipvlan, ip4_addr, false);
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return ret;
}
@@ -981,21 +987,24 @@ static int ipvlan_addr4_validator_event(struct notifier_block *unused,
struct in_validator_info *ivi = (struct in_validator_info *)ptr;
struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev;
struct ipvl_dev *ipvlan = netdev_priv(dev);
+ int ret = NOTIFY_OK;
if (!ipvlan_is_valid_dev(dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_UP:
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) {
NL_SET_ERR_MSG(ivi->extack,
"Address already assigned to an ipvlan device");
- return notifier_from_errno(-EADDRINUSE);
+ ret = notifier_from_errno(-EADDRINUSE);
}
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
break;
}
- return NOTIFY_OK;
+ return ret;
}
static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 9c77e6ab2b30..49f9f75f5c12 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -55,7 +55,7 @@ struct macvlan_port {
struct macvlan_source_entry {
struct hlist_node hlist;
- struct macvlan_dev *vlan;
+ struct macvlan_dev __rcu *vlan;
unsigned char addr[6+2] __aligned(sizeof(u16));
struct rcu_head rcu;
};
@@ -141,7 +141,7 @@ static struct macvlan_source_entry *macvlan_hash_lookup_source(
hlist_for_each_entry_rcu(entry, h, hlist, lockdep_rtnl_is_held()) {
if (ether_addr_equal_64bits(entry->addr, addr) &&
- entry->vlan == vlan)
+ rcu_access_pointer(entry->vlan) == vlan)
return entry;
}
return NULL;
@@ -163,7 +163,7 @@ static int macvlan_hash_add_source(struct macvlan_dev *vlan,
return -ENOMEM;
ether_addr_copy(entry->addr, addr);
- entry->vlan = vlan;
+ RCU_INIT_POINTER(entry->vlan, vlan);
h = &port->vlan_source_hash[macvlan_eth_hash(addr)];
hlist_add_head_rcu(&entry->hlist, h);
vlan->macaddr_count++;
@@ -182,6 +182,7 @@ static void macvlan_hash_add(struct macvlan_dev *vlan)
static void macvlan_hash_del_source(struct macvlan_source_entry *entry)
{
+ RCU_INIT_POINTER(entry->vlan, NULL);
hlist_del_rcu(&entry->hlist);
kfree_rcu(entry, rcu);
}
@@ -270,25 +271,22 @@ static void macvlan_broadcast(struct sk_buff *skb,
if (skb->protocol == htons(ETH_P_PAUSE))
return;
- for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
- hlist_for_each_entry_rcu(vlan, &port->vlan_hash[i], hlist) {
- if (vlan->dev == src || !(vlan->mode & mode))
- continue;
+ hash_for_each_rcu(port->vlan_hash, i, vlan, hlist) {
+ if (vlan->dev == src || !(vlan->mode & mode))
+ continue;
- hash = mc_hash(vlan, eth->h_dest);
- if (!test_bit(hash, vlan->mc_filter))
- continue;
+ hash = mc_hash(vlan, eth->h_dest);
+ if (!test_bit(hash, vlan->mc_filter))
+ continue;
- err = NET_RX_DROP;
- nskb = skb_clone(skb, GFP_ATOMIC);
- if (likely(nskb))
- err = macvlan_broadcast_one(
- nskb, vlan, eth,
+ err = NET_RX_DROP;
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (likely(nskb))
+ err = macvlan_broadcast_one(nskb, vlan, eth,
mode == MACVLAN_MODE_BRIDGE) ?:
- netif_rx_ni(nskb);
- macvlan_count_rx(vlan, skb->len + ETH_HLEN,
- err == NET_RX_SUCCESS, true);
- }
+ netif_rx_ni(nskb);
+ macvlan_count_rx(vlan, skb->len + ETH_HLEN,
+ err == NET_RX_SUCCESS, true);
}
}
@@ -378,20 +376,14 @@ static void macvlan_broadcast_enqueue(struct macvlan_port *port,
static void macvlan_flush_sources(struct macvlan_port *port,
struct macvlan_dev *vlan)
{
+ struct macvlan_source_entry *entry;
+ struct hlist_node *next;
int i;
- for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
- struct hlist_node *h, *n;
-
- hlist_for_each_safe(h, n, &port->vlan_source_hash[i]) {
- struct macvlan_source_entry *entry;
+ hash_for_each_safe(port->vlan_source_hash, i, next, entry, hlist)
+ if (rcu_access_pointer(entry->vlan) == vlan)
+ macvlan_hash_del_source(entry);
- entry = hlist_entry(h, struct macvlan_source_entry,
- hlist);
- if (entry->vlan == vlan)
- macvlan_hash_del_source(entry);
- }
- }
vlan->macaddr_count = 0;
}
@@ -421,18 +413,29 @@ static void macvlan_forward_source_one(struct sk_buff *skb,
macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, false);
}
-static void macvlan_forward_source(struct sk_buff *skb,
+static bool macvlan_forward_source(struct sk_buff *skb,
struct macvlan_port *port,
const unsigned char *addr)
{
struct macvlan_source_entry *entry;
u32 idx = macvlan_eth_hash(addr);
struct hlist_head *h = &port->vlan_source_hash[idx];
+ bool consume = false;
hlist_for_each_entry_rcu(entry, h, hlist) {
- if (ether_addr_equal_64bits(entry->addr, addr))
- macvlan_forward_source_one(skb, entry->vlan);
+ if (ether_addr_equal_64bits(entry->addr, addr)) {
+ struct macvlan_dev *vlan = rcu_dereference(entry->vlan);
+
+ if (!vlan)
+ continue;
+
+ if (vlan->flags & MACVLAN_FLAG_NODST)
+ consume = true;
+ macvlan_forward_source_one(skb, vlan);
+ }
}
+
+ return consume;
}
/* called under rcu_read_lock() from netif_receive_skb */
@@ -461,7 +464,10 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
return RX_HANDLER_CONSUMED;
*pskb = skb;
eth = eth_hdr(skb);
- macvlan_forward_source(skb, port, eth->h_source);
+ if (macvlan_forward_source(skb, port, eth->h_source)) {
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
src = macvlan_hash_lookup(port, eth->h_source);
if (src && src->mode != MACVLAN_MODE_VEPA &&
src->mode != MACVLAN_MODE_BRIDGE) {
@@ -480,7 +486,10 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
return RX_HANDLER_PASS;
}
- macvlan_forward_source(skb, port, eth->h_source);
+ if (macvlan_forward_source(skb, port, eth->h_source)) {
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
if (macvlan_passthru(port))
vlan = list_first_or_null_rcu(&port->vlans,
struct macvlan_dev, list);
@@ -1283,7 +1292,8 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[],
return 0;
if (data[IFLA_MACVLAN_FLAGS] &&
- nla_get_u16(data[IFLA_MACVLAN_FLAGS]) & ~MACVLAN_FLAG_NOPROMISC)
+ nla_get_u16(data[IFLA_MACVLAN_FLAGS]) & ~(MACVLAN_FLAG_NOPROMISC |
+ MACVLAN_FLAG_NODST))
return -EINVAL;
if (data[IFLA_MACVLAN_MODE]) {
@@ -1615,7 +1625,7 @@ static int macvlan_fill_info_macaddr(struct sk_buff *skb,
struct macvlan_source_entry *entry;
hlist_for_each_entry_rcu(entry, h, hlist, lockdep_rtnl_is_held()) {
- if (entry->vlan != vlan)
+ if (rcu_access_pointer(entry->vlan) != vlan)
continue;
if (nla_put(skb, IFLA_MACVLAN_MACADDR, ETH_ALEN, entry->addr))
return 1;
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index 9b7db5fd9e08..287d54ada471 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -602,10 +602,6 @@ static const struct usb_device_id products[] = {
USB_DEVICE(0x0fe6, 0x8101), /* DM9601 USB to Fast Ethernet Adapter */
.driver_info = (unsigned long)&dm9601_info,
},
- {
- USB_DEVICE(0x0fe6, 0x9700), /* DM9601 USB to Fast Ethernet Adapter */
- .driver_info = (unsigned long)&dm9601_info,
- },
{
USB_DEVICE(0x0a46, 0x9000), /* DM9000E */
.driver_info = (unsigned long)&dm9601_info,
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 9ac9fbdad5c0..4a83228a2db5 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1745,9 +1745,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
if ((dev->driver_info->flags & FLAG_NOARP) != 0)
net->flags |= IFF_NOARP;
- /* maybe the remote can't receive an Ethernet MTU */
- if (net->mtu > (dev->hard_mtu - net->hard_header_len))
- net->mtu = dev->hard_mtu - net->hard_header_len;
+ if (net->max_mtu > (dev->hard_mtu - net->hard_header_len))
+ net->max_mtu = dev->hard_mtu - net->hard_header_len;
+
+ if (net->mtu > net->max_mtu)
+ net->mtu = net->max_mtu;
+
} else if (!info->in || !info->out)
status = usbnet_get_endpoints (dev, udev);
else {
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index c45c814fd122..000f199fc68b 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -1791,8 +1791,8 @@ static void _ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
(ce_state->src_ring->nentries *
sizeof(struct ce_desc) +
CE_DESC_RING_ALIGN),
- ce_state->src_ring->base_addr_owner_space,
- ce_state->src_ring->base_addr_ce_space);
+ ce_state->src_ring->base_addr_owner_space_unaligned,
+ ce_state->src_ring->base_addr_ce_space_unaligned);
kfree(ce_state->src_ring);
}
@@ -1801,8 +1801,8 @@ static void _ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
(ce_state->dest_ring->nentries *
sizeof(struct ce_desc) +
CE_DESC_RING_ALIGN),
- ce_state->dest_ring->base_addr_owner_space,
- ce_state->dest_ring->base_addr_ce_space);
+ ce_state->dest_ring->base_addr_owner_space_unaligned,
+ ce_state->dest_ring->base_addr_ce_space_unaligned);
kfree(ce_state->dest_ring);
}
@@ -1822,8 +1822,8 @@ static void _ath10k_ce_free_pipe_64(struct ath10k *ar, int ce_id)
(ce_state->src_ring->nentries *
sizeof(struct ce_desc_64) +
CE_DESC_RING_ALIGN),
- ce_state->src_ring->base_addr_owner_space,
- ce_state->src_ring->base_addr_ce_space);
+ ce_state->src_ring->base_addr_owner_space_unaligned,
+ ce_state->src_ring->base_addr_ce_space_unaligned);
kfree(ce_state->src_ring);
}
@@ -1832,8 +1832,8 @@ static void _ath10k_ce_free_pipe_64(struct ath10k *ar, int ce_id)
(ce_state->dest_ring->nentries *
sizeof(struct ce_desc_64) +
CE_DESC_RING_ALIGN),
- ce_state->dest_ring->base_addr_owner_space,
- ce_state->dest_ring->base_addr_ce_space);
+ ce_state->dest_ring->base_addr_owner_space_unaligned,
+ ce_state->dest_ring->base_addr_ce_space_unaligned);
kfree(ce_state->dest_ring);
}
diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
index cbe4a200e4ea..c053a5419e19 100644
--- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
@@ -839,7 +839,7 @@ void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter,
bool coex_flag)
{
- u8 i;
+ u8 i, j;
u32 rx_win_size;
struct mwifiex_private *priv;
@@ -879,8 +879,8 @@ static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter,
if (rx_win_size != priv->add_ba_param.rx_win_size) {
if (!priv->media_connected)
continue;
- for (i = 0; i < MAX_NUM_TID; i++)
- mwifiex_11n_delba(priv, i);
+ for (j = 0; j < MAX_NUM_TID; j++)
+ mwifiex_11n_delba(priv, j);
}
}
}
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index 8abf9f699db1..dc3f5222f4b5 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -2005,6 +2005,7 @@ int rsi_mac80211_attach(struct rsi_common *common)
hw->queues = MAX_HW_QUEUES;
hw->extra_tx_headroom = RSI_NEEDED_HEADROOM;
+ hw->vif_data_size = sizeof(struct vif_priv);
hw->max_rates = 1;
hw->max_rate_tries = MAX_RETRIES;
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index 7015fba2e512..e3a22100003f 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -253,6 +253,21 @@ int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val)
}
EXPORT_SYMBOL_GPL(nvmf_reg_write32);
+int nvmf_subsystem_reset(struct nvme_ctrl *ctrl)
+{
+ int ret;
+
+ if (!nvme_wait_reset(ctrl))
+ return -EBUSY;
+
+ ret = ctrl->ops->reg_write32(ctrl, NVME_REG_NSSR, NVME_SUBSYS_RESET);
+ if (ret)
+ return ret;
+
+ return nvme_try_sched_reset(ctrl);
+}
+EXPORT_SYMBOL_GPL(nvmf_subsystem_reset);
+
/**
* nvmf_log_connect_error() - Error-parsing-diagnostic print
* out function for connect() errors.
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index 78467cb3f343..fc2471c7df66 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -166,6 +166,7 @@ nvmf_ctlr_matches_baseopts(struct nvme_ctrl *ctrl,
int nvmf_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val);
int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val);
int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val);
+int nvmf_subsystem_reset(struct nvme_ctrl *ctrl);
int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl);
int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid, bool poll);
int nvmf_register_transport(struct nvmf_transport_ops *ops);
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index dbc9173ec0f8..db905c30fbc6 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2409,7 +2409,7 @@ nvme_fc_ctrl_get(struct nvme_fc_ctrl *ctrl)
* controller. Called after last nvme_put_ctrl() call
*/
static void
-nvme_fc_nvme_ctrl_freed(struct nvme_ctrl *nctrl)
+nvme_fc_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
@@ -3350,7 +3350,8 @@ static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = {
.reg_read32 = nvmf_reg_read32,
.reg_read64 = nvmf_reg_read64,
.reg_write32 = nvmf_reg_write32,
- .free_ctrl = nvme_fc_nvme_ctrl_freed,
+ .subsystem_reset = nvmf_subsystem_reset,
+ .free_ctrl = nvme_fc_free_ctrl,
.submit_async_event = nvme_fc_submit_async_event,
.delete_ctrl = nvme_fc_delete_ctrl,
.get_address = nvmf_get_address,
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 1e56fe8e8157..8dfa293ccedf 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -485,6 +485,7 @@ struct nvme_ctrl_ops {
int (*reg_read64)(struct nvme_ctrl *ctrl, u32 off, u64 *val);
void (*free_ctrl)(struct nvme_ctrl *ctrl);
void (*submit_async_event)(struct nvme_ctrl *ctrl);
+ int (*subsystem_reset)(struct nvme_ctrl *ctrl);
void (*delete_ctrl)(struct nvme_ctrl *ctrl);
void (*stop_ctrl)(struct nvme_ctrl *ctrl);
int (*get_address)(struct nvme_ctrl *ctrl, char *buf, int size);
@@ -554,18 +555,9 @@ int nvme_try_sched_reset(struct nvme_ctrl *ctrl);
static inline int nvme_reset_subsystem(struct nvme_ctrl *ctrl)
{
- int ret;
-
- if (!ctrl->subsystem)
+ if (!ctrl->subsystem || !ctrl->ops->subsystem_reset)
return -ENOTTY;
- if (!nvme_wait_reset(ctrl))
- return -EBUSY;
-
- ret = ctrl->ops->reg_write32(ctrl, NVME_REG_NSSR, 0x4E564D65);
- if (ret)
- return ret;
-
- return nvme_try_sched_reset(ctrl);
+ return ctrl->ops->subsystem_reset(ctrl);
}
/*
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index a56baaafe79d..7a6827306e74 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1123,6 +1123,44 @@ static void nvme_pci_submit_async_event(struct nvme_ctrl *ctrl)
spin_unlock(&nvmeq->sq_lock);
}
+static int nvme_pci_subsystem_reset(struct nvme_ctrl *ctrl)
+{
+ struct nvme_dev *dev = to_nvme_dev(ctrl);
+ int ret = 0;
+
+ /*
+ * Taking the shutdown_lock ensures the BAR mapping is not being
+ * altered by reset_work. Holding this lock before the RESETTING state
+ * change, if successful, also ensures nvme_remove won't be able to
+ * proceed to iounmap until we're done.
+ */
+ mutex_lock(&dev->shutdown_lock);
+ if (!dev->bar_mapped_size) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ writel(NVME_SUBSYS_RESET, dev->bar + NVME_REG_NSSR);
+
+ if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_CONNECTING) ||
+ !nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE))
+ goto unlock;
+
+ /*
+ * Read controller status to flush the previous write and trigger a
+ * pcie read error.
+ */
+ readl(dev->bar + NVME_REG_CSTS);
+unlock:
+ mutex_unlock(&dev->shutdown_lock);
+ return ret;
+}
+
static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
{
struct nvme_command c;
@@ -2844,6 +2882,7 @@ static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.reg_read64 = nvme_pci_reg_read64,
.free_ctrl = nvme_pci_free_ctrl,
.submit_async_event = nvme_pci_submit_async_event,
+ .subsystem_reset = nvme_pci_subsystem_reset,
.get_address = nvme_pci_get_address,
};
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index ecc3f822df24..824d4c088646 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -2293,6 +2293,7 @@ static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = {
.reg_read32 = nvmf_reg_read32,
.reg_read64 = nvmf_reg_read64,
.reg_write32 = nvmf_reg_write32,
+ .subsystem_reset = nvmf_subsystem_reset,
.free_ctrl = nvme_rdma_free_ctrl,
.submit_async_event = nvme_rdma_submit_async_event,
.delete_ctrl = nvme_rdma_delete_ctrl,
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 7709a604d0be..26ede87e95cd 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -2530,6 +2530,7 @@ static const struct nvme_ctrl_ops nvme_tcp_ctrl_ops = {
.reg_read32 = nvmf_reg_read32,
.reg_read64 = nvmf_reg_read64,
.reg_write32 = nvmf_reg_write32,
+ .subsystem_reset = nvmf_subsystem_reset,
.free_ctrl = nvme_tcp_free_ctrl,
.submit_async_event = nvme_tcp_submit_async_event,
.delete_ctrl = nvme_tcp_delete_ctrl,
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index 6019eee1f9bd..94ed4b5b725c 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -933,8 +933,7 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
if (unlikely(data->ttag >= queue->nr_cmds)) {
pr_err("queue %d: received out of bound ttag %u, nr_cmds %u\n",
queue->idx, data->ttag, queue->nr_cmds);
- nvmet_tcp_fatal_error(queue);
- return -EPROTO;
+ goto err_proto;
}
cmd = &queue->cmds[data->ttag];
} else {
@@ -945,9 +944,7 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
pr_err("ttag %u unexpected data offset %u (expected %u)\n",
data->ttag, le32_to_cpu(data->data_offset),
cmd->rbytes_done);
- /* FIXME: use path and transport errors */
- nvmet_tcp_fatal_error(queue);
- return -EPROTO;
+ goto err_proto;
}
exp_data_len = le32_to_cpu(data->hdr.plen) -
@@ -960,9 +957,19 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
cmd->pdu_len == 0 ||
cmd->pdu_len > NVMET_TCP_MAXH2CDATA)) {
pr_err("H2CData PDU len %u is invalid\n", cmd->pdu_len);
- /* FIXME: use proper transport errors */
- nvmet_tcp_fatal_error(queue);
- return -EPROTO;
+ goto err_proto;
+ }
+ /*
+ * Ensure command data structures are initialized. We must check both
+ * cmd->req.sg and cmd->iov because they can have different NULL states:
+ * - Uninitialized commands: both NULL
+ * - READ commands: cmd->req.sg allocated, cmd->iov NULL
+ * - WRITE commands: both allocated
+ */
+ if (unlikely(!cmd->req.sg || !cmd->iov)) {
+ pr_err("queue %d: H2CData PDU received for invalid command state (ttag %u)\n",
+ queue->idx, data->ttag);
+ goto err_proto;
}
cmd->pdu_recv = 0;
nvmet_tcp_map_pdu_iovec(cmd);
@@ -970,6 +977,11 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
queue->rcv_state = NVMET_TCP_RECV_DATA;
return 0;
+
+err_proto:
+ /* FIXME: use proper transport errors */
+ nvmet_tcp_fatal_error(queue);
+ return -EPROTO;
}
static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 1029173ded4d..d5c2e56b226f 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2018,13 +2018,17 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
end--;
len = end - start;
- if (kstrtoint(end, 10, &id) < 0)
+ if (kstrtoint(end, 10, &id) < 0) {
+ of_node_put(np);
continue;
+ }
/* Allocate an alias_prop with enough space for the stem */
ap = dt_alloc(sizeof(*ap) + len + 1, __alignof__(*ap));
- if (!ap)
+ if (!ap) {
+ of_node_put(np);
continue;
+ }
memset(ap, 0, sizeof(*ap) + len + 1);
ap->alias = start;
of_alias_add(ap, np, id, start, len);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 43748c6480c8..9ad6224afa9d 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -533,7 +533,7 @@ static int __init of_platform_default_populate_init(void)
node = of_find_node_by_path("/firmware");
if (node) {
- of_platform_populate(node, NULL, NULL, NULL);
+ of_platform_default_populate(node, NULL, NULL);
of_node_put(node);
}
diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c
index 45b366855e80..e04a42fe6b65 100644
--- a/drivers/phy/broadcom/phy-bcm-ns-usb3.c
+++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c
@@ -203,7 +203,7 @@ static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev)
usb3->dev = dev;
usb3->mdiodev = mdiodev;
- usb3->family = (enum bcm_ns_family)device_get_match_data(dev);
+ usb3->family = (unsigned long)device_get_match_data(dev);
syscon_np = of_parse_phandle(dev->of_node, "usb3-dmp-syscon", 0);
err = of_address_to_resource(syscon_np, 0, &res);
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index cab6a94bf161..93e587f181bd 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -689,17 +689,20 @@ static void rockchip_chg_detect_work(struct work_struct *work)
container_of(work, struct rockchip_usb2phy_port, chg_work.work);
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
struct regmap *base = get_reg_base(rphy);
- bool is_dcd, tmout, vout;
+ bool is_dcd, tmout, vout, vbus_attach;
unsigned long delay;
+ vbus_attach = property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid);
+
dev_dbg(&rport->phy->dev, "chg detection work state = %d\n",
rphy->chg_state);
switch (rphy->chg_state) {
case USB_CHG_STATE_UNDEFINED:
- if (!rport->suspended)
+ if (!rport->suspended && !vbus_attach)
rockchip_usb2phy_power_off(rport->phy);
/* put the controller in non-driving mode */
- property_enable(base, &rphy->phy_cfg->chg_det.opmode, false);
+ if (!vbus_attach)
+ property_enable(base, &rphy->phy_cfg->chg_det.opmode, false);
/* Start DCD processing stage 1 */
rockchip_chg_enable_dcd(rphy, true);
rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
@@ -763,7 +766,8 @@ static void rockchip_chg_detect_work(struct work_struct *work)
fallthrough;
case USB_CHG_STATE_DETECTED:
/* put the controller in normal mode */
- property_enable(base, &rphy->phy_cfg->chg_det.opmode, true);
+ if (!vbus_attach)
+ property_enable(base, &rphy->phy_cfg->chg_det.opmode, true);
rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
dev_dbg(&rport->phy->dev, "charger = %s\n",
chg_to_string(rphy->chg_type));
diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c
index 03fc567e9f18..9b1b6e9d819c 100644
--- a/drivers/phy/st/phy-stm32-usbphyc.c
+++ b/drivers/phy/st/phy-stm32-usbphyc.c
@@ -391,7 +391,7 @@ static int stm32_usbphyc_probe(struct platform_device *pdev)
}
ret = of_property_read_u32(child, "reg", &index);
- if (ret || index > usbphyc->nphys) {
+ if (ret || index >= usbphyc->nphys) {
dev_err(&phy->dev, "invalid reg property: %d\n", ret);
if (!ret)
ret = -EINVAL;
diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c
index b3fd0370cd0f..d55ba7699334 100644
--- a/drivers/phy/tegra/xusb-tegra186.c
+++ b/drivers/phy/tegra/xusb-tegra186.c
@@ -84,6 +84,7 @@
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
#define BIAS_PAD_PD BIT(11)
#define HS_SQUELCH_LEVEL(x) (((x) & 0x7) << 0)
+#define HS_DISCON_LEVEL(x) (((x) & 0x7) << 3)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1 0x288
#define USB2_TRK_START_TIMER(x) (((x) & 0x7f) << 12)
@@ -214,6 +215,8 @@ static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
value &= ~BIAS_PAD_PD;
value &= ~HS_SQUELCH_LEVEL(~0);
value |= HS_SQUELCH_LEVEL(priv->calib.hs_squelch);
+ value &= ~HS_DISCON_LEVEL(~0);
+ value |= HS_DISCON_LEVEL(0x7);
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
udelay(1);
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index ae72edba8a1f..292e44c20dc0 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -614,7 +614,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
pc->chip.set = meson_gpio_set;
pc->chip.base = -1;
pc->chip.ngpio = pc->data->num_pins;
- pc->chip.can_sleep = false;
+ pc->chip.can_sleep = true;
pc->chip.of_node = pc->of_node;
pc->chip.of_gpio_n_cells = 2;
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index 8eb902fe73a9..1d6ce1c6c877 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -102,14 +102,16 @@ int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
return 0;
}
-int ptp_open(struct posix_clock *pc, fmode_t fmode)
+int ptp_open(struct posix_clock_context *pccontext, fmode_t fmode)
{
return 0;
}
-long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
+long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
+ unsigned long arg)
{
- struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ struct ptp_clock *ptp =
+ container_of(pccontext->clk, struct ptp_clock, clock);
struct ptp_sys_offset_extended *extoff = NULL;
struct ptp_sys_offset_precise precise_offset;
struct system_device_crosststamp xtstamp;
@@ -144,6 +146,10 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
case PTP_EXTTS_REQUEST:
case PTP_EXTTS_REQUEST2:
+ if ((pccontext->fp->f_mode & FMODE_WRITE) == 0) {
+ err = -EACCES;
+ break;
+ }
memset(&req, 0, sizeof(req));
if (copy_from_user(&req.extts, (void __user *)arg,
@@ -185,6 +191,10 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
case PTP_PEROUT_REQUEST:
case PTP_PEROUT_REQUEST2:
+ if ((pccontext->fp->f_mode & FMODE_WRITE) == 0) {
+ err = -EACCES;
+ break;
+ }
memset(&req, 0, sizeof(req));
if (copy_from_user(&req.perout, (void __user *)arg,
@@ -253,6 +263,10 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
case PTP_ENABLE_PPS:
case PTP_ENABLE_PPS2:
+ if ((pccontext->fp->f_mode & FMODE_WRITE) == 0) {
+ err = -EACCES;
+ break;
+ }
memset(&req, 0, sizeof(req));
if (!capable(CAP_SYS_TIME))
@@ -391,6 +405,10 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
case PTP_PIN_SETFUNC:
case PTP_PIN_SETFUNC2:
+ if ((pccontext->fp->f_mode & FMODE_WRITE) == 0) {
+ err = -EACCES;
+ break;
+ }
if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
err = -EFAULT;
break;
@@ -430,9 +448,11 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
return err;
}
-__poll_t ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
+__poll_t ptp_poll(struct posix_clock_context *pccontext, struct file *fp,
+ poll_table *wait)
{
- struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ struct ptp_clock *ptp =
+ container_of(pccontext->clk, struct ptp_clock, clock);
poll_wait(fp, &ptp->tsev_wq, wait);
@@ -441,10 +461,11 @@ __poll_t ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
#define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event))
-ssize_t ptp_read(struct posix_clock *pc,
- uint rdflags, char __user *buf, size_t cnt)
+ssize_t ptp_read(struct posix_clock_context *pccontext, uint rdflags,
+ char __user *buf, size_t cnt)
{
- struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ struct ptp_clock *ptp =
+ container_of(pccontext->clk, struct ptp_clock, clock);
struct timestamp_event_queue *queue = &ptp->tsevq;
struct ptp_extts_event *event;
unsigned long flags;
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index d2cb95670676..ba3a1a29a288 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -73,16 +73,18 @@ static inline int queue_cnt(const struct timestamp_event_queue *q)
int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
enum ptp_pin_function func, unsigned int chan);
-long ptp_ioctl(struct posix_clock *pc,
- unsigned int cmd, unsigned long arg);
+long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
+ unsigned long arg);
-int ptp_open(struct posix_clock *pc, fmode_t fmode);
+int ptp_open(struct posix_clock_context *pccontext, fmode_t fmode);
-ssize_t ptp_read(struct posix_clock *pc,
- uint flags, char __user *buf, size_t cnt);
+int ptp_release(struct posix_clock_context *pccontext);
-__poll_t ptp_poll(struct posix_clock *pc,
- struct file *fp, poll_table *wait);
+ssize_t ptp_read(struct posix_clock_context *pccontext, uint flags, char __user *buf,
+ size_t cnt);
+
+__poll_t ptp_poll(struct posix_clock_context *pccontext, struct file *fp,
+ poll_table *wait);
/*
* see ptp_sysfs.c
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 96d6e384b2b2..17ebc6358357 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1019,6 +1019,7 @@ unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba)
&nonemb_cmd->dma,
GFP_KERNEL);
if (!nonemb_cmd->va) {
+ free_mcc_wrb(ctrl, tag);
mutex_unlock(&ctrl->mbox_lock);
return 0;
}
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index ffc6f3031e82..4e9114f06983 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -241,11 +241,20 @@ static void scsi_eh_inc_host_failed(struct rcu_head *head)
{
struct scsi_cmnd *scmd = container_of(head, typeof(*scmd), rcu);
struct Scsi_Host *shost = scmd->device->host;
- unsigned int busy = scsi_host_busy(shost);
+ unsigned int busy;
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
shost->host_failed++;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ /*
+ * The counting of busy requests needs to occur after adding to
+ * host_failed or after the lock acquire for adding to host_failed
+ * to prevent a race with host unbusy and missing an eh wakeup.
+ */
+ busy = scsi_host_busy(shost);
+
+ spin_lock_irqsave(shost->host_lock, flags);
scsi_eh_wakeup(shost, busy);
spin_unlock_irqrestore(shost->host_lock, flags);
}
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index fb48d47e9183..8d570632982f 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -310,6 +310,14 @@ static void scsi_dec_host_busy(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
rcu_read_lock();
__clear_bit(SCMD_STATE_INFLIGHT, &cmd->state);
if (unlikely(scsi_host_in_recovery(shost))) {
+ /*
+ * Ensure the clear of SCMD_STATE_INFLIGHT is visible to
+ * other CPUs before counting busy requests. Otherwise,
+ * reordering can cause CPUs to race and miss an eh wakeup
+ * when no CPU sees all busy requests as done or timed out.
+ */
+ smp_mb();
+
unsigned int busy = scsi_host_busy(shost);
spin_lock_irqsave(shost->host_lock, flags);
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 8c98dc672ca8..917ba169d418 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1154,7 +1154,7 @@ static void storvsc_on_io_completion(struct storvsc_device *stor_device,
* The current SCSI handling on the host side does
* not correctly handle:
* INQUIRY command with page code parameter set to 0x80
- * MODE_SENSE command with cmd[2] == 0x1c
+ * MODE_SENSE and MODE_SENSE_10 command with cmd[2] == 0x1c
* MAINTENANCE_IN is not supported by HyperV FC passthrough
*
* Setup srb and scsi status so this won't be fatal.
@@ -1164,6 +1164,7 @@ static void storvsc_on_io_completion(struct storvsc_device *stor_device,
if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) ||
(stor_pkt->vm_srb.cdb[0] == MODE_SENSE) ||
+ (stor_pkt->vm_srb.cdb[0] == MODE_SENSE_10) ||
(stor_pkt->vm_srb.cdb[0] == MAINTENANCE_IN &&
hv_dev_is_fc(device))) {
vstor_packet->vm_srb.scsi_status = 0;
diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c
index 69f6178f294c..bf2547df228a 100644
--- a/drivers/slimbus/core.c
+++ b/drivers/slimbus/core.c
@@ -380,6 +380,8 @@ struct slim_device *slim_get_device(struct slim_controller *ctrl,
sbdev = slim_alloc_device(ctrl, e_addr, NULL);
if (!sbdev)
return ERR_PTR(-ENOMEM);
+
+ get_device(&sbdev->dev);
}
return sbdev;
@@ -498,21 +500,24 @@ int slim_device_report_present(struct slim_controller *ctrl,
if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
dev_err(ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n",
ctrl->sched.clk_state, ret);
- goto slimbus_not_active;
+ goto out_put_rpm;
}
sbdev = slim_get_device(ctrl, e_addr);
- if (IS_ERR(sbdev))
- return -ENODEV;
+ if (IS_ERR(sbdev)) {
+ ret = -ENODEV;
+ goto out_put_rpm;
+ }
if (sbdev->is_laddr_valid) {
*laddr = sbdev->laddr;
- return 0;
+ ret = 0;
+ } else {
+ ret = slim_device_alloc_laddr(sbdev, true);
}
- ret = slim_device_alloc_laddr(sbdev, true);
-
-slimbus_not_active:
+ put_device(&sbdev->dev);
+out_put_rpm:
pm_runtime_mark_last_busy(ctrl->dev);
pm_runtime_put_autosuspend(ctrl->dev);
return ret;
diff --git a/drivers/staging/comedi/comedi.h b/drivers/staging/comedi/comedi.h
index b5d00a006dbb..a4e2a61e7cb0 100644
--- a/drivers/staging/comedi/comedi.h
+++ b/drivers/staging/comedi/comedi.h
@@ -640,7 +640,7 @@ struct comedi_chaninfo {
/**
* struct comedi_rangeinfo - used to retrieve the range table for a channel
- * @range_type: Encodes subdevice index (bits 27:24), channel index
+ * @range_type: Encodes subdevice index (bits 31:24), channel index
* (bits 23:16) and range table length (bits 15:0).
* @range_ptr: Pointer to array of @struct comedi_krange to be filled
* in with the range table for the channel or subdevice.
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index de89922e7397..1c36d34f9e33 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -1095,7 +1095,7 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
for (i = 0; i < s->n_chan; i++) {
int x;
- x = (dev->minor << 28) | (it->subdev << 24) | (i << 16) |
+ x = (it->subdev << 24) | (i << 16) |
(s->range_table_list[i]->length);
if (put_user(x, it->rangelist + i))
return -EFAULT;
diff --git a/drivers/staging/comedi/drivers/dmm32at.c b/drivers/staging/comedi/drivers/dmm32at.c
index 56682f01242f..85a0f58c134f 100644
--- a/drivers/staging/comedi/drivers/dmm32at.c
+++ b/drivers/staging/comedi/drivers/dmm32at.c
@@ -331,6 +331,7 @@ static int dmm32at_ai_cmdtest(struct comedi_device *dev,
static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
{
+ unsigned long irq_flags;
unsigned char lo1, lo2, hi2;
unsigned short both2;
@@ -343,6 +344,9 @@ static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
/* set counter clocks to 10MHz, disable all aux dio */
outb(0, dev->iobase + DMM32AT_CTRDIO_CFG_REG);
+ /* serialize access to control register and paged registers */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+
/* get access to the clock regs */
outb(DMM32AT_CTRL_PAGE_8254, dev->iobase + DMM32AT_CTRL_REG);
@@ -355,6 +359,8 @@ static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
outb(lo2, dev->iobase + DMM32AT_CLK2);
outb(hi2, dev->iobase + DMM32AT_CLK2);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
/* enable the ai conversion interrupt and the clock to start scans */
outb(DMM32AT_INTCLK_ADINT |
DMM32AT_INTCLK_CLKEN | DMM32AT_INTCLK_CLKSEL,
@@ -364,13 +370,19 @@ static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long irq_flags;
int ret;
dmm32at_ai_set_chanspec(dev, s, cmd->chanlist[0], cmd->chanlist_len);
+ /* serialize access to control register and paged registers */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+
/* reset the interrupt just in case */
outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
/*
* wait for circuit to settle
* we don't have the 'insn' here but it's not needed
@@ -430,8 +442,13 @@ static irqreturn_t dmm32at_isr(int irq, void *d)
comedi_handle_events(dev, s);
}
+ /* serialize access to control register and paged registers */
+ spin_lock(&dev->spinlock);
+
/* reset the interrupt */
outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
+
+ spin_unlock(&dev->spinlock);
return IRQ_HANDLED;
}
@@ -482,14 +499,25 @@ static int dmm32at_ao_insn_write(struct comedi_device *dev,
static int dmm32at_8255_io(struct comedi_device *dev,
int dir, int port, int data, unsigned long regbase)
{
+ unsigned long irq_flags;
+ int ret;
+
+ /* serialize access to control register and paged registers */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+
/* get access to the DIO regs */
outb(DMM32AT_CTRL_PAGE_8255, dev->iobase + DMM32AT_CTRL_REG);
if (dir) {
outb(data, dev->iobase + regbase + port);
- return 0;
+ ret = 0;
+ } else {
+ ret = inb(dev->iobase + regbase + port);
}
- return inb(dev->iobase + regbase + port);
+
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return ret;
}
/* Make sure the board is there and put it to a known state */
diff --git a/drivers/staging/comedi/range.c b/drivers/staging/comedi/range.c
index a4e6fe0fb729..1f38c896ed5b 100644
--- a/drivers/staging/comedi/range.c
+++ b/drivers/staging/comedi/range.c
@@ -52,7 +52,7 @@ int do_rangeinfo_ioctl(struct comedi_device *dev,
const struct comedi_lrange *lr;
struct comedi_subdevice *s;
- subd = (it->range_type >> 24) & 0xf;
+ subd = (it->range_type >> 24) & 0xff;
chan = (it->range_type >> 16) & 0xff;
if (!dev->attached)
diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c
index 20183b2ea127..cc66c3d7141a 100644
--- a/drivers/staging/iio/adc/ad7280a.c
+++ b/drivers/staging/iio/adc/ad7280a.c
@@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
+#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -23,78 +24,86 @@
#include "ad7280a.h"
/* Registers */
-#define AD7280A_CELL_VOLTAGE_1 0x0 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_2 0x1 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_3 0x2 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_4 0x3 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_5 0x4 /* D11 to D0, Read only */
-#define AD7280A_CELL_VOLTAGE_6 0x5 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_1 0x6 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_2 0x7 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_3 0x8 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_4 0x9 /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_5 0xA /* D11 to D0, Read only */
-#define AD7280A_AUX_ADC_6 0xB /* D11 to D0, Read only */
-#define AD7280A_SELF_TEST 0xC /* D11 to D0, Read only */
-#define AD7280A_CONTROL_HB 0xD /* D15 to D8, Read/write */
-#define AD7280A_CONTROL_LB 0xE /* D7 to D0, Read/write */
-#define AD7280A_CELL_OVERVOLTAGE 0xF /* D7 to D0, Read/write */
-#define AD7280A_CELL_UNDERVOLTAGE 0x10 /* D7 to D0, Read/write */
-#define AD7280A_AUX_ADC_OVERVOLTAGE 0x11 /* D7 to D0, Read/write */
-#define AD7280A_AUX_ADC_UNDERVOLTAGE 0x12 /* D7 to D0, Read/write */
-#define AD7280A_ALERT 0x13 /* D7 to D0, Read/write */
-#define AD7280A_CELL_BALANCE 0x14 /* D7 to D0, Read/write */
-#define AD7280A_CB1_TIMER 0x15 /* D7 to D0, Read/write */
-#define AD7280A_CB2_TIMER 0x16 /* D7 to D0, Read/write */
-#define AD7280A_CB3_TIMER 0x17 /* D7 to D0, Read/write */
-#define AD7280A_CB4_TIMER 0x18 /* D7 to D0, Read/write */
-#define AD7280A_CB5_TIMER 0x19 /* D7 to D0, Read/write */
-#define AD7280A_CB6_TIMER 0x1A /* D7 to D0, Read/write */
-#define AD7280A_PD_TIMER 0x1B /* D7 to D0, Read/write */
-#define AD7280A_READ 0x1C /* D7 to D0, Read/write */
-#define AD7280A_CNVST_CONTROL 0x1D /* D7 to D0, Read/write */
-
-/* Bits and Masks */
-#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0
-#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_4 BIT(6)
-#define AD7280A_CTRL_HB_CONV_INPUT_6CELL BIT(7)
-#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST (BIT(7) | BIT(6))
-#define AD7280A_CTRL_HB_CONV_RES_READ_ALL 0
-#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL_AUX1_3_4 BIT(4)
-#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL BIT(5)
-#define AD7280A_CTRL_HB_CONV_RES_READ_NO (BIT(5) | BIT(4))
-#define AD7280A_CTRL_HB_CONV_START_CNVST 0
-#define AD7280A_CTRL_HB_CONV_START_CS BIT(3)
-#define AD7280A_CTRL_HB_CONV_AVG_DIS 0
-#define AD7280A_CTRL_HB_CONV_AVG_2 BIT(1)
-#define AD7280A_CTRL_HB_CONV_AVG_4 BIT(2)
-#define AD7280A_CTRL_HB_CONV_AVG_8 (BIT(2) | BIT(1))
-#define AD7280A_CTRL_HB_CONV_AVG(x) ((x) << 1)
-#define AD7280A_CTRL_HB_PWRDN_SW BIT(0)
-
-#define AD7280A_CTRL_LB_SWRST BIT(7)
-#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0
-#define AD7280A_CTRL_LB_ACQ_TIME_800ns BIT(5)
-#define AD7280A_CTRL_LB_ACQ_TIME_1200ns BIT(6)
-#define AD7280A_CTRL_LB_ACQ_TIME_1600ns (BIT(6) | BIT(5))
-#define AD7280A_CTRL_LB_ACQ_TIME(x) ((x) << 5)
-#define AD7280A_CTRL_LB_MUST_SET BIT(4)
-#define AD7280A_CTRL_LB_THERMISTOR_EN BIT(3)
-#define AD7280A_CTRL_LB_LOCK_DEV_ADDR BIT(2)
-#define AD7280A_CTRL_LB_INC_DEV_ADDR BIT(1)
-#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN BIT(0)
-
-#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6)
-#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6))
+#define AD7280A_CELL_VOLTAGE_1_REG 0x0 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_2_REG 0x1 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_3_REG 0x2 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_4_REG 0x3 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_5_REG 0x4 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_6_REG 0x5 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_1_REG 0x6 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_2_REG 0x7 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_3_REG 0x8 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_4_REG 0x9 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_5_REG 0xA /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_6_REG 0xB /* D11 to D0, Read only */
+#define AD7280A_SELF_TEST_REG 0xC /* D11 to D0, Read only */
+
+#define AD7280A_CTRL_HB_REG 0xD /* D15 to D8, Read/write */
+#define AD7280A_CTRL_HB_CONV_INPUT_MSK GENMASK(7, 6)
+#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_5 1
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL 2
+#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST 3
+#define AD7280A_CTRL_HB_CONV_RREAD_MSK GENMASK(5, 4)
+#define AD7280A_CTRL_HB_CONV_RREAD_ALL 0
+#define AD7280A_CTRL_HB_CONV_RREAD_6CELL_AUX1_3_5 1
+#define AD7280A_CTRL_HB_CONV_RREAD_6CELL 2
+#define AD7280A_CTRL_HB_CONV_RREAD_NO 3
+#define AD7280A_CTRL_HB_CONV_START_MSK BIT(3)
+#define AD7280A_CTRL_HB_CONV_START_CNVST 0
+#define AD7280A_CTRL_HB_CONV_START_CS 1
+#define AD7280A_CTRL_HB_CONV_AVG_MSK GENMASK(2, 1)
+#define AD7280A_CTRL_HB_CONV_AVG_DIS 0
+#define AD7280A_CTRL_HB_CONV_AVG_2 1
+#define AD7280A_CTRL_HB_CONV_AVG_4 2
+#define AD7280A_CTRL_HB_CONV_AVG_8 3
+#define AD7280A_CTRL_HB_PWRDN_SW BIT(0)
+
+#define AD7280A_CTRL_LB_REG 0xE /* D7 to D0, Read/write */
+#define AD7280A_CTRL_LB_SWRST_MSK BIT(7)
+#define AD7280A_CTRL_LB_ACQ_TIME_MSK GENMASK(6, 5)
+#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0
+#define AD7280A_CTRL_LB_ACQ_TIME_800ns 1
+#define AD7280A_CTRL_LB_ACQ_TIME_1200ns 2
+#define AD7280A_CTRL_LB_ACQ_TIME_1600ns 3
+#define AD7280A_CTRL_LB_MUST_SET BIT(4)
+#define AD7280A_CTRL_LB_THERMISTOR_MSK BIT(3)
+#define AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK BIT(2)
+#define AD7280A_CTRL_LB_INC_DEV_ADDR_MSK BIT(1)
+#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK BIT(0)
+
+#define AD7280A_CELL_OVERVOLTAGE_REG 0xF /* D7 to D0, Read/write */
+#define AD7280A_CELL_UNDERVOLTAGE_REG 0x10 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_OVERVOLTAGE_REG 0x11 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_UNDERVOLTAGE_REG 0x12 /* D7 to D0, Read/write */
+
+#define AD7280A_ALERT_REG 0x13 /* D7 to D0, Read/write */
+#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6)
+#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6))
+
+#define AD7280A_CELL_BALANCE_REG 0x14 /* D7 to D0, Read/write */
+#define AD7280A_CB1_TIMER_REG 0x15 /* D7 to D0, Read/write */
+#define AD7280A_CB_TIMER_VAL_MSK GENMASK(7, 3)
+#define AD7280A_CB2_TIMER_REG 0x16 /* D7 to D0, Read/write */
+#define AD7280A_CB3_TIMER_REG 0x17 /* D7 to D0, Read/write */
+#define AD7280A_CB4_TIMER_REG 0x18 /* D7 to D0, Read/write */
+#define AD7280A_CB5_TIMER_REG 0x19 /* D7 to D0, Read/write */
+#define AD7280A_CB6_TIMER_REG 0x1A /* D7 to D0, Read/write */
+#define AD7280A_PD_TIMER_REG 0x1B /* D7 to D0, Read/write */
+#define AD7280A_READ_REG 0x1C /* D7 to D0, Read/write */
+#define AD7280A_READ_ADDR_MSK GENMASK(7, 2)
+#define AD7280A_CNVST_CTRL_REG 0x1D /* D7 to D0, Read/write */
+
+/* Magic value used to indicate this special case */
#define AD7280A_ALL_CELLS (0xAD << 16)
#define AD7280A_MAX_SPI_CLK_HZ 700000 /* < 1MHz */
#define AD7280A_MAX_CHAIN 8
#define AD7280A_CELLS_PER_DEV 6
#define AD7280A_BITS 12
-#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \
- AD7280A_CELL_VOLTAGE_1 + 1)
+#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6_REG - \
+ AD7280A_CELL_VOLTAGE_1_REG + 1)
#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \
(c))
@@ -222,23 +231,28 @@ static int ad7280_read(struct ad7280_state *st, unsigned int devaddr,
unsigned int tmp;
/* turns off the read operation on all parts */
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_NO |
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_NO) |
st->ctrl_hb);
if (ret)
return ret;
/* turns on the read operation on the addressed part */
- ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_ALL |
+ ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
st->ctrl_hb);
if (ret)
return ret;
/* Set register address on the part to be read from */
- ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2);
+ ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, addr));
if (ret)
return ret;
@@ -261,21 +275,27 @@ static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr,
int ret;
unsigned int tmp;
- ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2);
+ ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, addr));
if (ret)
return ret;
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_NO |
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_NO) |
st->ctrl_hb);
if (ret)
return ret;
- ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_ALL |
- AD7280A_CTRL_HB_CONV_START_CS |
+ ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK,
+ AD7280A_CTRL_HB_CONV_START_CS) |
st->ctrl_hb);
if (ret)
return ret;
@@ -301,15 +321,18 @@ static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt,
int i, ret;
unsigned int tmp, sum = 0;
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1,
- AD7280A_CELL_VOLTAGE_1 << 2);
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1,
+ AD7280A_CELL_VOLTAGE_1_REG << 2);
if (ret)
return ret;
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
- AD7280A_CTRL_HB_CONV_INPUT_ALL |
- AD7280A_CTRL_HB_CONV_RES_READ_ALL |
- AD7280A_CTRL_HB_CONV_START_CS |
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK,
+ AD7280A_CTRL_HB_CONV_START_CS) |
st->ctrl_hb);
if (ret)
return ret;
@@ -327,7 +350,7 @@ static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt,
if (array)
array[i] = tmp;
/* only sum cell voltages */
- if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6)
+ if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6_REG)
sum += ((tmp >> 11) & 0xFFF);
}
@@ -338,7 +361,7 @@ static void ad7280_sw_power_down(void *data)
{
struct ad7280_state *st = data;
- ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
+ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
}
@@ -347,25 +370,26 @@ static int ad7280_chain_setup(struct ad7280_state *st)
unsigned int val, n;
int ret;
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1,
- AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN |
- AD7280A_CTRL_LB_LOCK_DEV_ADDR |
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) |
+ FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) |
AD7280A_CTRL_LB_MUST_SET |
- AD7280A_CTRL_LB_SWRST |
+ FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 1) |
st->ctrl_lb);
if (ret)
return ret;
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1,
- AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN |
- AD7280A_CTRL_LB_LOCK_DEV_ADDR |
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) |
+ FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) |
AD7280A_CTRL_LB_MUST_SET |
+ FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 0) |
st->ctrl_lb);
if (ret)
goto error_power_down;
- ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1,
- AD7280A_CONTROL_LB << 2);
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, AD7280A_CTRL_LB_REG));
if (ret)
goto error_power_down;
@@ -390,7 +414,7 @@ static int ad7280_chain_setup(struct ad7280_state *st)
ret = -EFAULT;
error_power_down:
- ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
+ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
return ret;
@@ -434,7 +458,7 @@ static ssize_t ad7280_store_balance_sw(struct device *dev,
else
st->cb_mask[devaddr] &= ~(1 << (ch + 2));
- ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE,
+ ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE_REG,
0, st->cb_mask[devaddr]);
mutex_unlock(&st->lock);
@@ -459,7 +483,7 @@ static ssize_t ad7280_show_balance_timer(struct device *dev,
if (ret < 0)
return ret;
- msecs = (ret >> 3) * 71500;
+ msecs = FIELD_GET(AD7280A_CB_TIMER_VAL_MSK, ret) * 71500;
return sprintf(buf, "%u\n", msecs);
}
@@ -486,8 +510,8 @@ static ssize_t ad7280_store_balance_timer(struct device *dev,
mutex_lock(&st->lock);
ret = ad7280_write(st, this_attr->address >> 8,
- this_attr->address & 0xFF,
- 0, (val & 0x1F) << 3);
+ this_attr->address & 0xFF, 0,
+ FIELD_PREP(AD7280A_CB_TIMER_VAL_MSK, val));
mutex_unlock(&st->lock);
return ret ? ret : len;
@@ -559,10 +583,10 @@ static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt)
int addr, ch, i;
struct iio_chan_spec *chan;
- for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++) {
+ for (ch = AD7280A_CELL_VOLTAGE_1_REG; ch <= AD7280A_AUX_ADC_6_REG; ch++) {
chan = &st->channels[*cnt];
- if (ch < AD7280A_AUX_ADC_1) {
+ if (ch < AD7280A_AUX_ADC_1_REG) {
i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch);
ad7280_voltage_channel_init(chan, i);
} else {
@@ -634,7 +658,7 @@ static int ad7280_init_dev_attrs(struct ad7280_state *st, int dev, int *cnt)
struct iio_dev_attr *iio_attr;
struct device *sdev = &st->spi->dev;
- for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; ch++) {
+ for (ch = AD7280A_CELL_VOLTAGE_1_REG; ch <= AD7280A_CELL_VOLTAGE_6_REG; ch++) {
iio_attr = &st->iio_attr[*cnt];
addr = ad7280a_devaddr(dev) << 8 | ch;
i = dev * AD7280A_CELLS_PER_DEV + ch;
@@ -647,7 +671,7 @@ static int ad7280_init_dev_attrs(struct ad7280_state *st, int dev, int *cnt)
(*cnt)++;
iio_attr = &st->iio_attr[*cnt];
- addr = ad7280a_devaddr(dev) << 8 | (AD7280A_CB1_TIMER + ch);
+ addr = ad7280a_devaddr(dev) << 8 | (AD7280A_CB1_TIMER_REG + ch);
ret = ad7280_balance_timer_attr_init(iio_attr, sdev, addr, i);
if (ret < 0)
@@ -691,16 +715,16 @@ static ssize_t ad7280_read_channel_config(struct device *dev,
unsigned int val;
switch (this_attr->address) {
- case AD7280A_CELL_OVERVOLTAGE:
+ case AD7280A_CELL_OVERVOLTAGE_REG:
val = 1000 + (st->cell_threshhigh * 1568) / 100;
break;
- case AD7280A_CELL_UNDERVOLTAGE:
+ case AD7280A_CELL_UNDERVOLTAGE_REG:
val = 1000 + (st->cell_threshlow * 1568) / 100;
break;
- case AD7280A_AUX_ADC_OVERVOLTAGE:
+ case AD7280A_AUX_ADC_OVERVOLTAGE_REG:
val = (st->aux_threshhigh * 196) / 10;
break;
- case AD7280A_AUX_ADC_UNDERVOLTAGE:
+ case AD7280A_AUX_ADC_UNDERVOLTAGE_REG:
val = (st->aux_threshlow * 196) / 10;
break;
default:
@@ -727,12 +751,12 @@ static ssize_t ad7280_write_channel_config(struct device *dev,
return ret;
switch (this_attr->address) {
- case AD7280A_CELL_OVERVOLTAGE:
- case AD7280A_CELL_UNDERVOLTAGE:
+ case AD7280A_CELL_OVERVOLTAGE_REG:
+ case AD7280A_CELL_UNDERVOLTAGE_REG:
val = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */
break;
- case AD7280A_AUX_ADC_OVERVOLTAGE:
- case AD7280A_AUX_ADC_UNDERVOLTAGE:
+ case AD7280A_AUX_ADC_OVERVOLTAGE_REG:
+ case AD7280A_AUX_ADC_UNDERVOLTAGE_REG:
val = (val * 10) / 196; /* LSB 19.6mV */
break;
default:
@@ -743,16 +767,16 @@ static ssize_t ad7280_write_channel_config(struct device *dev,
mutex_lock(&st->lock);
switch (this_attr->address) {
- case AD7280A_CELL_OVERVOLTAGE:
+ case AD7280A_CELL_OVERVOLTAGE_REG:
st->cell_threshhigh = val;
break;
- case AD7280A_CELL_UNDERVOLTAGE:
+ case AD7280A_CELL_UNDERVOLTAGE_REG:
st->cell_threshlow = val;
break;
- case AD7280A_AUX_ADC_OVERVOLTAGE:
+ case AD7280A_AUX_ADC_OVERVOLTAGE_REG:
st->aux_threshhigh = val;
break;
- case AD7280A_AUX_ADC_UNDERVOLTAGE:
+ case AD7280A_AUX_ADC_UNDERVOLTAGE_REG:
st->aux_threshlow = val;
break;
}
@@ -781,7 +805,7 @@ static irqreturn_t ad7280_event_handler(int irq, void *private)
goto out;
for (i = 0; i < st->scan_cnt; i++) {
- if (((channels[i] >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) {
+ if (((channels[i] >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6_REG) {
if (((channels[i] >> 11) & 0xFFF) >=
st->cell_threshhigh) {
u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0,
@@ -801,7 +825,7 @@ static irqreturn_t ad7280_event_handler(int irq, void *private)
}
} else {
if (((channels[i] >> 11) & 0xFFF) >=
- st->aux_threshhigh) {
+ st->aux_threshhigh) {
u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING);
@@ -833,26 +857,26 @@ static IIO_DEVICE_ATTR_NAMED(in_thresh_low_value,
0644,
ad7280_read_channel_config,
ad7280_write_channel_config,
- AD7280A_CELL_UNDERVOLTAGE);
+ AD7280A_CELL_UNDERVOLTAGE_REG);
static IIO_DEVICE_ATTR_NAMED(in_thresh_high_value,
in_voltage-voltage_thresh_high_value,
0644,
ad7280_read_channel_config,
ad7280_write_channel_config,
- AD7280A_CELL_OVERVOLTAGE);
+ AD7280A_CELL_OVERVOLTAGE_REG);
static IIO_DEVICE_ATTR(in_temp_thresh_low_value,
0644,
ad7280_read_channel_config,
ad7280_write_channel_config,
- AD7280A_AUX_ADC_UNDERVOLTAGE);
+ AD7280A_AUX_ADC_UNDERVOLTAGE_REG);
static IIO_DEVICE_ATTR(in_temp_thresh_high_value,
0644,
ad7280_read_channel_config,
ad7280_write_channel_config,
- AD7280A_AUX_ADC_OVERVOLTAGE);
+ AD7280A_AUX_ADC_OVERVOLTAGE_REG);
static struct attribute *ad7280_event_attributes[] = {
&iio_dev_attr_in_thresh_low_value.dev_attr.attr,
@@ -892,7 +916,7 @@ static int ad7280_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6)
+ if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6_REG)
*val = 4000;
else
*val = 5000;
@@ -940,12 +964,13 @@ static int ad7280_probe(struct spi_device *spi)
st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ;
st->spi->mode = SPI_MODE_1;
- spi_setup(st->spi);
+ ret = spi_setup(st->spi);
+ if (ret < 0)
+ return ret;
- st->ctrl_lb = AD7280A_CTRL_LB_ACQ_TIME(pdata->acquisition_time & 0x3);
- st->ctrl_hb = AD7280A_CTRL_HB_CONV_AVG(pdata->conversion_averaging
- & 0x3) | (pdata->thermistor_term_en ?
- AD7280A_CTRL_LB_THERMISTOR_EN : 0);
+ st->ctrl_lb = FIELD_PREP(AD7280A_CTRL_LB_ACQ_TIME_MSK, pdata->acquisition_time) |
+ FIELD_PREP(AD7280A_CTRL_LB_THERMISTOR_MSK, pdata->thermistor_term_en);
+ st->ctrl_hb = FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, pdata->conversion_averaging);
ret = ad7280_chain_setup(st);
if (ret < 0)
@@ -998,13 +1023,13 @@ static int ad7280_probe(struct spi_device *spi)
if (spi->irq > 0) {
ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
- AD7280A_ALERT, 1,
+ AD7280A_ALERT_REG, 1,
AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN);
if (ret)
return ret;
ret = ad7280_write(st, ad7280a_devaddr(st->slave_num),
- AD7280A_ALERT, 0,
+ AD7280A_ALERT_REG, 0,
AD7280A_ALERT_GEN_STATIC_HIGH |
(pdata->chain_last_alert_ignore & 0xF));
if (ret)
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index e4a9b9fe3dfb..9445bf3409fd 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -1989,12 +1989,12 @@ static struct se_portal_group *sbp_make_tpg(struct se_wwn *wwn,
container_of(wwn, struct sbp_tport, tport_wwn);
struct sbp_tpg *tpg;
- unsigned long tpgt;
+ u16 tpgt;
int ret;
if (strstr(name, "tpgt_") != name)
return ERR_PTR(-EINVAL);
- if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ if (kstrtou16(name + 5, 10, &tpgt))
return ERR_PTR(-EINVAL);
if (tport->tpg) {
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 118ab7790366..77be06b68091 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -778,6 +778,8 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
dwc->ip = DWC3_GSNPS_ID(reg);
+ if (dwc->ip == DWC4_IP)
+ dwc->ip = DWC32_IP;
/* This should read as U3 followed by revision number */
if (DWC3_IP_IS(DWC3)) {
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4ccbd2dc1bf8..685a7a369ebc 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1142,6 +1142,7 @@ struct dwc3 {
#define DWC3_IP 0x5533
#define DWC31_IP 0x3331
#define DWC32_IP 0x3332
+#define DWC4_IP 0x3430
u32 revision;
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 4a8456f12a73..365021e608d5 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -359,3 +359,4 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Hauke Mehrtens");
MODULE_AUTHOR("Alan Stern");
MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: ehci_platform");
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index c0834bac4c95..5b43dd7b091c 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -190,3 +190,4 @@ static struct platform_driver uhci_platform_driver = {
.of_match_table = platform_uhci_ids,
},
};
+MODULE_SOFTDEP("pre: ehci_platform");
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 17e03baf09a2..5d05fa597569 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -828,6 +828,7 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID, 1) },
{ USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID, 1) },
{ USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID, 1) },
+ { USB_DEVICE(FTDI_VID, FTDI_AXE027_PID) },
{ USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TURTELIZER_PID, 1) },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_SCU18) },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index cebd3a4dbef6..1bbd4d3f1950 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -89,6 +89,8 @@
#define LMI_LM3S_EVAL_BOARD_PID 0xbcd9
#define LMI_LM3S_ICDI_BOARD_PID 0xbcda
+#define FTDI_AXE027_PID 0xBD90 /* PICAXE AXE027 USB download cable */
+
#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */
/* OpenDCC (www.opendcc.de) product id */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index aa43c22dfce6..a89cba300745 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1505,6 +1505,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1231, 0xff), /* Telit LE910Cx (RNDIS) */
.driver_info = NCTRL(2) | RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x1250, 0xff, 0x00, 0x00) }, /* Telit LE910Cx (rmnet) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1252, 0xff) }, /* Telit LE910Cx (MBIM) */
{ USB_DEVICE(TELIT_VENDOR_ID, 0x1260),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, 0x1261),
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 478f6628c3a9..00cd29a3bddc 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -2424,15 +2424,19 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct fbcon_display *p = &fb_display[vc->vc_num];
- int resize;
- char *old_data = NULL;
+ int resize, ret, old_userfont, old_width, old_height, old_charcount;
+ u8 *old_data = vc->vc_font.data;
resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
- if (p->userfont)
- old_data = vc->vc_font.data;
vc->vc_font.data = (void *)(p->fontdata = data);
+ old_userfont = p->userfont;
if ((p->userfont = userfont))
REFCOUNT(data)++;
+
+ old_width = vc->vc_font.width;
+ old_height = vc->vc_font.height;
+ old_charcount = vc->vc_font.charcount;
+
vc->vc_font.width = w;
vc->vc_font.height = h;
vc->vc_font.charcount = charcount;
@@ -2448,16 +2452,34 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
cols /= w;
rows /= h;
- vc_resize(vc, cols, rows);
+ ret = vc_resize(vc, cols, rows);
+ if (ret)
+ goto err_out;
} else if (con_is_visible(vc)
&& vc->vc_mode == KD_TEXT) {
fbcon_clear_margins(vc, 0);
update_screen(vc);
}
- if (old_data && (--REFCOUNT(old_data) == 0))
+ if (old_userfont && (--REFCOUNT(old_data) == 0))
kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
return 0;
+
+err_out:
+ p->fontdata = old_data;
+ vc->vc_font.data = old_data;
+
+ if (userfont) {
+ p->userfont = old_userfont;
+ if (--REFCOUNT(data) == 0)
+ kfree(data - FONT_EXTRA_WORDS * sizeof(int));
+ }
+
+ vc->vc_font.width = old_width;
+ vc->vc_font.height = old_height;
+ vc->vc_font.charcount = old_charcount;
+
+ return ret;
}
/*
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 3888643a22f6..8c97706cfc28 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -1781,62 +1781,43 @@ static ssize_t alarms_store(struct device *device,
struct w1_slave *sl = dev_to_w1_slave(device);
struct therm_info info;
u8 new_config_register[3]; /* array of data to be written */
- int temp, ret;
- char *token = NULL;
- s8 tl, th, tt; /* 1 byte per value + temp ring order */
- char *p_args, *orig;
-
- p_args = orig = kmalloc(size, GFP_KERNEL);
- /* Safe string copys as buf is const */
- if (!p_args) {
- dev_warn(device,
- "%s: error unable to allocate memory %d\n",
- __func__, -ENOMEM);
- return size;
- }
- strcpy(p_args, buf);
-
- /* Split string using space char */
- token = strsep(&p_args, " ");
-
- if (!token) {
- dev_info(device,
- "%s: error parsing args %d\n", __func__, -EINVAL);
- goto free_m;
- }
-
- /* Convert 1st entry to int */
- ret = kstrtoint (token, 10, &temp);
+ long long temp;
+ int ret = 0;
+ s8 tl, th; /* 1 byte per value + temp ring order */
+ const char *p = buf;
+ char *endp;
+
+ temp = simple_strtoll(p, &endp, 10);
+ if (p == endp || *endp != ' ')
+ ret = -EINVAL;
+ else if (temp < INT_MIN || temp > INT_MAX)
+ ret = -ERANGE;
if (ret) {
dev_info(device,
"%s: error parsing args %d\n", __func__, ret);
- goto free_m;
+ return size;
}
tl = int_to_short(temp);
- /* Split string using space char */
- token = strsep(&p_args, " ");
- if (!token) {
- dev_info(device,
- "%s: error parsing args %d\n", __func__, -EINVAL);
- goto free_m;
- }
- /* Convert 2nd entry to int */
- ret = kstrtoint (token, 10, &temp);
+ p = endp + 1;
+ temp = simple_strtoll(p, &endp, 10);
+ if (p == endp)
+ ret = -EINVAL;
+ else if (temp < INT_MIN || temp > INT_MAX)
+ ret = -ERANGE;
if (ret) {
dev_info(device,
"%s: error parsing args %d\n", __func__, ret);
- goto free_m;
+ return size;
}
/* Prepare to cast to short by eliminating out of range values */
th = int_to_short(temp);
/* Reorder if required th and tl */
- if (tl > th) {
- tt = tl; tl = th; th = tt;
- }
+ if (tl > th)
+ swap(tl, th);
/*
* Read the scratchpad to change only the required bits
@@ -1851,7 +1832,7 @@ static ssize_t alarms_store(struct device *device,
dev_info(device,
"%s: error reading from the slave device %d\n",
__func__, ret);
- goto free_m;
+ return size;
}
/* Write data in the device RAM */
@@ -1859,7 +1840,7 @@ static ssize_t alarms_store(struct device *device,
dev_info(device,
"%s: Device not supported by the driver %d\n",
__func__, -ENODEV);
- goto free_m;
+ return size;
}
ret = SLAVE_SPECIFIC_FUNC(sl)->write_data(sl, new_config_register);
@@ -1868,10 +1849,6 @@ static ssize_t alarms_store(struct device *device,
"%s: error writing to the slave device %d\n",
__func__, ret);
-free_m:
- /* free allocated memory */
- kfree(orig);
-
return size;
}
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 1c1a9438f4b6..35ed951aeb71 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -765,8 +765,6 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
if (err < 0) {
dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__,
sl->name);
- dev->slave_count--;
- w1_family_put(sl->family);
atomic_dec(&sl->master->refcnt);
kfree(sl);
return err;
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index 9cd4fe8ce680..fd0ce2f6bbe4 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -1202,6 +1202,7 @@ static int scsiback_remove(struct xenbus_device *dev)
gnttab_page_cache_shrink(&info->free_pages, 0);
dev_set_drvdata(&dev->dev, NULL);
+ kfree(info);
return 0;
}
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index d558f354b8b8..f9b27cf776c9 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -514,13 +514,14 @@ static inline int is_transaction_blocked(struct btrfs_transaction *trans)
* when this is done, it is safe to start a new transaction, but the current
* transaction might not be fully on disk.
*/
-static void wait_current_trans(struct btrfs_fs_info *fs_info)
+static void wait_current_trans(struct btrfs_fs_info *fs_info, unsigned int type)
{
struct btrfs_transaction *cur_trans;
spin_lock(&fs_info->trans_lock);
cur_trans = fs_info->running_transaction;
- if (cur_trans && is_transaction_blocked(cur_trans)) {
+ if (cur_trans && is_transaction_blocked(cur_trans) &&
+ (btrfs_blocked_trans_types[cur_trans->state] & type)) {
refcount_inc(&cur_trans->use_count);
spin_unlock(&fs_info->trans_lock);
@@ -669,12 +670,12 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
sb_start_intwrite(fs_info->sb);
if (may_wait_transaction(fs_info, type))
- wait_current_trans(fs_info);
+ wait_current_trans(fs_info, type);
do {
ret = join_transaction(fs_info, type);
if (ret == -EBUSY) {
- wait_current_trans(fs_info);
+ wait_current_trans(fs_info, type);
if (unlikely(type == TRANS_ATTACH ||
type == TRANS_JOIN_NOSTART))
ret = -ENOENT;
@@ -902,7 +903,7 @@ int btrfs_wait_for_commit(struct btrfs_fs_info *fs_info, u64 transid)
void btrfs_throttle(struct btrfs_fs_info *fs_info)
{
- wait_current_trans(fs_info);
+ wait_current_trans(fs_info, TRANS_START);
}
static int should_end_transaction(struct btrfs_trans_handle *trans)
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 1e0344e24266..bbdacb5b9c0a 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -980,6 +980,7 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode,
ext4_error_inode(ea_inode, __func__, __LINE__, 0,
"EA inode %lu ref wraparound: ref_count=%lld ref_change=%d",
ea_inode->i_ino, ref_count, ref_change);
+ brelse(iloc.bh);
ret = -EFSCORRUPTED;
goto out;
}
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index ba70508b405d..f464c9da7bc6 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -2187,12 +2187,14 @@ static void wakeup_dirtytime_writeback(struct work_struct *w)
wb_wakeup(wb);
}
rcu_read_unlock();
- schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ if (dirtytime_expire_interval)
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
}
static int __init start_dirtytime_writeback(void)
{
- schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ if (dirtytime_expire_interval)
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
return 0;
}
__initcall(start_dirtytime_writeback);
@@ -2203,8 +2205,12 @@ int dirtytime_interval_handler(struct ctl_table *table, int write,
int ret;
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
- if (ret == 0 && write)
- mod_delayed_work(system_wq, &dirtytime_work, 0);
+ if (ret == 0 && write) {
+ if (dirtytime_expire_interval)
+ mod_delayed_work(system_wq, &dirtytime_work, 0);
+ else
+ cancel_delayed_work_sync(&dirtytime_work);
+ }
return ret;
}
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index 11777d33a85e..35cac4d3f2e8 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -103,7 +103,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
sizeof(struct nfs4_ff_ds_version),
gfp_flags);
if (!ds_versions)
- goto out_scratch;
+ goto out_err_drain_dsaddrs;
for (i = 0; i < version_count; i++) {
/* 20 = version(4) + minor_version(4) + rsize(4) + wsize(4) +
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 974e71bc4a3a..7e8ba9698aba 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -777,14 +777,15 @@ xfs_ialloc_ag_alloc(
* invalid inode records, such as records that start at agbno 0
* or extend beyond the AG.
*
- * Set min agbno to the first aligned, non-zero agbno and max to
- * the last aligned agbno that is at least one full chunk from
- * the end of the AG.
+ * Set min agbno to the first chunk aligned, non-zero agbno and
+ * max to one less than the last chunk aligned agbno from the
+ * end of the AG. We subtract 1 from max so that the cluster
+ * allocation alignment takes over and allows allocation within
+ * the last full inode chunk in the AG.
*/
args.min_agbno = args.mp->m_sb.sb_inoalignmt;
args.max_agbno = round_down(args.mp->m_sb.sb_agblocks,
- args.mp->m_sb.sb_inoalignmt) -
- igeo->ialloc_blks;
+ args.mp->m_sb.sb_inoalignmt) - 1;
error = xfs_alloc_vextent(&args);
if (error)
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 88dbb2009080..705d8798bed5 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -1282,7 +1282,10 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_at_120[0xa];
u8 log_max_ra_req_dc[0x6];
- u8 reserved_at_130[0xa];
+ u8 reserved_at_130[0x2];
+ u8 eth_wqe_too_small[0x1];
+ u8 reserved_at_133[0x6];
+ u8 vnic_env_cq_overrun[0x1];
u8 log_max_ra_res_dc[0x6];
u8 reserved_at_140[0x6];
@@ -1472,7 +1475,11 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 nic_receive_steering_discard[0x1];
u8 receive_discard_vport_down[0x1];
u8 transmit_discard_vport_down[0x1];
- u8 reserved_at_343[0x5];
+ u8 eq_overrun_count[0x1];
+ u8 reserved_at_344[0x1];
+ u8 invalid_command_count[0x1];
+ u8 quota_exceeded_count[0x1];
+ u8 reserved_at_347[0x1];
u8 log_max_flow_counter_bulk[0x8];
u8 max_flow_counter_15_0[0x10];
@@ -3128,11 +3135,23 @@ struct mlx5_ifc_vnic_diagnostic_statistics_bits {
u8 transmit_discard_vport_down[0x40];
- u8 reserved_at_140[0xa0];
+ u8 async_eq_overrun[0x20];
+
+ u8 comp_eq_overrun[0x20];
+
+ u8 reserved_at_180[0x20];
+
+ u8 invalid_command[0x20];
+
+ u8 quota_exceeded_command[0x20];
u8 internal_rq_out_of_buffer[0x20];
- u8 reserved_at_200[0xe00];
+ u8 cq_overrun[0x20];
+
+ u8 eth_wqe_too_small[0x20];
+
+ u8 reserved_at_220[0xdc0];
};
struct mlx5_ifc_traffic_counter_bits {
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index ddf9ae37a2cc..5727185ffe42 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -27,6 +27,9 @@
#define NVME_NSID_ALL 0xffffffff
+/* Special NSSR value, 'NVMe' */
+#define NVME_SUBSYS_RESET 0x4E564D65
+
enum nvme_subsys_type {
NVME_NQN_DISC = 1, /* Discovery type target subsystem */
NVME_NQN_NVME = 2, /* NVME type target subsystem */
diff --git a/include/linux/pagewalk.h b/include/linux/pagewalk.h
index b1cb6b753abb..3670b46cedb1 100644
--- a/include/linux/pagewalk.h
+++ b/include/linux/pagewalk.h
@@ -99,6 +99,9 @@ int walk_page_range_novma(struct mm_struct *mm, unsigned long start,
unsigned long end, const struct mm_walk_ops *ops,
pgd_t *pgd,
void *private);
+int walk_page_range_vma(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, const struct mm_walk_ops *ops,
+ void *private);
int walk_page_vma(struct vm_area_struct *vma, const struct mm_walk_ops *ops,
void *private);
int walk_page_mapping(struct address_space *mapping, pgoff_t first_index,
diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
index 468328b1e1dd..a500d3160fe8 100644
--- a/include/linux/posix-clock.h
+++ b/include/linux/posix-clock.h
@@ -14,6 +14,7 @@
#include <linux/rwsem.h>
struct posix_clock;
+struct posix_clock_context;
/**
* struct posix_clock_operations - functional interface to the clock
@@ -50,18 +51,18 @@ struct posix_clock_operations {
/*
* Optional character device methods:
*/
- long (*ioctl) (struct posix_clock *pc,
- unsigned int cmd, unsigned long arg);
+ long (*ioctl)(struct posix_clock_context *pccontext, unsigned int cmd,
+ unsigned long arg);
- int (*open) (struct posix_clock *pc, fmode_t f_mode);
+ int (*open)(struct posix_clock_context *pccontext, fmode_t f_mode);
- __poll_t (*poll) (struct posix_clock *pc,
- struct file *file, poll_table *wait);
+ __poll_t (*poll)(struct posix_clock_context *pccontext, struct file *file,
+ poll_table *wait);
- int (*release) (struct posix_clock *pc);
+ int (*release)(struct posix_clock_context *pccontext);
- ssize_t (*read) (struct posix_clock *pc,
- uint flags, char __user *buf, size_t cnt);
+ ssize_t (*read)(struct posix_clock_context *pccontext, uint flags,
+ char __user *buf, size_t cnt);
};
/**
@@ -90,6 +91,28 @@ struct posix_clock {
bool zombie;
};
+/**
+ * struct posix_clock_context - represents clock file operations context
+ *
+ * @clk: Pointer to the clock
+ * @fp: Pointer to the file used to open the clock
+ * @private_clkdata: Pointer to user data
+ *
+ * Drivers should use struct posix_clock_context during specific character
+ * device file operation methods to access the posix clock. In particular,
+ * the file pointer can be used to verify correct access mode for ioctl()
+ * calls.
+ *
+ * Drivers can store a private data structure during the open operation
+ * if they have specific information that is required in other file
+ * operations.
+ */
+struct posix_clock_context {
+ struct posix_clock *clk;
+ struct file *fp;
+ void *private_clkdata;
+};
+
/**
* posix_clock_register() - register a new clock
* @clk: Pointer to the clock. Caller must provide 'ops' field
diff --git a/include/linux/textsearch.h b/include/linux/textsearch.h
index 13770cfe33ad..1389959b6842 100644
--- a/include/linux/textsearch.h
+++ b/include/linux/textsearch.h
@@ -35,6 +35,7 @@ struct ts_state
* @get_pattern: return head of pattern
* @get_pattern_len: return length of pattern
* @owner: module reference to algorithm
+ * @list: list to search
*/
struct ts_ops
{
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 32890e43f06c..91f153d0bbb0 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -215,6 +215,8 @@ static inline void nfc_free_device(struct nfc_dev *dev)
int nfc_register_device(struct nfc_dev *dev);
+void nfc_unregister_rfkill(struct nfc_dev *dev);
+void nfc_remove_device(struct nfc_dev *dev);
void nfc_unregister_device(struct nfc_dev *dev);
/**
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index c573c6a7da12..bc1f2e2a5117 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -1334,7 +1334,7 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s
#define snd_pcm_lib_mmap_iomem NULL
#endif
-void snd_pcm_runtime_buffer_set_silence(struct snd_pcm_runtime *runtime);
+int snd_pcm_runtime_buffer_set_silence(struct snd_pcm_runtime *runtime);
/**
* snd_pcm_limit_isa_dma_size - Get the max size fitting with ISA DMA transfer
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 9334f2128bb2..33c3b684f6de 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -609,6 +609,7 @@ enum macvlan_macaddr_mode {
};
#define MACVLAN_FLAG_NOPROMISC 1
+#define MACVLAN_FLAG_NODST 2 /* skip dst macvlan if matching src macvlan */
/* VRF section */
enum {
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 85927c2aa343..c2168320839f 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -1915,22 +1915,22 @@ static bool cg_sockopt_is_valid_access(int off, int size,
}
switch (off) {
- case offsetof(struct bpf_sockopt, sk):
+ case bpf_ctx_range_ptr(struct bpf_sockopt, sk):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCKET;
break;
- case offsetof(struct bpf_sockopt, optval):
+ case bpf_ctx_range_ptr(struct bpf_sockopt, optval):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET;
break;
- case offsetof(struct bpf_sockopt, optval_end):
+ case bpf_ctx_range_ptr(struct bpf_sockopt, optval_end):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET_END;
break;
- case offsetof(struct bpf_sockopt, retval):
+ case bpf_ctx_range(struct bpf_sockopt, retval):
if (size != size_default)
return false;
return prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT;
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 32efef166009..484050de72fd 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -271,15 +271,20 @@ struct page *dma_alloc_from_pool(struct device *dev, size_t size,
{
struct gen_pool *pool = NULL;
struct page *page;
+ bool pool_found = false;
while ((pool = dma_guess_pool(pool, gfp))) {
+ pool_found = true;
page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,
phys_addr_ok);
if (page)
return page;
}
- WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
+ if (pool_found)
+ WARN(!(gfp & __GFP_NOWARN), "DMA pool exhausted for %s\n", dev_name(dev));
+ else
+ WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
return NULL;
}
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c
index 05e73d209aa8..827abede7274 100644
--- a/kernel/time/posix-clock.c
+++ b/kernel/time/posix-clock.c
@@ -19,7 +19,8 @@
*/
static struct posix_clock *get_posix_clock(struct file *fp)
{
- struct posix_clock *clk = fp->private_data;
+ struct posix_clock_context *pccontext = fp->private_data;
+ struct posix_clock *clk = pccontext->clk;
down_read(&clk->rwsem);
@@ -39,6 +40,7 @@ static void put_posix_clock(struct posix_clock *clk)
static ssize_t posix_clock_read(struct file *fp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = get_posix_clock(fp);
int err = -EINVAL;
@@ -46,7 +48,7 @@ static ssize_t posix_clock_read(struct file *fp, char __user *buf,
return -ENODEV;
if (clk->ops.read)
- err = clk->ops.read(clk, fp->f_flags, buf, count);
+ err = clk->ops.read(pccontext, fp->f_flags, buf, count);
put_posix_clock(clk);
@@ -55,6 +57,7 @@ static ssize_t posix_clock_read(struct file *fp, char __user *buf,
static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
{
+ struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = get_posix_clock(fp);
__poll_t result = 0;
@@ -62,7 +65,7 @@ static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
return EPOLLERR;
if (clk->ops.poll)
- result = clk->ops.poll(clk, fp, wait);
+ result = clk->ops.poll(pccontext, fp, wait);
put_posix_clock(clk);
@@ -72,6 +75,7 @@ static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
static long posix_clock_ioctl(struct file *fp,
unsigned int cmd, unsigned long arg)
{
+ struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = get_posix_clock(fp);
int err = -ENOTTY;
@@ -79,7 +83,7 @@ static long posix_clock_ioctl(struct file *fp,
return -ENODEV;
if (clk->ops.ioctl)
- err = clk->ops.ioctl(clk, cmd, arg);
+ err = clk->ops.ioctl(pccontext, cmd, arg);
put_posix_clock(clk);
@@ -90,6 +94,7 @@ static long posix_clock_ioctl(struct file *fp,
static long posix_clock_compat_ioctl(struct file *fp,
unsigned int cmd, unsigned long arg)
{
+ struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = get_posix_clock(fp);
int err = -ENOTTY;
@@ -97,7 +102,7 @@ static long posix_clock_compat_ioctl(struct file *fp,
return -ENODEV;
if (clk->ops.ioctl)
- err = clk->ops.ioctl(clk, cmd, arg);
+ err = clk->ops.ioctl(pccontext, cmd, arg);
put_posix_clock(clk);
@@ -110,6 +115,7 @@ static int posix_clock_open(struct inode *inode, struct file *fp)
int err;
struct posix_clock *clk =
container_of(inode->i_cdev, struct posix_clock, cdev);
+ struct posix_clock_context *pccontext;
down_read(&clk->rwsem);
@@ -117,15 +123,24 @@ static int posix_clock_open(struct inode *inode, struct file *fp)
err = -ENODEV;
goto out;
}
- if (clk->ops.open)
- err = clk->ops.open(clk, fp->f_mode);
- else
- err = 0;
-
- if (!err) {
- get_device(clk->dev);
- fp->private_data = clk;
+ pccontext = kzalloc(sizeof(*pccontext), GFP_KERNEL);
+ if (!pccontext) {
+ err = -ENOMEM;
+ goto out;
}
+ pccontext->clk = clk;
+ pccontext->fp = fp;
+ if (clk->ops.open) {
+ err = clk->ops.open(pccontext, fp->f_mode);
+ if (err) {
+ kfree(pccontext);
+ goto out;
+ }
+ }
+
+ fp->private_data = pccontext;
+ get_device(clk->dev);
+ err = 0;
out:
up_read(&clk->rwsem);
return err;
@@ -133,14 +148,20 @@ static int posix_clock_open(struct inode *inode, struct file *fp)
static int posix_clock_release(struct inode *inode, struct file *fp)
{
- struct posix_clock *clk = fp->private_data;
+ struct posix_clock_context *pccontext = fp->private_data;
+ struct posix_clock *clk;
int err = 0;
+ if (!pccontext)
+ return -ENODEV;
+ clk = pccontext->clk;
+
if (clk->ops.release)
- err = clk->ops.release(clk);
+ err = clk->ops.release(pccontext);
put_device(clk->dev);
+ kfree(pccontext);
fp->private_data = NULL;
return err;
@@ -232,7 +253,7 @@ static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx)
if (err)
return err;
- if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
+ if (tx->modes && (cd.fp->f_mode & FMODE_WRITE) == 0) {
err = -EACCES;
goto out;
}
diff --git a/mm/ksm.c b/mm/ksm.c
index 25b8362a4f89..94d17fb3f184 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -38,6 +38,7 @@
#include <linux/freezer.h>
#include <linux/oom.h>
#include <linux/numa.h>
+#include <linux/pagewalk.h>
#include <asm/tlbflush.h>
#include "internal.h"
@@ -2223,6 +2224,89 @@ static struct rmap_item *get_next_rmap_item(struct mm_slot *mm_slot,
return rmap_item;
}
+struct ksm_next_page_arg {
+ struct page *page;
+ unsigned long addr;
+};
+
+static int ksm_next_page_pmd_entry(pmd_t *pmdp, unsigned long addr, unsigned long end,
+ struct mm_walk *walk)
+{
+ struct ksm_next_page_arg *private = walk->private;
+ struct vm_area_struct *vma = walk->vma;
+ pte_t *start_ptep = NULL, *ptep, pte;
+ struct mm_struct *mm = walk->mm;
+ struct page *page;
+ spinlock_t *ptl;
+ pmd_t pmd;
+
+ if (ksm_test_exit(mm))
+ return 0;
+
+ cond_resched();
+
+ pmd = pmd_read_atomic(pmdp);
+ if (!pmd_present(pmd))
+ return 0;
+
+ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && pmd_leaf(pmd)) {
+ ptl = pmd_lock(mm, pmdp);
+ pmd = READ_ONCE(*pmdp);
+
+ if (!pmd_present(pmd)) {
+ goto not_found_unlock;
+ } else if (pmd_leaf(pmd)) {
+ page = vm_normal_page_pmd(vma, addr, pmd);
+ if (!page)
+ goto not_found_unlock;
+
+ if (is_zone_device_page(page) || !PageAnon(page))
+ goto not_found_unlock;
+
+ page += ((addr & (PMD_SIZE - 1)) >> PAGE_SHIFT);
+ goto found_unlock;
+ }
+ spin_unlock(ptl);
+ }
+
+ start_ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl);
+ if (!start_ptep)
+ return 0;
+
+ for (ptep = start_ptep; addr < end; ptep++, addr += PAGE_SIZE) {
+ pte = ptep_get(ptep);
+
+ if (!pte_present(pte))
+ continue;
+
+ page = vm_normal_page(vma, addr, pte);
+ if (!page)
+ continue;
+
+ if (is_zone_device_page(page) || !PageAnon(page))
+ continue;
+ goto found_unlock;
+ }
+
+not_found_unlock:
+ spin_unlock(ptl);
+ if (start_ptep)
+ pte_unmap(start_ptep);
+ return 0;
+found_unlock:
+ get_page(page);
+ spin_unlock(ptl);
+ if (start_ptep)
+ pte_unmap(start_ptep);
+ private->page = page;
+ private->addr = addr;
+ return 1;
+}
+
+static struct mm_walk_ops ksm_next_page_ops = {
+ .pmd_entry = ksm_next_page_pmd_entry,
+};
+
static struct rmap_item *scan_get_next_rmap_item(struct page **page)
{
struct mm_struct *mm;
@@ -2302,29 +2386,40 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
ksm_scan.address = vma->vm_end;
while (ksm_scan.address < vma->vm_end) {
+ struct ksm_next_page_arg ksm_next_page_arg;
+ struct page *tmp_page = NULL;
+ int found;
+
if (ksm_test_exit(mm))
break;
- *page = follow_page(vma, ksm_scan.address, FOLL_GET);
- if (IS_ERR_OR_NULL(*page)) {
- ksm_scan.address += PAGE_SIZE;
- cond_resched();
- continue;
+
+ found = walk_page_range_vma(vma, ksm_scan.address,
+ vma->vm_end,
+ &ksm_next_page_ops,
+ &ksm_next_page_arg);
+
+ if (found > 0) {
+ tmp_page = ksm_next_page_arg.page;
+ ksm_scan.address = ksm_next_page_arg.addr;
+ } else {
+ VM_WARN_ON_ONCE(found < 0);
+ ksm_scan.address = vma->vm_end - PAGE_SIZE;
}
- if (PageAnon(*page)) {
- flush_anon_page(vma, *page, ksm_scan.address);
- flush_dcache_page(*page);
+ if (tmp_page) {
+ flush_anon_page(vma, tmp_page, ksm_scan.address);
+ flush_dcache_page(tmp_page);
rmap_item = get_next_rmap_item(slot,
ksm_scan.rmap_list, ksm_scan.address);
if (rmap_item) {
ksm_scan.rmap_list =
&rmap_item->rmap_list;
ksm_scan.address += PAGE_SIZE;
+ *page = tmp_page;
} else
- put_page(*page);
+ put_page(tmp_page);
mmap_read_unlock(mm);
return rmap_item;
}
- put_page(*page);
ksm_scan.address += PAGE_SIZE;
cond_resched();
}
diff --git a/mm/migrate.c b/mm/migrate.c
index aafad2112ec8..bf59b09455ad 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1289,6 +1289,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
struct page *new_hpage;
struct anon_vma *anon_vma = NULL;
struct address_space *mapping = NULL;
+ enum ttu_flags ttu = TTU_MIGRATION|TTU_IGNORE_MLOCK;
/*
* Migratability of hugepages depends on architectures and their size.
@@ -1336,9 +1337,6 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
goto put_anon;
if (page_mapped(hpage)) {
- bool mapping_locked = false;
- enum ttu_flags ttu = TTU_MIGRATION|TTU_IGNORE_MLOCK;
-
if (!PageAnon(hpage)) {
/*
* In shared mappings, try_to_unmap could potentially
@@ -1350,15 +1348,11 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
if (unlikely(!mapping))
goto unlock_put_anon;
- mapping_locked = true;
ttu |= TTU_RMAP_LOCKED;
}
try_to_unmap(hpage, ttu);
page_was_mapped = 1;
-
- if (mapping_locked)
- i_mmap_unlock_write(mapping);
}
if (!page_mapped(hpage))
@@ -1366,7 +1360,11 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
if (page_was_mapped)
remove_migration_ptes(hpage,
- rc == MIGRATEPAGE_SUCCESS ? new_hpage : hpage, false);
+ rc == MIGRATEPAGE_SUCCESS ? new_hpage : hpage,
+ (ttu & TTU_RMAP_LOCKED) ? true : false);
+
+ if (ttu & TTU_RMAP_LOCKED)
+ i_mmap_unlock_write(mapping);
unlock_put_anon:
unlock_page(new_hpage);
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index 371ec21a1989..3f5cbd6eb4e1 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -461,6 +461,26 @@ int walk_page_range_novma(struct mm_struct *mm, unsigned long start,
return walk_pgd_range(start, end, &walk);
}
+int walk_page_range_vma(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, const struct mm_walk_ops *ops,
+ void *private)
+{
+ struct mm_walk walk = {
+ .ops = ops,
+ .mm = vma->vm_mm,
+ .vma = vma,
+ .private = private,
+ };
+
+ if (start >= end || !walk.mm)
+ return -EINVAL;
+ if (start < vma->vm_start || end > vma->vm_end)
+ return -EINVAL;
+
+ mmap_assert_locked(walk.mm);
+ return __walk_page_range(start, end, &walk);
+}
+
int walk_page_vma(struct vm_area_struct *vma, const struct mm_walk_ops *ops,
void *private)
{
diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
index 7df14a0e380c..027d6ba8c154 100644
--- a/net/bpf/test_run.c
+++ b/net/bpf/test_run.c
@@ -503,6 +503,11 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb)
if (__skb->gso_segs > GSO_MAX_SEGS)
return -EINVAL;
+
+ /* Currently GSO type is zero/unset. If this gets extended with
+ * a small list of accepted GSO types in future, the filter for
+ * an unset GSO type in bpf_clone_redirect() can be lifted.
+ */
skb_shinfo(skb)->gso_segs = __skb->gso_segs;
skb_shinfo(skb)->gso_size = __skb->gso_size;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 52dd0708fd14..f9d4b86e3186 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -218,7 +218,7 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb)
int ret;
net = dev_net(skb->dev);
-#ifdef HAVE_JUMP_LABEL
+#ifdef CONFIG_JUMP_LABEL
if (!static_key_false(&nf_hooks_needed[NFPROTO_BRIDGE][NF_BR_PRE_ROUTING]))
goto frame_finish;
#endif
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index 25e4cdf2df22..9f77a730be48 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -1680,8 +1680,16 @@ static int j1939_xtp_rx_rts_session_active(struct j1939_session *session,
j1939_session_timers_cancel(session);
j1939_session_cancel(session, J1939_XTP_ABORT_BUSY);
- if (session->transmission)
+ if (session->transmission) {
j1939_session_deactivate_activate_next(session);
+ } else if (session->state == J1939_SESSION_WAITING_ABORT) {
+ /* Force deactivation for the receiver.
+ * If we rely on the timer starting in j1939_session_cancel,
+ * a second RTS call here will cancel that timer and fail
+ * to restart it because the state is already WAITING_ABORT.
+ */
+ j1939_session_deactivate_activate_next(session);
+ }
return -EBUSY;
}
diff --git a/net/core/filter.c b/net/core/filter.c
index 1d1d4e92cb66..7002368d1592 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2450,6 +2450,13 @@ BPF_CALL_3(bpf_clone_redirect, struct sk_buff *, skb, u32, ifindex, u64, flags)
if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))
return -EINVAL;
+ /* BPF test infra's convert___skb_to_skb() can create type-less
+ * GSO packets. gso_features_check() will detect this as a bad
+ * offload. However, lets not leak them out in the first place.
+ */
+ if (unlikely(skb_is_gso(skb) && !skb_shinfo(skb)->gso_type))
+ return -EBADMSG;
+
dev = dev_get_by_index_rcu(dev_net(skb->dev), ifindex);
if (unlikely(!dev))
return -EINVAL;
@@ -7627,7 +7634,7 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct __sk_buff, sk):
+ case bpf_ctx_range_ptr(struct __sk_buff, sk):
if (type == BPF_WRITE || size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCK_COMMON_OR_NULL;
@@ -8144,7 +8151,7 @@ static bool sock_addr_is_valid_access(int off, int size,
return false;
}
break;
- case offsetof(struct bpf_sock_addr, sk):
+ case bpf_ctx_range_ptr(struct bpf_sock_addr, sk):
if (type != BPF_READ)
return false;
if (size != sizeof(__u64))
@@ -8198,17 +8205,17 @@ static bool sock_ops_is_valid_access(int off, int size,
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct bpf_sock_ops, sk):
+ case bpf_ctx_range_ptr(struct bpf_sock_ops, sk):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCKET_OR_NULL;
break;
- case offsetof(struct bpf_sock_ops, skb_data):
+ case bpf_ctx_range_ptr(struct bpf_sock_ops, skb_data):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET;
break;
- case offsetof(struct bpf_sock_ops, skb_data_end):
+ case bpf_ctx_range_ptr(struct bpf_sock_ops, skb_data_end):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_PACKET_END;
@@ -8282,17 +8289,17 @@ static bool sk_msg_is_valid_access(int off, int size,
return false;
switch (off) {
- case offsetof(struct sk_msg_md, data):
+ case bpf_ctx_range_ptr(struct sk_msg_md, data):
info->reg_type = PTR_TO_PACKET;
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct sk_msg_md, data_end):
+ case bpf_ctx_range_ptr(struct sk_msg_md, data_end):
info->reg_type = PTR_TO_PACKET_END;
if (size != sizeof(__u64))
return false;
break;
- case offsetof(struct sk_msg_md, sk):
+ case bpf_ctx_range_ptr(struct sk_msg_md, sk):
if (size != sizeof(__u64))
return false;
info->reg_type = PTR_TO_SOCKET;
@@ -10317,7 +10324,7 @@ static bool sk_lookup_is_valid_access(int off, int size,
return false;
switch (off) {
- case offsetof(struct bpf_sk_lookup, sk):
+ case bpf_ctx_range_ptr(struct bpf_sk_lookup, sk):
info->reg_type = PTR_TO_SOCKET_OR_NULL;
return size == sizeof(__u64);
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index bbdd9c44f14e..d1c8d4beb77d 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_IP_MROUTE) += ipmr.o
obj-$(CONFIG_IP_MROUTE_COMMON) += ipmr_base.o
obj-$(CONFIG_NET_IPIP) += ipip.o
gre-y := gre_demux.o
+fou-y := fou_core.o fou_nl.o
obj-$(CONFIG_NET_FOU) += fou.o
obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o
obj-$(CONFIG_NET_IPGRE) += ip_gre.o
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
deleted file mode 100644
index b1a8e4eec3f6..000000000000
--- a/net/ipv4/fou.c
+++ /dev/null
@@ -1,1315 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/socket.h>
-#include <linux/skbuff.h>
-#include <linux/ip.h>
-#include <linux/icmp.h>
-#include <linux/udp.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <net/genetlink.h>
-#include <net/gue.h>
-#include <net/fou.h>
-#include <net/ip.h>
-#include <net/protocol.h>
-#include <net/udp.h>
-#include <net/udp_tunnel.h>
-#include <net/xfrm.h>
-#include <uapi/linux/fou.h>
-#include <uapi/linux/genetlink.h>
-
-struct fou {
- struct socket *sock;
- u8 protocol;
- u8 flags;
- __be16 port;
- u8 family;
- u16 type;
- struct list_head list;
- struct rcu_head rcu;
-};
-
-#define FOU_F_REMCSUM_NOPARTIAL BIT(0)
-
-struct fou_cfg {
- u16 type;
- u8 protocol;
- u8 flags;
- struct udp_port_cfg udp_config;
-};
-
-static unsigned int fou_net_id;
-
-struct fou_net {
- struct list_head fou_list;
- struct mutex fou_lock;
-};
-
-static inline struct fou *fou_from_sock(struct sock *sk)
-{
- return rcu_dereference_sk_user_data(sk);
-}
-
-static int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len)
-{
- /* Remove 'len' bytes from the packet (UDP header and
- * FOU header if present).
- */
- if (fou->family == AF_INET)
- ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
- else
- ipv6_hdr(skb)->payload_len =
- htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
-
- __skb_pull(skb, len);
- skb_postpull_rcsum(skb, udp_hdr(skb), len);
- skb_reset_transport_header(skb);
- return iptunnel_pull_offloads(skb);
-}
-
-static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
-{
- struct fou *fou = fou_from_sock(sk);
-
- if (!fou)
- return 1;
-
- if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
- goto drop;
-
- return -fou->protocol;
-
-drop:
- kfree_skb(skb);
- return 0;
-}
-
-static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
- void *data, size_t hdrlen, u8 ipproto,
- bool nopartial)
-{
- __be16 *pd = data;
- size_t start = ntohs(pd[0]);
- size_t offset = ntohs(pd[1]);
- size_t plen = sizeof(struct udphdr) + hdrlen +
- max_t(size_t, offset + sizeof(u16), start);
-
- if (skb->remcsum_offload)
- return guehdr;
-
- if (!pskb_may_pull(skb, plen))
- return NULL;
- guehdr = (struct guehdr *)&udp_hdr(skb)[1];
-
- skb_remcsum_process(skb, (void *)guehdr + hdrlen,
- start, offset, nopartial);
-
- return guehdr;
-}
-
-static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
-{
- /* No support yet */
- kfree_skb(skb);
- return 0;
-}
-
-static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
-{
- struct fou *fou = fou_from_sock(sk);
- size_t len, optlen, hdrlen;
- struct guehdr *guehdr;
- void *data;
- u16 doffset = 0;
- u8 proto_ctype;
-
- if (!fou)
- return 1;
-
- len = sizeof(struct udphdr) + sizeof(struct guehdr);
- if (!pskb_may_pull(skb, len))
- goto drop;
-
- guehdr = (struct guehdr *)&udp_hdr(skb)[1];
-
- switch (guehdr->version) {
- case 0: /* Full GUE header present */
- break;
-
- case 1: {
- /* Direct encapsulation of IPv4 or IPv6 */
-
- int prot;
-
- switch (((struct iphdr *)guehdr)->version) {
- case 4:
- prot = IPPROTO_IPIP;
- break;
- case 6:
- prot = IPPROTO_IPV6;
- break;
- default:
- goto drop;
- }
-
- if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
- goto drop;
-
- return -prot;
- }
-
- default: /* Undefined version */
- goto drop;
- }
-
- optlen = guehdr->hlen << 2;
- len += optlen;
-
- if (!pskb_may_pull(skb, len))
- goto drop;
-
- /* guehdr may change after pull */
- guehdr = (struct guehdr *)&udp_hdr(skb)[1];
-
- if (validate_gue_flags(guehdr, optlen))
- goto drop;
-
- hdrlen = sizeof(struct guehdr) + optlen;
-
- if (fou->family == AF_INET)
- ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
- else
- ipv6_hdr(skb)->payload_len =
- htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
-
- /* Pull csum through the guehdr now . This can be used if
- * there is a remote checksum offload.
- */
- skb_postpull_rcsum(skb, udp_hdr(skb), len);
-
- data = &guehdr[1];
-
- if (guehdr->flags & GUE_FLAG_PRIV) {
- __be32 flags = *(__be32 *)(data + doffset);
-
- doffset += GUE_LEN_PRIV;
-
- if (flags & GUE_PFLAG_REMCSUM) {
- guehdr = gue_remcsum(skb, guehdr, data + doffset,
- hdrlen, guehdr->proto_ctype,
- !!(fou->flags &
- FOU_F_REMCSUM_NOPARTIAL));
- if (!guehdr)
- goto drop;
-
- data = &guehdr[1];
-
- doffset += GUE_PLEN_REMCSUM;
- }
- }
-
- if (unlikely(guehdr->control))
- return gue_control_message(skb, guehdr);
-
- proto_ctype = guehdr->proto_ctype;
- __skb_pull(skb, sizeof(struct udphdr) + hdrlen);
- skb_reset_transport_header(skb);
-
- if (iptunnel_pull_offloads(skb))
- goto drop;
-
- return -proto_ctype;
-
-drop:
- kfree_skb(skb);
- return 0;
-}
-
-static struct sk_buff *fou_gro_receive(struct sock *sk,
- struct list_head *head,
- struct sk_buff *skb)
-{
- const struct net_offload __rcu **offloads;
- struct fou *fou = fou_from_sock(sk);
- const struct net_offload *ops;
- struct sk_buff *pp = NULL;
- u8 proto;
-
- if (!fou)
- goto out;
-
- proto = fou->protocol;
-
- /* We can clear the encap_mark for FOU as we are essentially doing
- * one of two possible things. We are either adding an L4 tunnel
- * header to the outer L3 tunnel header, or we are simply
- * treating the GRE tunnel header as though it is a UDP protocol
- * specific header such as VXLAN or GENEVE.
- */
- NAPI_GRO_CB(skb)->encap_mark = 0;
-
- /* Flag this frame as already having an outer encap header */
- NAPI_GRO_CB(skb)->is_fou = 1;
-
- offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
- ops = rcu_dereference(offloads[proto]);
- if (!ops || !ops->callbacks.gro_receive)
- goto out;
-
- pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
-
-out:
- return pp;
-}
-
-static int fou_gro_complete(struct sock *sk, struct sk_buff *skb,
- int nhoff)
-{
- const struct net_offload __rcu **offloads;
- struct fou *fou = fou_from_sock(sk);
- const struct net_offload *ops;
- u8 proto;
- int err;
-
- if (!fou) {
- err = -ENOENT;
- goto out;
- }
-
- proto = fou->protocol;
-
- offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
- ops = rcu_dereference(offloads[proto]);
- if (WARN_ON(!ops || !ops->callbacks.gro_complete)) {
- err = -ENOSYS;
- goto out;
- }
-
- err = ops->callbacks.gro_complete(skb, nhoff);
-
- skb_set_inner_mac_header(skb, nhoff);
-
-out:
- return err;
-}
-
-static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
- struct guehdr *guehdr, void *data,
- size_t hdrlen, struct gro_remcsum *grc,
- bool nopartial)
-{
- __be16 *pd = data;
- size_t start = ntohs(pd[0]);
- size_t offset = ntohs(pd[1]);
-
- if (skb->remcsum_offload)
- return guehdr;
-
- if (!NAPI_GRO_CB(skb)->csum_valid)
- return NULL;
-
- guehdr = skb_gro_remcsum_process(skb, (void *)guehdr, off, hdrlen,
- start, offset, grc, nopartial);
-
- skb->remcsum_offload = 1;
-
- return guehdr;
-}
-
-static struct sk_buff *gue_gro_receive(struct sock *sk,
- struct list_head *head,
- struct sk_buff *skb)
-{
- const struct net_offload __rcu **offloads;
- const struct net_offload *ops;
- struct sk_buff *pp = NULL;
- struct sk_buff *p;
- struct guehdr *guehdr;
- size_t len, optlen, hdrlen, off;
- void *data;
- u16 doffset = 0;
- int flush = 1;
- struct fou *fou = fou_from_sock(sk);
- struct gro_remcsum grc;
- u8 proto;
-
- skb_gro_remcsum_init(&grc);
-
- if (!fou)
- goto out;
-
- off = skb_gro_offset(skb);
- len = off + sizeof(*guehdr);
-
- guehdr = skb_gro_header_fast(skb, off);
- if (skb_gro_header_hard(skb, len)) {
- guehdr = skb_gro_header_slow(skb, len, off);
- if (unlikely(!guehdr))
- goto out;
- }
-
- switch (guehdr->version) {
- case 0:
- break;
- case 1:
- switch (((struct iphdr *)guehdr)->version) {
- case 4:
- proto = IPPROTO_IPIP;
- break;
- case 6:
- proto = IPPROTO_IPV6;
- break;
- default:
- goto out;
- }
- goto next_proto;
- default:
- goto out;
- }
-
- optlen = guehdr->hlen << 2;
- len += optlen;
-
- if (skb_gro_header_hard(skb, len)) {
- guehdr = skb_gro_header_slow(skb, len, off);
- if (unlikely(!guehdr))
- goto out;
- }
-
- if (unlikely(guehdr->control) || guehdr->version != 0 ||
- validate_gue_flags(guehdr, optlen))
- goto out;
-
- hdrlen = sizeof(*guehdr) + optlen;
-
- /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr,
- * this is needed if there is a remote checkcsum offload.
- */
- skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
-
- data = &guehdr[1];
-
- if (guehdr->flags & GUE_FLAG_PRIV) {
- __be32 flags = *(__be32 *)(data + doffset);
-
- doffset += GUE_LEN_PRIV;
-
- if (flags & GUE_PFLAG_REMCSUM) {
- guehdr = gue_gro_remcsum(skb, off, guehdr,
- data + doffset, hdrlen, &grc,
- !!(fou->flags &
- FOU_F_REMCSUM_NOPARTIAL));
-
- if (!guehdr)
- goto out;
-
- data = &guehdr[1];
-
- doffset += GUE_PLEN_REMCSUM;
- }
- }
-
- skb_gro_pull(skb, hdrlen);
-
- list_for_each_entry(p, head, list) {
- const struct guehdr *guehdr2;
-
- if (!NAPI_GRO_CB(p)->same_flow)
- continue;
-
- guehdr2 = (struct guehdr *)(p->data + off);
-
- /* Compare base GUE header to be equal (covers
- * hlen, version, proto_ctype, and flags.
- */
- if (guehdr->word != guehdr2->word) {
- NAPI_GRO_CB(p)->same_flow = 0;
- continue;
- }
-
- /* Compare optional fields are the same. */
- if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
- guehdr->hlen << 2)) {
- NAPI_GRO_CB(p)->same_flow = 0;
- continue;
- }
- }
-
- proto = guehdr->proto_ctype;
-
-next_proto:
-
- /* We can clear the encap_mark for GUE as we are essentially doing
- * one of two possible things. We are either adding an L4 tunnel
- * header to the outer L3 tunnel header, or we are simply
- * treating the GRE tunnel header as though it is a UDP protocol
- * specific header such as VXLAN or GENEVE.
- */
- NAPI_GRO_CB(skb)->encap_mark = 0;
-
- /* Flag this frame as already having an outer encap header */
- NAPI_GRO_CB(skb)->is_fou = 1;
-
- offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
- ops = rcu_dereference(offloads[proto]);
- if (!ops || !ops->callbacks.gro_receive)
- goto out;
-
- pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
- flush = 0;
-
-out:
- skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
-
- return pp;
-}
-
-static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
-{
- struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
- const struct net_offload __rcu **offloads;
- const struct net_offload *ops;
- unsigned int guehlen = 0;
- u8 proto;
- int err = -ENOENT;
-
- switch (guehdr->version) {
- case 0:
- proto = guehdr->proto_ctype;
- guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
- break;
- case 1:
- switch (((struct iphdr *)guehdr)->version) {
- case 4:
- proto = IPPROTO_IPIP;
- break;
- case 6:
- proto = IPPROTO_IPV6;
- break;
- default:
- return err;
- }
- break;
- default:
- return err;
- }
-
- offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
- ops = rcu_dereference(offloads[proto]);
- if (WARN_ON(!ops || !ops->callbacks.gro_complete))
- goto out;
-
- err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
-
- skb_set_inner_mac_header(skb, nhoff + guehlen);
-
-out:
- return err;
-}
-
-static bool fou_cfg_cmp(struct fou *fou, struct fou_cfg *cfg)
-{
- struct sock *sk = fou->sock->sk;
- struct udp_port_cfg *udp_cfg = &cfg->udp_config;
-
- if (fou->family != udp_cfg->family ||
- fou->port != udp_cfg->local_udp_port ||
- sk->sk_dport != udp_cfg->peer_udp_port ||
- sk->sk_bound_dev_if != udp_cfg->bind_ifindex)
- return false;
-
- if (fou->family == AF_INET) {
- if (sk->sk_rcv_saddr != udp_cfg->local_ip.s_addr ||
- sk->sk_daddr != udp_cfg->peer_ip.s_addr)
- return false;
- else
- return true;
-#if IS_ENABLED(CONFIG_IPV6)
- } else {
- if (ipv6_addr_cmp(&sk->sk_v6_rcv_saddr, &udp_cfg->local_ip6) ||
- ipv6_addr_cmp(&sk->sk_v6_daddr, &udp_cfg->peer_ip6))
- return false;
- else
- return true;
-#endif
- }
-
- return false;
-}
-
-static int fou_add_to_port_list(struct net *net, struct fou *fou,
- struct fou_cfg *cfg)
-{
- struct fou_net *fn = net_generic(net, fou_net_id);
- struct fou *fout;
-
- mutex_lock(&fn->fou_lock);
- list_for_each_entry(fout, &fn->fou_list, list) {
- if (fou_cfg_cmp(fout, cfg)) {
- mutex_unlock(&fn->fou_lock);
- return -EALREADY;
- }
- }
-
- list_add(&fou->list, &fn->fou_list);
- mutex_unlock(&fn->fou_lock);
-
- return 0;
-}
-
-static void fou_release(struct fou *fou)
-{
- struct socket *sock = fou->sock;
-
- list_del(&fou->list);
- udp_tunnel_sock_release(sock);
-
- kfree_rcu(fou, rcu);
-}
-
-static int fou_create(struct net *net, struct fou_cfg *cfg,
- struct socket **sockp)
-{
- struct socket *sock = NULL;
- struct fou *fou = NULL;
- struct sock *sk;
- struct udp_tunnel_sock_cfg tunnel_cfg;
- int err;
-
- /* Open UDP socket */
- err = udp_sock_create(net, &cfg->udp_config, &sock);
- if (err < 0)
- goto error;
-
- /* Allocate FOU port structure */
- fou = kzalloc(sizeof(*fou), GFP_KERNEL);
- if (!fou) {
- err = -ENOMEM;
- goto error;
- }
-
- sk = sock->sk;
-
- fou->port = cfg->udp_config.local_udp_port;
- fou->family = cfg->udp_config.family;
- fou->flags = cfg->flags;
- fou->type = cfg->type;
- fou->sock = sock;
-
- memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
- tunnel_cfg.encap_type = 1;
- tunnel_cfg.sk_user_data = fou;
- tunnel_cfg.encap_destroy = NULL;
-
- /* Initial for fou type */
- switch (cfg->type) {
- case FOU_ENCAP_DIRECT:
- tunnel_cfg.encap_rcv = fou_udp_recv;
- tunnel_cfg.gro_receive = fou_gro_receive;
- tunnel_cfg.gro_complete = fou_gro_complete;
- fou->protocol = cfg->protocol;
- break;
- case FOU_ENCAP_GUE:
- tunnel_cfg.encap_rcv = gue_udp_recv;
- tunnel_cfg.gro_receive = gue_gro_receive;
- tunnel_cfg.gro_complete = gue_gro_complete;
- break;
- default:
- err = -EINVAL;
- goto error;
- }
-
- setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
-
- sk->sk_allocation = GFP_ATOMIC;
-
- err = fou_add_to_port_list(net, fou, cfg);
- if (err)
- goto error;
-
- if (sockp)
- *sockp = sock;
-
- return 0;
-
-error:
- kfree(fou);
- if (sock)
- udp_tunnel_sock_release(sock);
-
- return err;
-}
-
-static int fou_destroy(struct net *net, struct fou_cfg *cfg)
-{
- struct fou_net *fn = net_generic(net, fou_net_id);
- int err = -EINVAL;
- struct fou *fou;
-
- mutex_lock(&fn->fou_lock);
- list_for_each_entry(fou, &fn->fou_list, list) {
- if (fou_cfg_cmp(fou, cfg)) {
- fou_release(fou);
- err = 0;
- break;
- }
- }
- mutex_unlock(&fn->fou_lock);
-
- return err;
-}
-
-static struct genl_family fou_nl_family;
-
-static const struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
- [FOU_ATTR_PORT] = { .type = NLA_U16, },
- [FOU_ATTR_AF] = { .type = NLA_U8, },
- [FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
- [FOU_ATTR_TYPE] = { .type = NLA_U8, },
- [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, },
- [FOU_ATTR_LOCAL_V4] = { .type = NLA_U32, },
- [FOU_ATTR_PEER_V4] = { .type = NLA_U32, },
- [FOU_ATTR_LOCAL_V6] = { .len = sizeof(struct in6_addr), },
- [FOU_ATTR_PEER_V6] = { .len = sizeof(struct in6_addr), },
- [FOU_ATTR_PEER_PORT] = { .type = NLA_U16, },
- [FOU_ATTR_IFINDEX] = { .type = NLA_S32, },
-};
-
-static int parse_nl_config(struct genl_info *info,
- struct fou_cfg *cfg)
-{
- bool has_local = false, has_peer = false;
- struct nlattr *attr;
- int ifindex;
- __be16 port;
-
- memset(cfg, 0, sizeof(*cfg));
-
- cfg->udp_config.family = AF_INET;
-
- if (info->attrs[FOU_ATTR_AF]) {
- u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
-
- switch (family) {
- case AF_INET:
- break;
- case AF_INET6:
- cfg->udp_config.ipv6_v6only = 1;
- break;
- default:
- return -EAFNOSUPPORT;
- }
-
- cfg->udp_config.family = family;
- }
-
- if (info->attrs[FOU_ATTR_PORT]) {
- port = nla_get_be16(info->attrs[FOU_ATTR_PORT]);
- cfg->udp_config.local_udp_port = port;
- }
-
- if (info->attrs[FOU_ATTR_IPPROTO])
- cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
-
- if (info->attrs[FOU_ATTR_TYPE])
- cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
-
- if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL])
- cfg->flags |= FOU_F_REMCSUM_NOPARTIAL;
-
- if (cfg->udp_config.family == AF_INET) {
- if (info->attrs[FOU_ATTR_LOCAL_V4]) {
- attr = info->attrs[FOU_ATTR_LOCAL_V4];
- cfg->udp_config.local_ip.s_addr = nla_get_in_addr(attr);
- has_local = true;
- }
-
- if (info->attrs[FOU_ATTR_PEER_V4]) {
- attr = info->attrs[FOU_ATTR_PEER_V4];
- cfg->udp_config.peer_ip.s_addr = nla_get_in_addr(attr);
- has_peer = true;
- }
-#if IS_ENABLED(CONFIG_IPV6)
- } else {
- if (info->attrs[FOU_ATTR_LOCAL_V6]) {
- attr = info->attrs[FOU_ATTR_LOCAL_V6];
- cfg->udp_config.local_ip6 = nla_get_in6_addr(attr);
- has_local = true;
- }
-
- if (info->attrs[FOU_ATTR_PEER_V6]) {
- attr = info->attrs[FOU_ATTR_PEER_V6];
- cfg->udp_config.peer_ip6 = nla_get_in6_addr(attr);
- has_peer = true;
- }
-#endif
- }
-
- if (has_peer) {
- if (info->attrs[FOU_ATTR_PEER_PORT]) {
- port = nla_get_be16(info->attrs[FOU_ATTR_PEER_PORT]);
- cfg->udp_config.peer_udp_port = port;
- } else {
- return -EINVAL;
- }
- }
-
- if (info->attrs[FOU_ATTR_IFINDEX]) {
- if (!has_local)
- return -EINVAL;
-
- ifindex = nla_get_s32(info->attrs[FOU_ATTR_IFINDEX]);
-
- cfg->udp_config.bind_ifindex = ifindex;
- }
-
- return 0;
-}
-
-static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info)
-{
- struct net *net = genl_info_net(info);
- struct fou_cfg cfg;
- int err;
-
- err = parse_nl_config(info, &cfg);
- if (err)
- return err;
-
- return fou_create(net, &cfg, NULL);
-}
-
-static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info)
-{
- struct net *net = genl_info_net(info);
- struct fou_cfg cfg;
- int err;
-
- err = parse_nl_config(info, &cfg);
- if (err)
- return err;
-
- return fou_destroy(net, &cfg);
-}
-
-static int fou_fill_info(struct fou *fou, struct sk_buff *msg)
-{
- struct sock *sk = fou->sock->sk;
-
- if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) ||
- nla_put_be16(msg, FOU_ATTR_PORT, fou->port) ||
- nla_put_be16(msg, FOU_ATTR_PEER_PORT, sk->sk_dport) ||
- nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) ||
- nla_put_u8(msg, FOU_ATTR_TYPE, fou->type) ||
- nla_put_s32(msg, FOU_ATTR_IFINDEX, sk->sk_bound_dev_if))
- return -1;
-
- if (fou->flags & FOU_F_REMCSUM_NOPARTIAL)
- if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL))
- return -1;
-
- if (fou->sock->sk->sk_family == AF_INET) {
- if (nla_put_in_addr(msg, FOU_ATTR_LOCAL_V4, sk->sk_rcv_saddr))
- return -1;
-
- if (nla_put_in_addr(msg, FOU_ATTR_PEER_V4, sk->sk_daddr))
- return -1;
-#if IS_ENABLED(CONFIG_IPV6)
- } else {
- if (nla_put_in6_addr(msg, FOU_ATTR_LOCAL_V6,
- &sk->sk_v6_rcv_saddr))
- return -1;
-
- if (nla_put_in6_addr(msg, FOU_ATTR_PEER_V6, &sk->sk_v6_daddr))
- return -1;
-#endif
- }
-
- return 0;
-}
-
-static int fou_dump_info(struct fou *fou, u32 portid, u32 seq,
- u32 flags, struct sk_buff *skb, u8 cmd)
-{
- void *hdr;
-
- hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd);
- if (!hdr)
- return -ENOMEM;
-
- if (fou_fill_info(fou, skb) < 0)
- goto nla_put_failure;
-
- genlmsg_end(skb, hdr);
- return 0;
-
-nla_put_failure:
- genlmsg_cancel(skb, hdr);
- return -EMSGSIZE;
-}
-
-static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info)
-{
- struct net *net = genl_info_net(info);
- struct fou_net *fn = net_generic(net, fou_net_id);
- struct sk_buff *msg;
- struct fou_cfg cfg;
- struct fou *fout;
- __be16 port;
- u8 family;
- int ret;
-
- ret = parse_nl_config(info, &cfg);
- if (ret)
- return ret;
- port = cfg.udp_config.local_udp_port;
- if (port == 0)
- return -EINVAL;
-
- family = cfg.udp_config.family;
- if (family != AF_INET && family != AF_INET6)
- return -EINVAL;
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- ret = -ESRCH;
- mutex_lock(&fn->fou_lock);
- list_for_each_entry(fout, &fn->fou_list, list) {
- if (fou_cfg_cmp(fout, &cfg)) {
- ret = fou_dump_info(fout, info->snd_portid,
- info->snd_seq, 0, msg,
- info->genlhdr->cmd);
- break;
- }
- }
- mutex_unlock(&fn->fou_lock);
- if (ret < 0)
- goto out_free;
-
- return genlmsg_reply(msg, info);
-
-out_free:
- nlmsg_free(msg);
- return ret;
-}
-
-static int fou_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
- struct net *net = sock_net(skb->sk);
- struct fou_net *fn = net_generic(net, fou_net_id);
- struct fou *fout;
- int idx = 0, ret;
-
- mutex_lock(&fn->fou_lock);
- list_for_each_entry(fout, &fn->fou_list, list) {
- if (idx++ < cb->args[0])
- continue;
- ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, NLM_F_MULTI,
- skb, FOU_CMD_GET);
- if (ret)
- break;
- }
- mutex_unlock(&fn->fou_lock);
-
- cb->args[0] = idx;
- return skb->len;
-}
-
-static const struct genl_small_ops fou_nl_ops[] = {
- {
- .cmd = FOU_CMD_ADD,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = fou_nl_cmd_add_port,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = FOU_CMD_DEL,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = fou_nl_cmd_rm_port,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = FOU_CMD_GET,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = fou_nl_cmd_get_port,
- .dumpit = fou_nl_dump,
- },
-};
-
-static struct genl_family fou_nl_family __ro_after_init = {
- .hdrsize = 0,
- .name = FOU_GENL_NAME,
- .version = FOU_GENL_VERSION,
- .maxattr = FOU_ATTR_MAX,
- .policy = fou_nl_policy,
- .netnsok = true,
- .module = THIS_MODULE,
- .small_ops = fou_nl_ops,
- .n_small_ops = ARRAY_SIZE(fou_nl_ops),
-};
-
-size_t fou_encap_hlen(struct ip_tunnel_encap *e)
-{
- return sizeof(struct udphdr);
-}
-EXPORT_SYMBOL(fou_encap_hlen);
-
-size_t gue_encap_hlen(struct ip_tunnel_encap *e)
-{
- size_t len;
- bool need_priv = false;
-
- len = sizeof(struct udphdr) + sizeof(struct guehdr);
-
- if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) {
- len += GUE_PLEN_REMCSUM;
- need_priv = true;
- }
-
- len += need_priv ? GUE_LEN_PRIV : 0;
-
- return len;
-}
-EXPORT_SYMBOL(gue_encap_hlen);
-
-int __fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
- u8 *protocol, __be16 *sport, int type)
-{
- int err;
-
- err = iptunnel_handle_offloads(skb, type);
- if (err)
- return err;
-
- *sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
- skb, 0, 0, false);
-
- return 0;
-}
-EXPORT_SYMBOL(__fou_build_header);
-
-int __gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
- u8 *protocol, __be16 *sport, int type)
-{
- struct guehdr *guehdr;
- size_t hdrlen, optlen = 0;
- void *data;
- bool need_priv = false;
- int err;
-
- if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
- skb->ip_summed == CHECKSUM_PARTIAL) {
- optlen += GUE_PLEN_REMCSUM;
- type |= SKB_GSO_TUNNEL_REMCSUM;
- need_priv = true;
- }
-
- optlen += need_priv ? GUE_LEN_PRIV : 0;
-
- err = iptunnel_handle_offloads(skb, type);
- if (err)
- return err;
-
- /* Get source port (based on flow hash) before skb_push */
- *sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
- skb, 0, 0, false);
-
- hdrlen = sizeof(struct guehdr) + optlen;
-
- skb_push(skb, hdrlen);
-
- guehdr = (struct guehdr *)skb->data;
-
- guehdr->control = 0;
- guehdr->version = 0;
- guehdr->hlen = optlen >> 2;
- guehdr->flags = 0;
- guehdr->proto_ctype = *protocol;
-
- data = &guehdr[1];
-
- if (need_priv) {
- __be32 *flags = data;
-
- guehdr->flags |= GUE_FLAG_PRIV;
- *flags = 0;
- data += GUE_LEN_PRIV;
-
- if (type & SKB_GSO_TUNNEL_REMCSUM) {
- u16 csum_start = skb_checksum_start_offset(skb);
- __be16 *pd = data;
-
- if (csum_start < hdrlen)
- return -EINVAL;
-
- csum_start -= hdrlen;
- pd[0] = htons(csum_start);
- pd[1] = htons(csum_start + skb->csum_offset);
-
- if (!skb_is_gso(skb)) {
- skb->ip_summed = CHECKSUM_NONE;
- skb->encapsulation = 0;
- }
-
- *flags |= GUE_PFLAG_REMCSUM;
- data += GUE_PLEN_REMCSUM;
- }
-
- }
-
- return 0;
-}
-EXPORT_SYMBOL(__gue_build_header);
-
-#ifdef CONFIG_NET_FOU_IP_TUNNELS
-
-static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
- struct flowi4 *fl4, u8 *protocol, __be16 sport)
-{
- struct udphdr *uh;
-
- skb_push(skb, sizeof(struct udphdr));
- skb_reset_transport_header(skb);
-
- uh = udp_hdr(skb);
-
- uh->dest = e->dport;
- uh->source = sport;
- uh->len = htons(skb->len);
- udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
- fl4->saddr, fl4->daddr, skb->len);
-
- *protocol = IPPROTO_UDP;
-}
-
-static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
- u8 *protocol, struct flowi4 *fl4)
-{
- int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
- SKB_GSO_UDP_TUNNEL;
- __be16 sport;
- int err;
-
- err = __fou_build_header(skb, e, protocol, &sport, type);
- if (err)
- return err;
-
- fou_build_udp(skb, e, fl4, protocol, sport);
-
- return 0;
-}
-
-static int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
- u8 *protocol, struct flowi4 *fl4)
-{
- int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
- SKB_GSO_UDP_TUNNEL;
- __be16 sport;
- int err;
-
- err = __gue_build_header(skb, e, protocol, &sport, type);
- if (err)
- return err;
-
- fou_build_udp(skb, e, fl4, protocol, sport);
-
- return 0;
-}
-
-static int gue_err_proto_handler(int proto, struct sk_buff *skb, u32 info)
-{
- const struct net_protocol *ipprot = rcu_dereference(inet_protos[proto]);
-
- if (ipprot && ipprot->err_handler) {
- if (!ipprot->err_handler(skb, info))
- return 0;
- }
-
- return -ENOENT;
-}
-
-static int gue_err(struct sk_buff *skb, u32 info)
-{
- int transport_offset = skb_transport_offset(skb);
- struct guehdr *guehdr;
- size_t len, optlen;
- int ret;
-
- len = sizeof(struct udphdr) + sizeof(struct guehdr);
- if (!pskb_may_pull(skb, transport_offset + len))
- return -EINVAL;
-
- guehdr = (struct guehdr *)&udp_hdr(skb)[1];
-
- switch (guehdr->version) {
- case 0: /* Full GUE header present */
- break;
- case 1: {
- /* Direct encapsulation of IPv4 or IPv6 */
- skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
-
- switch (((struct iphdr *)guehdr)->version) {
- case 4:
- ret = gue_err_proto_handler(IPPROTO_IPIP, skb, info);
- goto out;
-#if IS_ENABLED(CONFIG_IPV6)
- case 6:
- ret = gue_err_proto_handler(IPPROTO_IPV6, skb, info);
- goto out;
-#endif
- default:
- ret = -EOPNOTSUPP;
- goto out;
- }
- }
- default: /* Undefined version */
- return -EOPNOTSUPP;
- }
-
- if (guehdr->control)
- return -ENOENT;
-
- optlen = guehdr->hlen << 2;
-
- if (!pskb_may_pull(skb, transport_offset + len + optlen))
- return -EINVAL;
-
- guehdr = (struct guehdr *)&udp_hdr(skb)[1];
- if (validate_gue_flags(guehdr, optlen))
- return -EINVAL;
-
- /* Handling exceptions for direct UDP encapsulation in GUE would lead to
- * recursion. Besides, this kind of encapsulation can't even be
- * configured currently. Discard this.
- */
- if (guehdr->proto_ctype == IPPROTO_UDP ||
- guehdr->proto_ctype == IPPROTO_UDPLITE)
- return -EOPNOTSUPP;
-
- skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
- ret = gue_err_proto_handler(guehdr->proto_ctype, skb, info);
-
-out:
- skb_set_transport_header(skb, transport_offset);
- return ret;
-}
-
-
-static const struct ip_tunnel_encap_ops fou_iptun_ops = {
- .encap_hlen = fou_encap_hlen,
- .build_header = fou_build_header,
- .err_handler = gue_err,
-};
-
-static const struct ip_tunnel_encap_ops gue_iptun_ops = {
- .encap_hlen = gue_encap_hlen,
- .build_header = gue_build_header,
- .err_handler = gue_err,
-};
-
-static int ip_tunnel_encap_add_fou_ops(void)
-{
- int ret;
-
- ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
- if (ret < 0) {
- pr_err("can't add fou ops\n");
- return ret;
- }
-
- ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
- if (ret < 0) {
- pr_err("can't add gue ops\n");
- ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
- return ret;
- }
-
- return 0;
-}
-
-static void ip_tunnel_encap_del_fou_ops(void)
-{
- ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
- ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
-}
-
-#else
-
-static int ip_tunnel_encap_add_fou_ops(void)
-{
- return 0;
-}
-
-static void ip_tunnel_encap_del_fou_ops(void)
-{
-}
-
-#endif
-
-static __net_init int fou_init_net(struct net *net)
-{
- struct fou_net *fn = net_generic(net, fou_net_id);
-
- INIT_LIST_HEAD(&fn->fou_list);
- mutex_init(&fn->fou_lock);
- return 0;
-}
-
-static __net_exit void fou_exit_net(struct net *net)
-{
- struct fou_net *fn = net_generic(net, fou_net_id);
- struct fou *fou, *next;
-
- /* Close all the FOU sockets */
- mutex_lock(&fn->fou_lock);
- list_for_each_entry_safe(fou, next, &fn->fou_list, list)
- fou_release(fou);
- mutex_unlock(&fn->fou_lock);
-}
-
-static struct pernet_operations fou_net_ops = {
- .init = fou_init_net,
- .exit = fou_exit_net,
- .id = &fou_net_id,
- .size = sizeof(struct fou_net),
-};
-
-static int __init fou_init(void)
-{
- int ret;
-
- ret = register_pernet_device(&fou_net_ops);
- if (ret)
- goto exit;
-
- ret = genl_register_family(&fou_nl_family);
- if (ret < 0)
- goto unregister;
-
- ret = ip_tunnel_encap_add_fou_ops();
- if (ret == 0)
- return 0;
-
- genl_unregister_family(&fou_nl_family);
-unregister:
- unregister_pernet_device(&fou_net_ops);
-exit:
- return ret;
-}
-
-static void __exit fou_fini(void)
-{
- ip_tunnel_encap_del_fou_ops();
- genl_unregister_family(&fou_nl_family);
- unregister_pernet_device(&fou_net_ops);
-}
-
-module_init(fou_init);
-module_exit(fou_fini);
-MODULE_AUTHOR("Tom Herbert <therbert@...gle.com>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Foo over UDP");
diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c
new file mode 100644
index 000000000000..118b48279da3
--- /dev/null
+++ b/net/ipv4/fou_core.c
@@ -0,0 +1,1285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <net/gue.h>
+#include <net/fou.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/udp_tunnel.h>
+#include <net/xfrm.h>
+#include <uapi/linux/fou.h>
+#include <uapi/linux/genetlink.h>
+
+#include "fou_nl.h"
+
+struct fou {
+ struct socket *sock;
+ u8 protocol;
+ u8 flags;
+ __be16 port;
+ u8 family;
+ u16 type;
+ struct list_head list;
+ struct rcu_head rcu;
+};
+
+#define FOU_F_REMCSUM_NOPARTIAL BIT(0)
+
+struct fou_cfg {
+ u16 type;
+ u8 protocol;
+ u8 flags;
+ struct udp_port_cfg udp_config;
+};
+
+static unsigned int fou_net_id;
+
+struct fou_net {
+ struct list_head fou_list;
+ struct mutex fou_lock;
+};
+
+static inline struct fou *fou_from_sock(struct sock *sk)
+{
+ return rcu_dereference_sk_user_data(sk);
+}
+
+static int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len)
+{
+ /* Remove 'len' bytes from the packet (UDP header and
+ * FOU header if present).
+ */
+ if (fou->family == AF_INET)
+ ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
+ else
+ ipv6_hdr(skb)->payload_len =
+ htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
+
+ __skb_pull(skb, len);
+ skb_postpull_rcsum(skb, udp_hdr(skb), len);
+ skb_reset_transport_header(skb);
+ return iptunnel_pull_offloads(skb);
+}
+
+static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
+{
+ struct fou *fou = fou_from_sock(sk);
+
+ if (!fou)
+ return 1;
+
+ if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
+ goto drop;
+
+ return -fou->protocol;
+
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
+static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
+ void *data, size_t hdrlen, u8 ipproto,
+ bool nopartial)
+{
+ __be16 *pd = data;
+ size_t start = ntohs(pd[0]);
+ size_t offset = ntohs(pd[1]);
+ size_t plen = sizeof(struct udphdr) + hdrlen +
+ max_t(size_t, offset + sizeof(u16), start);
+
+ if (skb->remcsum_offload)
+ return guehdr;
+
+ if (!pskb_may_pull(skb, plen))
+ return NULL;
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+ skb_remcsum_process(skb, (void *)guehdr + hdrlen,
+ start, offset, nopartial);
+
+ return guehdr;
+}
+
+static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
+{
+ /* No support yet */
+ kfree_skb(skb);
+ return 0;
+}
+
+static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
+{
+ struct fou *fou = fou_from_sock(sk);
+ size_t len, optlen, hdrlen;
+ struct guehdr *guehdr;
+ void *data;
+ u16 doffset = 0;
+ u8 proto_ctype;
+
+ if (!fou)
+ return 1;
+
+ len = sizeof(struct udphdr) + sizeof(struct guehdr);
+ if (!pskb_may_pull(skb, len))
+ goto drop;
+
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+ switch (guehdr->version) {
+ case 0: /* Full GUE header present */
+ break;
+
+ case 1: {
+ /* Direct encapsulation of IPv4 or IPv6 */
+
+ int prot;
+
+ switch (((struct iphdr *)guehdr)->version) {
+ case 4:
+ prot = IPPROTO_IPIP;
+ break;
+ case 6:
+ prot = IPPROTO_IPV6;
+ break;
+ default:
+ goto drop;
+ }
+
+ if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
+ goto drop;
+
+ return -prot;
+ }
+
+ default: /* Undefined version */
+ goto drop;
+ }
+
+ optlen = guehdr->hlen << 2;
+ len += optlen;
+
+ if (!pskb_may_pull(skb, len))
+ goto drop;
+
+ /* guehdr may change after pull */
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+ if (validate_gue_flags(guehdr, optlen))
+ goto drop;
+
+ hdrlen = sizeof(struct guehdr) + optlen;
+
+ if (fou->family == AF_INET)
+ ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
+ else
+ ipv6_hdr(skb)->payload_len =
+ htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
+
+ /* Pull csum through the guehdr now . This can be used if
+ * there is a remote checksum offload.
+ */
+ skb_postpull_rcsum(skb, udp_hdr(skb), len);
+
+ data = &guehdr[1];
+
+ if (guehdr->flags & GUE_FLAG_PRIV) {
+ __be32 flags = *(__be32 *)(data + doffset);
+
+ doffset += GUE_LEN_PRIV;
+
+ if (flags & GUE_PFLAG_REMCSUM) {
+ guehdr = gue_remcsum(skb, guehdr, data + doffset,
+ hdrlen, guehdr->proto_ctype,
+ !!(fou->flags &
+ FOU_F_REMCSUM_NOPARTIAL));
+ if (!guehdr)
+ goto drop;
+
+ data = &guehdr[1];
+
+ doffset += GUE_PLEN_REMCSUM;
+ }
+ }
+
+ if (unlikely(guehdr->control))
+ return gue_control_message(skb, guehdr);
+
+ proto_ctype = guehdr->proto_ctype;
+ if (unlikely(!proto_ctype))
+ goto drop;
+
+ __skb_pull(skb, sizeof(struct udphdr) + hdrlen);
+ skb_reset_transport_header(skb);
+
+ if (iptunnel_pull_offloads(skb))
+ goto drop;
+
+ return -proto_ctype;
+
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
+static struct sk_buff *fou_gro_receive(struct sock *sk,
+ struct list_head *head,
+ struct sk_buff *skb)
+{
+ const struct net_offload __rcu **offloads;
+ struct fou *fou = fou_from_sock(sk);
+ const struct net_offload *ops;
+ struct sk_buff *pp = NULL;
+ u8 proto;
+
+ if (!fou)
+ goto out;
+
+ proto = fou->protocol;
+
+ /* We can clear the encap_mark for FOU as we are essentially doing
+ * one of two possible things. We are either adding an L4 tunnel
+ * header to the outer L3 tunnel header, or we are simply
+ * treating the GRE tunnel header as though it is a UDP protocol
+ * specific header such as VXLAN or GENEVE.
+ */
+ NAPI_GRO_CB(skb)->encap_mark = 0;
+
+ /* Flag this frame as already having an outer encap header */
+ NAPI_GRO_CB(skb)->is_fou = 1;
+
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
+ ops = rcu_dereference(offloads[proto]);
+ if (!ops || !ops->callbacks.gro_receive)
+ goto out;
+
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
+
+out:
+ return pp;
+}
+
+static int fou_gro_complete(struct sock *sk, struct sk_buff *skb,
+ int nhoff)
+{
+ const struct net_offload __rcu **offloads;
+ struct fou *fou = fou_from_sock(sk);
+ const struct net_offload *ops;
+ u8 proto;
+ int err;
+
+ if (!fou) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ proto = fou->protocol;
+
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
+ ops = rcu_dereference(offloads[proto]);
+ if (WARN_ON(!ops || !ops->callbacks.gro_complete)) {
+ err = -ENOSYS;
+ goto out;
+ }
+
+ err = ops->callbacks.gro_complete(skb, nhoff);
+
+ skb_set_inner_mac_header(skb, nhoff);
+
+out:
+ return err;
+}
+
+static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
+ struct guehdr *guehdr, void *data,
+ size_t hdrlen, struct gro_remcsum *grc,
+ bool nopartial)
+{
+ __be16 *pd = data;
+ size_t start = ntohs(pd[0]);
+ size_t offset = ntohs(pd[1]);
+
+ if (skb->remcsum_offload)
+ return guehdr;
+
+ if (!NAPI_GRO_CB(skb)->csum_valid)
+ return NULL;
+
+ guehdr = skb_gro_remcsum_process(skb, (void *)guehdr, off, hdrlen,
+ start, offset, grc, nopartial);
+
+ skb->remcsum_offload = 1;
+
+ return guehdr;
+}
+
+static struct sk_buff *gue_gro_receive(struct sock *sk,
+ struct list_head *head,
+ struct sk_buff *skb)
+{
+ const struct net_offload __rcu **offloads;
+ const struct net_offload *ops;
+ struct sk_buff *pp = NULL;
+ struct sk_buff *p;
+ struct guehdr *guehdr;
+ size_t len, optlen, hdrlen, off;
+ void *data;
+ u16 doffset = 0;
+ int flush = 1;
+ struct fou *fou = fou_from_sock(sk);
+ struct gro_remcsum grc;
+ u8 proto;
+
+ skb_gro_remcsum_init(&grc);
+
+ if (!fou)
+ goto out;
+
+ off = skb_gro_offset(skb);
+ len = off + sizeof(*guehdr);
+
+ guehdr = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, len)) {
+ guehdr = skb_gro_header_slow(skb, len, off);
+ if (unlikely(!guehdr))
+ goto out;
+ }
+
+ switch (guehdr->version) {
+ case 0:
+ break;
+ case 1:
+ switch (((struct iphdr *)guehdr)->version) {
+ case 4:
+ proto = IPPROTO_IPIP;
+ break;
+ case 6:
+ proto = IPPROTO_IPV6;
+ break;
+ default:
+ goto out;
+ }
+ goto next_proto;
+ default:
+ goto out;
+ }
+
+ optlen = guehdr->hlen << 2;
+ len += optlen;
+
+ if (skb_gro_header_hard(skb, len)) {
+ guehdr = skb_gro_header_slow(skb, len, off);
+ if (unlikely(!guehdr))
+ goto out;
+ }
+
+ if (unlikely(guehdr->control) || guehdr->version != 0 ||
+ validate_gue_flags(guehdr, optlen))
+ goto out;
+
+ hdrlen = sizeof(*guehdr) + optlen;
+
+ /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr,
+ * this is needed if there is a remote checkcsum offload.
+ */
+ skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
+
+ data = &guehdr[1];
+
+ if (guehdr->flags & GUE_FLAG_PRIV) {
+ __be32 flags = *(__be32 *)(data + doffset);
+
+ doffset += GUE_LEN_PRIV;
+
+ if (flags & GUE_PFLAG_REMCSUM) {
+ guehdr = gue_gro_remcsum(skb, off, guehdr,
+ data + doffset, hdrlen, &grc,
+ !!(fou->flags &
+ FOU_F_REMCSUM_NOPARTIAL));
+
+ if (!guehdr)
+ goto out;
+
+ data = &guehdr[1];
+
+ doffset += GUE_PLEN_REMCSUM;
+ }
+ }
+
+ skb_gro_pull(skb, hdrlen);
+
+ list_for_each_entry(p, head, list) {
+ const struct guehdr *guehdr2;
+
+ if (!NAPI_GRO_CB(p)->same_flow)
+ continue;
+
+ guehdr2 = (struct guehdr *)(p->data + off);
+
+ /* Compare base GUE header to be equal (covers
+ * hlen, version, proto_ctype, and flags.
+ */
+ if (guehdr->word != guehdr2->word) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+
+ /* Compare optional fields are the same. */
+ if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
+ guehdr->hlen << 2)) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+ }
+
+ proto = guehdr->proto_ctype;
+
+next_proto:
+
+ /* We can clear the encap_mark for GUE as we are essentially doing
+ * one of two possible things. We are either adding an L4 tunnel
+ * header to the outer L3 tunnel header, or we are simply
+ * treating the GRE tunnel header as though it is a UDP protocol
+ * specific header such as VXLAN or GENEVE.
+ */
+ NAPI_GRO_CB(skb)->encap_mark = 0;
+
+ /* Flag this frame as already having an outer encap header */
+ NAPI_GRO_CB(skb)->is_fou = 1;
+
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
+ ops = rcu_dereference(offloads[proto]);
+ if (!ops || !ops->callbacks.gro_receive)
+ goto out;
+
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
+ flush = 0;
+
+out:
+ skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
+
+ return pp;
+}
+
+static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
+{
+ struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
+ const struct net_offload __rcu **offloads;
+ const struct net_offload *ops;
+ unsigned int guehlen = 0;
+ u8 proto;
+ int err = -ENOENT;
+
+ switch (guehdr->version) {
+ case 0:
+ proto = guehdr->proto_ctype;
+ guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
+ break;
+ case 1:
+ switch (((struct iphdr *)guehdr)->version) {
+ case 4:
+ proto = IPPROTO_IPIP;
+ break;
+ case 6:
+ proto = IPPROTO_IPV6;
+ break;
+ default:
+ return err;
+ }
+ break;
+ default:
+ return err;
+ }
+
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
+ ops = rcu_dereference(offloads[proto]);
+ if (WARN_ON(!ops || !ops->callbacks.gro_complete))
+ goto out;
+
+ err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
+
+ skb_set_inner_mac_header(skb, nhoff + guehlen);
+
+out:
+ return err;
+}
+
+static bool fou_cfg_cmp(struct fou *fou, struct fou_cfg *cfg)
+{
+ struct sock *sk = fou->sock->sk;
+ struct udp_port_cfg *udp_cfg = &cfg->udp_config;
+
+ if (fou->family != udp_cfg->family ||
+ fou->port != udp_cfg->local_udp_port ||
+ sk->sk_dport != udp_cfg->peer_udp_port ||
+ sk->sk_bound_dev_if != udp_cfg->bind_ifindex)
+ return false;
+
+ if (fou->family == AF_INET) {
+ if (sk->sk_rcv_saddr != udp_cfg->local_ip.s_addr ||
+ sk->sk_daddr != udp_cfg->peer_ip.s_addr)
+ return false;
+ else
+ return true;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ if (ipv6_addr_cmp(&sk->sk_v6_rcv_saddr, &udp_cfg->local_ip6) ||
+ ipv6_addr_cmp(&sk->sk_v6_daddr, &udp_cfg->peer_ip6))
+ return false;
+ else
+ return true;
+#endif
+ }
+
+ return false;
+}
+
+static int fou_add_to_port_list(struct net *net, struct fou *fou,
+ struct fou_cfg *cfg)
+{
+ struct fou_net *fn = net_generic(net, fou_net_id);
+ struct fou *fout;
+
+ mutex_lock(&fn->fou_lock);
+ list_for_each_entry(fout, &fn->fou_list, list) {
+ if (fou_cfg_cmp(fout, cfg)) {
+ mutex_unlock(&fn->fou_lock);
+ return -EALREADY;
+ }
+ }
+
+ list_add(&fou->list, &fn->fou_list);
+ mutex_unlock(&fn->fou_lock);
+
+ return 0;
+}
+
+static void fou_release(struct fou *fou)
+{
+ struct socket *sock = fou->sock;
+
+ list_del(&fou->list);
+ udp_tunnel_sock_release(sock);
+
+ kfree_rcu(fou, rcu);
+}
+
+static int fou_create(struct net *net, struct fou_cfg *cfg,
+ struct socket **sockp)
+{
+ struct socket *sock = NULL;
+ struct fou *fou = NULL;
+ struct sock *sk;
+ struct udp_tunnel_sock_cfg tunnel_cfg;
+ int err;
+
+ /* Open UDP socket */
+ err = udp_sock_create(net, &cfg->udp_config, &sock);
+ if (err < 0)
+ goto error;
+
+ /* Allocate FOU port structure */
+ fou = kzalloc(sizeof(*fou), GFP_KERNEL);
+ if (!fou) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ sk = sock->sk;
+
+ fou->port = cfg->udp_config.local_udp_port;
+ fou->family = cfg->udp_config.family;
+ fou->flags = cfg->flags;
+ fou->type = cfg->type;
+ fou->sock = sock;
+
+ memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
+ tunnel_cfg.encap_type = 1;
+ tunnel_cfg.sk_user_data = fou;
+ tunnel_cfg.encap_destroy = NULL;
+
+ /* Initial for fou type */
+ switch (cfg->type) {
+ case FOU_ENCAP_DIRECT:
+ tunnel_cfg.encap_rcv = fou_udp_recv;
+ tunnel_cfg.gro_receive = fou_gro_receive;
+ tunnel_cfg.gro_complete = fou_gro_complete;
+ fou->protocol = cfg->protocol;
+ break;
+ case FOU_ENCAP_GUE:
+ tunnel_cfg.encap_rcv = gue_udp_recv;
+ tunnel_cfg.gro_receive = gue_gro_receive;
+ tunnel_cfg.gro_complete = gue_gro_complete;
+ break;
+ default:
+ err = -EINVAL;
+ goto error;
+ }
+
+ setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
+
+ sk->sk_allocation = GFP_ATOMIC;
+
+ err = fou_add_to_port_list(net, fou, cfg);
+ if (err)
+ goto error;
+
+ if (sockp)
+ *sockp = sock;
+
+ return 0;
+
+error:
+ kfree(fou);
+ if (sock)
+ udp_tunnel_sock_release(sock);
+
+ return err;
+}
+
+static int fou_destroy(struct net *net, struct fou_cfg *cfg)
+{
+ struct fou_net *fn = net_generic(net, fou_net_id);
+ int err = -EINVAL;
+ struct fou *fou;
+
+ mutex_lock(&fn->fou_lock);
+ list_for_each_entry(fou, &fn->fou_list, list) {
+ if (fou_cfg_cmp(fou, cfg)) {
+ fou_release(fou);
+ err = 0;
+ break;
+ }
+ }
+ mutex_unlock(&fn->fou_lock);
+
+ return err;
+}
+
+static struct genl_family fou_nl_family;
+
+static int parse_nl_config(struct genl_info *info,
+ struct fou_cfg *cfg)
+{
+ bool has_local = false, has_peer = false;
+ struct nlattr *attr;
+ int ifindex;
+ __be16 port;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ cfg->udp_config.family = AF_INET;
+
+ if (info->attrs[FOU_ATTR_AF]) {
+ u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
+
+ switch (family) {
+ case AF_INET:
+ break;
+ case AF_INET6:
+ cfg->udp_config.ipv6_v6only = 1;
+ break;
+ default:
+ return -EAFNOSUPPORT;
+ }
+
+ cfg->udp_config.family = family;
+ }
+
+ if (info->attrs[FOU_ATTR_PORT]) {
+ port = nla_get_be16(info->attrs[FOU_ATTR_PORT]);
+ cfg->udp_config.local_udp_port = port;
+ }
+
+ if (info->attrs[FOU_ATTR_IPPROTO])
+ cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
+
+ if (info->attrs[FOU_ATTR_TYPE])
+ cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
+
+ if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL])
+ cfg->flags |= FOU_F_REMCSUM_NOPARTIAL;
+
+ if (cfg->udp_config.family == AF_INET) {
+ if (info->attrs[FOU_ATTR_LOCAL_V4]) {
+ attr = info->attrs[FOU_ATTR_LOCAL_V4];
+ cfg->udp_config.local_ip.s_addr = nla_get_in_addr(attr);
+ has_local = true;
+ }
+
+ if (info->attrs[FOU_ATTR_PEER_V4]) {
+ attr = info->attrs[FOU_ATTR_PEER_V4];
+ cfg->udp_config.peer_ip.s_addr = nla_get_in_addr(attr);
+ has_peer = true;
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ if (info->attrs[FOU_ATTR_LOCAL_V6]) {
+ attr = info->attrs[FOU_ATTR_LOCAL_V6];
+ cfg->udp_config.local_ip6 = nla_get_in6_addr(attr);
+ has_local = true;
+ }
+
+ if (info->attrs[FOU_ATTR_PEER_V6]) {
+ attr = info->attrs[FOU_ATTR_PEER_V6];
+ cfg->udp_config.peer_ip6 = nla_get_in6_addr(attr);
+ has_peer = true;
+ }
+#endif
+ }
+
+ if (has_peer) {
+ if (info->attrs[FOU_ATTR_PEER_PORT]) {
+ port = nla_get_be16(info->attrs[FOU_ATTR_PEER_PORT]);
+ cfg->udp_config.peer_udp_port = port;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ if (info->attrs[FOU_ATTR_IFINDEX]) {
+ if (!has_local)
+ return -EINVAL;
+
+ ifindex = nla_get_s32(info->attrs[FOU_ATTR_IFINDEX]);
+
+ cfg->udp_config.bind_ifindex = ifindex;
+ }
+
+ return 0;
+}
+
+int fou_nl_add_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct fou_cfg cfg;
+ int err;
+
+ err = parse_nl_config(info, &cfg);
+ if (err)
+ return err;
+
+ return fou_create(net, &cfg, NULL);
+}
+
+int fou_nl_del_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct fou_cfg cfg;
+ int err;
+
+ err = parse_nl_config(info, &cfg);
+ if (err)
+ return err;
+
+ return fou_destroy(net, &cfg);
+}
+
+static int fou_fill_info(struct fou *fou, struct sk_buff *msg)
+{
+ struct sock *sk = fou->sock->sk;
+
+ if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) ||
+ nla_put_be16(msg, FOU_ATTR_PORT, fou->port) ||
+ nla_put_be16(msg, FOU_ATTR_PEER_PORT, sk->sk_dport) ||
+ nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) ||
+ nla_put_u8(msg, FOU_ATTR_TYPE, fou->type) ||
+ nla_put_s32(msg, FOU_ATTR_IFINDEX, sk->sk_bound_dev_if))
+ return -1;
+
+ if (fou->flags & FOU_F_REMCSUM_NOPARTIAL)
+ if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL))
+ return -1;
+
+ if (fou->sock->sk->sk_family == AF_INET) {
+ if (nla_put_in_addr(msg, FOU_ATTR_LOCAL_V4, sk->sk_rcv_saddr))
+ return -1;
+
+ if (nla_put_in_addr(msg, FOU_ATTR_PEER_V4, sk->sk_daddr))
+ return -1;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ if (nla_put_in6_addr(msg, FOU_ATTR_LOCAL_V6,
+ &sk->sk_v6_rcv_saddr))
+ return -1;
+
+ if (nla_put_in6_addr(msg, FOU_ATTR_PEER_V6, &sk->sk_v6_daddr))
+ return -1;
+#endif
+ }
+
+ return 0;
+}
+
+static int fou_dump_info(struct fou *fou, u32 portid, u32 seq,
+ u32 flags, struct sk_buff *skb, u8 cmd)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd);
+ if (!hdr)
+ return -ENOMEM;
+
+ if (fou_fill_info(fou, skb) < 0)
+ goto nla_put_failure;
+
+ genlmsg_end(skb, hdr);
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+int fou_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct fou_net *fn = net_generic(net, fou_net_id);
+ struct sk_buff *msg;
+ struct fou_cfg cfg;
+ struct fou *fout;
+ __be16 port;
+ u8 family;
+ int ret;
+
+ ret = parse_nl_config(info, &cfg);
+ if (ret)
+ return ret;
+ port = cfg.udp_config.local_udp_port;
+ if (port == 0)
+ return -EINVAL;
+
+ family = cfg.udp_config.family;
+ if (family != AF_INET && family != AF_INET6)
+ return -EINVAL;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ ret = -ESRCH;
+ mutex_lock(&fn->fou_lock);
+ list_for_each_entry(fout, &fn->fou_list, list) {
+ if (fou_cfg_cmp(fout, &cfg)) {
+ ret = fou_dump_info(fout, info->snd_portid,
+ info->snd_seq, 0, msg,
+ info->genlhdr->cmd);
+ break;
+ }
+ }
+ mutex_unlock(&fn->fou_lock);
+ if (ret < 0)
+ goto out_free;
+
+ return genlmsg_reply(msg, info);
+
+out_free:
+ nlmsg_free(msg);
+ return ret;
+}
+
+int fou_nl_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct fou_net *fn = net_generic(net, fou_net_id);
+ struct fou *fout;
+ int idx = 0, ret;
+
+ mutex_lock(&fn->fou_lock);
+ list_for_each_entry(fout, &fn->fou_list, list) {
+ if (idx++ < cb->args[0])
+ continue;
+ ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ skb, FOU_CMD_GET);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&fn->fou_lock);
+
+ cb->args[0] = idx;
+ return skb->len;
+}
+
+static struct genl_family fou_nl_family __ro_after_init = {
+ .hdrsize = 0,
+ .name = FOU_GENL_NAME,
+ .version = FOU_GENL_VERSION,
+ .maxattr = FOU_ATTR_MAX,
+ .policy = fou_nl_policy,
+ .netnsok = true,
+ .module = THIS_MODULE,
+ .small_ops = fou_nl_ops,
+ .n_small_ops = ARRAY_SIZE(fou_nl_ops),
+};
+
+size_t fou_encap_hlen(struct ip_tunnel_encap *e)
+{
+ return sizeof(struct udphdr);
+}
+EXPORT_SYMBOL(fou_encap_hlen);
+
+size_t gue_encap_hlen(struct ip_tunnel_encap *e)
+{
+ size_t len;
+ bool need_priv = false;
+
+ len = sizeof(struct udphdr) + sizeof(struct guehdr);
+
+ if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) {
+ len += GUE_PLEN_REMCSUM;
+ need_priv = true;
+ }
+
+ len += need_priv ? GUE_LEN_PRIV : 0;
+
+ return len;
+}
+EXPORT_SYMBOL(gue_encap_hlen);
+
+int __fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ u8 *protocol, __be16 *sport, int type)
+{
+ int err;
+
+ err = iptunnel_handle_offloads(skb, type);
+ if (err)
+ return err;
+
+ *sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
+ skb, 0, 0, false);
+
+ return 0;
+}
+EXPORT_SYMBOL(__fou_build_header);
+
+int __gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ u8 *protocol, __be16 *sport, int type)
+{
+ struct guehdr *guehdr;
+ size_t hdrlen, optlen = 0;
+ void *data;
+ bool need_priv = false;
+ int err;
+
+ if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
+ skb->ip_summed == CHECKSUM_PARTIAL) {
+ optlen += GUE_PLEN_REMCSUM;
+ type |= SKB_GSO_TUNNEL_REMCSUM;
+ need_priv = true;
+ }
+
+ optlen += need_priv ? GUE_LEN_PRIV : 0;
+
+ err = iptunnel_handle_offloads(skb, type);
+ if (err)
+ return err;
+
+ /* Get source port (based on flow hash) before skb_push */
+ *sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
+ skb, 0, 0, false);
+
+ hdrlen = sizeof(struct guehdr) + optlen;
+
+ skb_push(skb, hdrlen);
+
+ guehdr = (struct guehdr *)skb->data;
+
+ guehdr->control = 0;
+ guehdr->version = 0;
+ guehdr->hlen = optlen >> 2;
+ guehdr->flags = 0;
+ guehdr->proto_ctype = *protocol;
+
+ data = &guehdr[1];
+
+ if (need_priv) {
+ __be32 *flags = data;
+
+ guehdr->flags |= GUE_FLAG_PRIV;
+ *flags = 0;
+ data += GUE_LEN_PRIV;
+
+ if (type & SKB_GSO_TUNNEL_REMCSUM) {
+ u16 csum_start = skb_checksum_start_offset(skb);
+ __be16 *pd = data;
+
+ if (csum_start < hdrlen)
+ return -EINVAL;
+
+ csum_start -= hdrlen;
+ pd[0] = htons(csum_start);
+ pd[1] = htons(csum_start + skb->csum_offset);
+
+ if (!skb_is_gso(skb)) {
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->encapsulation = 0;
+ }
+
+ *flags |= GUE_PFLAG_REMCSUM;
+ data += GUE_PLEN_REMCSUM;
+ }
+
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(__gue_build_header);
+
+#ifdef CONFIG_NET_FOU_IP_TUNNELS
+
+static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ struct flowi4 *fl4, u8 *protocol, __be16 sport)
+{
+ struct udphdr *uh;
+
+ skb_push(skb, sizeof(struct udphdr));
+ skb_reset_transport_header(skb);
+
+ uh = udp_hdr(skb);
+
+ uh->dest = e->dport;
+ uh->source = sport;
+ uh->len = htons(skb->len);
+ udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
+ fl4->saddr, fl4->daddr, skb->len);
+
+ *protocol = IPPROTO_UDP;
+}
+
+static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ u8 *protocol, struct flowi4 *fl4)
+{
+ int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
+ SKB_GSO_UDP_TUNNEL;
+ __be16 sport;
+ int err;
+
+ err = __fou_build_header(skb, e, protocol, &sport, type);
+ if (err)
+ return err;
+
+ fou_build_udp(skb, e, fl4, protocol, sport);
+
+ return 0;
+}
+
+static int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ u8 *protocol, struct flowi4 *fl4)
+{
+ int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
+ SKB_GSO_UDP_TUNNEL;
+ __be16 sport;
+ int err;
+
+ err = __gue_build_header(skb, e, protocol, &sport, type);
+ if (err)
+ return err;
+
+ fou_build_udp(skb, e, fl4, protocol, sport);
+
+ return 0;
+}
+
+static int gue_err_proto_handler(int proto, struct sk_buff *skb, u32 info)
+{
+ const struct net_protocol *ipprot = rcu_dereference(inet_protos[proto]);
+
+ if (ipprot && ipprot->err_handler) {
+ if (!ipprot->err_handler(skb, info))
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int gue_err(struct sk_buff *skb, u32 info)
+{
+ int transport_offset = skb_transport_offset(skb);
+ struct guehdr *guehdr;
+ size_t len, optlen;
+ int ret;
+
+ len = sizeof(struct udphdr) + sizeof(struct guehdr);
+ if (!pskb_may_pull(skb, transport_offset + len))
+ return -EINVAL;
+
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+ switch (guehdr->version) {
+ case 0: /* Full GUE header present */
+ break;
+ case 1: {
+ /* Direct encapsulation of IPv4 or IPv6 */
+ skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
+
+ switch (((struct iphdr *)guehdr)->version) {
+ case 4:
+ ret = gue_err_proto_handler(IPPROTO_IPIP, skb, info);
+ goto out;
+#if IS_ENABLED(CONFIG_IPV6)
+ case 6:
+ ret = gue_err_proto_handler(IPPROTO_IPV6, skb, info);
+ goto out;
+#endif
+ default:
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+ }
+ default: /* Undefined version */
+ return -EOPNOTSUPP;
+ }
+
+ if (guehdr->control)
+ return -ENOENT;
+
+ optlen = guehdr->hlen << 2;
+
+ if (!pskb_may_pull(skb, transport_offset + len + optlen))
+ return -EINVAL;
+
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+ if (validate_gue_flags(guehdr, optlen))
+ return -EINVAL;
+
+ /* Handling exceptions for direct UDP encapsulation in GUE would lead to
+ * recursion. Besides, this kind of encapsulation can't even be
+ * configured currently. Discard this.
+ */
+ if (guehdr->proto_ctype == IPPROTO_UDP ||
+ guehdr->proto_ctype == IPPROTO_UDPLITE)
+ return -EOPNOTSUPP;
+
+ skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
+ ret = gue_err_proto_handler(guehdr->proto_ctype, skb, info);
+
+out:
+ skb_set_transport_header(skb, transport_offset);
+ return ret;
+}
+
+
+static const struct ip_tunnel_encap_ops fou_iptun_ops = {
+ .encap_hlen = fou_encap_hlen,
+ .build_header = fou_build_header,
+ .err_handler = gue_err,
+};
+
+static const struct ip_tunnel_encap_ops gue_iptun_ops = {
+ .encap_hlen = gue_encap_hlen,
+ .build_header = gue_build_header,
+ .err_handler = gue_err,
+};
+
+static int ip_tunnel_encap_add_fou_ops(void)
+{
+ int ret;
+
+ ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
+ if (ret < 0) {
+ pr_err("can't add fou ops\n");
+ return ret;
+ }
+
+ ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
+ if (ret < 0) {
+ pr_err("can't add gue ops\n");
+ ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ip_tunnel_encap_del_fou_ops(void)
+{
+ ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
+ ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
+}
+
+#else
+
+static int ip_tunnel_encap_add_fou_ops(void)
+{
+ return 0;
+}
+
+static void ip_tunnel_encap_del_fou_ops(void)
+{
+}
+
+#endif
+
+static __net_init int fou_init_net(struct net *net)
+{
+ struct fou_net *fn = net_generic(net, fou_net_id);
+
+ INIT_LIST_HEAD(&fn->fou_list);
+ mutex_init(&fn->fou_lock);
+ return 0;
+}
+
+static __net_exit void fou_exit_net(struct net *net)
+{
+ struct fou_net *fn = net_generic(net, fou_net_id);
+ struct fou *fou, *next;
+
+ /* Close all the FOU sockets */
+ mutex_lock(&fn->fou_lock);
+ list_for_each_entry_safe(fou, next, &fn->fou_list, list)
+ fou_release(fou);
+ mutex_unlock(&fn->fou_lock);
+}
+
+static struct pernet_operations fou_net_ops = {
+ .init = fou_init_net,
+ .exit = fou_exit_net,
+ .id = &fou_net_id,
+ .size = sizeof(struct fou_net),
+};
+
+static int __init fou_init(void)
+{
+ int ret;
+
+ ret = register_pernet_device(&fou_net_ops);
+ if (ret)
+ goto exit;
+
+ ret = genl_register_family(&fou_nl_family);
+ if (ret < 0)
+ goto unregister;
+
+ ret = ip_tunnel_encap_add_fou_ops();
+ if (ret == 0)
+ return 0;
+
+ genl_unregister_family(&fou_nl_family);
+unregister:
+ unregister_pernet_device(&fou_net_ops);
+exit:
+ return ret;
+}
+
+static void __exit fou_fini(void)
+{
+ ip_tunnel_encap_del_fou_ops();
+ genl_unregister_family(&fou_nl_family);
+ unregister_pernet_device(&fou_net_ops);
+}
+
+module_init(fou_init);
+module_exit(fou_fini);
+MODULE_AUTHOR("Tom Herbert <therbert@...gle.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Foo over UDP");
diff --git a/net/ipv4/fou_nl.c b/net/ipv4/fou_nl.c
new file mode 100644
index 000000000000..5bb8133ed7a8
--- /dev/null
+++ b/net/ipv4/fou_nl.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/fou.yaml */
+/* YNL-GEN kernel source */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "fou_nl.h"
+
+#include <linux/fou.h>
+
+/* Global operation policy for fou */
+const struct nla_policy fou_nl_policy[FOU_ATTR_IFINDEX + 1] = {
+ [FOU_ATTR_PORT] = { .type = NLA_U16, },
+ [FOU_ATTR_AF] = { .type = NLA_U8, },
+ [FOU_ATTR_IPPROTO] = NLA_POLICY_MIN(NLA_U8, 1),
+ [FOU_ATTR_TYPE] = { .type = NLA_U8, },
+ [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, },
+ [FOU_ATTR_LOCAL_V4] = { .type = NLA_U32, },
+ [FOU_ATTR_LOCAL_V6] = { .len = 16, },
+ [FOU_ATTR_PEER_V4] = { .type = NLA_U32, },
+ [FOU_ATTR_PEER_V6] = { .len = 16, },
+ [FOU_ATTR_PEER_PORT] = { .type = NLA_U16, },
+ [FOU_ATTR_IFINDEX] = { .type = NLA_S32, },
+};
+
+/* Ops table for fou */
+const struct genl_small_ops fou_nl_ops[3] = {
+ {
+ .cmd = FOU_CMD_ADD,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = fou_nl_add_doit,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = FOU_CMD_DEL,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = fou_nl_del_doit,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = FOU_CMD_GET,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = fou_nl_get_doit,
+ .dumpit = fou_nl_get_dumpit,
+ },
+};
diff --git a/net/ipv4/fou_nl.h b/net/ipv4/fou_nl.h
new file mode 100644
index 000000000000..b7a68121ce6f
--- /dev/null
+++ b/net/ipv4/fou_nl.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/fou.yaml */
+/* YNL-GEN kernel header */
+
+#ifndef _LINUX_FOU_GEN_H
+#define _LINUX_FOU_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <linux/fou.h>
+
+/* Global operation policy for fou */
+extern const struct nla_policy fou_nl_policy[FOU_ATTR_IFINDEX + 1];
+
+/* Ops table for fou */
+extern const struct genl_small_ops fou_nl_ops[3];
+
+int fou_nl_add_doit(struct sk_buff *skb, struct genl_info *info);
+int fou_nl_del_doit(struct sk_buff *skb, struct genl_info *info);
+int fou_nl_get_doit(struct sk_buff *skb, struct genl_info *info);
+int fou_nl_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+
+#endif /* _LINUX_FOU_GEN_H */
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index ae0189828aa9..c56848f36298 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -852,10 +852,17 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
const void *daddr, const void *saddr, unsigned int len)
{
struct ip_tunnel *t = netdev_priv(dev);
- struct iphdr *iph;
struct gre_base_hdr *greh;
+ struct iphdr *iph;
+ int needed;
+
+ needed = t->hlen + sizeof(*iph);
+ if (skb_headroom(skb) < needed &&
+ pskb_expand_head(skb, HH_DATA_ALIGN(needed - skb_headroom(skb)),
+ 0, GFP_ATOMIC))
+ return -needed;
- iph = skb_push(skb, t->hlen + sizeof(*iph));
+ iph = skb_push(skb, needed);
greh = (struct gre_base_hdr *)(iph+1);
greh->flags = gre_tnl_flags_to_gre_flags(t->parms.o_flags);
greh->protocol = htons(type);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 6efad8d6653b..855622a6a304 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -876,7 +876,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
skb_reset_network_header(skb);
- if (!pskb_inet_may_pull(skb)) {
+ if (skb_vlan_inet_prepare(skb, true)) {
DEV_STATS_INC(tunnel->dev, rx_length_errors);
DEV_STATS_INC(tunnel->dev, rx_errors);
goto drop;
diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
index 4a3f7bb027ed..f82fcd8908e1 100644
--- a/net/ipv6/seg6_hmac.c
+++ b/net/ipv6/seg6_hmac.c
@@ -36,6 +36,7 @@
#include <crypto/hash.h>
#include <crypto/sha.h>
+#include <crypto/algapi.h>
#include <net/seg6.h>
#include <net/genetlink.h>
#include <net/seg6_hmac.h>
@@ -270,7 +271,7 @@ bool seg6_hmac_validate_skb(struct sk_buff *skb)
if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output))
return false;
- if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0)
+ if (crypto_memneq(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN))
return false;
return true;
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index b6dcfca740c1..83615f5968dd 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1252,8 +1252,6 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
{
struct l2tp_tunnel *tunnel = container_of(work, struct l2tp_tunnel,
del_work);
- struct sock *sk = tunnel->sock;
- struct socket *sock = sk->sk_socket;
l2tp_tunnel_closeall(tunnel);
@@ -1261,6 +1259,8 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
* the sk API to release it here.
*/
if (tunnel->fd < 0) {
+ struct socket *sock = tunnel->sock->sk_socket;
+
if (sock) {
kernel_sock_shutdown(sock, SHUT_RDWR);
sock_release(sock);
diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c
index 9b63792dcc02..548dd5adbe97 100644
--- a/net/netfilter/nft_connlimit.c
+++ b/net/netfilter/nft_connlimit.c
@@ -209,7 +209,7 @@ static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src,
struct nft_connlimit *priv_src = nft_expr_priv(src);
priv_dst->list = kmalloc(sizeof(*priv_dst->list), GFP_ATOMIC);
- if (priv_dst->list)
+ if (!priv_dst->list)
return -ENOMEM;
nf_conncount_list_init(priv_dst->list);
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index 8e15a0c96614..9ea038b5d6ca 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -749,7 +749,7 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
unsigned char *dptr;
ax25_cb *ax25s;
int ret;
- struct sk_buff *skbn;
+ struct sk_buff *nskb, *oskb;
/*
* Reject malformed packets early. Check that it contains at least 2
@@ -808,14 +808,16 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
/* We are going to change the netrom headers so we should get our
own skb, we also did not know until now how much header space
we had to reserve... - RXQ */
- if ((skbn=skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC)) == NULL) {
+ nskb = skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC);
+
+ if (!nskb) {
nr_node_unlock(nr_node);
nr_node_put(nr_node);
dev_put(dev);
return 0;
}
- kfree_skb(skb);
- skb=skbn;
+ oskb = skb;
+ skb = nskb;
skb->data[14]--;
dptr = skb_push(skb, 1);
@@ -834,6 +836,9 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
nr_node_unlock(nr_node);
nr_node_put(nr_node);
+ if (ret)
+ kfree_skb(oskb);
+
return ret;
}
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 00cb55e2528d..2e32af33df7d 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -1139,14 +1139,14 @@ int nfc_register_device(struct nfc_dev *dev)
EXPORT_SYMBOL(nfc_register_device);
/**
- * nfc_unregister_device - unregister a nfc device in the nfc subsystem
+ * nfc_unregister_rfkill - unregister a nfc device in the rfkill subsystem
*
* @dev: The nfc device to unregister
*/
-void nfc_unregister_device(struct nfc_dev *dev)
+void nfc_unregister_rfkill(struct nfc_dev *dev)
{
- int rc;
struct rfkill *rfk = NULL;
+ int rc;
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
@@ -1167,7 +1167,16 @@ void nfc_unregister_device(struct nfc_dev *dev)
rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
+}
+EXPORT_SYMBOL(nfc_unregister_rfkill);
+/**
+ * nfc_remove_device - remove a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to remove
+ */
+void nfc_remove_device(struct nfc_dev *dev)
+{
if (dev->ops->check_presence) {
del_timer_sync(&dev->check_pres_timer);
cancel_work_sync(&dev->check_pres_work);
@@ -1180,6 +1189,18 @@ void nfc_unregister_device(struct nfc_dev *dev)
device_del(&dev->dev);
mutex_unlock(&nfc_devlist_mutex);
}
+EXPORT_SYMBOL(nfc_remove_device);
+
+/**
+ * nfc_unregister_device - unregister a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to unregister
+ */
+void nfc_unregister_device(struct nfc_dev *dev)
+{
+ nfc_unregister_rfkill(dev);
+ nfc_remove_device(dev);
+}
EXPORT_SYMBOL(nfc_unregister_device);
static int __init nfc_init(void)
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index 5b8754ae7d3a..706da71c5f29 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -786,8 +786,23 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
if (likely(frag_len > 0))
skb_put_data(pdu, msg_ptr, frag_len);
+ spin_lock(&local->tx_queue.lock);
+
+ if (list_empty(&local->list)) {
+ spin_unlock(&local->tx_queue.lock);
+
+ kfree_skb(pdu);
+
+ len -= remaining_len;
+ if (len == 0)
+ len = -ENXIO;
+ break;
+ }
+
/* No need to check for the peer RW for UI frames */
- skb_queue_tail(&local->tx_queue, pdu);
+ __skb_queue_tail(&local->tx_queue, pdu);
+
+ spin_unlock(&local->tx_queue.lock);
remaining_len -= frag_len;
msg_ptr += frag_len;
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index da3cb0d29b97..504245aeb4e2 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -316,7 +316,9 @@ static struct nfc_llcp_local *nfc_llcp_remove_local(struct nfc_dev *dev)
spin_lock(&llcp_devices_lock);
list_for_each_entry_safe(local, tmp, &llcp_devices, list)
if (local->dev == dev) {
- list_del(&local->list);
+ spin_lock(&local->tx_queue.lock);
+ list_del_init(&local->list);
+ spin_unlock(&local->tx_queue.lock);
spin_unlock(&llcp_devices_lock);
return local;
}
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 3182b4228cfa..3514686eb53f 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -1281,6 +1281,8 @@ void nci_unregister_device(struct nci_dev *ndev)
{
struct nci_conn_info *conn_info, *n;
+ nfc_unregister_rfkill(ndev->nfc_dev);
+
/* This set_bit is not protected with specialized barrier,
* However, it is fine because the mutex_lock(&ndev->req_lock);
* in nci_close_device() will help to emit one.
@@ -1298,7 +1300,7 @@ void nci_unregister_device(struct nci_dev *ndev)
/* conn_info is allocated with devm_kzalloc */
}
- nfc_unregister_device(ndev->nfc_dev);
+ nfc_remove_device(ndev->nfc_dev);
}
EXPORT_SYMBOL(nci_unregister_device);
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
index 892d4824d81d..1211d0bee777 100644
--- a/net/sched/act_ife.c
+++ b/net/sched/act_ife.c
@@ -648,9 +648,9 @@ static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind,
memset(&opt, 0, sizeof(opt));
- opt.index = ife->tcf_index,
- opt.refcnt = refcount_read(&ife->tcf_refcnt) - ref,
- opt.bindcnt = atomic_read(&ife->tcf_bindcnt) - bind,
+ opt.index = ife->tcf_index;
+ opt.refcnt = refcount_read(&ife->tcf_refcnt) - ref;
+ opt.bindcnt = atomic_read(&ife->tcf_bindcnt) - bind;
spin_lock_bh(&ife->tcf_lock);
opt.action = ife->tcf_action;
@@ -820,6 +820,7 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
/* could be stupid policy setup or mtu config
* so lets be conservative.. */
if ((action == TC_ACT_SHOT) || exceed_mtu) {
+drop:
qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats));
return TC_ACT_SHOT;
}
@@ -828,6 +829,8 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
skb_push(skb, skb->dev->hard_header_len);
ife_meta = ife_encode(skb, metalen);
+ if (!ife_meta)
+ goto drop;
spin_lock(&ife->tcf_lock);
@@ -843,8 +846,7 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
if (err < 0) {
/* too corrupt to keep around if overwritten */
spin_unlock(&ife->tcf_lock);
- qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats));
- return TC_ACT_SHOT;
+ goto drop;
}
skboff += err;
}
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index 34a6c4ec9a15..1c38447b456a 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -375,7 +375,7 @@ static void qfq_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl)
/* Deschedule class and remove it from its parent aggregate. */
static void qfq_deact_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl)
{
- if (cl->qdisc->q.qlen > 0) /* class is active */
+ if (cl_is_active(cl)) /* class is active */
qfq_deactivate_class(q, cl);
qfq_rm_from_agg(q, cl);
@@ -532,8 +532,10 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
return 0;
destroy_class:
- qdisc_put(cl->qdisc);
- kfree(cl);
+ if (!existing) {
+ qdisc_put(cl->qdisc);
+ kfree(cl);
+ }
return err;
}
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 79aaab51cbf5..e9dfa140799c 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -178,6 +178,11 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
if (m->dev == dev)
return -ELOOP;
+ if (sch->parent != TC_H_ROOT) {
+ NL_SET_ERR_MSG_MOD(extack, "teql can only be used as root");
+ return -EOPNOTSUPP;
+ }
+
q->m = m;
skb_queue_head_init(&q->q);
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 29b879bf8697..c91f712ce1fa 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -361,7 +361,7 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net,
/* If the INIT is coming toward a closing socket, we'll send back
* and ABORT. Essentially, this catches the race of INIT being
- * backloged to the socket at the same time as the user isses close().
+ * backloged to the socket at the same time as the user issues close().
* Since the socket and all its associations are going away, we
* can treat this OOTB
*/
@@ -594,6 +594,11 @@ enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net,
sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT,
SCTP_PEER_INIT(initchunk));
+ /* SCTP-AUTH: generate the association shared keys so that
+ * we can potentially sign the COOKIE-ECHO.
+ */
+ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_SHKEY, SCTP_NULL());
+
/* Reset init error count upon receipt of INIT-ACK. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL());
@@ -608,11 +613,6 @@ enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_COOKIE_ECHOED));
- /* SCTP-AUTH: genereate the assocition shared keys so that
- * we can potentially signe the COOKIE-ECHO.
- */
- sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_SHKEY, SCTP_NULL());
-
/* 5.1 C) "A" shall then send the State Cookie received in the
* INIT ACK chunk in a COOKIE ECHO chunk, ...
*/
@@ -791,7 +791,7 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net,
goto nomem_init;
/* SCTP-AUTH: Now that we've populate required fields in
- * sctp_process_init, set up the assocaition shared keys as
+ * sctp_process_init, set up the association shared keys as
* necessary so that we can potentially authenticate the ACK
*/
error = sctp_auth_asoc_init_active_key(new_asoc, GFP_ATOMIC);
@@ -842,7 +842,7 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net,
/* Add all the state machine commands now since we've created
* everything. This way we don't introduce memory corruptions
- * during side-effect processing and correclty count established
+ * during side-effect processing and correctly count established
* associations.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
@@ -928,7 +928,7 @@ enum sctp_disposition sctp_sf_do_5_1E_ca(struct net *net,
commands);
/* Reset init error count upon receipt of COOKIE-ACK,
- * to avoid problems with the managemement of this
+ * to avoid problems with the management of this
* counter in stale cookie situations when a transition back
* from the COOKIE-ECHOED state to the COOKIE-WAIT
* state is performed.
@@ -2935,7 +2935,7 @@ __sctp_sf_do_9_2_reshutack(struct net *net, const struct sctp_endpoint *ep,
commands);
/* Since we are not going to really process this INIT, there
- * is no point in verifying chunk boundries. Just generate
+ * is no point in verifying chunk boundaries. Just generate
* the SHUTDOWN ACK.
*/
reply = sctp_make_shutdown_ack(asoc, chunk);
@@ -3526,7 +3526,7 @@ enum sctp_disposition sctp_sf_do_9_2_final(struct net *net,
goto nomem_chunk;
/* Do all the commands now (after allocation), so that we
- * have consistent state if memory allocation failes
+ * have consistent state if memory allocation fails
*/
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
@@ -3710,7 +3710,7 @@ static enum sctp_disposition sctp_sf_shut_8_4_5(
SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
/* We need to discard the rest of the packet to prevent
- * potential bomming attacks from additional bundled chunks.
+ * potential boomming attacks from additional bundled chunks.
* This is documented in SCTP Threats ID.
*/
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
@@ -4221,7 +4221,7 @@ enum sctp_disposition sctp_sf_eat_fwd_tsn_fast(
}
/*
- * SCTP-AUTH Section 6.3 Receiving authenticated chukns
+ * SCTP-AUTH Section 6.3 Receiving authenticated chunks
*
* The receiver MUST use the HMAC algorithm indicated in the HMAC
* Identifier field. If this algorithm was not specified by the
@@ -4782,7 +4782,7 @@ static enum sctp_disposition sctp_sf_violation_ctsn(
/* Handle protocol violation of an invalid chunk bundling. For example,
* when we have an association and we receive bundled INIT-ACK, or
- * SHUDOWN-COMPLETE, our peer is clearly violationg the "MUST NOT bundle"
+ * SHUTDOWN-COMPLETE, our peer is clearly violating the "MUST NOT bundle"
* statement from the specs. Additionally, there might be an attacker
* on the path and we may not want to continue this communication.
*/
@@ -5178,7 +5178,7 @@ enum sctp_disposition sctp_sf_cookie_wait_prm_shutdown(
* Inputs
* (endpoint, asoc)
*
- * The RFC does not explcitly address this issue, but is the route through the
+ * The RFC does not explicitly address this issue, but is the route through the
* state table when someone issues a shutdown while in COOKIE_ECHOED state.
*
* Outputs
@@ -5902,7 +5902,7 @@ enum sctp_disposition sctp_sf_t1_cookie_timer_expire(
/* RFC2960 9.2 If the timer expires, the endpoint must re-send the SHUTDOWN
* with the updated last sequential TSN received from its peer.
*
- * An endpoint should limit the number of retransmissions of the
+ * An endpoint should limit the number of retransmission of the
* SHUTDOWN chunk to the protocol parameter 'Association.Max.Retrans'.
* If this threshold is exceeded the endpoint should destroy the TCB and
* MUST report the peer endpoint unreachable to the upper layer (and
@@ -5980,7 +5980,7 @@ enum sctp_disposition sctp_sf_t2_timer_expire(
}
/*
- * ADDIP Section 4.1 ASCONF CHunk Procedures
+ * ADDIP Section 4.1 ASCONF Chunk Procedures
* If the T4 RTO timer expires the endpoint should do B1 to B5
*/
enum sctp_disposition sctp_sf_t4_timer_expire(
@@ -6410,7 +6410,7 @@ static int sctp_eat_data(const struct sctp_association *asoc,
chunk->ecn_ce_done = 1;
if (af->is_ce(sctp_gso_headskb(chunk->skb))) {
- /* Do real work as sideffect. */
+ /* Do real work as side effect. */
sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
SCTP_U32(tsn));
}
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 8eb5fef41dbe..f5d07759b928 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1078,7 +1078,9 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
runtime->oss.params = 0;
runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
- snd_pcm_runtime_buffer_set_silence(runtime);
+ err = snd_pcm_runtime_buffer_set_silence(runtime);
+ if (err < 0)
+ goto failure;
runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 145e5157515f..a6a3e8909be7 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -687,13 +687,18 @@ static void snd_pcm_buffer_access_unlock(struct snd_pcm_runtime *runtime)
}
/* fill the PCM buffer with the current silence format; called from pcm_oss.c */
-void snd_pcm_runtime_buffer_set_silence(struct snd_pcm_runtime *runtime)
+int snd_pcm_runtime_buffer_set_silence(struct snd_pcm_runtime *runtime)
{
- snd_pcm_buffer_access_lock(runtime);
+ int err;
+
+ err = snd_pcm_buffer_access_lock(runtime);
+ if (err < 0)
+ return err;
if (runtime->dma_area)
snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
bytes_to_samples(runtime, runtime->dma_bytes));
snd_pcm_buffer_access_unlock(runtime);
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_pcm_runtime_buffer_set_silence);
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
index 6d636bdcaa5a..db4e27b9145d 100644
--- a/sound/pci/ctxfi/ctamixer.c
+++ b/sound/pci/ctxfi/ctamixer.c
@@ -205,6 +205,7 @@ static int amixer_rsc_init(struct amixer *amixer,
/* Set amixer specific operations */
amixer->rsc.ops = &amixer_basic_rsc_ops;
+ amixer->rsc.conj = 0;
amixer->ops = &amixer_ops;
amixer->input = NULL;
amixer->sum = NULL;
@@ -369,6 +370,7 @@ static int sum_rsc_init(struct sum *sum,
return err;
sum->rsc.ops = &sum_basic_rsc_ops;
+ sum->rsc.conj = 0;
return 0;
}
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index a6241a045369..c7a591ee2590 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -673,7 +673,7 @@ static int adcx140_hw_params(struct snd_pcm_substream *substream,
struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
u8 data = 0;
- switch (params_width(params)) {
+ switch (params_physical_width(params)) {
case 16:
data = ADCX140_16_BIT_WORD;
break;
@@ -688,7 +688,7 @@ static int adcx140_hw_params(struct snd_pcm_substream *substream,
break;
default:
dev_err(component->dev, "%s: Unsupported width %d\n",
- __func__, params_width(params));
+ __func__, params_physical_width(params));
return -EINVAL;
}
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 949b17137726..1cbc23bcb80c 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1741,11 +1741,10 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
range = (cval->max - cval->min) / cval->res;
/*
- * Are there devices with volume range more than 255? I use a bit more
- * to be sure. 384 is a resolution magic number found on Logitech
- * devices. It will definitively catch all buggy Logitech devices.
+ * There are definitely devices with a range of ~20,000, so let's be
+ * conservative and allow for a bit more.
*/
- if (range > 384) {
+ if (range > 65535) {
usb_audio_warn(mixer->chip,
"Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
range);
@@ -2859,10 +2858,23 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
{
+ struct usb_mixer_elem_list *list, *next;
+ int id;
+
/* kill pending URBs */
snd_usb_mixer_disconnect(mixer);
- kfree(mixer->id_elems);
+ /* Unregister controls first, snd_ctl_remove() frees the element */
+ if (mixer->id_elems) {
+ for (id = 0; id < MAX_ID_ELEMS; id++) {
+ for (list = mixer->id_elems[id]; list; list = next) {
+ next = list->next_id_elem;
+ if (list->kctl)
+ snd_ctl_remove(mixer->chip->card, list->kctl);
+ }
+ }
+ kfree(mixer->id_elems);
+ }
if (mixer->urb) {
kfree(mixer->urb->transfer_buffer);
usb_free_urb(mixer->urb);
diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh
index 2f5cdbc5dee3..806c409de124 100755
--- a/tools/testing/selftests/net/fcnal-test.sh
+++ b/tools/testing/selftests/net/fcnal-test.sh
@@ -183,7 +183,7 @@ show_hint()
kill_procs()
{
killall nettest ping ping6 >/dev/null 2>&1
- slowwait 2 sh -c 'test -z "$(pgrep '"'^(nettest|ping|ping6)$'"')"'
+ sleep 1
}
do_run_cmd()
diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c
index 93cbd6f603f9..356acac85d86 100644
--- a/tools/testing/vsock/util.c
+++ b/tools/testing/vsock/util.c
@@ -338,6 +338,18 @@ void run_tests(const struct test_case *test_cases,
printf("ok\n");
}
+
+ printf("All tests have been executed. Waiting other peer...");
+ fflush(stdout);
+
+ /*
+ * Final full barrier, to ensure that all tests have been run and
+ * that even the last one has been successful on both sides.
+ */
+ control_writeln("COMPLETED");
+ control_expectln("COMPLETED");
+
+ printf("ok\n");
}
void list_tests(const struct test_case *test_cases)
Powered by blists - more mailing lists