diff options
author | 2020-10-14 16:38:37 -0400 | |
---|---|---|
committer | 2020-10-14 16:38:37 -0400 | |
commit | 0d84d996394498a013226bca10c8c5e84cca985e (patch) | |
tree | ec11f705cce0b130cc429163b081be68243f5572 | |
parent | Linux patch 5.8.14 (diff) | |
download | linux-patches-5.8-18.tar.gz linux-patches-5.8-18.tar.bz2 linux-patches-5.8-18.zip |
Linux patch 5.8.155.8-18
Signed-off-by: Mike Pagano <mpagano@gentoo.org>
-rw-r--r-- | 0000_README | 4 | ||||
-rw-r--r-- | 1014_linux-5.8.15.patch | 7265 |
2 files changed, 7269 insertions, 0 deletions
diff --git a/0000_README b/0000_README index 6e16f1d7..34004944 100644 --- a/0000_README +++ b/0000_README @@ -99,6 +99,10 @@ Patch: 1013_linux-5.8.14.patch From: http://www.kernel.org Desc: Linux 5.8.14 +Patch: 1014_linux-5.8.15.patch +From: http://www.kernel.org +Desc: Linux 5.8.15 + Patch: 1500_XATTR_USER_PREFIX.patch From: https://bugs.gentoo.org/show_bug.cgi?id=470644 Desc: Support for namespace user.pax.* on tmpfs. diff --git a/1014_linux-5.8.15.patch b/1014_linux-5.8.15.patch new file mode 100644 index 00000000..0710af71 --- /dev/null +++ b/1014_linux-5.8.15.patch @@ -0,0 +1,7265 @@ +diff --git a/Makefile b/Makefile +index 33ceda527e5ef..6c787cd1cb514 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 5 + PATCHLEVEL = 8 +-SUBLEVEL = 14 ++SUBLEVEL = 15 + EXTRAVERSION = + NAME = Kleptomaniac Octopus + +diff --git a/arch/arm64/crypto/aes-neonbs-core.S b/arch/arm64/crypto/aes-neonbs-core.S +index b357164379f6d..63a52ad9a75c0 100644 +--- a/arch/arm64/crypto/aes-neonbs-core.S ++++ b/arch/arm64/crypto/aes-neonbs-core.S +@@ -788,7 +788,7 @@ SYM_FUNC_START_LOCAL(__xts_crypt8) + + 0: mov bskey, x21 + mov rounds, x22 +- br x7 ++ br x16 + SYM_FUNC_END(__xts_crypt8) + + .macro __xts_crypt, do8, o0, o1, o2, o3, o4, o5, o6, o7 +@@ -806,7 +806,7 @@ SYM_FUNC_END(__xts_crypt8) + uzp1 v30.4s, v30.4s, v25.4s + ld1 {v25.16b}, [x24] + +-99: adr x7, \do8 ++99: adr x16, \do8 + bl __xts_crypt8 + + ldp q16, q17, [sp, #.Lframe_local_offset] +diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c +index e229d95f470b8..7c1dadf14f567 100644 +--- a/arch/riscv/mm/init.c ++++ b/arch/riscv/mm/init.c +@@ -515,6 +515,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa) + #else + dtb_early_va = (void *)dtb_pa; + #endif ++ dtb_early_pa = dtb_pa; + } + + static inline void setup_vm_final(void) +diff --git a/block/partitions/ibm.c b/block/partitions/ibm.c +index d6e18df9c53c6..4b044e620d353 100644 +--- a/block/partitions/ibm.c ++++ b/block/partitions/ibm.c +@@ -305,8 +305,6 @@ int ibm_partition(struct parsed_partitions *state) + if (!disk->fops->getgeo) + goto out_exit; + fn = symbol_get(dasd_biodasdinfo); +- if (!fn) +- goto out_exit; + blocksize = bdev_logical_block_size(bdev); + if (blocksize <= 0) + goto out_symbol; +@@ -326,7 +324,7 @@ int ibm_partition(struct parsed_partitions *state) + geo->start = get_start_sect(bdev); + if (disk->fops->getgeo(bdev, geo)) + goto out_freeall; +- if (fn(disk, info)) { ++ if (!fn || fn(disk, info)) { + kfree(info); + info = NULL; + } +@@ -370,7 +368,8 @@ out_nolab: + out_nogeo: + kfree(info); + out_symbol: +- symbol_put(dasd_biodasdinfo); ++ if (fn) ++ symbol_put(dasd_biodasdinfo); + out_exit: + return res; + } +diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c +index ef722f04f88a9..72108404718fe 100644 +--- a/block/scsi_ioctl.c ++++ b/block/scsi_ioctl.c +@@ -651,6 +651,7 @@ struct compat_cdrom_generic_command { + compat_int_t stat; + compat_caddr_t sense; + unsigned char data_direction; ++ unsigned char pad[3]; + compat_int_t quiet; + compat_int_t timeout; + compat_caddr_t reserved[1]; +diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c +index 6e813b13d6988..02154d8b1c0d5 100644 +--- a/drivers/gpio/gpiolib.c ++++ b/drivers/gpio/gpiolib.c +@@ -838,7 +838,7 @@ static __poll_t lineevent_poll(struct file *filep, + + static ssize_t lineevent_get_size(void) + { +-#ifdef __x86_64__ ++#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) + /* i386 has no padding after 'id' */ + if (in_ia32_syscall()) { + struct compat_gpioeevent_data { +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +index e59c01a83dace..9a3267f06376f 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +@@ -1052,6 +1052,7 @@ static int amdgpu_ttm_tt_pin_userptr(struct ttm_tt *ttm) + + release_sg: + kfree(ttm->sg); ++ ttm->sg = NULL; + return r; + } + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +index 949d10ef83040..6dd1f3f8d9903 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +@@ -568,7 +568,7 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct + int i = 0; + + hdcp_work = kcalloc(max_caps, sizeof(*hdcp_work), GFP_KERNEL); +- if (hdcp_work == NULL) ++ if (ZERO_OR_NULL_PTR(hdcp_work)) + return NULL; + + hdcp_work->srm = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, sizeof(*hdcp_work->srm), GFP_KERNEL); +diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c +index 9ee8cf8267c88..43f7adff6cb74 100644 +--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c ++++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c +@@ -563,6 +563,8 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr, + struct smu10_hwmgr *data = hwmgr->backend; + uint32_t min_sclk = hwmgr->display_config->min_core_set_clock; + uint32_t min_mclk = hwmgr->display_config->min_mem_set_clock/100; ++ uint32_t index_fclk = data->clock_vol_info.vdd_dep_on_fclk->count - 1; ++ uint32_t index_socclk = data->clock_vol_info.vdd_dep_on_socclk->count - 1; + + if (hwmgr->smu_version < 0x1E3700) { + pr_info("smu firmware version too old, can not set dpm level\n"); +@@ -676,13 +678,13 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr, + smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetHardMinFclkByFreq, + hwmgr->display_config->num_display > 3 ? +- SMU10_UMD_PSTATE_PEAK_FCLK : ++ data->clock_vol_info.vdd_dep_on_fclk->entries[0].clk : + min_mclk, + NULL); + + smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetHardMinSocclkByFreq, +- SMU10_UMD_PSTATE_MIN_SOCCLK, ++ data->clock_vol_info.vdd_dep_on_socclk->entries[0].clk, + NULL); + smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetHardMinVcn, +@@ -695,11 +697,11 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr, + NULL); + smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetSoftMaxFclkByFreq, +- SMU10_UMD_PSTATE_PEAK_FCLK, ++ data->clock_vol_info.vdd_dep_on_fclk->entries[index_fclk].clk, + NULL); + smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetSoftMaxSocclkByFreq, +- SMU10_UMD_PSTATE_PEAK_SOCCLK, ++ data->clock_vol_info.vdd_dep_on_socclk->entries[index_socclk].clk, + NULL); + smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetSoftMaxVcn, +diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c +index c002f89685073..9682f30ab6f68 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_mem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_mem.c +@@ -176,6 +176,8 @@ void + nouveau_mem_del(struct ttm_mem_reg *reg) + { + struct nouveau_mem *mem = nouveau_mem(reg); ++ if (!mem) ++ return; + nouveau_mem_fini(mem); + kfree(reg->mm_node); + reg->mm_node = NULL; +diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +index 5b90c2a1bf3d3..7c2e5db840be5 100644 +--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c ++++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +@@ -3149,6 +3149,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func, + case 0x168: device->chip = &nv168_chipset; break; + default: + nvdev_error(device, "unknown chipset (%08x)\n", boot0); ++ ret = -ENODEV; + goto done; + } + +diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c +index c5dec572fc48e..ef73a42577cc7 100644 +--- a/drivers/i2c/busses/i2c-meson.c ++++ b/drivers/i2c/busses/i2c-meson.c +@@ -5,6 +5,7 @@ + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + */ + ++#include <linux/bitfield.h> + #include <linux/clk.h> + #include <linux/completion.h> + #include <linux/i2c.h> +@@ -33,12 +34,17 @@ + #define REG_CTRL_ACK_IGNORE BIT(1) + #define REG_CTRL_STATUS BIT(2) + #define REG_CTRL_ERROR BIT(3) +-#define REG_CTRL_CLKDIV_SHIFT 12 +-#define REG_CTRL_CLKDIV_MASK GENMASK(21, 12) +-#define REG_CTRL_CLKDIVEXT_SHIFT 28 +-#define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28) ++#define REG_CTRL_CLKDIV GENMASK(21, 12) ++#define REG_CTRL_CLKDIVEXT GENMASK(29, 28) ++ ++#define REG_SLV_ADDR GENMASK(7, 0) ++#define REG_SLV_SDA_FILTER GENMASK(10, 8) ++#define REG_SLV_SCL_FILTER GENMASK(13, 11) ++#define REG_SLV_SCL_LOW GENMASK(27, 16) ++#define REG_SLV_SCL_LOW_EN BIT(28) + + #define I2C_TIMEOUT_MS 500 ++#define FILTER_DELAY 15 + + enum { + TOKEN_END = 0, +@@ -133,19 +139,24 @@ static void meson_i2c_set_clk_div(struct meson_i2c *i2c, unsigned int freq) + unsigned long clk_rate = clk_get_rate(i2c->clk); + unsigned int div; + +- div = DIV_ROUND_UP(clk_rate, freq * i2c->data->div_factor); ++ div = DIV_ROUND_UP(clk_rate, freq); ++ div -= FILTER_DELAY; ++ div = DIV_ROUND_UP(div, i2c->data->div_factor); + + /* clock divider has 12 bits */ +- if (div >= (1 << 12)) { ++ if (div > GENMASK(11, 0)) { + dev_err(i2c->dev, "requested bus frequency too low\n"); +- div = (1 << 12) - 1; ++ div = GENMASK(11, 0); + } + +- meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK, +- (div & GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT); ++ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV, ++ FIELD_PREP(REG_CTRL_CLKDIV, div & GENMASK(9, 0))); ++ ++ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIVEXT, ++ FIELD_PREP(REG_CTRL_CLKDIVEXT, div >> 10)); + +- meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIVEXT_MASK, +- (div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT); ++ /* Disable HIGH/LOW mode */ ++ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_SCL_LOW_EN, 0); + + dev_dbg(i2c->dev, "%s: clk %lu, freq %u, div %u\n", __func__, + clk_rate, freq, div); +@@ -280,7 +291,10 @@ static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg) + token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ : + TOKEN_SLAVE_ADDR_WRITE; + +- writel(msg->addr << 1, i2c->regs + REG_SLAVE_ADDR); ++ ++ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_ADDR, ++ FIELD_PREP(REG_SLV_ADDR, msg->addr << 1)); ++ + meson_i2c_add_token(i2c, TOKEN_START); + meson_i2c_add_token(i2c, token); + } +@@ -357,16 +371,12 @@ static int meson_i2c_xfer_messages(struct i2c_adapter *adap, + struct meson_i2c *i2c = adap->algo_data; + int i, ret = 0; + +- clk_enable(i2c->clk); +- + for (i = 0; i < num; i++) { + ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1, atomic); + if (ret) + break; + } + +- clk_disable(i2c->clk); +- + return ret ?: i; + } + +@@ -435,7 +445,7 @@ static int meson_i2c_probe(struct platform_device *pdev) + return ret; + } + +- ret = clk_prepare(i2c->clk); ++ ret = clk_prepare_enable(i2c->clk); + if (ret < 0) { + dev_err(&pdev->dev, "can't prepare clock\n"); + return ret; +@@ -457,10 +467,14 @@ static int meson_i2c_probe(struct platform_device *pdev) + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) { +- clk_unprepare(i2c->clk); ++ clk_disable_unprepare(i2c->clk); + return ret; + } + ++ /* Disable filtering */ ++ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, ++ REG_SLV_SDA_FILTER | REG_SLV_SCL_FILTER, 0); ++ + meson_i2c_set_clk_div(i2c, timings.bus_freq_hz); + + return 0; +@@ -471,7 +485,7 @@ static int meson_i2c_remove(struct platform_device *pdev) + struct meson_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); +- clk_unprepare(i2c->clk); ++ clk_disable_unprepare(i2c->clk); + + return 0; + } +diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c +index 672f1f239bd6f..a163b8f308c14 100644 +--- a/drivers/i2c/busses/i2c-owl.c ++++ b/drivers/i2c/busses/i2c-owl.c +@@ -176,6 +176,9 @@ static irqreturn_t owl_i2c_interrupt(int irq, void *_dev) + fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT); + if (fifostat & OWL_I2C_FIFOSTAT_RNB) { + i2c_dev->err = -ENXIO; ++ /* Clear NACK error bit by writing "1" */ ++ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOSTAT, ++ OWL_I2C_FIFOSTAT_RNB, true); + goto stop; + } + +@@ -183,6 +186,9 @@ static irqreturn_t owl_i2c_interrupt(int irq, void *_dev) + stat = readl(i2c_dev->base + OWL_I2C_REG_STAT); + if (stat & OWL_I2C_STAT_BEB) { + i2c_dev->err = -EIO; ++ /* Clear BUS error bit by writing "1" */ ++ owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT, ++ OWL_I2C_STAT_BEB, true); + goto stop; + } + +diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c +index 305f0160506a0..8a36d78fed63a 100644 +--- a/drivers/input/misc/ati_remote2.c ++++ b/drivers/input/misc/ati_remote2.c +@@ -68,7 +68,7 @@ static int ati_remote2_get_channel_mask(char *buffer, + { + pr_debug("%s()\n", __func__); + +- return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg); ++ return sprintf(buffer, "0x%04x\n", *(unsigned int *)kp->arg); + } + + static int ati_remote2_set_mode_mask(const char *val, +@@ -84,7 +84,7 @@ static int ati_remote2_get_mode_mask(char *buffer, + { + pr_debug("%s()\n", __func__); + +- return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg); ++ return sprintf(buffer, "0x%02x\n", *(unsigned int *)kp->arg); + } + + static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; +diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c +index fbe0b0cc56edf..24a84d294fd01 100644 +--- a/drivers/iommu/intel/iommu.c ++++ b/drivers/iommu/intel/iommu.c +@@ -2617,7 +2617,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, + } + + /* Setup the PASID entry for requests without PASID: */ +- spin_lock(&iommu->lock); ++ spin_lock_irqsave(&iommu->lock, flags); + if (hw_pass_through && domain_type_is_si(domain)) + ret = intel_pasid_setup_pass_through(iommu, domain, + dev, PASID_RID2PASID); +@@ -2627,7 +2627,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, + else + ret = intel_pasid_setup_second_level(iommu, domain, + dev, PASID_RID2PASID); +- spin_unlock(&iommu->lock); ++ spin_unlock_irqrestore(&iommu->lock, flags); + if (ret) { + dev_err(dev, "Setup RID2PASID failed\n"); + dmar_remove_one_dev_info(dev); +diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c +index 4b1eb89b401d9..1ad518821157f 100644 +--- a/drivers/mmc/core/queue.c ++++ b/drivers/mmc/core/queue.c +@@ -190,7 +190,7 @@ static void mmc_queue_setup_discard(struct request_queue *q, + q->limits.discard_granularity = card->pref_erase << 9; + /* granularity must not be greater than max. discard */ + if (card->pref_erase > max_discard) +- q->limits.discard_granularity = 0; ++ q->limits.discard_granularity = SECTOR_SIZE; + if (mmc_can_secure_erase_trim(card)) + blk_queue_flag_set(QUEUE_FLAG_SECERASE, q); + } +diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c +index 500aa3e19a4c7..fddf7c502355b 100644 +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -1195,6 +1195,7 @@ static void bond_setup_by_slave(struct net_device *bond_dev, + + bond_dev->type = slave_dev->type; + bond_dev->hard_header_len = slave_dev->hard_header_len; ++ bond_dev->needed_headroom = slave_dev->needed_headroom; + bond_dev->addr_len = slave_dev->addr_len; + + memcpy(bond_dev->broadcast, slave_dev->broadcast, +diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c +index 7c167a394b762..259a612da0030 100644 +--- a/drivers/net/dsa/ocelot/felix_vsc9959.c ++++ b/drivers/net/dsa/ocelot/felix_vsc9959.c +@@ -1105,8 +1105,21 @@ static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port, + } + } + ++/* Watermark encode ++ * Bit 8: Unit; 0:1, 1:16 ++ * Bit 7-0: Value to be multiplied with unit ++ */ ++static u16 vsc9959_wm_enc(u16 value) ++{ ++ if (value >= BIT(8)) ++ return BIT(8) | (value / 16); ++ ++ return value; ++} ++ + static const struct ocelot_ops vsc9959_ops = { + .reset = vsc9959_reset, ++ .wm_enc = vsc9959_wm_enc, + }; + + static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) +@@ -1215,8 +1228,28 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot) + static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, + u32 speed) + { ++ u8 tas_speed; ++ ++ switch (speed) { ++ case SPEED_10: ++ tas_speed = OCELOT_SPEED_10; ++ break; ++ case SPEED_100: ++ tas_speed = OCELOT_SPEED_100; ++ break; ++ case SPEED_1000: ++ tas_speed = OCELOT_SPEED_1000; ++ break; ++ case SPEED_2500: ++ tas_speed = OCELOT_SPEED_2500; ++ break; ++ default: ++ tas_speed = OCELOT_SPEED_1000; ++ break; ++ } ++ + ocelot_rmw_rix(ocelot, +- QSYS_TAG_CONFIG_LINK_SPEED(speed), ++ QSYS_TAG_CONFIG_LINK_SPEED(tas_speed), + QSYS_TAG_CONFIG_LINK_SPEED_M, + QSYS_TAG_CONFIG, port); + } +diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c +index cbaa1924afbe1..706e959bf02ac 100644 +--- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c ++++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c +@@ -1219,7 +1219,7 @@ static int octeon_mgmt_open(struct net_device *netdev) + */ + if (netdev->phydev) { + netif_carrier_off(netdev); +- phy_start_aneg(netdev->phydev); ++ phy_start(netdev->phydev); + } + + netif_wake_queue(netdev); +@@ -1247,8 +1247,10 @@ static int octeon_mgmt_stop(struct net_device *netdev) + napi_disable(&p->napi); + netif_stop_queue(netdev); + +- if (netdev->phydev) ++ if (netdev->phydev) { ++ phy_stop(netdev->phydev); + phy_disconnect(netdev->phydev); ++ } + + netif_carrier_off(netdev); + +diff --git a/drivers/net/ethernet/huawei/hinic/Kconfig b/drivers/net/ethernet/huawei/hinic/Kconfig +index 936e2dd3bb135..b47bd5440c5f0 100644 +--- a/drivers/net/ethernet/huawei/hinic/Kconfig ++++ b/drivers/net/ethernet/huawei/hinic/Kconfig +@@ -6,6 +6,7 @@ + config HINIC + tristate "Huawei Intelligent PCIE Network Interface Card" + depends on (PCI_MSI && (X86 || ARM64)) ++ select NET_DEVLINK + help + This driver supports HiNIC PCIE Ethernet cards. + To compile this driver as part of the kernel, choose Y here. +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c +index 583fd24c29cf6..29e88e25a4a4f 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c +@@ -112,6 +112,26 @@ static u32 get_hw_cons_idx(struct hinic_api_cmd_chain *chain) + return HINIC_API_CMD_STATUS_GET(val, CONS_IDX); + } + ++static void dump_api_chain_reg(struct hinic_api_cmd_chain *chain) ++{ ++ u32 addr, val; ++ ++ addr = HINIC_CSR_API_CMD_STATUS_ADDR(chain->chain_type); ++ val = hinic_hwif_read_reg(chain->hwif, addr); ++ ++ dev_err(&chain->hwif->pdev->dev, "Chain type: 0x%x, cpld error: 0x%x, check error: 0x%x, current fsm: 0x%x\n", ++ chain->chain_type, HINIC_API_CMD_STATUS_GET(val, CPLD_ERR), ++ HINIC_API_CMD_STATUS_GET(val, CHKSUM_ERR), ++ HINIC_API_CMD_STATUS_GET(val, FSM)); ++ ++ dev_err(&chain->hwif->pdev->dev, "Chain hw current ci: 0x%x\n", ++ HINIC_API_CMD_STATUS_GET(val, CONS_IDX)); ++ ++ addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain->chain_type); ++ val = hinic_hwif_read_reg(chain->hwif, addr); ++ dev_err(&chain->hwif->pdev->dev, "Chain hw current pi: 0x%x\n", val); ++} ++ + /** + * chain_busy - check if the chain is still processing last requests + * @chain: chain to check +@@ -131,8 +151,10 @@ static int chain_busy(struct hinic_api_cmd_chain *chain) + + /* check for a space for a new command */ + if (chain->cons_idx == MASKED_IDX(chain, prod_idx + 1)) { +- dev_err(&pdev->dev, "API CMD chain %d is busy\n", +- chain->chain_type); ++ dev_err(&pdev->dev, "API CMD chain %d is busy, cons_idx: %d, prod_idx: %d\n", ++ chain->chain_type, chain->cons_idx, ++ chain->prod_idx); ++ dump_api_chain_reg(chain); + return -EBUSY; + } + break; +@@ -332,6 +354,7 @@ static int wait_for_api_cmd_completion(struct hinic_api_cmd_chain *chain) + err = wait_for_status_poll(chain); + if (err) { + dev_err(&pdev->dev, "API CMD Poll status timeout\n"); ++ dump_api_chain_reg(chain); + break; + } + break; +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h +index 0ba00fd828dfc..6d1654b050ad5 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h +@@ -103,10 +103,14 @@ + HINIC_API_CMD_STATUS_HEADER_##member##_MASK) + + #define HINIC_API_CMD_STATUS_CONS_IDX_SHIFT 0 ++#define HINIC_API_CMD_STATUS_FSM_SHIFT 24 + #define HINIC_API_CMD_STATUS_CHKSUM_ERR_SHIFT 28 ++#define HINIC_API_CMD_STATUS_CPLD_ERR_SHIFT 30 + + #define HINIC_API_CMD_STATUS_CONS_IDX_MASK 0xFFFFFF ++#define HINIC_API_CMD_STATUS_FSM_MASK 0xFU + #define HINIC_API_CMD_STATUS_CHKSUM_ERR_MASK 0x3 ++#define HINIC_API_CMD_STATUS_CPLD_ERR_MASK 0x1U + + #define HINIC_API_CMD_STATUS_GET(val, member) \ + (((val) >> HINIC_API_CMD_STATUS_##member##_SHIFT) & \ +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +index cb5b6e5f787f2..e0eb294779ec1 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +@@ -401,6 +401,7 @@ static int cmdq_sync_cmd_direct_resp(struct hinic_cmdq *cmdq, + + spin_unlock_bh(&cmdq->cmdq_lock); + ++ hinic_dump_ceq_info(cmdq->hwdev); + return -ETIMEDOUT; + } + +@@ -807,6 +808,7 @@ static int init_cmdqs_ctxt(struct hinic_hwdev *hwdev, + + cmdq_type = HINIC_CMDQ_SYNC; + for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { ++ cmdqs->cmdq[cmdq_type].hwdev = hwdev; + err = init_cmdq(&cmdqs->cmdq[cmdq_type], + &cmdqs->saved_wqs[cmdq_type], cmdq_type, + db_area[cmdq_type]); +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h +index 3e4b0aef9fe6c..f40c31e1879f1 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h +@@ -130,6 +130,8 @@ struct hinic_cmdq_ctxt { + }; + + struct hinic_cmdq { ++ struct hinic_hwdev *hwdev; ++ + struct hinic_wq *wq; + + enum hinic_cmdq_type cmdq_type; +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +index b735bc537508f..298ceb930cc62 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +@@ -253,9 +253,9 @@ static int init_fw_ctxt(struct hinic_hwdev *hwdev) + &fw_ctxt, sizeof(fw_ctxt), + &fw_ctxt, &out_size); + if (err || (out_size != sizeof(fw_ctxt)) || fw_ctxt.status) { +- dev_err(&pdev->dev, "Failed to init FW ctxt, ret = %d\n", +- fw_ctxt.status); +- return -EFAULT; ++ dev_err(&pdev->dev, "Failed to init FW ctxt, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, fw_ctxt.status, out_size); ++ return -EIO; + } + + return 0; +@@ -420,9 +420,9 @@ static int get_base_qpn(struct hinic_hwdev *hwdev, u16 *base_qpn) + &cmd_base_qpn, sizeof(cmd_base_qpn), + &cmd_base_qpn, &out_size); + if (err || (out_size != sizeof(cmd_base_qpn)) || cmd_base_qpn.status) { +- dev_err(&pdev->dev, "Failed to get base qpn, status = %d\n", +- cmd_base_qpn.status); +- return -EFAULT; ++ dev_err(&pdev->dev, "Failed to get base qpn, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, cmd_base_qpn.status, out_size); ++ return -EIO; + } + + *base_qpn = cmd_base_qpn.qpn; +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c +index 397936cac304c..ca8cb68a8d206 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c +@@ -953,3 +953,42 @@ void hinic_ceqs_free(struct hinic_ceqs *ceqs) + for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) + remove_eq(&ceqs->ceq[q_id]); + } ++ ++void hinic_dump_ceq_info(struct hinic_hwdev *hwdev) ++{ ++ struct hinic_eq *eq = NULL; ++ u32 addr, ci, pi; ++ int q_id; ++ ++ for (q_id = 0; q_id < hwdev->func_to_io.ceqs.num_ceqs; q_id++) { ++ eq = &hwdev->func_to_io.ceqs.ceq[q_id]; ++ addr = EQ_CONS_IDX_REG_ADDR(eq); ++ ci = hinic_hwif_read_reg(hwdev->hwif, addr); ++ addr = EQ_PROD_IDX_REG_ADDR(eq); ++ pi = hinic_hwif_read_reg(hwdev->hwif, addr); ++ dev_err(&hwdev->hwif->pdev->dev, "Ceq id: %d, ci: 0x%08x, sw_ci: 0x%08x, pi: 0x%x, tasklet_state: 0x%lx, wrap: %d, ceqe: 0x%x\n", ++ q_id, ci, eq->cons_idx, pi, ++ eq->ceq_tasklet.state, ++ eq->wrapped, be32_to_cpu(*(__be32 *)(GET_CURR_CEQ_ELEM(eq)))); ++ } ++} ++ ++void hinic_dump_aeq_info(struct hinic_hwdev *hwdev) ++{ ++ struct hinic_aeq_elem *aeqe_pos = NULL; ++ struct hinic_eq *eq = NULL; ++ u32 addr, ci, pi; ++ int q_id; ++ ++ for (q_id = 0; q_id < hwdev->aeqs.num_aeqs; q_id++) { ++ eq = &hwdev->aeqs.aeq[q_id]; ++ addr = EQ_CONS_IDX_REG_ADDR(eq); ++ ci = hinic_hwif_read_reg(hwdev->hwif, addr); ++ addr = EQ_PROD_IDX_REG_ADDR(eq); ++ pi = hinic_hwif_read_reg(hwdev->hwif, addr); ++ aeqe_pos = GET_CURR_AEQ_ELEM(eq); ++ dev_err(&hwdev->hwif->pdev->dev, "Aeq id: %d, ci: 0x%08x, pi: 0x%x, work_state: 0x%x, wrap: %d, desc: 0x%x\n", ++ q_id, ci, pi, work_busy(&eq->aeq_work.work), ++ eq->wrapped, be32_to_cpu(aeqe_pos->desc)); ++ } ++} +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h +index 74b9ff90640c2..43065fc708693 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h +@@ -162,7 +162,7 @@ enum hinic_eqe_state { + + struct hinic_aeq_elem { + u8 data[HINIC_AEQE_DATA_SIZE]; +- u32 desc; ++ __be32 desc; + }; + + struct hinic_eq_work { +@@ -254,4 +254,8 @@ int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif, + + void hinic_ceqs_free(struct hinic_ceqs *ceqs); + ++void hinic_dump_ceq_info(struct hinic_hwdev *hwdev); ++ ++void hinic_dump_aeq_info(struct hinic_hwdev *hwdev); ++ + #endif +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +index cf127d896ba69..bc8925c0c982c 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +@@ -21,6 +21,8 @@ + + #define WAIT_HWIF_READY_TIMEOUT 10000 + ++#define HINIC_SELFTEST_RESULT 0x883C ++ + /** + * hinic_msix_attr_set - set message attribute for msix entry + * @hwif: the HW interface of a pci function device +@@ -369,6 +371,26 @@ u16 hinic_pf_id_of_vf_hw(struct hinic_hwif *hwif) + return HINIC_FA0_GET(attr0, PF_IDX); + } + ++static void __print_selftest_reg(struct hinic_hwif *hwif) ++{ ++ u32 addr, attr0, attr1; ++ ++ addr = HINIC_CSR_FUNC_ATTR1_ADDR; ++ attr1 = hinic_hwif_read_reg(hwif, addr); ++ ++ if (attr1 == HINIC_PCIE_LINK_DOWN) { ++ dev_err(&hwif->pdev->dev, "PCIE is link down\n"); ++ return; ++ } ++ ++ addr = HINIC_CSR_FUNC_ATTR0_ADDR; ++ attr0 = hinic_hwif_read_reg(hwif, addr); ++ if (HINIC_FA0_GET(attr0, FUNC_TYPE) != HINIC_VF && ++ !HINIC_FA0_GET(attr0, PCI_INTF_IDX)) ++ dev_err(&hwif->pdev->dev, "Selftest reg: 0x%08x\n", ++ hinic_hwif_read_reg(hwif, HINIC_SELFTEST_RESULT)); ++} ++ + /** + * hinic_init_hwif - initialize the hw interface + * @hwif: the HW interface of a pci function device +@@ -398,6 +420,7 @@ int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev) + err = wait_hwif_ready(hwif); + if (err) { + dev_err(&pdev->dev, "HW interface is not ready\n"); ++ __print_selftest_reg(hwif); + goto err_hwif_ready; + } + +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +index 0872e035faa11..c06f2253151e2 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +@@ -12,6 +12,8 @@ + #include <linux/types.h> + #include <asm/byteorder.h> + ++#define HINIC_PCIE_LINK_DOWN 0xFFFFFFFF ++ + #define HINIC_DMA_ATTR_ST_SHIFT 0 + #define HINIC_DMA_ATTR_AT_SHIFT 8 + #define HINIC_DMA_ATTR_PH_SHIFT 10 +@@ -249,13 +251,17 @@ struct hinic_hwif { + + static inline u32 hinic_hwif_read_reg(struct hinic_hwif *hwif, u32 reg) + { +- return be32_to_cpu(readl(hwif->cfg_regs_bar + reg)); ++ u32 out = readl(hwif->cfg_regs_bar + reg); ++ ++ return be32_to_cpu(*(__be32 *)&out); + } + + static inline void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg, + u32 val) + { +- writel(cpu_to_be32(val), hwif->cfg_regs_bar + reg); ++ __be32 in = cpu_to_be32(val); ++ ++ writel(*(u32 *)&in, hwif->cfg_regs_bar + reg); + } + + int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +index bc2f87e6cb5d7..47c93f946b94d 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +@@ -650,6 +650,7 @@ wait_for_mbox_seg_completion(struct hinic_mbox_func_to_func *func_to_func, + if (!wait_for_completion_timeout(done, jif)) { + dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment timeout\n"); + dump_mox_reg(hwdev); ++ hinic_dump_aeq_info(hwdev); + return -ETIMEDOUT; + } + +@@ -897,6 +898,7 @@ int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, + set_mbox_to_func_event(func_to_func, EVENT_TIMEOUT); + dev_err(&func_to_func->hwif->pdev->dev, + "Send mbox msg timeout, msg_id: %d\n", msg_info.msg_id); ++ hinic_dump_aeq_info(func_to_func->hwdev); + err = -ETIMEDOUT; + goto err_send_mbox; + } +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +index 7fe39a155b329..070288c8b4f37 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +@@ -276,6 +276,7 @@ static int msg_to_mgmt_sync(struct hinic_pf_to_mgmt *pf_to_mgmt, + + if (!wait_for_completion_timeout(recv_done, timeo)) { + dev_err(&pdev->dev, "MGMT timeout, MSG id = %d\n", msg_id); ++ hinic_dump_aeq_info(pf_to_mgmt->hwdev); + err = -ETIMEDOUT; + goto unlock_sync_msg; + } +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c +index 175c0ee000384..2be7c254cca90 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_port.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c +@@ -58,11 +58,11 @@ static int change_mac(struct hinic_dev *nic_dev, const u8 *addr, + sizeof(port_mac_cmd), + &port_mac_cmd, &out_size); + if (err || out_size != sizeof(port_mac_cmd) || +- (port_mac_cmd.status && +- port_mac_cmd.status != HINIC_PF_SET_VF_ALREADY && +- port_mac_cmd.status != HINIC_MGMT_STATUS_EXIST)) { +- dev_err(&pdev->dev, "Failed to change MAC, ret = %d\n", +- port_mac_cmd.status); ++ (port_mac_cmd.status && ++ (port_mac_cmd.status != HINIC_PF_SET_VF_ALREADY || !HINIC_IS_VF(hwif)) && ++ port_mac_cmd.status != HINIC_MGMT_STATUS_EXIST)) { ++ dev_err(&pdev->dev, "Failed to change MAC, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, port_mac_cmd.status, out_size); + return -EFAULT; + } + +@@ -129,8 +129,8 @@ int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr) + &port_mac_cmd, sizeof(port_mac_cmd), + &port_mac_cmd, &out_size); + if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) { +- dev_err(&pdev->dev, "Failed to get mac, ret = %d\n", +- port_mac_cmd.status); ++ dev_err(&pdev->dev, "Failed to get mac, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, port_mac_cmd.status, out_size); + return -EFAULT; + } + +@@ -172,9 +172,9 @@ int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu) + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_CHANGE_MTU, + &port_mtu_cmd, sizeof(port_mtu_cmd), + &port_mtu_cmd, &out_size); +- if (err || (out_size != sizeof(port_mtu_cmd)) || port_mtu_cmd.status) { +- dev_err(&pdev->dev, "Failed to set mtu, ret = %d\n", +- port_mtu_cmd.status); ++ if (err || out_size != sizeof(port_mtu_cmd) || port_mtu_cmd.status) { ++ dev_err(&pdev->dev, "Failed to set mtu, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, port_mtu_cmd.status, out_size); + return -EFAULT; + } + +@@ -264,8 +264,8 @@ int hinic_port_link_state(struct hinic_dev *nic_dev, + &link_cmd, sizeof(link_cmd), + &link_cmd, &out_size); + if (err || (out_size != sizeof(link_cmd)) || link_cmd.status) { +- dev_err(&pdev->dev, "Failed to get link state, ret = %d\n", +- link_cmd.status); ++ dev_err(&pdev->dev, "Failed to get link state, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, link_cmd.status, out_size); + return -EINVAL; + } + +@@ -298,8 +298,8 @@ int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state) + &port_state, sizeof(port_state), + &port_state, &out_size); + if (err || (out_size != sizeof(port_state)) || port_state.status) { +- dev_err(&pdev->dev, "Failed to set port state, ret = %d\n", +- port_state.status); ++ dev_err(&pdev->dev, "Failed to set port state, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, port_state.status, out_size); + return -EFAULT; + } + +@@ -330,8 +330,8 @@ int hinic_port_set_func_state(struct hinic_dev *nic_dev, + &func_state, sizeof(func_state), + &func_state, &out_size); + if (err || (out_size != sizeof(func_state)) || func_state.status) { +- dev_err(&pdev->dev, "Failed to set port func state, ret = %d\n", +- func_state.status); ++ dev_err(&pdev->dev, "Failed to set port func state, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, func_state.status, out_size); + return -EFAULT; + } + +@@ -361,9 +361,9 @@ int hinic_port_get_cap(struct hinic_dev *nic_dev, + port_cap, &out_size); + if (err || (out_size != sizeof(*port_cap)) || port_cap->status) { + dev_err(&pdev->dev, +- "Failed to get port capabilities, ret = %d\n", +- port_cap->status); +- return -EINVAL; ++ "Failed to get port capabilities, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, port_cap->status, out_size); ++ return -EIO; + } + + return 0; +@@ -393,9 +393,9 @@ int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state) + &tso_cfg, &out_size); + if (err || out_size != sizeof(tso_cfg) || tso_cfg.status) { + dev_err(&pdev->dev, +- "Failed to set port tso, ret = %d\n", +- tso_cfg.status); +- return -EINVAL; ++ "Failed to set port tso, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, tso_cfg.status, out_size); ++ return -EIO; + } + + return 0; +@@ -423,9 +423,9 @@ int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en) + &rx_csum_cfg, &out_size); + if (err || !out_size || rx_csum_cfg.status) { + dev_err(&pdev->dev, +- "Failed to set rx csum offload, ret = %d\n", +- rx_csum_cfg.status); +- return -EINVAL; ++ "Failed to set rx csum offload, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, rx_csum_cfg.status, out_size); ++ return -EIO; + } + + return 0; +@@ -480,9 +480,9 @@ int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs) + &rq_num, &out_size); + if (err || !out_size || rq_num.status) { + dev_err(&pdev->dev, +- "Failed to rxq number, ret = %d\n", +- rq_num.status); +- return -EINVAL; ++ "Failed to set rxq number, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, rq_num.status, out_size); ++ return -EIO; + } + + return 0; +@@ -508,9 +508,9 @@ static int hinic_set_rx_lro(struct hinic_dev *nic_dev, u8 ipv4_en, u8 ipv6_en, + &lro_cfg, &out_size); + if (err || !out_size || lro_cfg.status) { + dev_err(&pdev->dev, +- "Failed to set lro offload, ret = %d\n", +- lro_cfg.status); +- return -EINVAL; ++ "Failed to set lro offload, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, lro_cfg.status, out_size); ++ return -EIO; + } + + return 0; +@@ -542,10 +542,10 @@ static int hinic_set_rx_lro_timer(struct hinic_dev *nic_dev, u32 timer_value) + + if (err || !out_size || lro_timer.status) { + dev_err(&pdev->dev, +- "Failed to set lro timer, ret = %d\n", +- lro_timer.status); ++ "Failed to set lro timer, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, lro_timer.status, out_size); + +- return -EINVAL; ++ return -EIO; + } + + return 0; +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c +index efab2dd2c889b..b757f7057b8fe 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c +@@ -38,11 +38,10 @@ static int hinic_set_mac(struct hinic_hwdev *hwdev, const u8 *mac_addr, + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_MAC, &mac_info, + sizeof(mac_info), &mac_info, &out_size); + if (err || out_size != sizeof(mac_info) || +- (mac_info.status && mac_info.status != HINIC_PF_SET_VF_ALREADY && +- mac_info.status != HINIC_MGMT_STATUS_EXIST)) { +- dev_err(&hwdev->func_to_io.hwif->pdev->dev, "Failed to change MAC, ret = %d\n", +- mac_info.status); +- return -EFAULT; ++ (mac_info.status && mac_info.status != HINIC_MGMT_STATUS_EXIST)) { ++ dev_err(&hwdev->func_to_io.hwif->pdev->dev, "Failed to set MAC, err: %d, status: 0x%x, out size: 0x%x\n", ++ err, mac_info.status, out_size); ++ return -EIO; + } + + return 0; +@@ -452,8 +451,7 @@ struct hinic_sriov_info *hinic_get_sriov_info_by_pcidev(struct pci_dev *pdev) + + static int hinic_check_mac_info(u8 status, u16 vlan_id) + { +- if ((status && status != HINIC_MGMT_STATUS_EXIST && +- status != HINIC_PF_SET_VF_ALREADY) || ++ if ((status && status != HINIC_MGMT_STATUS_EXIST) || + (vlan_id & CHECK_IPSU_15BIT && + status == HINIC_MGMT_STATUS_EXIST)) + return -EINVAL; +@@ -495,12 +493,6 @@ static int hinic_update_mac(struct hinic_hwdev *hwdev, u8 *old_mac, + return -EINVAL; + } + +- if (mac_info.status == HINIC_PF_SET_VF_ALREADY) { +- dev_warn(&hwdev->hwif->pdev->dev, +- "PF has already set VF MAC. Ignore update operation\n"); +- return HINIC_PF_SET_VF_ALREADY; +- } +- + if (mac_info.status == HINIC_MGMT_STATUS_EXIST) + dev_warn(&hwdev->hwif->pdev->dev, "MAC is repeated. Ignore update operation\n"); + +diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c +index d338efe5f3f55..91343e2d3a145 100644 +--- a/drivers/net/ethernet/intel/iavf/iavf_main.c ++++ b/drivers/net/ethernet/intel/iavf/iavf_main.c +@@ -3777,7 +3777,6 @@ err_dma: + return err; + } + +-#ifdef CONFIG_PM + /** + * iavf_suspend - Power management suspend routine + * @pdev: PCI device information struct +@@ -3785,11 +3784,10 @@ err_dma: + * + * Called when the system (VM) is entering sleep/suspend. + **/ +-static int iavf_suspend(struct pci_dev *pdev, pm_message_t state) ++static int __maybe_unused iavf_suspend(struct device *dev_d) + { +- struct net_device *netdev = pci_get_drvdata(pdev); ++ struct net_device *netdev = dev_get_drvdata(dev_d); + struct iavf_adapter *adapter = netdev_priv(netdev); +- int retval = 0; + + netif_device_detach(netdev); + +@@ -3807,12 +3805,6 @@ static int iavf_suspend(struct pci_dev *pdev, pm_message_t state) + + clear_bit(__IAVF_IN_CRITICAL_TASK, &adapter->crit_section); + +- retval = pci_save_state(pdev); +- if (retval) +- return retval; +- +- pci_disable_device(pdev); +- + return 0; + } + +@@ -3822,24 +3814,13 @@ static int iavf_suspend(struct pci_dev *pdev, pm_message_t state) + * + * Called when the system (VM) is resumed from sleep/suspend. + **/ +-static int iavf_resume(struct pci_dev *pdev) ++static int __maybe_unused iavf_resume(struct device *dev_d) + { +- struct iavf_adapter *adapter = pci_get_drvdata(pdev); +- struct net_device *netdev = adapter->netdev; ++ struct pci_dev *pdev = to_pci_dev(dev_d); ++ struct net_device *netdev = pci_get_drvdata(pdev); ++ struct iavf_adapter *adapter = netdev_priv(netdev); + u32 err; + +- pci_set_power_state(pdev, PCI_D0); +- pci_restore_state(pdev); +- /* pci_restore_state clears dev->state_saved so call +- * pci_save_state to restore it. +- */ +- pci_save_state(pdev); +- +- err = pci_enable_device_mem(pdev); +- if (err) { +- dev_err(&pdev->dev, "Cannot enable PCI device from suspend.\n"); +- return err; +- } + pci_set_master(pdev); + + rtnl_lock(); +@@ -3863,7 +3844,6 @@ static int iavf_resume(struct pci_dev *pdev) + return err; + } + +-#endif /* CONFIG_PM */ + /** + * iavf_remove - Device Removal Routine + * @pdev: PCI device information struct +@@ -3965,16 +3945,15 @@ static void iavf_remove(struct pci_dev *pdev) + pci_disable_device(pdev); + } + ++static SIMPLE_DEV_PM_OPS(iavf_pm_ops, iavf_suspend, iavf_resume); ++ + static struct pci_driver iavf_driver = { +- .name = iavf_driver_name, +- .id_table = iavf_pci_tbl, +- .probe = iavf_probe, +- .remove = iavf_remove, +-#ifdef CONFIG_PM +- .suspend = iavf_suspend, +- .resume = iavf_resume, +-#endif +- .shutdown = iavf_shutdown, ++ .name = iavf_driver_name, ++ .id_table = iavf_pci_tbl, ++ .probe = iavf_probe, ++ .remove = iavf_remove, ++ .driver.pm = &iavf_pm_ops, ++ .shutdown = iavf_shutdown, + }; + + /** +diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c +index 2e3a39cea2c03..4c5845a0965a9 100644 +--- a/drivers/net/ethernet/intel/ice/ice_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_lib.c +@@ -240,7 +240,7 @@ static int ice_get_free_slot(void *array, int size, int curr) + * ice_vsi_delete - delete a VSI from the switch + * @vsi: pointer to VSI being removed + */ +-void ice_vsi_delete(struct ice_vsi *vsi) ++static void ice_vsi_delete(struct ice_vsi *vsi) + { + struct ice_pf *pf = vsi->back; + struct ice_vsi_ctx *ctxt; +@@ -307,7 +307,7 @@ static void ice_vsi_free_arrays(struct ice_vsi *vsi) + * + * Returns 0 on success, negative on failure + */ +-int ice_vsi_clear(struct ice_vsi *vsi) ++static int ice_vsi_clear(struct ice_vsi *vsi) + { + struct ice_pf *pf = NULL; + struct device *dev; +@@ -557,7 +557,7 @@ static int ice_vsi_get_qs(struct ice_vsi *vsi) + * ice_vsi_put_qs - Release queues from VSI to PF + * @vsi: the VSI that is going to release queues + */ +-void ice_vsi_put_qs(struct ice_vsi *vsi) ++static void ice_vsi_put_qs(struct ice_vsi *vsi) + { + struct ice_pf *pf = vsi->back; + int i; +@@ -1190,6 +1190,18 @@ static void ice_vsi_clear_rings(struct ice_vsi *vsi) + { + int i; + ++ /* Avoid stale references by clearing map from vector to ring */ ++ if (vsi->q_vectors) { ++ ice_for_each_q_vector(vsi, i) { ++ struct ice_q_vector *q_vector = vsi->q_vectors[i]; ++ ++ if (q_vector) { ++ q_vector->tx.ring = NULL; ++ q_vector->rx.ring = NULL; ++ } ++ } ++ } ++ + if (vsi->tx_rings) { + for (i = 0; i < vsi->alloc_txq; i++) { + if (vsi->tx_rings[i]) { +@@ -2254,7 +2266,7 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi, + if (status) { + dev_err(dev, "VSI %d failed lan queue config, error %s\n", + vsi->vsi_num, ice_stat_str(status)); +- goto unroll_vector_base; ++ goto unroll_clear_rings; + } + + /* Add switch rule to drop all Tx Flow Control Frames, of look up +diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h b/drivers/net/ethernet/intel/ice/ice_lib.h +index d80e6afa45112..2954b30e6ec79 100644 +--- a/drivers/net/ethernet/intel/ice/ice_lib.h ++++ b/drivers/net/ethernet/intel/ice/ice_lib.h +@@ -43,10 +43,6 @@ int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena, bool vlan_promisc); + + void ice_cfg_sw_lldp(struct ice_vsi *vsi, bool tx, bool create); + +-void ice_vsi_delete(struct ice_vsi *vsi); +- +-int ice_vsi_clear(struct ice_vsi *vsi); +- + #ifdef CONFIG_DCB + int ice_vsi_cfg_tc(struct ice_vsi *vsi, u8 ena_tc); + #endif /* CONFIG_DCB */ +@@ -77,8 +73,6 @@ bool ice_is_reset_in_progress(unsigned long *state); + void + ice_write_qrxflxp_cntxt(struct ice_hw *hw, u16 pf_q, u32 rxdid, u32 prio); + +-void ice_vsi_put_qs(struct ice_vsi *vsi); +- + void ice_vsi_dis_irq(struct ice_vsi *vsi); + + void ice_vsi_free_irq(struct ice_vsi *vsi); +diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c +index 4cbd49c87568a..4b52f1dea7f3a 100644 +--- a/drivers/net/ethernet/intel/ice/ice_main.c ++++ b/drivers/net/ethernet/intel/ice/ice_main.c +@@ -2605,10 +2605,8 @@ static int ice_setup_pf_sw(struct ice_pf *pf) + return -EBUSY; + + vsi = ice_pf_vsi_setup(pf, pf->hw.port_info); +- if (!vsi) { +- status = -ENOMEM; +- goto unroll_vsi_setup; +- } ++ if (!vsi) ++ return -ENOMEM; + + status = ice_cfg_netdev(vsi); + if (status) { +@@ -2655,12 +2653,7 @@ unroll_napi_add: + } + + unroll_vsi_setup: +- if (vsi) { +- ice_vsi_free_q_vectors(vsi); +- ice_vsi_delete(vsi); +- ice_vsi_put_qs(vsi); +- ice_vsi_clear(vsi); +- } ++ ice_vsi_release(vsi); + return status; + } + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 7d5d9d34f4e47..69a234e83b8b7 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3372,24 +3372,15 @@ static int mvneta_txq_sw_init(struct mvneta_port *pp, + txq->last_desc = txq->size - 1; + + txq->buf = kmalloc_array(txq->size, sizeof(*txq->buf), GFP_KERNEL); +- if (!txq->buf) { +- dma_free_coherent(pp->dev->dev.parent, +- txq->size * MVNETA_DESC_ALIGNED_SIZE, +- txq->descs, txq->descs_phys); ++ if (!txq->buf) + return -ENOMEM; +- } + + /* Allocate DMA buffers for TSO MAC/IP/TCP headers */ + txq->tso_hdrs = dma_alloc_coherent(pp->dev->dev.parent, + txq->size * TSO_HEADER_SIZE, + &txq->tso_hdrs_phys, GFP_KERNEL); +- if (!txq->tso_hdrs) { +- kfree(txq->buf); +- dma_free_coherent(pp->dev->dev.parent, +- txq->size * MVNETA_DESC_ALIGNED_SIZE, +- txq->descs, txq->descs_phys); ++ if (!txq->tso_hdrs) + return -ENOMEM; +- } + + /* Setup XPS mapping */ + if (txq_number > 1) +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.c b/drivers/net/ethernet/marvell/octeontx2/af/mbox.c +index 387e33fa417aa..2718fe201c147 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.c +@@ -17,7 +17,7 @@ + + static const u16 msgs_offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); + +-void otx2_mbox_reset(struct otx2_mbox *mbox, int devid) ++void __otx2_mbox_reset(struct otx2_mbox *mbox, int devid) + { + void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE); + struct otx2_mbox_dev *mdev = &mbox->dev[devid]; +@@ -26,13 +26,21 @@ void otx2_mbox_reset(struct otx2_mbox *mbox, int devid) + tx_hdr = hw_mbase + mbox->tx_start; + rx_hdr = hw_mbase + mbox->rx_start; + +- spin_lock(&mdev->mbox_lock); + mdev->msg_size = 0; + mdev->rsp_size = 0; + tx_hdr->num_msgs = 0; + tx_hdr->msg_size = 0; + rx_hdr->num_msgs = 0; + rx_hdr->msg_size = 0; ++} ++EXPORT_SYMBOL(__otx2_mbox_reset); ++ ++void otx2_mbox_reset(struct otx2_mbox *mbox, int devid) ++{ ++ struct otx2_mbox_dev *mdev = &mbox->dev[devid]; ++ ++ spin_lock(&mdev->mbox_lock); ++ __otx2_mbox_reset(mbox, devid); + spin_unlock(&mdev->mbox_lock); + } + EXPORT_SYMBOL(otx2_mbox_reset); +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +index 6dfd0f90cd704..ab433789d2c31 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +@@ -93,6 +93,7 @@ struct mbox_msghdr { + }; + + void otx2_mbox_reset(struct otx2_mbox *mbox, int devid); ++void __otx2_mbox_reset(struct otx2_mbox *mbox, int devid); + void otx2_mbox_destroy(struct otx2_mbox *mbox); + int otx2_mbox_init(struct otx2_mbox *mbox, void __force *hwbase, + struct pci_dev *pdev, void __force *reg_base, +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +index dcf25a0920084..b89dde2c8b089 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +@@ -463,6 +463,7 @@ void rvu_nix_freemem(struct rvu *rvu); + int rvu_get_nixlf_count(struct rvu *rvu); + void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int npalf); + int nix_get_nixlf(struct rvu *rvu, u16 pcifunc, int *nixlf, int *nix_blkaddr); ++int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add); + + /* NPC APIs */ + int rvu_npc_init(struct rvu *rvu); +@@ -477,7 +478,7 @@ void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf); + void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf); + void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, + int nixlf, u64 chan); +-void rvu_npc_disable_bcast_entry(struct rvu *rvu, u16 pcifunc); ++void rvu_npc_enable_bcast_entry(struct rvu *rvu, u16 pcifunc, bool enable); + int rvu_npc_update_rxvlan(struct rvu *rvu, u16 pcifunc, int nixlf); + void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf); + void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf); +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +index 36953d4f51c73..3495b3a6828c0 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +@@ -17,7 +17,6 @@ + #include "npc.h" + #include "cgx.h" + +-static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add); + static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, + int type, int chan_id); + +@@ -2020,7 +2019,7 @@ static int nix_update_mce_list(struct nix_mce_list *mce_list, + return 0; + } + +-static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add) ++int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add) + { + int err = 0, idx, next_idx, last_idx; + struct nix_mce_list *mce_list; +@@ -2065,7 +2064,7 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add) + + /* Disable MCAM entry in NPC */ + if (!mce_list->count) { +- rvu_npc_disable_bcast_entry(rvu, pcifunc); ++ rvu_npc_enable_bcast_entry(rvu, pcifunc, false); + goto end; + } + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +index 0a214084406a6..fbaf9bcd83f2f 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +@@ -530,7 +530,7 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, + NIX_INTF_RX, &entry, true); + } + +-void rvu_npc_disable_bcast_entry(struct rvu *rvu, u16 pcifunc) ++void rvu_npc_enable_bcast_entry(struct rvu *rvu, u16 pcifunc, bool enable) + { + struct npc_mcam *mcam = &rvu->hw->mcam; + int blkaddr, index; +@@ -543,7 +543,7 @@ void rvu_npc_disable_bcast_entry(struct rvu *rvu, u16 pcifunc) + pcifunc = pcifunc & ~RVU_PFVF_FUNC_MASK; + + index = npc_get_nixlf_mcam_index(mcam, pcifunc, 0, NIXLF_BCAST_ENTRY); +- npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); ++ npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); + } + + void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, +@@ -622,23 +622,35 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, + nixlf, NIXLF_UCAST_ENTRY); + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); + +- /* For PF, ena/dis promisc and bcast MCAM match entries */ +- if (pcifunc & RVU_PFVF_FUNC_MASK) ++ /* For PF, ena/dis promisc and bcast MCAM match entries. ++ * For VFs add/delete from bcast list when RX multicast ++ * feature is present. ++ */ ++ if (pcifunc & RVU_PFVF_FUNC_MASK && !rvu->hw->cap.nix_rx_multicast) + return; + + /* For bcast, enable/disable only if it's action is not + * packet replication, incase if action is replication +- * then this PF's nixlf is removed from bcast replication ++ * then this PF/VF's nixlf is removed from bcast replication + * list. + */ +- index = npc_get_nixlf_mcam_index(mcam, pcifunc, ++ index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK, + nixlf, NIXLF_BCAST_ENTRY); + bank = npc_get_bank(mcam, index); + *(u64 *)&action = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_ACTION(index & (mcam->banksize - 1), bank)); +- if (action.op != NIX_RX_ACTIONOP_MCAST) ++ ++ /* VFs will not have BCAST entry */ ++ if (action.op != NIX_RX_ACTIONOP_MCAST && ++ !(pcifunc & RVU_PFVF_FUNC_MASK)) { + npc_enable_mcam_entry(rvu, mcam, + blkaddr, index, enable); ++ } else { ++ nix_update_bcast_mce_list(rvu, pcifunc, enable); ++ /* Enable PF's BCAST entry for packet replication */ ++ rvu_npc_enable_bcast_entry(rvu, pcifunc, enable); ++ } ++ + if (enable) + rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf); + else +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index 75a8c407e815c..2fb45670aca49 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -370,8 +370,8 @@ static int otx2_forward_vf_mbox_msgs(struct otx2_nic *pf, + dst_mbox = &pf->mbox; + dst_size = dst_mbox->mbox.tx_size - + ALIGN(sizeof(*mbox_hdr), MBOX_MSG_ALIGN); +- /* Check if msgs fit into destination area */ +- if (mbox_hdr->msg_size > dst_size) ++ /* Check if msgs fit into destination area and has valid size */ ++ if (mbox_hdr->msg_size > dst_size || !mbox_hdr->msg_size) + return -EINVAL; + + dst_mdev = &dst_mbox->mbox.dev[0]; +@@ -526,10 +526,10 @@ static void otx2_pfvf_mbox_up_handler(struct work_struct *work) + + end: + offset = mbox->rx_start + msg->next_msgoff; ++ if (mdev->msgs_acked == (vf_mbox->up_num_msgs - 1)) ++ __otx2_mbox_reset(mbox, 0); + mdev->msgs_acked++; + } +- +- otx2_mbox_reset(mbox, vf_idx); + } + + static irqreturn_t otx2_pfvf_mbox_intr_handler(int irq, void *pf_irq) +@@ -803,10 +803,11 @@ static void otx2_pfaf_mbox_handler(struct work_struct *work) + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + otx2_process_pfaf_mbox_msg(pf, msg); + offset = mbox->rx_start + msg->next_msgoff; ++ if (mdev->msgs_acked == (af_mbox->num_msgs - 1)) ++ __otx2_mbox_reset(mbox, 0); + mdev->msgs_acked++; + } + +- otx2_mbox_reset(mbox, 0); + } + + static void otx2_handle_link_event(struct otx2_nic *pf) +@@ -1560,10 +1561,13 @@ int otx2_open(struct net_device *netdev) + + err = otx2_rxtx_enable(pf, true); + if (err) +- goto err_free_cints; ++ goto err_tx_stop_queues; + + return 0; + ++err_tx_stop_queues: ++ netif_tx_stop_all_queues(netdev); ++ netif_carrier_off(netdev); + err_free_cints: + otx2_free_cints(pf, qidx); + vec = pci_irq_vector(pf->pdev, +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +index b04f5429d72d9..334eab976ee4a 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +@@ -524,6 +524,7 @@ static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + sqe_hdr->ol3type = NIX_SENDL3TYPE_IP4_CKSUM; + } else if (skb->protocol == htons(ETH_P_IPV6)) { + proto = ipv6_hdr(skb)->nexthdr; ++ sqe_hdr->ol3type = NIX_SENDL3TYPE_IP6; + } + + if (proto == IPPROTO_TCP) +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +index 92a3db69a6cd6..2f90f17214415 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +@@ -99,10 +99,10 @@ static void otx2vf_vfaf_mbox_handler(struct work_struct *work) + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + otx2vf_process_vfaf_mbox_msg(af_mbox->pfvf, msg); + offset = mbox->rx_start + msg->next_msgoff; ++ if (mdev->msgs_acked == (af_mbox->num_msgs - 1)) ++ __otx2_mbox_reset(mbox, 0); + mdev->msgs_acked++; + } +- +- otx2_mbox_reset(mbox, 0); + } + + static int otx2vf_process_mbox_msg_up(struct otx2_nic *vf, +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +index 1d91a0d0ab1d7..2b597ac365f84 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +@@ -69,12 +69,10 @@ enum { + MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR = 0x10, + }; + +-static struct mlx5_cmd_work_ent *alloc_cmd(struct mlx5_cmd *cmd, +- struct mlx5_cmd_msg *in, +- struct mlx5_cmd_msg *out, +- void *uout, int uout_size, +- mlx5_cmd_cbk_t cbk, +- void *context, int page_queue) ++static struct mlx5_cmd_work_ent * ++cmd_alloc_ent(struct mlx5_cmd *cmd, struct mlx5_cmd_msg *in, ++ struct mlx5_cmd_msg *out, void *uout, int uout_size, ++ mlx5_cmd_cbk_t cbk, void *context, int page_queue) + { + gfp_t alloc_flags = cbk ? GFP_ATOMIC : GFP_KERNEL; + struct mlx5_cmd_work_ent *ent; +@@ -83,6 +81,7 @@ static struct mlx5_cmd_work_ent *alloc_cmd(struct mlx5_cmd *cmd, + if (!ent) + return ERR_PTR(-ENOMEM); + ++ ent->idx = -EINVAL; + ent->in = in; + ent->out = out; + ent->uout = uout; +@@ -91,10 +90,16 @@ static struct mlx5_cmd_work_ent *alloc_cmd(struct mlx5_cmd *cmd, + ent->context = context; + ent->cmd = cmd; + ent->page_queue = page_queue; ++ refcount_set(&ent->refcnt, 1); + + return ent; + } + ++static void cmd_free_ent(struct mlx5_cmd_work_ent *ent) ++{ ++ kfree(ent); ++} ++ + static u8 alloc_token(struct mlx5_cmd *cmd) + { + u8 token; +@@ -109,7 +114,7 @@ static u8 alloc_token(struct mlx5_cmd *cmd) + return token; + } + +-static int alloc_ent(struct mlx5_cmd *cmd) ++static int cmd_alloc_index(struct mlx5_cmd *cmd) + { + unsigned long flags; + int ret; +@@ -123,7 +128,7 @@ static int alloc_ent(struct mlx5_cmd *cmd) + return ret < cmd->max_reg_cmds ? ret : -ENOMEM; + } + +-static void free_ent(struct mlx5_cmd *cmd, int idx) ++static void cmd_free_index(struct mlx5_cmd *cmd, int idx) + { + unsigned long flags; + +@@ -132,6 +137,22 @@ static void free_ent(struct mlx5_cmd *cmd, int idx) + spin_unlock_irqrestore(&cmd->alloc_lock, flags); + } + ++static void cmd_ent_get(struct mlx5_cmd_work_ent *ent) ++{ ++ refcount_inc(&ent->refcnt); ++} ++ ++static void cmd_ent_put(struct mlx5_cmd_work_ent *ent) ++{ ++ if (!refcount_dec_and_test(&ent->refcnt)) ++ return; ++ ++ if (ent->idx >= 0) ++ cmd_free_index(ent->cmd, ent->idx); ++ ++ cmd_free_ent(ent); ++} ++ + static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx) + { + return cmd->cmd_buf + (idx << cmd->log_stride); +@@ -219,11 +240,6 @@ static void poll_timeout(struct mlx5_cmd_work_ent *ent) + ent->ret = -ETIMEDOUT; + } + +-static void free_cmd(struct mlx5_cmd_work_ent *ent) +-{ +- kfree(ent); +-} +- + static int verify_signature(struct mlx5_cmd_work_ent *ent) + { + struct mlx5_cmd_mailbox *next = ent->out->next; +@@ -837,11 +853,22 @@ static void cb_timeout_handler(struct work_struct *work) + struct mlx5_core_dev *dev = container_of(ent->cmd, struct mlx5_core_dev, + cmd); + ++ mlx5_cmd_eq_recover(dev); ++ ++ /* Maybe got handled by eq recover ? */ ++ if (!test_bit(MLX5_CMD_ENT_STATE_PENDING_COMP, &ent->state)) { ++ mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) Async, recovered after timeout\n", ent->idx, ++ mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); ++ goto out; /* phew, already handled */ ++ } ++ + ent->ret = -ETIMEDOUT; +- mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n", +- mlx5_command_str(msg_to_opcode(ent->in)), +- msg_to_opcode(ent->in)); ++ mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) Async, timeout. Will cause a leak of a command resource\n", ++ ent->idx, mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); + mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true); ++ ++out: ++ cmd_ent_put(ent); /* for the cmd_ent_get() took on schedule delayed work */ + } + + static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg); +@@ -856,6 +883,25 @@ static bool opcode_allowed(struct mlx5_cmd *cmd, u16 opcode) + return cmd->allowed_opcode == opcode; + } + ++static int cmd_alloc_index_retry(struct mlx5_cmd *cmd) ++{ ++ unsigned long alloc_end = jiffies + msecs_to_jiffies(1000); ++ int idx; ++ ++retry: ++ idx = cmd_alloc_index(cmd); ++ if (idx < 0 && time_before(jiffies, alloc_end)) { ++ /* Index allocation can fail on heavy load of commands. This is a temporary ++ * situation as the current command already holds the semaphore, meaning that ++ * another command completion is being handled and it is expected to release ++ * the entry index soon. ++ */ ++ cpu_relax(); ++ goto retry; ++ } ++ return idx; ++} ++ + static void cmd_work_handler(struct work_struct *work) + { + struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work); +@@ -873,14 +919,14 @@ static void cmd_work_handler(struct work_struct *work) + sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; + down(sem); + if (!ent->page_queue) { +- alloc_ret = alloc_ent(cmd); ++ alloc_ret = cmd_alloc_index_retry(cmd); + if (alloc_ret < 0) { + mlx5_core_err_rl(dev, "failed to allocate command entry\n"); + if (ent->callback) { + ent->callback(-EAGAIN, ent->context); + mlx5_free_cmd_msg(dev, ent->out); + free_msg(dev, ent->in); +- free_cmd(ent); ++ cmd_ent_put(ent); + } else { + ent->ret = -EAGAIN; + complete(&ent->done); +@@ -916,8 +962,8 @@ static void cmd_work_handler(struct work_struct *work) + ent->ts1 = ktime_get_ns(); + cmd_mode = cmd->mode; + +- if (ent->callback) +- schedule_delayed_work(&ent->cb_timeout_work, cb_timeout); ++ if (ent->callback && schedule_delayed_work(&ent->cb_timeout_work, cb_timeout)) ++ cmd_ent_get(ent); + set_bit(MLX5_CMD_ENT_STATE_PENDING_COMP, &ent->state); + + /* Skip sending command to fw if internal error */ +@@ -933,13 +979,10 @@ static void cmd_work_handler(struct work_struct *work) + MLX5_SET(mbox_out, ent->out, syndrome, drv_synd); + + mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true); +- /* no doorbell, no need to keep the entry */ +- free_ent(cmd, ent->idx); +- if (ent->callback) +- free_cmd(ent); + return; + } + ++ cmd_ent_get(ent); /* for the _real_ FW event on completion */ + /* ring doorbell after the descriptor is valid */ + mlx5_core_dbg(dev, "writing 0x%x to command doorbell\n", 1 << ent->idx); + wmb(); +@@ -983,6 +1026,35 @@ static const char *deliv_status_to_str(u8 status) + } + } + ++enum { ++ MLX5_CMD_TIMEOUT_RECOVER_MSEC = 5 * 1000, ++}; ++ ++static void wait_func_handle_exec_timeout(struct mlx5_core_dev *dev, ++ struct mlx5_cmd_work_ent *ent) ++{ ++ unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_RECOVER_MSEC); ++ ++ mlx5_cmd_eq_recover(dev); ++ ++ /* Re-wait on the ent->done after executing the recovery flow. If the ++ * recovery flow (or any other recovery flow running simultaneously) ++ * has recovered an EQE, it should cause the entry to be completed by ++ * the command interface. ++ */ ++ if (wait_for_completion_timeout(&ent->done, timeout)) { ++ mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) recovered after timeout\n", ent->idx, ++ mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); ++ return; ++ } ++ ++ mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) No done completion\n", ent->idx, ++ mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); ++ ++ ent->ret = -ETIMEDOUT; ++ mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true); ++} ++ + static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent) + { + unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC); +@@ -994,12 +1066,10 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent) + ent->ret = -ECANCELED; + goto out_err; + } +- if (cmd->mode == CMD_MODE_POLLING || ent->polling) { ++ if (cmd->mode == CMD_MODE_POLLING || ent->polling) + wait_for_completion(&ent->done); +- } else if (!wait_for_completion_timeout(&ent->done, timeout)) { +- ent->ret = -ETIMEDOUT; +- mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true); +- } ++ else if (!wait_for_completion_timeout(&ent->done, timeout)) ++ wait_func_handle_exec_timeout(dev, ent); + + out_err: + err = ent->ret; +@@ -1039,11 +1109,16 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, + if (callback && page_queue) + return -EINVAL; + +- ent = alloc_cmd(cmd, in, out, uout, uout_size, callback, context, +- page_queue); ++ ent = cmd_alloc_ent(cmd, in, out, uout, uout_size, ++ callback, context, page_queue); + if (IS_ERR(ent)) + return PTR_ERR(ent); + ++ /* put for this ent is when consumed, depending on the use case ++ * 1) (!callback) blocking flow: by caller after wait_func completes ++ * 2) (callback) flow: by mlx5_cmd_comp_handler() when ent is handled ++ */ ++ + ent->token = token; + ent->polling = force_polling; + +@@ -1062,12 +1137,10 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, + } + + if (callback) +- goto out; ++ goto out; /* mlx5_cmd_comp_handler() will put(ent) */ + + err = wait_func(dev, ent); +- if (err == -ETIMEDOUT) +- goto out; +- if (err == -ECANCELED) ++ if (err == -ETIMEDOUT || err == -ECANCELED) + goto out_free; + + ds = ent->ts2 - ent->ts1; +@@ -1085,7 +1158,7 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, + *status = ent->status; + + out_free: +- free_cmd(ent); ++ cmd_ent_put(ent); + out: + return err; + } +@@ -1516,14 +1589,19 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force + if (!forced) { + mlx5_core_err(dev, "Command completion arrived after timeout (entry idx = %d).\n", + ent->idx); +- free_ent(cmd, ent->idx); +- free_cmd(ent); ++ cmd_ent_put(ent); + } + continue; + } + +- if (ent->callback) +- cancel_delayed_work(&ent->cb_timeout_work); ++ if (ent->callback && cancel_delayed_work(&ent->cb_timeout_work)) ++ cmd_ent_put(ent); /* timeout work was canceled */ ++ ++ if (!forced || /* Real FW completion */ ++ pci_channel_offline(dev->pdev) || /* FW is inaccessible */ ++ dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) ++ cmd_ent_put(ent); ++ + if (ent->page_queue) + sem = &cmd->pages_sem; + else +@@ -1545,10 +1623,6 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force + ent->ret, deliv_status_to_str(ent->status), ent->status); + } + +- /* only real completion will free the entry slot */ +- if (!forced) +- free_ent(cmd, ent->idx); +- + if (ent->callback) { + ds = ent->ts2 - ent->ts1; + if (ent->op < MLX5_CMD_OP_MAX) { +@@ -1576,10 +1650,13 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force + free_msg(dev, ent->in); + + err = err ? err : ent->status; +- if (!forced) +- free_cmd(ent); ++ /* final consumer is done, release ent */ ++ cmd_ent_put(ent); + callback(err, context); + } else { ++ /* release wait_func() so mlx5_cmd_invoke() ++ * can make the final ent_put() ++ */ + complete(&ent->done); + } + up(sem); +@@ -1589,8 +1666,11 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force + + void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev) + { ++ struct mlx5_cmd *cmd = &dev->cmd; ++ unsigned long bitmask; + unsigned long flags; + u64 vector; ++ int i; + + /* wait for pending handlers to complete */ + mlx5_eq_synchronize_cmd_irq(dev); +@@ -1599,11 +1679,20 @@ void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev) + if (!vector) + goto no_trig; + ++ bitmask = vector; ++ /* we must increment the allocated entries refcount before triggering the completions ++ * to guarantee pending commands will not get freed in the meanwhile. ++ * For that reason, it also has to be done inside the alloc_lock. ++ */ ++ for_each_set_bit(i, &bitmask, (1 << cmd->log_sz)) ++ cmd_ent_get(cmd->ent_arr[i]); + vector |= MLX5_TRIGGERED_CMD_COMP; + spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags); + + mlx5_core_dbg(dev, "vector 0x%llx\n", vector); + mlx5_cmd_comp_handler(dev, vector, true); ++ for_each_set_bit(i, &bitmask, (1 << cmd->log_sz)) ++ cmd_ent_put(cmd->ent_arr[i]); + return; + + no_trig: +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h +index 76b23ba7a4687..cb3857e136d62 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h +@@ -90,7 +90,12 @@ struct page_pool; + #define MLX5_MPWRQ_PAGES_PER_WQE BIT(MLX5_MPWRQ_WQE_PAGE_ORDER) + + #define MLX5_MTT_OCTW(npages) (ALIGN(npages, 8) / 2) +-#define MLX5E_REQUIRED_WQE_MTTS (ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8)) ++/* Add another page to MLX5E_REQUIRED_WQE_MTTS as a buffer between ++ * WQEs, This page will absorb write overflow by the hardware, when ++ * receiving packets larger than MTU. These oversize packets are ++ * dropped by the driver at a later stage. ++ */ ++#define MLX5E_REQUIRED_WQE_MTTS (ALIGN(MLX5_MPWRQ_PAGES_PER_WQE + 1, 8)) + #define MLX5E_LOG_ALIGNED_MPWQE_PPW (ilog2(MLX5E_REQUIRED_WQE_MTTS)) + #define MLX5E_REQUIRED_MTTS(wqes) (wqes * MLX5E_REQUIRED_WQE_MTTS) + #define MLX5E_MAX_RQ_NUM_MTTS \ +@@ -621,6 +626,7 @@ struct mlx5e_rq { + u32 rqn; + struct mlx5_core_dev *mdev; + struct mlx5_core_mkey umr_mkey; ++ struct mlx5e_dma_info wqe_overflow; + + /* XDP read-mostly */ + struct xdp_rxq_info xdp_rxq; +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c +index 98e909bf3c1ec..3e32264cf6131 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c +@@ -566,6 +566,9 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy) + if (fec_policy >= (1 << MLX5E_FEC_LLRS_272_257_1) && !fec_50g_per_lane) + return -EOPNOTSUPP; + ++ if (fec_policy && !mlx5e_fec_in_caps(dev, fec_policy)) ++ return -EOPNOTSUPP; ++ + MLX5_SET(pplm_reg, in, local_port, 1); + err = mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0); + if (err) +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c +index c3d167fa944c7..6a9d783d129b2 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c +@@ -109,11 +109,25 @@ static void mlx5e_rep_neigh_stats_work(struct work_struct *work) + rtnl_unlock(); + } + ++struct neigh_update_work { ++ struct work_struct work; ++ struct neighbour *n; ++ struct mlx5e_neigh_hash_entry *nhe; ++}; ++ ++static void mlx5e_release_neigh_update_work(struct neigh_update_work *update_work) ++{ ++ neigh_release(update_work->n); ++ mlx5e_rep_neigh_entry_release(update_work->nhe); ++ kfree(update_work); ++} ++ + static void mlx5e_rep_neigh_update(struct work_struct *work) + { +- struct mlx5e_neigh_hash_entry *nhe = +- container_of(work, struct mlx5e_neigh_hash_entry, neigh_update_work); +- struct neighbour *n = nhe->n; ++ struct neigh_update_work *update_work = container_of(work, struct neigh_update_work, ++ work); ++ struct mlx5e_neigh_hash_entry *nhe = update_work->nhe; ++ struct neighbour *n = update_work->n; + struct mlx5e_encap_entry *e; + unsigned char ha[ETH_ALEN]; + struct mlx5e_priv *priv; +@@ -145,30 +159,42 @@ static void mlx5e_rep_neigh_update(struct work_struct *work) + mlx5e_rep_update_flows(priv, e, neigh_connected, ha); + mlx5e_encap_put(priv, e); + } +- mlx5e_rep_neigh_entry_release(nhe); + rtnl_unlock(); +- neigh_release(n); ++ mlx5e_release_neigh_update_work(update_work); + } + +-static void mlx5e_rep_queue_neigh_update_work(struct mlx5e_priv *priv, +- struct mlx5e_neigh_hash_entry *nhe, +- struct neighbour *n) ++static struct neigh_update_work *mlx5e_alloc_neigh_update_work(struct mlx5e_priv *priv, ++ struct neighbour *n) + { +- /* Take a reference to ensure the neighbour and mlx5 encap +- * entry won't be destructed until we drop the reference in +- * delayed work. +- */ +- neigh_hold(n); ++ struct neigh_update_work *update_work; ++ struct mlx5e_neigh_hash_entry *nhe; ++ struct mlx5e_neigh m_neigh = {}; + +- /* This assignment is valid as long as the the neigh reference +- * is taken +- */ +- nhe->n = n; ++ update_work = kzalloc(sizeof(*update_work), GFP_ATOMIC); ++ if (WARN_ON(!update_work)) ++ return NULL; + +- if (!queue_work(priv->wq, &nhe->neigh_update_work)) { +- mlx5e_rep_neigh_entry_release(nhe); +- neigh_release(n); ++ m_neigh.dev = n->dev; ++ m_neigh.family = n->ops->family; ++ memcpy(&m_neigh.dst_ip, n->primary_key, n->tbl->key_len); ++ ++ /* Obtain reference to nhe as last step in order not to release it in ++ * atomic context. ++ */ ++ rcu_read_lock(); ++ nhe = mlx5e_rep_neigh_entry_lookup(priv, &m_neigh); ++ rcu_read_unlock(); ++ if (!nhe) { ++ kfree(update_work); ++ return NULL; + } ++ ++ INIT_WORK(&update_work->work, mlx5e_rep_neigh_update); ++ neigh_hold(n); ++ update_work->n = n; ++ update_work->nhe = nhe; ++ ++ return update_work; + } + + static int mlx5e_rep_netevent_event(struct notifier_block *nb, +@@ -180,7 +206,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb, + struct net_device *netdev = rpriv->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_neigh_hash_entry *nhe = NULL; +- struct mlx5e_neigh m_neigh = {}; ++ struct neigh_update_work *update_work; + struct neigh_parms *p; + struct neighbour *n; + bool found = false; +@@ -195,17 +221,11 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb, + #endif + return NOTIFY_DONE; + +- m_neigh.dev = n->dev; +- m_neigh.family = n->ops->family; +- memcpy(&m_neigh.dst_ip, n->primary_key, n->tbl->key_len); +- +- rcu_read_lock(); +- nhe = mlx5e_rep_neigh_entry_lookup(priv, &m_neigh); +- rcu_read_unlock(); +- if (!nhe) ++ update_work = mlx5e_alloc_neigh_update_work(priv, n); ++ if (!update_work) + return NOTIFY_DONE; + +- mlx5e_rep_queue_neigh_update_work(priv, nhe, n); ++ queue_work(priv->wq, &update_work->work); + break; + + case NETEVENT_DELAY_PROBE_TIME_UPDATE: +@@ -351,7 +371,6 @@ int mlx5e_rep_neigh_entry_create(struct mlx5e_priv *priv, + + (*nhe)->priv = priv; + memcpy(&(*nhe)->m_neigh, &e->m_neigh, sizeof(e->m_neigh)); +- INIT_WORK(&(*nhe)->neigh_update_work, mlx5e_rep_neigh_update); + spin_lock_init(&(*nhe)->encap_list_lock); + INIT_LIST_HEAD(&(*nhe)->encap_list); + refcount_set(&(*nhe)->refcnt, 1); +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +index 73d3dc07331f1..713dc210f710c 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +@@ -217,6 +217,9 @@ static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, + break; + } + ++ if (WARN_ONCE(*rule_p, "VLAN rule already exists type %d", rule_type)) ++ return 0; ++ + *rule_p = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); + + if (IS_ERR(*rule_p)) { +@@ -397,8 +400,7 @@ static void mlx5e_add_vlan_rules(struct mlx5e_priv *priv) + for_each_set_bit(i, priv->fs.vlan.active_svlans, VLAN_N_VID) + mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_STAG_VID, i); + +- if (priv->fs.vlan.cvlan_filter_disabled && +- !(priv->netdev->flags & IFF_PROMISC)) ++ if (priv->fs.vlan.cvlan_filter_disabled) + mlx5e_add_any_vid_rules(priv); + } + +@@ -415,8 +417,12 @@ static void mlx5e_del_vlan_rules(struct mlx5e_priv *priv) + for_each_set_bit(i, priv->fs.vlan.active_svlans, VLAN_N_VID) + mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_STAG_VID, i); + +- if (priv->fs.vlan.cvlan_filter_disabled && +- !(priv->netdev->flags & IFF_PROMISC)) ++ WARN_ON_ONCE(!(test_bit(MLX5E_STATE_DESTROYING, &priv->state))); ++ ++ /* must be called after DESTROY bit is set and ++ * set_rx_mode is called and flushed ++ */ ++ if (priv->fs.vlan.cvlan_filter_disabled) + mlx5e_del_any_vid_rules(priv); + } + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index cccf65fc116ee..f8a20dd814379 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -258,12 +258,17 @@ static int mlx5e_rq_alloc_mpwqe_info(struct mlx5e_rq *rq, + + static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev, + u64 npages, u8 page_shift, +- struct mlx5_core_mkey *umr_mkey) ++ struct mlx5_core_mkey *umr_mkey, ++ dma_addr_t filler_addr) + { +- int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); ++ struct mlx5_mtt *mtt; ++ int inlen; + void *mkc; + u32 *in; + int err; ++ int i; ++ ++ inlen = MLX5_ST_SZ_BYTES(create_mkey_in) + sizeof(*mtt) * npages; + + in = kvzalloc(inlen, GFP_KERNEL); + if (!in) +@@ -283,6 +288,18 @@ static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev, + MLX5_SET(mkc, mkc, translations_octword_size, + MLX5_MTT_OCTW(npages)); + MLX5_SET(mkc, mkc, log_page_size, page_shift); ++ MLX5_SET(create_mkey_in, in, translations_octword_actual_size, ++ MLX5_MTT_OCTW(npages)); ++ ++ /* Initialize the mkey with all MTTs pointing to a default ++ * page (filler_addr). When the channels are activated, UMR ++ * WQEs will redirect the RX WQEs to the actual memory from ++ * the RQ's pool, while the gaps (wqe_overflow) remain mapped ++ * to the default page. ++ */ ++ mtt = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt); ++ for (i = 0 ; i < npages ; i++) ++ mtt[i].ptag = cpu_to_be64(filler_addr); + + err = mlx5_core_create_mkey(mdev, umr_mkey, in, inlen); + +@@ -294,7 +311,8 @@ static int mlx5e_create_rq_umr_mkey(struct mlx5_core_dev *mdev, struct mlx5e_rq + { + u64 num_mtts = MLX5E_REQUIRED_MTTS(mlx5_wq_ll_get_size(&rq->mpwqe.wq)); + +- return mlx5e_create_umr_mkey(mdev, num_mtts, PAGE_SHIFT, &rq->umr_mkey); ++ return mlx5e_create_umr_mkey(mdev, num_mtts, PAGE_SHIFT, &rq->umr_mkey, ++ rq->wqe_overflow.addr); + } + + static inline u64 mlx5e_get_mpwqe_offset(struct mlx5e_rq *rq, u16 wqe_ix) +@@ -362,6 +380,28 @@ static void mlx5e_rq_err_cqe_work(struct work_struct *recover_work) + mlx5e_reporter_rq_cqe_err(rq); + } + ++static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq) ++{ ++ rq->wqe_overflow.page = alloc_page(GFP_KERNEL); ++ if (!rq->wqe_overflow.page) ++ return -ENOMEM; ++ ++ rq->wqe_overflow.addr = dma_map_page(rq->pdev, rq->wqe_overflow.page, 0, ++ PAGE_SIZE, rq->buff.map_dir); ++ if (dma_mapping_error(rq->pdev, rq->wqe_overflow.addr)) { ++ __free_page(rq->wqe_overflow.page); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++static void mlx5e_free_mpwqe_rq_drop_page(struct mlx5e_rq *rq) ++{ ++ dma_unmap_page(rq->pdev, rq->wqe_overflow.addr, PAGE_SIZE, ++ rq->buff.map_dir); ++ __free_page(rq->wqe_overflow.page); ++} ++ + static int mlx5e_alloc_rq(struct mlx5e_channel *c, + struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk, +@@ -421,6 +461,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, + if (err) + goto err_rq_wq_destroy; + ++ err = mlx5e_alloc_mpwqe_rq_drop_page(rq); ++ if (err) ++ goto err_rq_wq_destroy; ++ + rq->mpwqe.wq.db = &rq->mpwqe.wq.db[MLX5_RCV_DBR]; + + wq_sz = mlx5_wq_ll_get_size(&rq->mpwqe.wq); +@@ -459,7 +503,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, + + err = mlx5e_create_rq_umr_mkey(mdev, rq); + if (err) +- goto err_rq_wq_destroy; ++ goto err_rq_drop_page; + rq->mkey_be = cpu_to_be32(rq->umr_mkey.key); + + err = mlx5e_rq_alloc_mpwqe_info(rq, c); +@@ -598,6 +642,8 @@ err_free: + case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: + kvfree(rq->mpwqe.info); + mlx5_core_destroy_mkey(mdev, &rq->umr_mkey); ++err_rq_drop_page: ++ mlx5e_free_mpwqe_rq_drop_page(rq); + break; + default: /* MLX5_WQ_TYPE_CYCLIC */ + kvfree(rq->wqe.frags); +@@ -631,6 +677,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) + case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: + kvfree(rq->mpwqe.info); + mlx5_core_destroy_mkey(rq->mdev, &rq->umr_mkey); ++ mlx5e_free_mpwqe_rq_drop_page(rq); + break; + default: /* MLX5_WQ_TYPE_CYCLIC */ + kvfree(rq->wqe.frags); +@@ -4276,6 +4323,21 @@ void mlx5e_del_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti) + mlx5e_vxlan_queue_work(priv, be16_to_cpu(ti->port), 0); + } + ++static bool mlx5e_gre_tunnel_inner_proto_offload_supported(struct mlx5_core_dev *mdev, ++ struct sk_buff *skb) ++{ ++ switch (skb->inner_protocol) { ++ case htons(ETH_P_IP): ++ case htons(ETH_P_IPV6): ++ case htons(ETH_P_TEB): ++ return true; ++ case htons(ETH_P_MPLS_UC): ++ case htons(ETH_P_MPLS_MC): ++ return MLX5_CAP_ETH(mdev, tunnel_stateless_mpls_over_gre); ++ } ++ return false; ++} ++ + static netdev_features_t mlx5e_tunnel_features_check(struct mlx5e_priv *priv, + struct sk_buff *skb, + netdev_features_t features) +@@ -4298,7 +4360,9 @@ static netdev_features_t mlx5e_tunnel_features_check(struct mlx5e_priv *priv, + + switch (proto) { + case IPPROTO_GRE: +- return features; ++ if (mlx5e_gre_tunnel_inner_proto_offload_supported(priv->mdev, skb)) ++ return features; ++ break; + case IPPROTO_IPIP: + case IPPROTO_IPV6: + if (mlx5e_tunnel_proto_supported(priv->mdev, IPPROTO_IPIP)) +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +index 1d56698014843..fcaabafb2e56d 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +@@ -133,12 +133,6 @@ struct mlx5e_neigh_hash_entry { + /* encap list sharing the same neigh */ + struct list_head encap_list; + +- /* valid only when the neigh reference is taken during +- * neigh_update_work workqueue callback. +- */ +- struct neighbour *n; +- struct work_struct neigh_update_work; +- + /* neigh hash entry can be deleted only when the refcount is zero. + * refcount is needed to avoid neigh hash entry removal by TC, while + * it's used by the neigh notification call. +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c +index 31ef9f8420c87..22a19d391e179 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c +@@ -189,6 +189,29 @@ u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq) + return count_eqe; + } + ++static void mlx5_eq_async_int_lock(struct mlx5_eq_async *eq, unsigned long *flags) ++ __acquires(&eq->lock) ++{ ++ if (in_irq()) ++ spin_lock(&eq->lock); ++ else ++ spin_lock_irqsave(&eq->lock, *flags); ++} ++ ++static void mlx5_eq_async_int_unlock(struct mlx5_eq_async *eq, unsigned long *flags) ++ __releases(&eq->lock) ++{ ++ if (in_irq()) ++ spin_unlock(&eq->lock); ++ else ++ spin_unlock_irqrestore(&eq->lock, *flags); ++} ++ ++enum async_eq_nb_action { ++ ASYNC_EQ_IRQ_HANDLER = 0, ++ ASYNC_EQ_RECOVER = 1, ++}; ++ + static int mlx5_eq_async_int(struct notifier_block *nb, + unsigned long action, void *data) + { +@@ -198,11 +221,14 @@ static int mlx5_eq_async_int(struct notifier_block *nb, + struct mlx5_eq_table *eqt; + struct mlx5_core_dev *dev; + struct mlx5_eqe *eqe; ++ unsigned long flags; + int num_eqes = 0; + + dev = eq->dev; + eqt = dev->priv.eq_table; + ++ mlx5_eq_async_int_lock(eq_async, &flags); ++ + eqe = next_eqe_sw(eq); + if (!eqe) + goto out; +@@ -223,8 +249,19 @@ static int mlx5_eq_async_int(struct notifier_block *nb, + + out: + eq_update_ci(eq, 1); ++ mlx5_eq_async_int_unlock(eq_async, &flags); + +- return 0; ++ return unlikely(action == ASYNC_EQ_RECOVER) ? num_eqes : 0; ++} ++ ++void mlx5_cmd_eq_recover(struct mlx5_core_dev *dev) ++{ ++ struct mlx5_eq_async *eq = &dev->priv.eq_table->cmd_eq; ++ int eqes; ++ ++ eqes = mlx5_eq_async_int(&eq->irq_nb, ASYNC_EQ_RECOVER, NULL); ++ if (eqes) ++ mlx5_core_warn(dev, "Recovered %d EQEs on cmd_eq\n", eqes); + } + + static void init_eq_buf(struct mlx5_eq *eq) +@@ -569,6 +606,7 @@ setup_async_eq(struct mlx5_core_dev *dev, struct mlx5_eq_async *eq, + int err; + + eq->irq_nb.notifier_call = mlx5_eq_async_int; ++ spin_lock_init(&eq->lock); + + err = create_async_eq(dev, &eq->core, param); + if (err) { +@@ -656,8 +694,10 @@ static void destroy_async_eqs(struct mlx5_core_dev *dev) + + cleanup_async_eq(dev, &table->pages_eq, "pages"); + cleanup_async_eq(dev, &table->async_eq, "async"); ++ mlx5_cmd_allowed_opcode(dev, MLX5_CMD_OP_DESTROY_EQ); + mlx5_cmd_use_polling(dev); + cleanup_async_eq(dev, &table->cmd_eq, "cmd"); ++ mlx5_cmd_allowed_opcode(dev, CMD_ALLOWED_OPCODE_ALL); + mlx5_eq_notifier_unregister(dev, &table->cq_err_nb); + } + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h +index 4aaca7400fb29..5c681e31983bc 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h ++++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h +@@ -37,6 +37,7 @@ struct mlx5_eq { + struct mlx5_eq_async { + struct mlx5_eq core; + struct notifier_block irq_nb; ++ spinlock_t lock; /* To avoid irq EQ handle races with resiliency flows */ + }; + + struct mlx5_eq_comp { +@@ -81,6 +82,7 @@ void mlx5_cq_tasklet_cb(unsigned long data); + struct cpumask *mlx5_eq_comp_cpumask(struct mlx5_core_dev *dev, int ix); + + u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq); ++void mlx5_cmd_eq_recover(struct mlx5_core_dev *dev); + void mlx5_eq_synchronize_async_irq(struct mlx5_core_dev *dev); + void mlx5_eq_synchronize_cmd_irq(struct mlx5_core_dev *dev); + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +index 373981a659c7c..6fd9749203944 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +@@ -115,7 +115,7 @@ static int request_irqs(struct mlx5_core_dev *dev, int nvec) + return 0; + + err_request_irq: +- for (; i >= 0; i--) { ++ while (i--) { + struct mlx5_irq *irq = mlx5_irq_get(dev, i); + int irqn = pci_irq_vector(dev->pdev, i); + +diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +index 5c020403342f9..7cccc41dd69c9 100644 +--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c ++++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +@@ -292,13 +292,14 @@ mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp_acl_tcam *tcam, + int err; + + group->tcam = tcam; +- mutex_init(&group->lock); + INIT_LIST_HEAD(&group->region_list); + + err = mlxsw_sp_acl_tcam_group_id_get(tcam, &group->id); + if (err) + return err; + ++ mutex_init(&group->lock); ++ + return 0; + } + +diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile +index 91b33b55054e1..ad97a5cca6f99 100644 +--- a/drivers/net/ethernet/mscc/Makefile ++++ b/drivers/net/ethernet/mscc/Makefile +@@ -2,4 +2,4 @@ + obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o + mscc_ocelot_common-y := ocelot.o ocelot_io.o + mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o ocelot_ptp.o +-obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o ++obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_vsc7514.o +diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c +index d0b79cca51840..61bbb7a090042 100644 +--- a/drivers/net/ethernet/mscc/ocelot.c ++++ b/drivers/net/ethernet/mscc/ocelot.c +@@ -396,18 +396,6 @@ static void ocelot_vlan_init(struct ocelot *ocelot) + } + } + +-/* Watermark encode +- * Bit 8: Unit; 0:1, 1:16 +- * Bit 7-0: Value to be multiplied with unit +- */ +-static u16 ocelot_wm_enc(u16 value) +-{ +- if (value >= BIT(8)) +- return BIT(8) | (value / 16); +- +- return value; +-} +- + void ocelot_adjust_link(struct ocelot *ocelot, int port, + struct phy_device *phydev) + { +@@ -2012,7 +2000,8 @@ void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) + { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN; +- int atop_wm; ++ int pause_start, pause_stop; ++ int atop, atop_tot; + + if (port == ocelot->npi) { + maxlen += OCELOT_TAG_LEN; +@@ -2025,20 +2014,20 @@ void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) + + ocelot_port_writel(ocelot_port, maxlen, DEV_MAC_MAXLEN_CFG); + +- /* Set Pause WM hysteresis +- * 152 = 6 * maxlen / OCELOT_BUFFER_CELL_SZ +- * 101 = 4 * maxlen / OCELOT_BUFFER_CELL_SZ +- */ +- ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA | +- SYS_PAUSE_CFG_PAUSE_STOP(101) | +- SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, port); ++ /* Set Pause watermark hysteresis */ ++ pause_start = 6 * maxlen / OCELOT_BUFFER_CELL_SZ; ++ pause_stop = 4 * maxlen / OCELOT_BUFFER_CELL_SZ; ++ ocelot_rmw_rix(ocelot, SYS_PAUSE_CFG_PAUSE_START(pause_start), ++ SYS_PAUSE_CFG_PAUSE_START_M, SYS_PAUSE_CFG, port); ++ ocelot_rmw_rix(ocelot, SYS_PAUSE_CFG_PAUSE_STOP(pause_stop), ++ SYS_PAUSE_CFG_PAUSE_STOP_M, SYS_PAUSE_CFG, port); + +- /* Tail dropping watermark */ +- atop_wm = (ocelot->shared_queue_sz - 9 * maxlen) / ++ /* Tail dropping watermarks */ ++ atop_tot = (ocelot->shared_queue_sz - 9 * maxlen) / + OCELOT_BUFFER_CELL_SZ; +- ocelot_write_rix(ocelot, ocelot_wm_enc(9 * maxlen), +- SYS_ATOP, port); +- ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG); ++ atop = (9 * maxlen) / OCELOT_BUFFER_CELL_SZ; ++ ocelot_write_rix(ocelot, ocelot->ops->wm_enc(atop), SYS_ATOP, port); ++ ocelot_write(ocelot, ocelot->ops->wm_enc(atop_tot), SYS_ATOP_TOT_CFG); + } + EXPORT_SYMBOL(ocelot_port_set_maxlen); + +@@ -2094,6 +2083,10 @@ void ocelot_init_port(struct ocelot *ocelot, int port) + ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_HIGH_CFG); + ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_LOW_CFG); + ++ /* Enable transmission of pause frames */ ++ ocelot_rmw_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA, SYS_PAUSE_CFG_PAUSE_ENA, ++ SYS_PAUSE_CFG, port); ++ + /* Drop frames with multicast source address */ + ocelot_rmw_gix(ocelot, ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA, + ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA, +diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c +deleted file mode 100644 +index 4a15d2ff8b706..0000000000000 +--- a/drivers/net/ethernet/mscc/ocelot_board.c ++++ /dev/null +@@ -1,626 +0,0 @@ +-// SPDX-License-Identifier: (GPL-2.0 OR MIT) +-/* +- * Microsemi Ocelot Switch driver +- * +- * Copyright (c) 2017 Microsemi Corporation +- */ +-#include <linux/interrupt.h> +-#include <linux/module.h> +-#include <linux/of_net.h> +-#include <linux/netdevice.h> +-#include <linux/of_mdio.h> +-#include <linux/of_platform.h> +-#include <linux/mfd/syscon.h> +-#include <linux/skbuff.h> +-#include <net/switchdev.h> +- +-#include <soc/mscc/ocelot_vcap.h> +-#include "ocelot.h" +- +-#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0)) +-#define VSC7514_VCAP_IS2_CNT 64 +-#define VSC7514_VCAP_IS2_ENTRY_WIDTH 376 +-#define VSC7514_VCAP_IS2_ACTION_WIDTH 99 +-#define VSC7514_VCAP_PORT_CNT 11 +- +-static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info) +-{ +- u8 llen, wlen; +- u64 ifh[2]; +- +- ifh[0] = be64_to_cpu(((__force __be64 *)_ifh)[0]); +- ifh[1] = be64_to_cpu(((__force __be64 *)_ifh)[1]); +- +- wlen = IFH_EXTRACT_BITFIELD64(ifh[0], 7, 8); +- llen = IFH_EXTRACT_BITFIELD64(ifh[0], 15, 6); +- +- info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80; +- +- info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32); +- +- info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4); +- +- info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1); +- info->vid = IFH_EXTRACT_BITFIELD64(ifh[1], 0, 12); +- +- return 0; +-} +- +-static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh, +- u32 *rval) +-{ +- u32 val; +- u32 bytes_valid; +- +- val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); +- if (val == XTR_NOT_READY) { +- if (ifh) +- return -EIO; +- +- do { +- val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); +- } while (val == XTR_NOT_READY); +- } +- +- switch (val) { +- case XTR_ABORT: +- return -EIO; +- case XTR_EOF_0: +- case XTR_EOF_1: +- case XTR_EOF_2: +- case XTR_EOF_3: +- case XTR_PRUNED: +- bytes_valid = XTR_VALID_BYTES(val); +- val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); +- if (val == XTR_ESCAPE) +- *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); +- else +- *rval = val; +- +- return bytes_valid; +- case XTR_ESCAPE: +- *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); +- +- return 4; +- default: +- *rval = val; +- +- return 4; +- } +-} +- +-static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) +-{ +- struct ocelot *ocelot = arg; +- int i = 0, grp = 0; +- int err = 0; +- +- if (!(ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))) +- return IRQ_NONE; +- +- do { +- struct skb_shared_hwtstamps *shhwtstamps; +- struct ocelot_port_private *priv; +- struct ocelot_port *ocelot_port; +- u64 tod_in_ns, full_ts_in_ns; +- struct frame_info info = {}; +- struct net_device *dev; +- u32 ifh[4], val, *buf; +- struct timespec64 ts; +- int sz, len, buf_len; +- struct sk_buff *skb; +- +- for (i = 0; i < OCELOT_TAG_LEN / 4; i++) { +- err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]); +- if (err != 4) +- break; +- } +- +- if (err != 4) +- break; +- +- /* At this point the IFH was read correctly, so it is safe to +- * presume that there is no error. The err needs to be reset +- * otherwise a frame could come in CPU queue between the while +- * condition and the check for error later on. And in that case +- * the new frame is just removed and not processed. +- */ +- err = 0; +- +- ocelot_parse_ifh(ifh, &info); +- +- ocelot_port = ocelot->ports[info.port]; +- priv = container_of(ocelot_port, struct ocelot_port_private, +- port); +- dev = priv->dev; +- +- skb = netdev_alloc_skb(dev, info.len); +- +- if (unlikely(!skb)) { +- netdev_err(dev, "Unable to allocate sk_buff\n"); +- err = -ENOMEM; +- break; +- } +- buf_len = info.len - ETH_FCS_LEN; +- buf = (u32 *)skb_put(skb, buf_len); +- +- len = 0; +- do { +- sz = ocelot_rx_frame_word(ocelot, grp, false, &val); +- *buf++ = val; +- len += sz; +- } while (len < buf_len); +- +- /* Read the FCS */ +- sz = ocelot_rx_frame_word(ocelot, grp, false, &val); +- /* Update the statistics if part of the FCS was read before */ +- len -= ETH_FCS_LEN - sz; +- +- if (unlikely(dev->features & NETIF_F_RXFCS)) { +- buf = (u32 *)skb_put(skb, ETH_FCS_LEN); +- *buf = val; +- } +- +- if (sz < 0) { +- err = sz; +- break; +- } +- +- if (ocelot->ptp) { +- ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); +- +- tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec); +- if ((tod_in_ns & 0xffffffff) < info.timestamp) +- full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) | +- info.timestamp; +- else +- full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) | +- info.timestamp; +- +- shhwtstamps = skb_hwtstamps(skb); +- memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); +- shhwtstamps->hwtstamp = full_ts_in_ns; +- } +- +- /* Everything we see on an interface that is in the HW bridge +- * has already been forwarded. +- */ +- if (ocelot->bridge_mask & BIT(info.port)) +- skb->offload_fwd_mark = 1; +- +- skb->protocol = eth_type_trans(skb, dev); +- if (!skb_defer_rx_timestamp(skb)) +- netif_rx(skb); +- dev->stats.rx_bytes += len; +- dev->stats.rx_packets++; +- } while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)); +- +- if (err) +- while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) +- ocelot_read_rix(ocelot, QS_XTR_RD, grp); +- +- return IRQ_HANDLED; +-} +- +-static irqreturn_t ocelot_ptp_rdy_irq_handler(int irq, void *arg) +-{ +- struct ocelot *ocelot = arg; +- +- ocelot_get_txtstamp(ocelot); +- +- return IRQ_HANDLED; +-} +- +-static const struct of_device_id mscc_ocelot_match[] = { +- { .compatible = "mscc,vsc7514-switch" }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, mscc_ocelot_match); +- +-static int ocelot_reset(struct ocelot *ocelot) +-{ +- int retries = 100; +- u32 val; +- +- regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1); +- regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); +- +- do { +- msleep(1); +- regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], +- &val); +- } while (val && --retries); +- +- if (!retries) +- return -ETIMEDOUT; +- +- regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); +- regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); +- +- return 0; +-} +- +-static const struct ocelot_ops ocelot_ops = { +- .reset = ocelot_reset, +-}; +- +-static const struct vcap_field vsc7514_vcap_is2_keys[] = { +- /* Common: 46 bits */ +- [VCAP_IS2_TYPE] = { 0, 4}, +- [VCAP_IS2_HK_FIRST] = { 4, 1}, +- [VCAP_IS2_HK_PAG] = { 5, 8}, +- [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 12}, +- [VCAP_IS2_HK_RSV2] = { 25, 1}, +- [VCAP_IS2_HK_HOST_MATCH] = { 26, 1}, +- [VCAP_IS2_HK_L2_MC] = { 27, 1}, +- [VCAP_IS2_HK_L2_BC] = { 28, 1}, +- [VCAP_IS2_HK_VLAN_TAGGED] = { 29, 1}, +- [VCAP_IS2_HK_VID] = { 30, 12}, +- [VCAP_IS2_HK_DEI] = { 42, 1}, +- [VCAP_IS2_HK_PCP] = { 43, 3}, +- /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ +- [VCAP_IS2_HK_L2_DMAC] = { 46, 48}, +- [VCAP_IS2_HK_L2_SMAC] = { 94, 48}, +- /* MAC_ETYPE (TYPE=000) */ +- [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {142, 16}, +- [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {158, 16}, +- [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {174, 8}, +- [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {182, 3}, +- /* MAC_LLC (TYPE=001) */ +- [VCAP_IS2_HK_MAC_LLC_L2_LLC] = {142, 40}, +- /* MAC_SNAP (TYPE=010) */ +- [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {142, 40}, +- /* MAC_ARP (TYPE=011) */ +- [VCAP_IS2_HK_MAC_ARP_SMAC] = { 46, 48}, +- [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 94, 1}, +- [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 95, 1}, +- [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 96, 1}, +- [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 97, 1}, +- [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 98, 1}, +- [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 99, 1}, +- [VCAP_IS2_HK_MAC_ARP_OPCODE] = {100, 2}, +- [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = {102, 32}, +- [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {134, 32}, +- [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {166, 1}, +- /* IP4_TCP_UDP / IP4_OTHER common */ +- [VCAP_IS2_HK_IP4] = { 46, 1}, +- [VCAP_IS2_HK_L3_FRAGMENT] = { 47, 1}, +- [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 48, 1}, +- [VCAP_IS2_HK_L3_OPTIONS] = { 49, 1}, +- [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 50, 1}, +- [VCAP_IS2_HK_L3_TOS] = { 51, 8}, +- [VCAP_IS2_HK_L3_IP4_DIP] = { 59, 32}, +- [VCAP_IS2_HK_L3_IP4_SIP] = { 91, 32}, +- [VCAP_IS2_HK_DIP_EQ_SIP] = {123, 1}, +- /* IP4_TCP_UDP (TYPE=100) */ +- [VCAP_IS2_HK_TCP] = {124, 1}, +- [VCAP_IS2_HK_L4_SPORT] = {125, 16}, +- [VCAP_IS2_HK_L4_DPORT] = {141, 16}, +- [VCAP_IS2_HK_L4_RNG] = {157, 8}, +- [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {165, 1}, +- [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {166, 1}, +- [VCAP_IS2_HK_L4_URG] = {167, 1}, +- [VCAP_IS2_HK_L4_ACK] = {168, 1}, +- [VCAP_IS2_HK_L4_PSH] = {169, 1}, +- [VCAP_IS2_HK_L4_RST] = {170, 1}, +- [VCAP_IS2_HK_L4_SYN] = {171, 1}, +- [VCAP_IS2_HK_L4_FIN] = {172, 1}, +- [VCAP_IS2_HK_L4_1588_DOM] = {173, 8}, +- [VCAP_IS2_HK_L4_1588_VER] = {181, 4}, +- /* IP4_OTHER (TYPE=101) */ +- [VCAP_IS2_HK_IP4_L3_PROTO] = {124, 8}, +- [VCAP_IS2_HK_L3_PAYLOAD] = {132, 56}, +- /* IP6_STD (TYPE=110) */ +- [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 46, 1}, +- [VCAP_IS2_HK_L3_IP6_SIP] = { 47, 128}, +- [VCAP_IS2_HK_IP6_L3_PROTO] = {175, 8}, +- /* OAM (TYPE=111) */ +- [VCAP_IS2_HK_OAM_MEL_FLAGS] = {142, 7}, +- [VCAP_IS2_HK_OAM_VER] = {149, 5}, +- [VCAP_IS2_HK_OAM_OPCODE] = {154, 8}, +- [VCAP_IS2_HK_OAM_FLAGS] = {162, 8}, +- [VCAP_IS2_HK_OAM_MEPID] = {170, 16}, +- [VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = {186, 1}, +- [VCAP_IS2_HK_OAM_IS_Y1731] = {187, 1}, +-}; +- +-static const struct vcap_field vsc7514_vcap_is2_actions[] = { +- [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, +- [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, +- [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, +- [VCAP_IS2_ACT_MASK_MODE] = { 5, 2}, +- [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1}, +- [VCAP_IS2_ACT_LRN_DIS] = { 8, 1}, +- [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1}, +- [VCAP_IS2_ACT_POLICE_IDX] = { 10, 9}, +- [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1}, +- [VCAP_IS2_ACT_PORT_MASK] = { 20, 11}, +- [VCAP_IS2_ACT_REW_OP] = { 31, 9}, +- [VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 40, 1}, +- [VCAP_IS2_ACT_RSV] = { 41, 2}, +- [VCAP_IS2_ACT_ACL_ID] = { 43, 6}, +- [VCAP_IS2_ACT_HIT_CNT] = { 49, 32}, +-}; +- +-static const struct vcap_props vsc7514_vcap_props[] = { +- [VCAP_IS2] = { +- .tg_width = 2, +- .sw_count = 4, +- .entry_count = VSC7514_VCAP_IS2_CNT, +- .entry_width = VSC7514_VCAP_IS2_ENTRY_WIDTH, +- .action_count = VSC7514_VCAP_IS2_CNT + +- VSC7514_VCAP_PORT_CNT + 2, +- .action_width = 99, +- .action_type_width = 1, +- .action_table = { +- [IS2_ACTION_TYPE_NORMAL] = { +- .width = 49, +- .count = 2 +- }, +- [IS2_ACTION_TYPE_SMAC_SIP] = { +- .width = 6, +- .count = 4 +- }, +- }, +- .counter_words = 4, +- .counter_width = 32, +- }, +-}; +- +-static struct ptp_clock_info ocelot_ptp_clock_info = { +- .owner = THIS_MODULE, +- .name = "ocelot ptp", +- .max_adj = 0x7fffffff, +- .n_alarm = 0, +- .n_ext_ts = 0, +- .n_per_out = OCELOT_PTP_PINS_NUM, +- .n_pins = OCELOT_PTP_PINS_NUM, +- .pps = 0, +- .gettime64 = ocelot_ptp_gettime64, +- .settime64 = ocelot_ptp_settime64, +- .adjtime = ocelot_ptp_adjtime, +- .adjfine = ocelot_ptp_adjfine, +- .verify = ocelot_ptp_verify, +- .enable = ocelot_ptp_enable, +-}; +- +-static int mscc_ocelot_probe(struct platform_device *pdev) +-{ +- struct device_node *np = pdev->dev.of_node; +- struct device_node *ports, *portnp; +- int err, irq_xtr, irq_ptp_rdy; +- struct ocelot *ocelot; +- struct regmap *hsio; +- unsigned int i; +- +- struct { +- enum ocelot_target id; +- char *name; +- u8 optional:1; +- } io_target[] = { +- { SYS, "sys" }, +- { REW, "rew" }, +- { QSYS, "qsys" }, +- { ANA, "ana" }, +- { QS, "qs" }, +- { S2, "s2" }, +- { PTP, "ptp", 1 }, +- }; +- +- if (!np && !pdev->dev.platform_data) +- return -ENODEV; +- +- ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL); +- if (!ocelot) +- return -ENOMEM; +- +- platform_set_drvdata(pdev, ocelot); +- ocelot->dev = &pdev->dev; +- +- for (i = 0; i < ARRAY_SIZE(io_target); i++) { +- struct regmap *target; +- struct resource *res; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- io_target[i].name); +- +- target = ocelot_regmap_init(ocelot, res); +- if (IS_ERR(target)) { +- if (io_target[i].optional) { +- ocelot->targets[io_target[i].id] = NULL; +- continue; +- } +- return PTR_ERR(target); +- } +- +- ocelot->targets[io_target[i].id] = target; +- } +- +- hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio"); +- if (IS_ERR(hsio)) { +- dev_err(&pdev->dev, "missing hsio syscon\n"); +- return PTR_ERR(hsio); +- } +- +- ocelot->targets[HSIO] = hsio; +- +- err = ocelot_chip_init(ocelot, &ocelot_ops); +- if (err) +- return err; +- +- irq_xtr = platform_get_irq_byname(pdev, "xtr"); +- if (irq_xtr < 0) +- return -ENODEV; +- +- err = devm_request_threaded_irq(&pdev->dev, irq_xtr, NULL, +- ocelot_xtr_irq_handler, IRQF_ONESHOT, +- "frame extraction", ocelot); +- if (err) +- return err; +- +- irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy"); +- if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) { +- err = devm_request_threaded_irq(&pdev->dev, irq_ptp_rdy, NULL, +- ocelot_ptp_rdy_irq_handler, +- IRQF_ONESHOT, "ptp ready", +- ocelot); +- if (err) +- return err; +- +- /* Both the PTP interrupt and the PTP bank are available */ +- ocelot->ptp = 1; +- } +- +- ports = of_get_child_by_name(np, "ethernet-ports"); +- if (!ports) { +- dev_err(&pdev->dev, "no ethernet-ports child node found\n"); +- return -ENODEV; +- } +- +- ocelot->num_phys_ports = of_get_child_count(ports); +- +- ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports, +- sizeof(struct ocelot_port *), GFP_KERNEL); +- +- ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys; +- ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions; +- ocelot->vcap = vsc7514_vcap_props; +- +- ocelot_init(ocelot); +- if (ocelot->ptp) { +- err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info); +- if (err) { +- dev_err(ocelot->dev, +- "Timestamp initialization failed\n"); +- ocelot->ptp = 0; +- } +- } +- +- /* No NPI port */ +- ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE, +- OCELOT_TAG_PREFIX_NONE); +- +- for_each_available_child_of_node(ports, portnp) { +- struct ocelot_port_private *priv; +- struct ocelot_port *ocelot_port; +- struct device_node *phy_node; +- phy_interface_t phy_mode; +- struct phy_device *phy; +- struct resource *res; +- struct phy *serdes; +- void __iomem *regs; +- char res_name[8]; +- u32 port; +- +- if (of_property_read_u32(portnp, "reg", &port)) +- continue; +- +- snprintf(res_name, sizeof(res_name), "port%d", port); +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- res_name); +- regs = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(regs)) +- continue; +- +- phy_node = of_parse_phandle(portnp, "phy-handle", 0); +- if (!phy_node) +- continue; +- +- phy = of_phy_find_device(phy_node); +- of_node_put(phy_node); +- if (!phy) +- continue; +- +- err = ocelot_probe_port(ocelot, port, regs, phy); +- if (err) { +- of_node_put(portnp); +- goto out_put_ports; +- } +- +- ocelot_port = ocelot->ports[port]; +- priv = container_of(ocelot_port, struct ocelot_port_private, +- port); +- +- of_get_phy_mode(portnp, &phy_mode); +- +- ocelot_port->phy_mode = phy_mode; +- +- switch (ocelot_port->phy_mode) { +- case PHY_INTERFACE_MODE_NA: +- continue; +- case PHY_INTERFACE_MODE_SGMII: +- break; +- case PHY_INTERFACE_MODE_QSGMII: +- /* Ensure clock signals and speed is set on all +- * QSGMII links +- */ +- ocelot_port_writel(ocelot_port, +- DEV_CLOCK_CFG_LINK_SPEED +- (OCELOT_SPEED_1000), +- DEV_CLOCK_CFG); +- break; +- default: +- dev_err(ocelot->dev, +- "invalid phy mode for port%d, (Q)SGMII only\n", +- port); +- of_node_put(portnp); +- err = -EINVAL; +- goto out_put_ports; +- } +- +- serdes = devm_of_phy_get(ocelot->dev, portnp, NULL); +- if (IS_ERR(serdes)) { +- err = PTR_ERR(serdes); +- if (err == -EPROBE_DEFER) +- dev_dbg(ocelot->dev, "deferring probe\n"); +- else +- dev_err(ocelot->dev, +- "missing SerDes phys for port%d\n", +- port); +- +- of_node_put(portnp); +- goto out_put_ports; +- } +- +- priv->serdes = serdes; +- } +- +- register_netdevice_notifier(&ocelot_netdevice_nb); +- register_switchdev_notifier(&ocelot_switchdev_nb); +- register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); +- +- dev_info(&pdev->dev, "Ocelot switch probed\n"); +- +-out_put_ports: +- of_node_put(ports); +- return err; +-} +- +-static int mscc_ocelot_remove(struct platform_device *pdev) +-{ +- struct ocelot *ocelot = platform_get_drvdata(pdev); +- +- ocelot_deinit_timestamp(ocelot); +- ocelot_deinit(ocelot); +- unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); +- unregister_switchdev_notifier(&ocelot_switchdev_nb); +- unregister_netdevice_notifier(&ocelot_netdevice_nb); +- +- return 0; +-} +- +-static struct platform_driver mscc_ocelot_driver = { +- .probe = mscc_ocelot_probe, +- .remove = mscc_ocelot_remove, +- .driver = { +- .name = "ocelot-switch", +- .of_match_table = mscc_ocelot_match, +- }, +-}; +- +-module_platform_driver(mscc_ocelot_driver); +- +-MODULE_DESCRIPTION("Microsemi Ocelot switch driver"); +-MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>"); +-MODULE_LICENSE("Dual MIT/GPL"); +diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c +new file mode 100644 +index 0000000000000..66b58b242f778 +--- /dev/null ++++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c +@@ -0,0 +1,639 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Microsemi Ocelot Switch driver ++ * ++ * Copyright (c) 2017 Microsemi Corporation ++ */ ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/of_net.h> ++#include <linux/netdevice.h> ++#include <linux/of_mdio.h> ++#include <linux/of_platform.h> ++#include <linux/mfd/syscon.h> ++#include <linux/skbuff.h> ++#include <net/switchdev.h> ++ ++#include <soc/mscc/ocelot_vcap.h> ++#include "ocelot.h" ++ ++#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0)) ++#define VSC7514_VCAP_IS2_CNT 64 ++#define VSC7514_VCAP_IS2_ENTRY_WIDTH 376 ++#define VSC7514_VCAP_IS2_ACTION_WIDTH 99 ++#define VSC7514_VCAP_PORT_CNT 11 ++ ++static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info) ++{ ++ u8 llen, wlen; ++ u64 ifh[2]; ++ ++ ifh[0] = be64_to_cpu(((__force __be64 *)_ifh)[0]); ++ ifh[1] = be64_to_cpu(((__force __be64 *)_ifh)[1]); ++ ++ wlen = IFH_EXTRACT_BITFIELD64(ifh[0], 7, 8); ++ llen = IFH_EXTRACT_BITFIELD64(ifh[0], 15, 6); ++ ++ info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80; ++ ++ info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32); ++ ++ info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4); ++ ++ info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1); ++ info->vid = IFH_EXTRACT_BITFIELD64(ifh[1], 0, 12); ++ ++ return 0; ++} ++ ++static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh, ++ u32 *rval) ++{ ++ u32 val; ++ u32 bytes_valid; ++ ++ val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); ++ if (val == XTR_NOT_READY) { ++ if (ifh) ++ return -EIO; ++ ++ do { ++ val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); ++ } while (val == XTR_NOT_READY); ++ } ++ ++ switch (val) { ++ case XTR_ABORT: ++ return -EIO; ++ case XTR_EOF_0: ++ case XTR_EOF_1: ++ case XTR_EOF_2: ++ case XTR_EOF_3: ++ case XTR_PRUNED: ++ bytes_valid = XTR_VALID_BYTES(val); ++ val = ocelot_read_rix(ocelot, QS_XTR_RD, grp); ++ if (val == XTR_ESCAPE) ++ *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); ++ else ++ *rval = val; ++ ++ return bytes_valid; ++ case XTR_ESCAPE: ++ *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp); ++ ++ return 4; ++ default: ++ *rval = val; ++ ++ return 4; ++ } ++} ++ ++static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) ++{ ++ struct ocelot *ocelot = arg; ++ int i = 0, grp = 0; ++ int err = 0; ++ ++ if (!(ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))) ++ return IRQ_NONE; ++ ++ do { ++ struct skb_shared_hwtstamps *shhwtstamps; ++ struct ocelot_port_private *priv; ++ struct ocelot_port *ocelot_port; ++ u64 tod_in_ns, full_ts_in_ns; ++ struct frame_info info = {}; ++ struct net_device *dev; ++ u32 ifh[4], val, *buf; ++ struct timespec64 ts; ++ int sz, len, buf_len; ++ struct sk_buff *skb; ++ ++ for (i = 0; i < OCELOT_TAG_LEN / 4; i++) { ++ err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]); ++ if (err != 4) ++ break; ++ } ++ ++ if (err != 4) ++ break; ++ ++ /* At this point the IFH was read correctly, so it is safe to ++ * presume that there is no error. The err needs to be reset ++ * otherwise a frame could come in CPU queue between the while ++ * condition and the check for error later on. And in that case ++ * the new frame is just removed and not processed. ++ */ ++ err = 0; ++ ++ ocelot_parse_ifh(ifh, &info); ++ ++ ocelot_port = ocelot->ports[info.port]; ++ priv = container_of(ocelot_port, struct ocelot_port_private, ++ port); ++ dev = priv->dev; ++ ++ skb = netdev_alloc_skb(dev, info.len); ++ ++ if (unlikely(!skb)) { ++ netdev_err(dev, "Unable to allocate sk_buff\n"); ++ err = -ENOMEM; ++ break; ++ } ++ buf_len = info.len - ETH_FCS_LEN; ++ buf = (u32 *)skb_put(skb, buf_len); ++ ++ len = 0; ++ do { ++ sz = ocelot_rx_frame_word(ocelot, grp, false, &val); ++ *buf++ = val; ++ len += sz; ++ } while (len < buf_len); ++ ++ /* Read the FCS */ ++ sz = ocelot_rx_frame_word(ocelot, grp, false, &val); ++ /* Update the statistics if part of the FCS was read before */ ++ len -= ETH_FCS_LEN - sz; ++ ++ if (unlikely(dev->features & NETIF_F_RXFCS)) { ++ buf = (u32 *)skb_put(skb, ETH_FCS_LEN); ++ *buf = val; ++ } ++ ++ if (sz < 0) { ++ err = sz; ++ break; ++ } ++ ++ if (ocelot->ptp) { ++ ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); ++ ++ tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec); ++ if ((tod_in_ns & 0xffffffff) < info.timestamp) ++ full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) | ++ info.timestamp; ++ else ++ full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) | ++ info.timestamp; ++ ++ shhwtstamps = skb_hwtstamps(skb); ++ memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); ++ shhwtstamps->hwtstamp = full_ts_in_ns; ++ } ++ ++ /* Everything we see on an interface that is in the HW bridge ++ * has already been forwarded. ++ */ ++ if (ocelot->bridge_mask & BIT(info.port)) ++ skb->offload_fwd_mark = 1; ++ ++ skb->protocol = eth_type_trans(skb, dev); ++ if (!skb_defer_rx_timestamp(skb)) ++ netif_rx(skb); ++ dev->stats.rx_bytes += len; ++ dev->stats.rx_packets++; ++ } while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)); ++ ++ if (err) ++ while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) ++ ocelot_read_rix(ocelot, QS_XTR_RD, grp); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t ocelot_ptp_rdy_irq_handler(int irq, void *arg) ++{ ++ struct ocelot *ocelot = arg; ++ ++ ocelot_get_txtstamp(ocelot); ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct of_device_id mscc_ocelot_match[] = { ++ { .compatible = "mscc,vsc7514-switch" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, mscc_ocelot_match); ++ ++static int ocelot_reset(struct ocelot *ocelot) ++{ ++ int retries = 100; ++ u32 val; ++ ++ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1); ++ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); ++ ++ do { ++ msleep(1); ++ regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], ++ &val); ++ } while (val && --retries); ++ ++ if (!retries) ++ return -ETIMEDOUT; ++ ++ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); ++ regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); ++ ++ return 0; ++} ++ ++/* Watermark encode ++ * Bit 8: Unit; 0:1, 1:16 ++ * Bit 7-0: Value to be multiplied with unit ++ */ ++static u16 ocelot_wm_enc(u16 value) ++{ ++ if (value >= BIT(8)) ++ return BIT(8) | (value / 16); ++ ++ return value; ++} ++ ++static const struct ocelot_ops ocelot_ops = { ++ .reset = ocelot_reset, ++ .wm_enc = ocelot_wm_enc, ++}; ++ ++static const struct vcap_field vsc7514_vcap_is2_keys[] = { ++ /* Common: 46 bits */ ++ [VCAP_IS2_TYPE] = { 0, 4}, ++ [VCAP_IS2_HK_FIRST] = { 4, 1}, ++ [VCAP_IS2_HK_PAG] = { 5, 8}, ++ [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 12}, ++ [VCAP_IS2_HK_RSV2] = { 25, 1}, ++ [VCAP_IS2_HK_HOST_MATCH] = { 26, 1}, ++ [VCAP_IS2_HK_L2_MC] = { 27, 1}, ++ [VCAP_IS2_HK_L2_BC] = { 28, 1}, ++ [VCAP_IS2_HK_VLAN_TAGGED] = { 29, 1}, ++ [VCAP_IS2_HK_VID] = { 30, 12}, ++ [VCAP_IS2_HK_DEI] = { 42, 1}, ++ [VCAP_IS2_HK_PCP] = { 43, 3}, ++ /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ ++ [VCAP_IS2_HK_L2_DMAC] = { 46, 48}, ++ [VCAP_IS2_HK_L2_SMAC] = { 94, 48}, ++ /* MAC_ETYPE (TYPE=000) */ ++ [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {142, 16}, ++ [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {158, 16}, ++ [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {174, 8}, ++ [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {182, 3}, ++ /* MAC_LLC (TYPE=001) */ ++ [VCAP_IS2_HK_MAC_LLC_L2_LLC] = {142, 40}, ++ /* MAC_SNAP (TYPE=010) */ ++ [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {142, 40}, ++ /* MAC_ARP (TYPE=011) */ ++ [VCAP_IS2_HK_MAC_ARP_SMAC] = { 46, 48}, ++ [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 94, 1}, ++ [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 95, 1}, ++ [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 96, 1}, ++ [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 97, 1}, ++ [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 98, 1}, ++ [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 99, 1}, ++ [VCAP_IS2_HK_MAC_ARP_OPCODE] = {100, 2}, ++ [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = {102, 32}, ++ [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {134, 32}, ++ [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {166, 1}, ++ /* IP4_TCP_UDP / IP4_OTHER common */ ++ [VCAP_IS2_HK_IP4] = { 46, 1}, ++ [VCAP_IS2_HK_L3_FRAGMENT] = { 47, 1}, ++ [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 48, 1}, ++ [VCAP_IS2_HK_L3_OPTIONS] = { 49, 1}, ++ [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 50, 1}, ++ [VCAP_IS2_HK_L3_TOS] = { 51, 8}, ++ [VCAP_IS2_HK_L3_IP4_DIP] = { 59, 32}, ++ [VCAP_IS2_HK_L3_IP4_SIP] = { 91, 32}, ++ [VCAP_IS2_HK_DIP_EQ_SIP] = {123, 1}, ++ /* IP4_TCP_UDP (TYPE=100) */ ++ [VCAP_IS2_HK_TCP] = {124, 1}, ++ [VCAP_IS2_HK_L4_SPORT] = {125, 16}, ++ [VCAP_IS2_HK_L4_DPORT] = {141, 16}, ++ [VCAP_IS2_HK_L4_RNG] = {157, 8}, ++ [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {165, 1}, ++ [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {166, 1}, ++ [VCAP_IS2_HK_L4_URG] = {167, 1}, ++ [VCAP_IS2_HK_L4_ACK] = {168, 1}, ++ [VCAP_IS2_HK_L4_PSH] = {169, 1}, ++ [VCAP_IS2_HK_L4_RST] = {170, 1}, ++ [VCAP_IS2_HK_L4_SYN] = {171, 1}, ++ [VCAP_IS2_HK_L4_FIN] = {172, 1}, ++ [VCAP_IS2_HK_L4_1588_DOM] = {173, 8}, ++ [VCAP_IS2_HK_L4_1588_VER] = {181, 4}, ++ /* IP4_OTHER (TYPE=101) */ ++ [VCAP_IS2_HK_IP4_L3_PROTO] = {124, 8}, ++ [VCAP_IS2_HK_L3_PAYLOAD] = {132, 56}, ++ /* IP6_STD (TYPE=110) */ ++ [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 46, 1}, ++ [VCAP_IS2_HK_L3_IP6_SIP] = { 47, 128}, ++ [VCAP_IS2_HK_IP6_L3_PROTO] = {175, 8}, ++ /* OAM (TYPE=111) */ ++ [VCAP_IS2_HK_OAM_MEL_FLAGS] = {142, 7}, ++ [VCAP_IS2_HK_OAM_VER] = {149, 5}, ++ [VCAP_IS2_HK_OAM_OPCODE] = {154, 8}, ++ [VCAP_IS2_HK_OAM_FLAGS] = {162, 8}, ++ [VCAP_IS2_HK_OAM_MEPID] = {170, 16}, ++ [VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = {186, 1}, ++ [VCAP_IS2_HK_OAM_IS_Y1731] = {187, 1}, ++}; ++ ++static const struct vcap_field vsc7514_vcap_is2_actions[] = { ++ [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, ++ [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, ++ [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, ++ [VCAP_IS2_ACT_MASK_MODE] = { 5, 2}, ++ [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1}, ++ [VCAP_IS2_ACT_LRN_DIS] = { 8, 1}, ++ [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1}, ++ [VCAP_IS2_ACT_POLICE_IDX] = { 10, 9}, ++ [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1}, ++ [VCAP_IS2_ACT_PORT_MASK] = { 20, 11}, ++ [VCAP_IS2_ACT_REW_OP] = { 31, 9}, ++ [VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 40, 1}, ++ [VCAP_IS2_ACT_RSV] = { 41, 2}, ++ [VCAP_IS2_ACT_ACL_ID] = { 43, 6}, ++ [VCAP_IS2_ACT_HIT_CNT] = { 49, 32}, ++}; ++ ++static const struct vcap_props vsc7514_vcap_props[] = { ++ [VCAP_IS2] = { ++ .tg_width = 2, ++ .sw_count = 4, ++ .entry_count = VSC7514_VCAP_IS2_CNT, ++ .entry_width = VSC7514_VCAP_IS2_ENTRY_WIDTH, ++ .action_count = VSC7514_VCAP_IS2_CNT + ++ VSC7514_VCAP_PORT_CNT + 2, ++ .action_width = 99, ++ .action_type_width = 1, ++ .action_table = { ++ [IS2_ACTION_TYPE_NORMAL] = { ++ .width = 49, ++ .count = 2 ++ }, ++ [IS2_ACTION_TYPE_SMAC_SIP] = { ++ .width = 6, ++ .count = 4 ++ }, ++ }, ++ .counter_words = 4, ++ .counter_width = 32, ++ }, ++}; ++ ++static struct ptp_clock_info ocelot_ptp_clock_info = { ++ .owner = THIS_MODULE, ++ .name = "ocelot ptp", ++ .max_adj = 0x7fffffff, ++ .n_alarm = 0, ++ .n_ext_ts = 0, ++ .n_per_out = OCELOT_PTP_PINS_NUM, ++ .n_pins = OCELOT_PTP_PINS_NUM, ++ .pps = 0, ++ .gettime64 = ocelot_ptp_gettime64, ++ .settime64 = ocelot_ptp_settime64, ++ .adjtime = ocelot_ptp_adjtime, ++ .adjfine = ocelot_ptp_adjfine, ++ .verify = ocelot_ptp_verify, ++ .enable = ocelot_ptp_enable, ++}; ++ ++static int mscc_ocelot_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *ports, *portnp; ++ int err, irq_xtr, irq_ptp_rdy; ++ struct ocelot *ocelot; ++ struct regmap *hsio; ++ unsigned int i; ++ ++ struct { ++ enum ocelot_target id; ++ char *name; ++ u8 optional:1; ++ } io_target[] = { ++ { SYS, "sys" }, ++ { REW, "rew" }, ++ { QSYS, "qsys" }, ++ { ANA, "ana" }, ++ { QS, "qs" }, ++ { S2, "s2" }, ++ { PTP, "ptp", 1 }, ++ }; ++ ++ if (!np && !pdev->dev.platform_data) ++ return -ENODEV; ++ ++ ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL); ++ if (!ocelot) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, ocelot); ++ ocelot->dev = &pdev->dev; ++ ++ for (i = 0; i < ARRAY_SIZE(io_target); i++) { ++ struct regmap *target; ++ struct resource *res; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ io_target[i].name); ++ ++ target = ocelot_regmap_init(ocelot, res); ++ if (IS_ERR(target)) { ++ if (io_target[i].optional) { ++ ocelot->targets[io_target[i].id] = NULL; ++ continue; ++ } ++ return PTR_ERR(target); ++ } ++ ++ ocelot->targets[io_target[i].id] = target; ++ } ++ ++ hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio"); ++ if (IS_ERR(hsio)) { ++ dev_err(&pdev->dev, "missing hsio syscon\n"); ++ return PTR_ERR(hsio); ++ } ++ ++ ocelot->targets[HSIO] = hsio; ++ ++ err = ocelot_chip_init(ocelot, &ocelot_ops); ++ if (err) ++ return err; ++ ++ irq_xtr = platform_get_irq_byname(pdev, "xtr"); ++ if (irq_xtr < 0) ++ return -ENODEV; ++ ++ err = devm_request_threaded_irq(&pdev->dev, irq_xtr, NULL, ++ ocelot_xtr_irq_handler, IRQF_ONESHOT, ++ "frame extraction", ocelot); ++ if (err) ++ return err; ++ ++ irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy"); ++ if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) { ++ err = devm_request_threaded_irq(&pdev->dev, irq_ptp_rdy, NULL, ++ ocelot_ptp_rdy_irq_handler, ++ IRQF_ONESHOT, "ptp ready", ++ ocelot); ++ if (err) ++ return err; ++ ++ /* Both the PTP interrupt and the PTP bank are available */ ++ ocelot->ptp = 1; ++ } ++ ++ ports = of_get_child_by_name(np, "ethernet-ports"); ++ if (!ports) { ++ dev_err(&pdev->dev, "no ethernet-ports child node found\n"); ++ return -ENODEV; ++ } ++ ++ ocelot->num_phys_ports = of_get_child_count(ports); ++ ++ ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports, ++ sizeof(struct ocelot_port *), GFP_KERNEL); ++ ++ ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys; ++ ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions; ++ ocelot->vcap = vsc7514_vcap_props; ++ ++ ocelot_init(ocelot); ++ if (ocelot->ptp) { ++ err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info); ++ if (err) { ++ dev_err(ocelot->dev, ++ "Timestamp initialization failed\n"); ++ ocelot->ptp = 0; ++ } ++ } ++ ++ /* No NPI port */ ++ ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE, ++ OCELOT_TAG_PREFIX_NONE); ++ ++ for_each_available_child_of_node(ports, portnp) { ++ struct ocelot_port_private *priv; ++ struct ocelot_port *ocelot_port; ++ struct device_node *phy_node; ++ phy_interface_t phy_mode; ++ struct phy_device *phy; ++ struct resource *res; ++ struct phy *serdes; ++ void __iomem *regs; ++ char res_name[8]; ++ u32 port; ++ ++ if (of_property_read_u32(portnp, "reg", &port)) ++ continue; ++ ++ snprintf(res_name, sizeof(res_name), "port%d", port); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ res_name); ++ regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(regs)) ++ continue; ++ ++ phy_node = of_parse_phandle(portnp, "phy-handle", 0); ++ if (!phy_node) ++ continue; ++ ++ phy = of_phy_find_device(phy_node); ++ of_node_put(phy_node); ++ if (!phy) ++ continue; ++ ++ err = ocelot_probe_port(ocelot, port, regs, phy); ++ if (err) { ++ of_node_put(portnp); ++ goto out_put_ports; ++ } ++ ++ ocelot_port = ocelot->ports[port]; ++ priv = container_of(ocelot_port, struct ocelot_port_private, ++ port); ++ ++ of_get_phy_mode(portnp, &phy_mode); ++ ++ ocelot_port->phy_mode = phy_mode; ++ ++ switch (ocelot_port->phy_mode) { ++ case PHY_INTERFACE_MODE_NA: ++ continue; ++ case PHY_INTERFACE_MODE_SGMII: ++ break; ++ case PHY_INTERFACE_MODE_QSGMII: ++ /* Ensure clock signals and speed is set on all ++ * QSGMII links ++ */ ++ ocelot_port_writel(ocelot_port, ++ DEV_CLOCK_CFG_LINK_SPEED ++ (OCELOT_SPEED_1000), ++ DEV_CLOCK_CFG); ++ break; ++ default: ++ dev_err(ocelot->dev, ++ "invalid phy mode for port%d, (Q)SGMII only\n", ++ port); ++ of_node_put(portnp); ++ err = -EINVAL; ++ goto out_put_ports; ++ } ++ ++ serdes = devm_of_phy_get(ocelot->dev, portnp, NULL); ++ if (IS_ERR(serdes)) { ++ err = PTR_ERR(serdes); ++ if (err == -EPROBE_DEFER) ++ dev_dbg(ocelot->dev, "deferring probe\n"); ++ else ++ dev_err(ocelot->dev, ++ "missing SerDes phys for port%d\n", ++ port); ++ ++ of_node_put(portnp); ++ goto out_put_ports; ++ } ++ ++ priv->serdes = serdes; ++ } ++ ++ register_netdevice_notifier(&ocelot_netdevice_nb); ++ register_switchdev_notifier(&ocelot_switchdev_nb); ++ register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); ++ ++ dev_info(&pdev->dev, "Ocelot switch probed\n"); ++ ++out_put_ports: ++ of_node_put(ports); ++ return err; ++} ++ ++static int mscc_ocelot_remove(struct platform_device *pdev) ++{ ++ struct ocelot *ocelot = platform_get_drvdata(pdev); ++ ++ ocelot_deinit_timestamp(ocelot); ++ ocelot_deinit(ocelot); ++ unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); ++ unregister_switchdev_notifier(&ocelot_switchdev_nb); ++ unregister_netdevice_notifier(&ocelot_netdevice_nb); ++ ++ return 0; ++} ++ ++static struct platform_driver mscc_ocelot_driver = { ++ .probe = mscc_ocelot_probe, ++ .remove = mscc_ocelot_remove, ++ .driver = { ++ .name = "ocelot-switch", ++ .of_match_table = mscc_ocelot_match, ++ }, ++}; ++ ++module_platform_driver(mscc_ocelot_driver); ++ ++MODULE_DESCRIPTION("Microsemi Ocelot switch driver"); ++MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>"); ++MODULE_LICENSE("Dual MIT/GPL"); +diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c +index b660ddbe40251..fe173ea894e2c 100644 +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -2113,11 +2113,18 @@ static void rtl_release_firmware(struct rtl8169_private *tp) + + void r8169_apply_firmware(struct rtl8169_private *tp) + { ++ int val; ++ + /* TODO: release firmware if rtl_fw_write_firmware signals failure. */ + if (tp->rtl_fw) { + rtl_fw_write_firmware(tp, tp->rtl_fw); + /* At least one firmware doesn't reset tp->ocp_base. */ + tp->ocp_base = OCP_STD_PHY_BASE; ++ ++ /* PHY soft reset may still be in progress */ ++ phy_read_poll_timeout(tp->phydev, MII_BMCR, val, ++ !(val & BMCR_RESET), ++ 50000, 600000, true); + } + } + +@@ -2951,7 +2958,7 @@ static void rtl_hw_start_8168f_1(struct rtl8169_private *tp) + { 0x08, 0x0001, 0x0002 }, + { 0x09, 0x0000, 0x0080 }, + { 0x19, 0x0000, 0x0224 }, +- { 0x00, 0x0000, 0x0004 }, ++ { 0x00, 0x0000, 0x0008 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + +@@ -2968,7 +2975,7 @@ static void rtl_hw_start_8411(struct rtl8169_private *tp) + { 0x06, 0x00c0, 0x0020 }, + { 0x0f, 0xffff, 0x5200 }, + { 0x19, 0x0000, 0x0224 }, +- { 0x00, 0x0000, 0x0004 }, ++ { 0x00, 0x0000, 0x0008 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + +diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c +index df89d09b253e2..99f7aae102ce1 100644 +--- a/drivers/net/ethernet/renesas/ravb_main.c ++++ b/drivers/net/ethernet/renesas/ravb_main.c +@@ -1342,51 +1342,6 @@ static inline int ravb_hook_irq(unsigned int irq, irq_handler_t handler, + return error; + } + +-/* MDIO bus init function */ +-static int ravb_mdio_init(struct ravb_private *priv) +-{ +- struct platform_device *pdev = priv->pdev; +- struct device *dev = &pdev->dev; +- int error; +- +- /* Bitbang init */ +- priv->mdiobb.ops = &bb_ops; +- +- /* MII controller setting */ +- priv->mii_bus = alloc_mdio_bitbang(&priv->mdiobb); +- if (!priv->mii_bus) +- return -ENOMEM; +- +- /* Hook up MII support for ethtool */ +- priv->mii_bus->name = "ravb_mii"; +- priv->mii_bus->parent = dev; +- snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", +- pdev->name, pdev->id); +- +- /* Register MDIO bus */ +- error = of_mdiobus_register(priv->mii_bus, dev->of_node); +- if (error) +- goto out_free_bus; +- +- return 0; +- +-out_free_bus: +- free_mdio_bitbang(priv->mii_bus); +- return error; +-} +- +-/* MDIO bus release function */ +-static int ravb_mdio_release(struct ravb_private *priv) +-{ +- /* Unregister mdio bus */ +- mdiobus_unregister(priv->mii_bus); +- +- /* Free bitbang info */ +- free_mdio_bitbang(priv->mii_bus); +- +- return 0; +-} +- + /* Network device open function for Ethernet AVB */ + static int ravb_open(struct net_device *ndev) + { +@@ -1395,13 +1350,6 @@ static int ravb_open(struct net_device *ndev) + struct device *dev = &pdev->dev; + int error; + +- /* MDIO bus init */ +- error = ravb_mdio_init(priv); +- if (error) { +- netdev_err(ndev, "failed to initialize MDIO\n"); +- return error; +- } +- + napi_enable(&priv->napi[RAVB_BE]); + napi_enable(&priv->napi[RAVB_NC]); + +@@ -1479,7 +1427,6 @@ out_free_irq: + out_napi_off: + napi_disable(&priv->napi[RAVB_NC]); + napi_disable(&priv->napi[RAVB_BE]); +- ravb_mdio_release(priv); + return error; + } + +@@ -1789,8 +1736,6 @@ static int ravb_close(struct net_device *ndev) + ravb_ring_free(ndev, RAVB_BE); + ravb_ring_free(ndev, RAVB_NC); + +- ravb_mdio_release(priv); +- + return 0; + } + +@@ -1942,6 +1887,51 @@ static const struct net_device_ops ravb_netdev_ops = { + .ndo_set_features = ravb_set_features, + }; + ++/* MDIO bus init function */ ++static int ravb_mdio_init(struct ravb_private *priv) ++{ ++ struct platform_device *pdev = priv->pdev; ++ struct device *dev = &pdev->dev; ++ int error; ++ ++ /* Bitbang init */ ++ priv->mdiobb.ops = &bb_ops; ++ ++ /* MII controller setting */ ++ priv->mii_bus = alloc_mdio_bitbang(&priv->mdiobb); ++ if (!priv->mii_bus) ++ return -ENOMEM; ++ ++ /* Hook up MII support for ethtool */ ++ priv->mii_bus->name = "ravb_mii"; ++ priv->mii_bus->parent = dev; ++ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", ++ pdev->name, pdev->id); ++ ++ /* Register MDIO bus */ ++ error = of_mdiobus_register(priv->mii_bus, dev->of_node); ++ if (error) ++ goto out_free_bus; ++ ++ return 0; ++ ++out_free_bus: ++ free_mdio_bitbang(priv->mii_bus); ++ return error; ++} ++ ++/* MDIO bus release function */ ++static int ravb_mdio_release(struct ravb_private *priv) ++{ ++ /* Unregister mdio bus */ ++ mdiobus_unregister(priv->mii_bus); ++ ++ /* Free bitbang info */ ++ free_mdio_bitbang(priv->mii_bus); ++ ++ return 0; ++} ++ + static const struct of_device_id ravb_match_table[] = { + { .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 }, +@@ -2184,6 +2174,13 @@ static int ravb_probe(struct platform_device *pdev) + eth_hw_addr_random(ndev); + } + ++ /* MDIO bus init */ ++ error = ravb_mdio_init(priv); ++ if (error) { ++ dev_err(&pdev->dev, "failed to initialize MDIO\n"); ++ goto out_dma_free; ++ } ++ + netif_napi_add(ndev, &priv->napi[RAVB_BE], ravb_poll, 64); + netif_napi_add(ndev, &priv->napi[RAVB_NC], ravb_poll, 64); + +@@ -2205,6 +2202,8 @@ static int ravb_probe(struct platform_device *pdev) + out_napi_del: + netif_napi_del(&priv->napi[RAVB_NC]); + netif_napi_del(&priv->napi[RAVB_BE]); ++ ravb_mdio_release(priv); ++out_dma_free: + dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, + priv->desc_bat_dma); + +@@ -2236,6 +2235,7 @@ static int ravb_remove(struct platform_device *pdev) + unregister_netdev(ndev); + netif_napi_del(&priv->napi[RAVB_NC]); + netif_napi_del(&priv->napi[RAVB_BE]); ++ ravb_mdio_release(priv); + pm_runtime_disable(&pdev->dev); + free_netdev(ndev); + platform_set_drvdata(pdev, NULL); +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index 2ac9dfb3462c6..9e6d60e75f85d 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -653,7 +653,6 @@ static void intel_eth_pci_remove(struct pci_dev *pdev) + + pci_free_irq_vectors(pdev); + +- clk_disable_unprepare(priv->plat->stmmac_clk); + clk_unregister_fixed_rate(priv->plat->stmmac_clk); + + pcim_iounmap_regions(pdev, BIT(0)); +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +index 9c02fc754bf1b..545696971f65e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -203,6 +203,8 @@ struct stmmac_priv { + int eee_enabled; + int eee_active; + int tx_lpi_timer; ++ int tx_lpi_enabled; ++ int eee_tw_timer; + unsigned int mode; + unsigned int chain_mode; + int extend_desc; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +index eae11c5850251..b82c6715f95f3 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +@@ -652,6 +652,7 @@ static int stmmac_ethtool_op_get_eee(struct net_device *dev, + edata->eee_enabled = priv->eee_enabled; + edata->eee_active = priv->eee_active; + edata->tx_lpi_timer = priv->tx_lpi_timer; ++ edata->tx_lpi_enabled = priv->tx_lpi_enabled; + + return phylink_ethtool_get_eee(priv->phylink, edata); + } +@@ -662,24 +663,26 @@ static int stmmac_ethtool_op_set_eee(struct net_device *dev, + struct stmmac_priv *priv = netdev_priv(dev); + int ret; + +- if (!edata->eee_enabled) { ++ if (!priv->dma_cap.eee) ++ return -EOPNOTSUPP; ++ ++ if (priv->tx_lpi_enabled != edata->tx_lpi_enabled) ++ netdev_warn(priv->dev, ++ "Setting EEE tx-lpi is not supported\n"); ++ ++ if (!edata->eee_enabled) + stmmac_disable_eee_mode(priv); +- } else { +- /* We are asking for enabling the EEE but it is safe +- * to verify all by invoking the eee_init function. +- * In case of failure it will return an error. +- */ +- edata->eee_enabled = stmmac_eee_init(priv); +- if (!edata->eee_enabled) +- return -EOPNOTSUPP; +- } + + ret = phylink_ethtool_set_eee(priv->phylink, edata); + if (ret) + return ret; + +- priv->eee_enabled = edata->eee_enabled; +- priv->tx_lpi_timer = edata->tx_lpi_timer; ++ if (edata->eee_enabled && ++ priv->tx_lpi_timer != edata->tx_lpi_timer) { ++ priv->tx_lpi_timer = edata->tx_lpi_timer; ++ stmmac_eee_init(priv); ++ } ++ + return 0; + } + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 73677c3b33b65..73465e5f5a417 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -94,7 +94,7 @@ static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE | + static int eee_timer = STMMAC_DEFAULT_LPI_TIMER; + module_param(eee_timer, int, 0644); + MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec"); +-#define STMMAC_LPI_T(x) (jiffies + msecs_to_jiffies(x)) ++#define STMMAC_LPI_T(x) (jiffies + usecs_to_jiffies(x)) + + /* By default the driver will use the ring mode to manage tx and rx descriptors, + * but allow user to force to use the chain instead of the ring +@@ -370,7 +370,7 @@ static void stmmac_eee_ctrl_timer(struct timer_list *t) + struct stmmac_priv *priv = from_timer(priv, t, eee_ctrl_timer); + + stmmac_enable_eee_mode(priv); +- mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(eee_timer)); ++ mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(priv->tx_lpi_timer)); + } + + /** +@@ -383,7 +383,7 @@ static void stmmac_eee_ctrl_timer(struct timer_list *t) + */ + bool stmmac_eee_init(struct stmmac_priv *priv) + { +- int tx_lpi_timer = priv->tx_lpi_timer; ++ int eee_tw_timer = priv->eee_tw_timer; + + /* Using PCS we cannot dial with the phy registers at this stage + * so we do not support extra feature like EEE. +@@ -403,7 +403,7 @@ bool stmmac_eee_init(struct stmmac_priv *priv) + if (priv->eee_enabled) { + netdev_dbg(priv->dev, "disable EEE\n"); + del_timer_sync(&priv->eee_ctrl_timer); +- stmmac_set_eee_timer(priv, priv->hw, 0, tx_lpi_timer); ++ stmmac_set_eee_timer(priv, priv->hw, 0, eee_tw_timer); + } + mutex_unlock(&priv->lock); + return false; +@@ -411,11 +411,12 @@ bool stmmac_eee_init(struct stmmac_priv *priv) + + if (priv->eee_active && !priv->eee_enabled) { + timer_setup(&priv->eee_ctrl_timer, stmmac_eee_ctrl_timer, 0); +- mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(eee_timer)); + stmmac_set_eee_timer(priv, priv->hw, STMMAC_DEFAULT_LIT_LS, +- tx_lpi_timer); ++ eee_tw_timer); + } + ++ mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(priv->tx_lpi_timer)); ++ + mutex_unlock(&priv->lock); + netdev_dbg(priv->dev, "Energy-Efficient Ethernet initialized\n"); + return true; +@@ -930,6 +931,7 @@ static void stmmac_mac_link_down(struct phylink_config *config, + + stmmac_mac_set(priv, priv->ioaddr, false); + priv->eee_active = false; ++ priv->tx_lpi_enabled = false; + stmmac_eee_init(priv); + stmmac_set_eee_pls(priv, priv->hw, false); + } +@@ -1027,6 +1029,7 @@ static void stmmac_mac_link_up(struct phylink_config *config, + if (phy && priv->dma_cap.eee) { + priv->eee_active = phy_init_eee(phy, 1) >= 0; + priv->eee_enabled = stmmac_eee_init(priv); ++ priv->tx_lpi_enabled = priv->eee_enabled; + stmmac_set_eee_pls(priv, priv->hw, true); + } + } +@@ -2057,7 +2060,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) + + if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) { + stmmac_enable_eee_mode(priv); +- mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(eee_timer)); ++ mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(priv->tx_lpi_timer)); + } + + /* We still have pending packets, let's call for a new scheduling */ +@@ -2690,7 +2693,11 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) + netdev_warn(priv->dev, "PTP init failed\n"); + } + +- priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS; ++ priv->eee_tw_timer = STMMAC_DEFAULT_TWT_LS; ++ ++ /* Convert the timer from msec to usec */ ++ if (!priv->tx_lpi_timer) ++ priv->tx_lpi_timer = eee_timer * 1000; + + if (priv->use_riwt) { + if (!priv->rx_riwt) +diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c +index 9159846b8b938..787ac2c8e74eb 100644 +--- a/drivers/net/macsec.c ++++ b/drivers/net/macsec.c +@@ -1077,6 +1077,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) + struct macsec_rx_sa *rx_sa; + struct macsec_rxh_data *rxd; + struct macsec_dev *macsec; ++ unsigned int len; + sci_t sci; + u32 hdr_pn; + bool cbit; +@@ -1232,9 +1233,10 @@ deliver: + macsec_rxsc_put(rx_sc); + + skb_orphan(skb); ++ len = skb->len; + ret = gro_cells_receive(&macsec->gro_cells, skb); + if (ret == NET_RX_SUCCESS) +- count_rx(dev, skb->len); ++ count_rx(dev, len); + else + macsec->secy.netdev->stats.rx_dropped++; + +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index e351d65533aa8..06146ae4c6d8d 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -217,6 +217,7 @@ config MDIO_THUNDER + depends on 64BIT + depends on PCI + select MDIO_CAVIUM ++ select MDIO_DEVRES + help + This driver supports the MDIO interfaces found on Cavium + ThunderX SoCs when the MDIO bus device appears as a PCI +diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c +index c7229d022a27b..48ba757046cea 100644 +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -1,6 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0+ +-/* +- * drivers/net/phy/realtek.c ++/* drivers/net/phy/realtek.c + * + * Driver for Realtek PHYs + * +@@ -32,9 +31,9 @@ + #define RTL8211F_TX_DELAY BIT(8) + #define RTL8211F_RX_DELAY BIT(3) + +-#define RTL8211E_TX_DELAY BIT(1) +-#define RTL8211E_RX_DELAY BIT(2) +-#define RTL8211E_MODE_MII_GMII BIT(3) ++#define RTL8211E_CTRL_DELAY BIT(13) ++#define RTL8211E_TX_DELAY BIT(12) ++#define RTL8211E_RX_DELAY BIT(11) + + #define RTL8201F_ISR 0x1e + #define RTL8201F_IER 0x13 +@@ -246,16 +245,16 @@ static int rtl8211e_config_init(struct phy_device *phydev) + /* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: +- val = 0; ++ val = RTL8211E_CTRL_DELAY | 0; + break; + case PHY_INTERFACE_MODE_RGMII_ID: +- val = RTL8211E_TX_DELAY | RTL8211E_RX_DELAY; ++ val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: +- val = RTL8211E_RX_DELAY; ++ val = RTL8211E_CTRL_DELAY | RTL8211E_RX_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: +- val = RTL8211E_TX_DELAY; ++ val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY; + break; + default: /* the rest of the modes imply leaving delays as is. */ + return 0; +@@ -263,11 +262,12 @@ static int rtl8211e_config_init(struct phy_device *phydev) + + /* According to a sample driver there is a 0x1c config register on the + * 0xa4 extension page (0x7) layout. It can be used to disable/enable +- * the RX/TX delays otherwise controlled by RXDLY/TXDLY pins. It can +- * also be used to customize the whole configuration register: +- * 8:6 = PHY Address, 5:4 = Auto-Negotiation, 3 = Interface Mode Select, +- * 2 = RX Delay, 1 = TX Delay, 0 = SELRGV (see original PHY datasheet +- * for details). ++ * the RX/TX delays otherwise controlled by RXDLY/TXDLY pins. ++ * The configuration register definition: ++ * 14 = reserved ++ * 13 = Force Tx RX Delay controlled by bit12 bit11, ++ * 12 = RX Delay, 11 = TX Delay ++ * 10:0 = Test && debug settings reserved by realtek + */ + oldpage = phy_select_page(phydev, 0x7); + if (oldpage < 0) +@@ -277,7 +277,8 @@ static int rtl8211e_config_init(struct phy_device *phydev) + if (ret) + goto err_restore_page; + +- ret = __phy_modify(phydev, 0x1c, RTL8211E_TX_DELAY | RTL8211E_RX_DELAY, ++ ret = __phy_modify(phydev, 0x1c, RTL8211E_CTRL_DELAY ++ | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY, + val); + + err_restore_page: +diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c +index 8c1e02752ff61..bcc4a4c011f1f 100644 +--- a/drivers/net/team/team.c ++++ b/drivers/net/team/team.c +@@ -287,7 +287,7 @@ inst_rollback: + for (i--; i >= 0; i--) + __team_option_inst_del_option(team, dst_opts[i]); + +- i = option_count - 1; ++ i = option_count; + alloc_rollback: + for (i--; i >= 0; i--) + kfree(dst_opts[i]); +@@ -2112,6 +2112,7 @@ static void team_setup_by_port(struct net_device *dev, + dev->header_ops = port_dev->header_ops; + dev->type = port_dev->type; + dev->hard_header_len = port_dev->hard_header_len; ++ dev->needed_headroom = port_dev->needed_headroom; + dev->addr_len = port_dev->addr_len; + dev->mtu = port_dev->mtu; + memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len); +diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c +index a38e868e44d46..f0ef3706aad96 100644 +--- a/drivers/net/usb/ax88179_178a.c ++++ b/drivers/net/usb/ax88179_178a.c +@@ -1823,6 +1823,7 @@ static const struct driver_info belkin_info = { + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, ++ .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c +index e7c630d375899..63a4da0b2d6dd 100644 +--- a/drivers/net/usb/rtl8150.c ++++ b/drivers/net/usb/rtl8150.c +@@ -274,12 +274,20 @@ static int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg) + return 1; + } + +-static inline void set_ethernet_addr(rtl8150_t * dev) ++static void set_ethernet_addr(rtl8150_t *dev) + { +- u8 node_id[6]; ++ u8 node_id[ETH_ALEN]; ++ int ret; ++ ++ ret = get_registers(dev, IDR, sizeof(node_id), node_id); + +- get_registers(dev, IDR, sizeof(node_id), node_id); +- memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id)); ++ if (ret == sizeof(node_id)) { ++ ether_addr_copy(dev->netdev->dev_addr, node_id); ++ } else { ++ eth_hw_addr_random(dev->netdev); ++ netdev_notice(dev->netdev, "Assigned a random MAC address: %pM\n", ++ dev->netdev->dev_addr); ++ } + } + + static int rtl8150_set_mac_address(struct net_device *netdev, void *p) +diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c +index ba38765dc4905..c34927b1d806e 100644 +--- a/drivers/net/virtio_net.c ++++ b/drivers/net/virtio_net.c +@@ -63,6 +63,11 @@ static const unsigned long guest_offloads[] = { + VIRTIO_NET_F_GUEST_CSUM + }; + ++#define GUEST_OFFLOAD_LRO_MASK ((1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ ++ (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ ++ (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ ++ (1ULL << VIRTIO_NET_F_GUEST_UFO)) ++ + struct virtnet_stat_desc { + char desc[ETH_GSTRING_LEN]; + size_t offset; +@@ -2547,7 +2552,8 @@ static int virtnet_set_features(struct net_device *dev, + if (features & NETIF_F_LRO) + offloads = vi->guest_offloads_capable; + else +- offloads = 0; ++ offloads = vi->guest_offloads_capable & ++ ~GUEST_OFFLOAD_LRO_MASK; + + err = virtnet_set_guest_offloads(vi, offloads); + if (err) +diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c +index 2818015324b8b..336504b7531d9 100644 +--- a/drivers/net/vmxnet3/vmxnet3_drv.c ++++ b/drivers/net/vmxnet3/vmxnet3_drv.c +@@ -1032,7 +1032,6 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, + /* Use temporary descriptor to avoid touching bits multiple times */ + union Vmxnet3_GenericDesc tempTxDesc; + #endif +- struct udphdr *udph; + + count = txd_estimate(skb); + +@@ -1135,8 +1134,7 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, + gdesc->txd.om = VMXNET3_OM_ENCAP; + gdesc->txd.msscof = ctx.mss; + +- udph = udp_hdr(skb); +- if (udph->check) ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM) + gdesc->txd.oco = 1; + } else { + gdesc->txd.hlen = ctx.l4_offset + ctx.l4_hdr_size; +@@ -3371,6 +3369,7 @@ vmxnet3_probe_device(struct pci_dev *pdev, + .ndo_change_mtu = vmxnet3_change_mtu, + .ndo_fix_features = vmxnet3_fix_features, + .ndo_set_features = vmxnet3_set_features, ++ .ndo_features_check = vmxnet3_features_check, + .ndo_get_stats64 = vmxnet3_get_stats64, + .ndo_tx_timeout = vmxnet3_tx_timeout, + .ndo_set_rx_mode = vmxnet3_set_mc, +diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c +index def27afa1c69f..3586677920046 100644 +--- a/drivers/net/vmxnet3/vmxnet3_ethtool.c ++++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c +@@ -267,6 +267,34 @@ netdev_features_t vmxnet3_fix_features(struct net_device *netdev, + return features; + } + ++netdev_features_t vmxnet3_features_check(struct sk_buff *skb, ++ struct net_device *netdev, ++ netdev_features_t features) ++{ ++ struct vmxnet3_adapter *adapter = netdev_priv(netdev); ++ ++ /* Validate if the tunneled packet is being offloaded by the device */ ++ if (VMXNET3_VERSION_GE_4(adapter) && ++ skb->encapsulation && skb->ip_summed == CHECKSUM_PARTIAL) { ++ u8 l4_proto = 0; ++ ++ switch (vlan_get_protocol(skb)) { ++ case htons(ETH_P_IP): ++ l4_proto = ip_hdr(skb)->protocol; ++ break; ++ case htons(ETH_P_IPV6): ++ l4_proto = ipv6_hdr(skb)->nexthdr; ++ break; ++ default: ++ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); ++ } ++ ++ if (l4_proto != IPPROTO_UDP) ++ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); ++ } ++ return features; ++} ++ + static void vmxnet3_enable_encap_offloads(struct net_device *netdev) + { + struct vmxnet3_adapter *adapter = netdev_priv(netdev); +diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h +index 5d2b062215a27..d958b92c94299 100644 +--- a/drivers/net/vmxnet3/vmxnet3_int.h ++++ b/drivers/net/vmxnet3/vmxnet3_int.h +@@ -470,6 +470,10 @@ vmxnet3_rq_destroy_all(struct vmxnet3_adapter *adapter); + netdev_features_t + vmxnet3_fix_features(struct net_device *netdev, netdev_features_t features); + ++netdev_features_t ++vmxnet3_features_check(struct sk_buff *skb, ++ struct net_device *netdev, netdev_features_t features); ++ + int + vmxnet3_set_features(struct net_device *netdev, netdev_features_t features); + +diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c +index 69165a8f7c1f0..a9d5682cdea54 100644 +--- a/drivers/nvme/host/core.c ++++ b/drivers/nvme/host/core.c +@@ -3061,8 +3061,10 @@ static int nvme_dev_open(struct inode *inode, struct file *file) + } + + nvme_get_ctrl(ctrl); +- if (!try_module_get(ctrl->ops->module)) ++ if (!try_module_get(ctrl->ops->module)) { ++ nvme_put_ctrl(ctrl); + return -EINVAL; ++ } + + file->private_data = ctrl; + return 0; +diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c +index 24467eea73999..9b3827a6f3a39 100644 +--- a/drivers/nvme/host/tcp.c ++++ b/drivers/nvme/host/tcp.c +@@ -889,12 +889,11 @@ static int nvme_tcp_try_send_data(struct nvme_tcp_request *req) + else + flags |= MSG_MORE | MSG_SENDPAGE_NOTLAST; + +- /* can't zcopy slab pages */ +- if (unlikely(PageSlab(page))) { +- ret = sock_no_sendpage(queue->sock, page, offset, len, ++ if (sendpage_ok(page)) { ++ ret = kernel_sendpage(queue->sock, page, offset, len, + flags); + } else { +- ret = kernel_sendpage(queue->sock, page, offset, len, ++ ret = sock_no_sendpage(queue->sock, page, offset, len, + flags); + } + if (ret <= 0) +diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c +index 190e4a6186ef7..f64b82824db28 100644 +--- a/drivers/platform/olpc/olpc-ec.c ++++ b/drivers/platform/olpc/olpc-ec.c +@@ -439,7 +439,9 @@ static int olpc_ec_probe(struct platform_device *pdev) + &config); + if (IS_ERR(ec->dcon_rdev)) { + dev_err(&pdev->dev, "failed to register DCON regulator\n"); +- return PTR_ERR(ec->dcon_rdev); ++ err = PTR_ERR(ec->dcon_rdev); ++ kfree(ec); ++ return err; + } + + ec->dbgfs_dir = olpc_ec_setup_debugfs(); +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index 0581a54cf562f..a5ad36083b671 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -469,6 +469,7 @@ config FUJITSU_LAPTOP + depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n + select INPUT_SPARSEKMAP ++ select NEW_LEDS + select LEDS_CLASS + help + This is a driver for laptops built by Fujitsu: +@@ -1091,6 +1092,7 @@ config LG_LAPTOP + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP ++ select NEW_LEDS + select LEDS_CLASS + help + This driver adds support for hotkeys as well as control of keyboard +diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c +index 6c42f73c1dfd3..805ce604dee6a 100644 +--- a/drivers/platform/x86/asus-nb-wmi.c ++++ b/drivers/platform/x86/asus-nb-wmi.c +@@ -120,6 +120,10 @@ static struct quirk_entry quirk_asus_ga502i = { + .wmi_backlight_set_devstate = true, + }; + ++static struct quirk_entry quirk_asus_use_kbd_dock_devid = { ++ .use_kbd_dock_devid = true, ++}; ++ + static int dmi_matched(const struct dmi_system_id *dmi) + { + pr_info("Identified laptop model '%s'\n", dmi->ident); +@@ -493,6 +497,34 @@ static const struct dmi_system_id asus_quirks[] = { + }, + .driver_data = &quirk_asus_ga502i, + }, ++ { ++ .callback = dmi_matched, ++ .ident = "Asus Transformer T100TA / T100HA / T100CHI", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ /* Match *T100* */ ++ DMI_MATCH(DMI_PRODUCT_NAME, "T100"), ++ }, ++ .driver_data = &quirk_asus_use_kbd_dock_devid, ++ }, ++ { ++ .callback = dmi_matched, ++ .ident = "Asus Transformer T101HA", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "T101HA"), ++ }, ++ .driver_data = &quirk_asus_use_kbd_dock_devid, ++ }, ++ { ++ .callback = dmi_matched, ++ .ident = "Asus Transformer T200TA", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"), ++ }, ++ .driver_data = &quirk_asus_use_kbd_dock_devid, ++ }, + {}, + }; + +diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c +index 8f4acdc06b134..ae6289d37faf6 100644 +--- a/drivers/platform/x86/asus-wmi.c ++++ b/drivers/platform/x86/asus-wmi.c +@@ -365,12 +365,14 @@ static int asus_wmi_input_init(struct asus_wmi *asus) + if (err) + goto err_free_dev; + +- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_DOCK); +- if (result >= 0) { +- input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE); +- input_report_switch(asus->inputdev, SW_TABLET_MODE, !result); +- } else if (result != -ENODEV) { +- pr_err("Error checking for keyboard-dock: %d\n", result); ++ if (asus->driver->quirks->use_kbd_dock_devid) { ++ result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_DOCK); ++ if (result >= 0) { ++ input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE); ++ input_report_switch(asus->inputdev, SW_TABLET_MODE, !result); ++ } else if (result != -ENODEV) { ++ pr_err("Error checking for keyboard-dock: %d\n", result); ++ } + } + + err = input_register_device(asus->inputdev); +@@ -2114,7 +2116,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) + return; + } + +- if (code == NOTIFY_KBD_DOCK_CHANGE) { ++ if (asus->driver->quirks->use_kbd_dock_devid && code == NOTIFY_KBD_DOCK_CHANGE) { + result = asus_wmi_get_devstate_simple(asus, + ASUS_WMI_DEVID_KBD_DOCK); + if (result >= 0) { +diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h +index 4f31b68642a08..1a95c172f94b0 100644 +--- a/drivers/platform/x86/asus-wmi.h ++++ b/drivers/platform/x86/asus-wmi.h +@@ -33,6 +33,7 @@ struct quirk_entry { + bool wmi_backlight_native; + bool wmi_backlight_set_devstate; + bool wmi_force_als_set; ++ bool use_kbd_dock_devid; + int wapf; + /* + * For machines with AMD graphic chips, it will send out WMI event +diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c +index e85d8e58320c1..663197fecb20d 100644 +--- a/drivers/platform/x86/intel-vbtn.c ++++ b/drivers/platform/x86/intel-vbtn.c +@@ -15,9 +15,13 @@ + #include <linux/platform_device.h> + #include <linux/suspend.h> + ++/* Returned when NOT in tablet mode on some HP Stream x360 11 models */ ++#define VGBS_TABLET_MODE_FLAG_ALT 0x10 + /* When NOT in tablet mode, VGBS returns with the flag 0x40 */ +-#define TABLET_MODE_FLAG 0x40 +-#define DOCK_MODE_FLAG 0x80 ++#define VGBS_TABLET_MODE_FLAG 0x40 ++#define VGBS_DOCK_MODE_FLAG 0x80 ++ ++#define VGBS_TABLET_MODE_FLAGS (VGBS_TABLET_MODE_FLAG | VGBS_TABLET_MODE_FLAG_ALT) + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("AceLan Kao"); +@@ -72,9 +76,9 @@ static void detect_tablet_mode(struct platform_device *device) + if (ACPI_FAILURE(status)) + return; + +- m = !(vgbs & TABLET_MODE_FLAG); ++ m = !(vgbs & VGBS_TABLET_MODE_FLAGS); + input_report_switch(priv->input_dev, SW_TABLET_MODE, m); +- m = (vgbs & DOCK_MODE_FLAG) ? 1 : 0; ++ m = (vgbs & VGBS_DOCK_MODE_FLAG) ? 1 : 0; + input_report_switch(priv->input_dev, SW_DOCK, m); + } + +@@ -167,20 +171,54 @@ static bool intel_vbtn_has_buttons(acpi_handle handle) + return ACPI_SUCCESS(status); + } + ++/* ++ * There are several laptops (non 2-in-1) models out there which support VGBS, ++ * but simply always return 0, which we translate to SW_TABLET_MODE=1. This in ++ * turn causes userspace (libinput) to suppress events from the builtin ++ * keyboard and touchpad, making the laptop essentially unusable. ++ * ++ * Since the problem of wrongly reporting SW_TABLET_MODE=1 in combination ++ * with libinput, leads to a non-usable system. Where as OTOH many people will ++ * not even notice when SW_TABLET_MODE is not being reported, a DMI based allow ++ * list is used here. This list mainly matches on the chassis-type of 2-in-1s. ++ * ++ * There are also some 2-in-1s which use the intel-vbtn ACPI interface to report ++ * SW_TABLET_MODE with a chassis-type of 8 ("Portable") or 10 ("Notebook"), ++ * these are matched on a per model basis, since many normal laptops with a ++ * possible broken VGBS ACPI-method also use these chassis-types. ++ */ ++static const struct dmi_system_id dmi_switches_allow_list[] = { ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "31" /* Convertible */), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "32" /* Detachable */), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7130"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "HP Stream x360 Convertible PC 11"), ++ }, ++ }, ++ {} /* Array terminator */ ++}; ++ + static bool intel_vbtn_has_switches(acpi_handle handle) + { +- const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); + unsigned long long vgbs; + acpi_status status; + +- /* +- * Some normal laptops have a VGBS method despite being non-convertible +- * and their VGBS method always returns 0, causing detect_tablet_mode() +- * to report SW_TABLET_MODE=1 to userspace, which causes issues. +- * These laptops have a DMI chassis_type of 9 ("Laptop"), do not report +- * switches on any devices with a DMI chassis_type of 9. +- */ +- if (chassis_type && strcmp(chassis_type, "9") == 0) ++ if (!dmi_check_system(dmi_switches_allow_list)) + return false; + + status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs); +diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c +index 0f6fceda5fc0b..fe8386ab363c9 100644 +--- a/drivers/platform/x86/thinkpad_acpi.c ++++ b/drivers/platform/x86/thinkpad_acpi.c +@@ -2569,7 +2569,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, + */ + static int hotkey_kthread(void *data) + { +- struct tp_nvram_state s[2]; ++ struct tp_nvram_state s[2] = { 0 }; + u32 poll_mask, event_mask; + unsigned int si, so; + unsigned long t; +@@ -6829,8 +6829,10 @@ static int __init tpacpi_query_bcl_levels(acpi_handle handle) + list_for_each_entry(child, &device->children, node) { + acpi_status status = acpi_evaluate_object(child->handle, "_BCL", + NULL, &buffer); +- if (ACPI_FAILURE(status)) ++ if (ACPI_FAILURE(status)) { ++ buffer.length = ACPI_ALLOCATE_BUFFER; + continue; ++ } + + obj = (union acpi_object *)buffer.pointer; + if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { +diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c +index 31bb3647a99c3..8e74654c1b271 100644 +--- a/drivers/tty/vt/selection.c ++++ b/drivers/tty/vt/selection.c +@@ -193,7 +193,7 @@ static int vc_selection_store_chars(struct vc_data *vc, bool unicode) + /* Allocate a new buffer before freeing the old one ... */ + /* chars can take up to 4 bytes with unicode */ + bp = kmalloc_array((vc_sel.end - vc_sel.start) / 2 + 1, unicode ? 4 : 1, +- GFP_KERNEL); ++ GFP_KERNEL | __GFP_NOWARN); + if (!bp) { + printk(KERN_WARNING "selection: kmalloc() failed\n"); + clear_selection(); +diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c +index a54b60d6623f0..e172c2efc663c 100644 +--- a/drivers/vhost/vdpa.c ++++ b/drivers/vhost/vdpa.c +@@ -527,6 +527,9 @@ static int vhost_vdpa_map(struct vhost_vdpa *v, + r = iommu_map(v->domain, iova, pa, size, + perm_to_iommu_flags(perm)); + ++ if (r) ++ vhost_iotlb_del_range(dev->iotlb, iova, iova + size - 1); ++ + return r; + } + +@@ -552,21 +555,19 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, + struct vhost_dev *dev = &v->vdev; + struct vhost_iotlb *iotlb = dev->iotlb; + struct page **page_list; +- unsigned long list_size = PAGE_SIZE / sizeof(struct page *); ++ struct vm_area_struct **vmas; + unsigned int gup_flags = FOLL_LONGTERM; +- unsigned long npages, cur_base, map_pfn, last_pfn = 0; +- unsigned long locked, lock_limit, pinned, i; ++ unsigned long map_pfn, last_pfn = 0; ++ unsigned long npages, lock_limit; ++ unsigned long i, nmap = 0; + u64 iova = msg->iova; ++ long pinned; + int ret = 0; + + if (vhost_iotlb_itree_first(iotlb, msg->iova, + msg->iova + msg->size - 1)) + return -EEXIST; + +- page_list = (struct page **) __get_free_page(GFP_KERNEL); +- if (!page_list) +- return -ENOMEM; +- + if (msg->perm & VHOST_ACCESS_WO) + gup_flags |= FOLL_WRITE; + +@@ -574,61 +575,86 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, + if (!npages) + return -EINVAL; + ++ page_list = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); ++ vmas = kvmalloc_array(npages, sizeof(struct vm_area_struct *), ++ GFP_KERNEL); ++ if (!page_list || !vmas) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ + mmap_read_lock(dev->mm); + +- locked = atomic64_add_return(npages, &dev->mm->pinned_vm); + lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; +- +- if (locked > lock_limit) { ++ if (npages + atomic64_read(&dev->mm->pinned_vm) > lock_limit) { + ret = -ENOMEM; +- goto out; ++ goto unlock; + } + +- cur_base = msg->uaddr & PAGE_MASK; +- iova &= PAGE_MASK; ++ pinned = pin_user_pages(msg->uaddr & PAGE_MASK, npages, gup_flags, ++ page_list, vmas); ++ if (npages != pinned) { ++ if (pinned < 0) { ++ ret = pinned; ++ } else { ++ unpin_user_pages(page_list, pinned); ++ ret = -ENOMEM; ++ } ++ goto unlock; ++ } + +- while (npages) { +- pinned = min_t(unsigned long, npages, list_size); +- ret = pin_user_pages(cur_base, pinned, +- gup_flags, page_list, NULL); +- if (ret != pinned) +- goto out; +- +- if (!last_pfn) +- map_pfn = page_to_pfn(page_list[0]); +- +- for (i = 0; i < ret; i++) { +- unsigned long this_pfn = page_to_pfn(page_list[i]); +- u64 csize; +- +- if (last_pfn && (this_pfn != last_pfn + 1)) { +- /* Pin a contiguous chunk of memory */ +- csize = (last_pfn - map_pfn + 1) << PAGE_SHIFT; +- if (vhost_vdpa_map(v, iova, csize, +- map_pfn << PAGE_SHIFT, +- msg->perm)) +- goto out; +- map_pfn = this_pfn; +- iova += csize; ++ iova &= PAGE_MASK; ++ map_pfn = page_to_pfn(page_list[0]); ++ ++ /* One more iteration to avoid extra vdpa_map() call out of loop. */ ++ for (i = 0; i <= npages; i++) { ++ unsigned long this_pfn; ++ u64 csize; ++ ++ /* The last chunk may have no valid PFN next to it */ ++ this_pfn = i < npages ? page_to_pfn(page_list[i]) : -1UL; ++ ++ if (last_pfn && (this_pfn == -1UL || ++ this_pfn != last_pfn + 1)) { ++ /* Pin a contiguous chunk of memory */ ++ csize = last_pfn - map_pfn + 1; ++ ret = vhost_vdpa_map(v, iova, csize << PAGE_SHIFT, ++ map_pfn << PAGE_SHIFT, ++ msg->perm); ++ if (ret) { ++ /* ++ * Unpin the rest chunks of memory on the ++ * flight with no corresponding vdpa_map() ++ * calls having been made yet. On the other ++ * hand, vdpa_unmap() in the failure path ++ * is in charge of accounting the number of ++ * pinned pages for its own. ++ * This asymmetrical pattern of accounting ++ * is for efficiency to pin all pages at ++ * once, while there is no other callsite ++ * of vdpa_map() than here above. ++ */ ++ unpin_user_pages(&page_list[nmap], ++ npages - nmap); ++ goto out; + } +- +- last_pfn = this_pfn; ++ atomic64_add(csize, &dev->mm->pinned_vm); ++ nmap += csize; ++ iova += csize << PAGE_SHIFT; ++ map_pfn = this_pfn; + } +- +- cur_base += ret << PAGE_SHIFT; +- npages -= ret; ++ last_pfn = this_pfn; + } + +- /* Pin the rest chunk */ +- ret = vhost_vdpa_map(v, iova, (last_pfn - map_pfn + 1) << PAGE_SHIFT, +- map_pfn << PAGE_SHIFT, msg->perm); ++ WARN_ON(nmap != npages); + out: +- if (ret) { ++ if (ret) + vhost_vdpa_unmap(v, msg->iova, msg->size); +- atomic64_sub(npages, &dev->mm->pinned_vm); +- } ++unlock: + mmap_read_unlock(dev->mm); +- free_page((unsigned long)page_list); ++free: ++ kvfree(vmas); ++ kvfree(page_list); + return ret; + } + +diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c +index d7b8df3edffcf..5f0030e086888 100644 +--- a/drivers/vhost/vhost.c ++++ b/drivers/vhost/vhost.c +@@ -1283,6 +1283,11 @@ static bool vq_access_ok(struct vhost_virtqueue *vq, unsigned int num, + vring_used_t __user *used) + + { ++ /* If an IOTLB device is present, the vring addresses are ++ * GIOVAs. Access validation occurs at prefetch time. */ ++ if (vq->iotlb) ++ return true; ++ + return access_ok(desc, vhost_get_desc_size(vq, num)) && + access_ok(avail, vhost_get_avail_size(vq, num)) && + access_ok(used, vhost_get_used_size(vq, num)); +@@ -1376,10 +1381,6 @@ bool vhost_vq_access_ok(struct vhost_virtqueue *vq) + if (!vq_log_access_ok(vq, vq->log_base)) + return false; + +- /* Access validation occurs at prefetch time with IOTLB */ +- if (vq->iotlb) +- return true; +- + return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used); + } + EXPORT_SYMBOL_GPL(vhost_vq_access_ok); +@@ -1511,8 +1512,7 @@ static long vhost_vring_set_addr(struct vhost_dev *d, + /* Also validate log access for used ring if enabled. */ + if ((a.flags & (0x1 << VHOST_VRING_F_LOG)) && + !log_access_ok(vq->log_base, a.log_guest_addr, +- sizeof *vq->used + +- vq->num * sizeof *vq->used->ring)) ++ vhost_get_used_size(vq, vq->num))) + return -EINVAL; + } + +diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c +index df3c52d721597..8653950c56d45 100644 +--- a/drivers/video/console/newport_con.c ++++ b/drivers/video/console/newport_con.c +@@ -35,12 +35,6 @@ + + #define FONT_DATA ((unsigned char *)font_vga_8x16.data) + +-/* borrowed from fbcon.c */ +-#define REFCOUNT(fd) (((int *)(fd))[-1]) +-#define FNTSIZE(fd) (((int *)(fd))[-2]) +-#define FNTCHARCNT(fd) (((int *)(fd))[-3]) +-#define FONT_EXTRA_WORDS 3 +- + static unsigned char *font_data[MAX_NR_CONSOLES]; + + static struct newport_regs *npregs; +@@ -522,6 +516,7 @@ static int newport_set_font(int unit, struct console_font *op) + FNTSIZE(new_data) = size; + FNTCHARCNT(new_data) = op->charcount; + REFCOUNT(new_data) = 0; /* usage counter */ ++ FNTSUM(new_data) = 0; + + p = new_data; + for (i = 0; i < op->charcount; i++) { +diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c +index 09cb46e94f405..3e82f632841d9 100644 +--- a/drivers/video/fbdev/core/fbcon.c ++++ b/drivers/video/fbdev/core/fbcon.c +@@ -2299,6 +2299,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) + + if (font->width <= 8) { + j = vc->vc_font.height; ++ if (font->charcount * j > FNTSIZE(fontdata)) ++ return -EINVAL; ++ + for (i = 0; i < font->charcount; i++) { + memcpy(data, fontdata, j); + memset(data + j, 0, 32 - j); +@@ -2307,6 +2310,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) + } + } else if (font->width <= 16) { + j = vc->vc_font.height * 2; ++ if (font->charcount * j > FNTSIZE(fontdata)) ++ return -EINVAL; ++ + for (i = 0; i < font->charcount; i++) { + memcpy(data, fontdata, j); + memset(data + j, 0, 64 - j); +@@ -2314,6 +2320,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) + fontdata += j; + } + } else if (font->width <= 24) { ++ if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata)) ++ return -EINVAL; ++ + for (i = 0; i < font->charcount; i++) { + for (j = 0; j < vc->vc_font.height; j++) { + *data++ = fontdata[0]; +@@ -2326,6 +2335,9 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) + } + } else { + j = vc->vc_font.height * 4; ++ if (font->charcount * j > FNTSIZE(fontdata)) ++ return -EINVAL; ++ + for (i = 0; i < font->charcount; i++) { + memcpy(data, fontdata, j); + memset(data + j, 0, 128 - j); +diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h +index 78bb14c03643e..9315b360c8981 100644 +--- a/drivers/video/fbdev/core/fbcon.h ++++ b/drivers/video/fbdev/core/fbcon.h +@@ -152,13 +152,6 @@ static inline int attr_col_ec(int shift, struct vc_data *vc, + #define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0) + #define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1) + +-/* Font */ +-#define REFCOUNT(fd) (((int *)(fd))[-1]) +-#define FNTSIZE(fd) (((int *)(fd))[-2]) +-#define FNTCHARCNT(fd) (((int *)(fd))[-3]) +-#define FNTSUM(fd) (((int *)(fd))[-4]) +-#define FONT_EXTRA_WORDS 4 +- + /* + * Scroll Method + */ +diff --git a/drivers/video/fbdev/core/fbcon_rotate.c b/drivers/video/fbdev/core/fbcon_rotate.c +index c0d445294aa7c..ac72d4f85f7d0 100644 +--- a/drivers/video/fbdev/core/fbcon_rotate.c ++++ b/drivers/video/fbdev/core/fbcon_rotate.c +@@ -14,6 +14,7 @@ + #include <linux/fb.h> + #include <linux/vt_kern.h> + #include <linux/console.h> ++#include <linux/font.h> + #include <asm/types.h> + #include "fbcon.h" + #include "fbcon_rotate.h" +diff --git a/drivers/video/fbdev/core/tileblit.c b/drivers/video/fbdev/core/tileblit.c +index eb664dbf96f66..adff8d6ffe6f9 100644 +--- a/drivers/video/fbdev/core/tileblit.c ++++ b/drivers/video/fbdev/core/tileblit.c +@@ -13,6 +13,7 @@ + #include <linux/fb.h> + #include <linux/vt_kern.h> + #include <linux/console.h> ++#include <linux/font.h> + #include <asm/types.h> + #include "fbcon.h" + +diff --git a/fs/afs/inode.c b/fs/afs/inode.c +index 1d13d2e882ada..0fe8844b4bee2 100644 +--- a/fs/afs/inode.c ++++ b/fs/afs/inode.c +@@ -810,14 +810,32 @@ void afs_evict_inode(struct inode *inode) + + static void afs_setattr_success(struct afs_operation *op) + { +- struct inode *inode = &op->file[0].vnode->vfs_inode; ++ struct afs_vnode_param *vp = &op->file[0]; ++ struct inode *inode = &vp->vnode->vfs_inode; ++ loff_t old_i_size = i_size_read(inode); ++ ++ op->setattr.old_i_size = old_i_size; ++ afs_vnode_commit_status(op, vp); ++ /* inode->i_size has now been changed. */ ++ ++ if (op->setattr.attr->ia_valid & ATTR_SIZE) { ++ loff_t size = op->setattr.attr->ia_size; ++ if (size > old_i_size) ++ pagecache_isize_extended(inode, old_i_size, size); ++ } ++} ++ ++static void afs_setattr_edit_file(struct afs_operation *op) ++{ ++ struct afs_vnode_param *vp = &op->file[0]; ++ struct inode *inode = &vp->vnode->vfs_inode; + +- afs_vnode_commit_status(op, &op->file[0]); + if (op->setattr.attr->ia_valid & ATTR_SIZE) { +- loff_t i_size = inode->i_size, size = op->setattr.attr->ia_size; +- if (size > i_size) +- pagecache_isize_extended(inode, i_size, size); +- truncate_pagecache(inode, size); ++ loff_t size = op->setattr.attr->ia_size; ++ loff_t i_size = op->setattr.old_i_size; ++ ++ if (size < i_size) ++ truncate_pagecache(inode, size); + } + } + +@@ -825,6 +843,7 @@ static const struct afs_operation_ops afs_setattr_operation = { + .issue_afs_rpc = afs_fs_setattr, + .issue_yfs_rpc = yfs_fs_setattr, + .success = afs_setattr_success, ++ .edit_dir = afs_setattr_edit_file, + }; + + /* +@@ -863,11 +882,16 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr) + if (S_ISREG(vnode->vfs_inode.i_mode)) + filemap_write_and_wait(vnode->vfs_inode.i_mapping); + ++ /* Prevent any new writebacks from starting whilst we do this. */ ++ down_write(&vnode->validate_lock); ++ + op = afs_alloc_operation(((attr->ia_valid & ATTR_FILE) ? + afs_file_key(attr->ia_file) : NULL), + vnode->volume); +- if (IS_ERR(op)) +- return PTR_ERR(op); ++ if (IS_ERR(op)) { ++ ret = PTR_ERR(op); ++ goto out_unlock; ++ } + + afs_op_set_vnode(op, 0, vnode); + op->setattr.attr = attr; +@@ -880,5 +904,10 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr) + op->file[0].update_ctime = 1; + + op->ops = &afs_setattr_operation; +- return afs_do_sync_operation(op); ++ ret = afs_do_sync_operation(op); ++ ++out_unlock: ++ up_write(&vnode->validate_lock); ++ _leave(" = %d", ret); ++ return ret; + } +diff --git a/fs/afs/internal.h b/fs/afs/internal.h +index 792ac711985eb..e1ebead2e505a 100644 +--- a/fs/afs/internal.h ++++ b/fs/afs/internal.h +@@ -810,6 +810,7 @@ struct afs_operation { + } store; + struct { + struct iattr *attr; ++ loff_t old_i_size; + } setattr; + struct afs_acl *acl; + struct yfs_acl *yacl; +diff --git a/fs/afs/write.c b/fs/afs/write.c +index a121c247d95a3..0a98cf36e78a3 100644 +--- a/fs/afs/write.c ++++ b/fs/afs/write.c +@@ -738,11 +738,21 @@ static int afs_writepages_region(struct address_space *mapping, + int afs_writepages(struct address_space *mapping, + struct writeback_control *wbc) + { ++ struct afs_vnode *vnode = AFS_FS_I(mapping->host); + pgoff_t start, end, next; + int ret; + + _enter(""); + ++ /* We have to be careful as we can end up racing with setattr() ++ * truncating the pagecache since the caller doesn't take a lock here ++ * to prevent it. ++ */ ++ if (wbc->sync_mode == WB_SYNC_ALL) ++ down_read(&vnode->validate_lock); ++ else if (!down_read_trylock(&vnode->validate_lock)) ++ return 0; ++ + if (wbc->range_cyclic) { + start = mapping->writeback_index; + end = -1; +@@ -762,6 +772,7 @@ int afs_writepages(struct address_space *mapping, + ret = afs_writepages_region(mapping, wbc, start, end, &next); + } + ++ up_read(&vnode->validate_lock); + _leave(" = %d", ret); + return ret; + } +diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c +index eb86e4b88c73a..e4a1c6afe35dc 100644 +--- a/fs/btrfs/dev-replace.c ++++ b/fs/btrfs/dev-replace.c +@@ -783,7 +783,9 @@ error: + /* replace the sysfs entry */ + btrfs_sysfs_remove_devices_dir(fs_info->fs_devices, src_device); + btrfs_sysfs_update_devid(tgt_device); +- btrfs_rm_dev_replace_free_srcdev(src_device); ++ if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &src_device->dev_state)) ++ btrfs_scratch_superblocks(fs_info, src_device->bdev, ++ src_device->name->str); + + /* write back the superblocks */ + trans = btrfs_start_transaction(root, 0); +@@ -792,6 +794,8 @@ error: + + mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); + ++ btrfs_rm_dev_replace_free_srcdev(src_device); ++ + return 0; + } + +diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c +index 956eb0d6bc584..79e9a80bd37a0 100644 +--- a/fs/btrfs/volumes.c ++++ b/fs/btrfs/volumes.c +@@ -1999,9 +1999,9 @@ static u64 btrfs_num_devices(struct btrfs_fs_info *fs_info) + return num_devices; + } + +-static void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info, +- struct block_device *bdev, +- const char *device_path) ++void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info, ++ struct block_device *bdev, ++ const char *device_path) + { + struct btrfs_super_block *disk_super; + int copy_num; +@@ -2224,11 +2224,7 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_device *srcdev) + struct btrfs_fs_info *fs_info = srcdev->fs_info; + struct btrfs_fs_devices *fs_devices = srcdev->fs_devices; + +- if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &srcdev->dev_state)) { +- /* zero out the old super if it is writable */ +- btrfs_scratch_superblocks(fs_info, srcdev->bdev, +- srcdev->name->str); +- } ++ mutex_lock(&uuid_mutex); + + btrfs_close_bdev(srcdev); + synchronize_rcu(); +@@ -2258,6 +2254,7 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_device *srcdev) + close_fs_devices(fs_devices); + free_fs_devices(fs_devices); + } ++ mutex_unlock(&uuid_mutex); + } + + void btrfs_destroy_dev_replace_tgtdev(struct btrfs_device *tgtdev) +diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h +index 75af2334b2e37..83862e27f5663 100644 +--- a/fs/btrfs/volumes.h ++++ b/fs/btrfs/volumes.h +@@ -573,6 +573,9 @@ void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info); + void btrfs_reset_fs_info_ptr(struct btrfs_fs_info *fs_info); + bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info, + struct btrfs_device *failing_dev); ++void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info, ++ struct block_device *bdev, ++ const char *device_path); + + int btrfs_bg_type_to_factor(u64 flags); + const char *btrfs_bg_type_to_raid_name(u64 flags); +diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c +index 32f90dc82c840..d44df8f95bcd4 100644 +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -1208,7 +1208,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + rqst[1].rq_iov = si_iov; + rqst[1].rq_nvec = 1; + +- len = sizeof(ea) + ea_name_len + ea_value_len + 1; ++ len = sizeof(*ea) + ea_name_len + ea_value_len + 1; + ea = kzalloc(len, GFP_KERNEL); + if (ea == NULL) { + rc = -ENOMEM; +diff --git a/fs/exfat/cache.c b/fs/exfat/cache.c +index 03d0824fc368a..5a2f119b7e8c7 100644 +--- a/fs/exfat/cache.c ++++ b/fs/exfat/cache.c +@@ -17,7 +17,6 @@ + #include "exfat_raw.h" + #include "exfat_fs.h" + +-#define EXFAT_CACHE_VALID 0 + #define EXFAT_MAX_CACHE 16 + + struct exfat_cache { +@@ -61,16 +60,6 @@ void exfat_cache_shutdown(void) + kmem_cache_destroy(exfat_cachep); + } + +-void exfat_cache_init_inode(struct inode *inode) +-{ +- struct exfat_inode_info *ei = EXFAT_I(inode); +- +- spin_lock_init(&ei->cache_lru_lock); +- ei->nr_caches = 0; +- ei->cache_valid_id = EXFAT_CACHE_VALID + 1; +- INIT_LIST_HEAD(&ei->cache_lru); +-} +- + static inline struct exfat_cache *exfat_cache_alloc(void) + { + return kmem_cache_alloc(exfat_cachep, GFP_NOFS); +diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h +index 75c7bdbeba6d3..fb49928687bb5 100644 +--- a/fs/exfat/exfat_fs.h ++++ b/fs/exfat/exfat_fs.h +@@ -250,6 +250,8 @@ struct exfat_sb_info { + struct rcu_head rcu; + }; + ++#define EXFAT_CACHE_VALID 0 ++ + /* + * EXFAT file system inode in-memory data + */ +@@ -429,7 +431,6 @@ extern const struct dentry_operations exfat_utf8_dentry_ops; + /* cache.c */ + int exfat_cache_init(void); + void exfat_cache_shutdown(void); +-void exfat_cache_init_inode(struct inode *inode); + void exfat_cache_inval_inode(struct inode *inode); + int exfat_get_cluster(struct inode *inode, unsigned int cluster, + unsigned int *fclus, unsigned int *dclus, +diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c +index cf9ca6c4d046e..1952b88e14dbd 100644 +--- a/fs/exfat/inode.c ++++ b/fs/exfat/inode.c +@@ -610,8 +610,6 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) + ei->i_crtime = info->crtime; + inode->i_atime = info->atime; + +- exfat_cache_init_inode(inode); +- + return 0; + } + +diff --git a/fs/exfat/super.c b/fs/exfat/super.c +index 253a92460d522..142f3459c7ca7 100644 +--- a/fs/exfat/super.c ++++ b/fs/exfat/super.c +@@ -361,7 +361,6 @@ static int exfat_read_root(struct inode *inode) + inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime = + current_time(inode); + exfat_truncate_atime(&inode->i_atime); +- exfat_cache_init_inode(inode); + return 0; + } + +@@ -747,6 +746,10 @@ static void exfat_inode_init_once(void *foo) + { + struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; + ++ spin_lock_init(&ei->cache_lru_lock); ++ ei->nr_caches = 0; ++ ei->cache_valid_id = EXFAT_CACHE_VALID + 1; ++ INIT_LIST_HEAD(&ei->cache_lru); + INIT_HLIST_NODE(&ei->i_hash_fat); + inode_init_once(&ei->vfs_inode); + } +diff --git a/fs/io_uring.c b/fs/io_uring.c +index ebc3586b18795..d2bb2ae9551f0 100644 +--- a/fs/io_uring.c ++++ b/fs/io_uring.c +@@ -7998,11 +7998,19 @@ static int io_uring_show_cred(int id, void *p, void *data) + + static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) + { ++ bool has_lock; + int i; + +- mutex_lock(&ctx->uring_lock); ++ /* ++ * Avoid ABBA deadlock between the seq lock and the io_uring mutex, ++ * since fdinfo case grabs it in the opposite direction of normal use ++ * cases. If we fail to get the lock, we just don't iterate any ++ * structures that could be going away outside the io_uring mutex. ++ */ ++ has_lock = mutex_trylock(&ctx->uring_lock); ++ + seq_printf(m, "UserFiles:\t%u\n", ctx->nr_user_files); +- for (i = 0; i < ctx->nr_user_files; i++) { ++ for (i = 0; has_lock && i < ctx->nr_user_files; i++) { + struct fixed_file_table *table; + struct file *f; + +@@ -8014,13 +8022,13 @@ static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) + seq_printf(m, "%5u: <none>\n", i); + } + seq_printf(m, "UserBufs:\t%u\n", ctx->nr_user_bufs); +- for (i = 0; i < ctx->nr_user_bufs; i++) { ++ for (i = 0; has_lock && i < ctx->nr_user_bufs; i++) { + struct io_mapped_ubuf *buf = &ctx->user_bufs[i]; + + seq_printf(m, "%5u: 0x%llx/%u\n", i, buf->ubuf, + (unsigned int) buf->len); + } +- if (!idr_is_empty(&ctx->personality_idr)) { ++ if (has_lock && !idr_is_empty(&ctx->personality_idr)) { + seq_printf(m, "Personalities:\n"); + idr_for_each(&ctx->personality_idr, io_uring_show_cred, m); + } +@@ -8035,7 +8043,8 @@ static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) + req->task->task_works != NULL); + } + spin_unlock_irq(&ctx->completion_lock); +- mutex_unlock(&ctx->uring_lock); ++ if (has_lock) ++ mutex_unlock(&ctx->uring_lock); + } + + static void io_uring_show_fdinfo(struct seq_file *m, struct file *f) +diff --git a/fs/pipe.c b/fs/pipe.c +index 117db82b10af5..0ac197658a2d6 100644 +--- a/fs/pipe.c ++++ b/fs/pipe.c +@@ -894,19 +894,18 @@ int create_pipe_files(struct file **res, int flags) + { + struct inode *inode = get_pipe_inode(); + struct file *f; ++ int error; + + if (!inode) + return -ENFILE; + + if (flags & O_NOTIFICATION_PIPE) { +-#ifdef CONFIG_WATCH_QUEUE +- if (watch_queue_init(inode->i_pipe) < 0) { ++ error = watch_queue_init(inode->i_pipe); ++ if (error) { ++ free_pipe_info(inode->i_pipe); + iput(inode); +- return -ENOMEM; ++ return error; + } +-#else +- return -ENOPKG; +-#endif + } + + f = alloc_file_pseudo(inode, pipe_mnt, "", +diff --git a/fs/splice.c b/fs/splice.c +index c3d00dfc73446..ce75aec522744 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -526,6 +526,22 @@ static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_des + return 1; + } + ++/* We know we have a pipe buffer, but maybe it's empty? */ ++static inline bool eat_empty_buffer(struct pipe_inode_info *pipe) ++{ ++ unsigned int tail = pipe->tail; ++ unsigned int mask = pipe->ring_size - 1; ++ struct pipe_buffer *buf = &pipe->bufs[tail & mask]; ++ ++ if (unlikely(!buf->len)) { ++ pipe_buf_release(pipe, buf); ++ pipe->tail = tail+1; ++ return true; ++ } ++ ++ return false; ++} ++ + /** + * splice_from_pipe_next - wait for some data to splice from + * @pipe: pipe to splice from +@@ -545,6 +561,7 @@ static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_des + if (signal_pending(current)) + return -ERESTARTSYS; + ++repeat: + while (pipe_empty(pipe->head, pipe->tail)) { + if (!pipe->writers) + return 0; +@@ -566,6 +583,9 @@ static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_des + pipe_wait_readable(pipe); + } + ++ if (eat_empty_buffer(pipe)) ++ goto repeat; ++ + return 1; + } + +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index 9e9b1ec30b902..d7eb9e7689b45 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -641,7 +641,7 @@ + #define BTF \ + .BTF : AT(ADDR(.BTF) - LOAD_OFFSET) { \ + __start_BTF = .; \ +- *(.BTF) \ ++ KEEP(*(.BTF)) \ + __stop_BTF = .; \ + } + #else +diff --git a/include/linux/font.h b/include/linux/font.h +index 51b91c8b69d58..59faa80f586df 100644 +--- a/include/linux/font.h ++++ b/include/linux/font.h +@@ -59,4 +59,17 @@ extern const struct font_desc *get_default_font(int xres, int yres, + /* Max. length for the name of a predefined font */ + #define MAX_FONT_NAME 32 + ++/* Extra word getters */ ++#define REFCOUNT(fd) (((int *)(fd))[-1]) ++#define FNTSIZE(fd) (((int *)(fd))[-2]) ++#define FNTCHARCNT(fd) (((int *)(fd))[-3]) ++#define FNTSUM(fd) (((int *)(fd))[-4]) ++ ++#define FONT_EXTRA_WORDS 4 ++ ++struct font_data { ++ unsigned int extra[FONT_EXTRA_WORDS]; ++ const unsigned char data[]; ++} __packed; ++ + #endif /* _VIDEO_FONT_H */ +diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h +index bc45ea1efbf79..c941b73773216 100644 +--- a/include/linux/khugepaged.h ++++ b/include/linux/khugepaged.h +@@ -15,6 +15,7 @@ extern int __khugepaged_enter(struct mm_struct *mm); + extern void __khugepaged_exit(struct mm_struct *mm); + extern int khugepaged_enter_vma_merge(struct vm_area_struct *vma, + unsigned long vm_flags); ++extern void khugepaged_min_free_kbytes_update(void); + #ifdef CONFIG_SHMEM + extern void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr); + #else +@@ -85,6 +86,10 @@ static inline void collapse_pte_mapped_thp(struct mm_struct *mm, + unsigned long addr) + { + } ++ ++static inline void khugepaged_min_free_kbytes_update(void) ++{ ++} + #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + + #endif /* _LINUX_KHUGEPAGED_H */ +diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h +index 1e6ca716635a9..484cd8ba869c5 100644 +--- a/include/linux/mlx5/driver.h ++++ b/include/linux/mlx5/driver.h +@@ -764,6 +764,8 @@ struct mlx5_cmd_work_ent { + u64 ts2; + u16 op; + bool polling; ++ /* Track the max comp handlers */ ++ refcount_t refcnt; + }; + + struct mlx5_pas { +diff --git a/include/linux/net.h b/include/linux/net.h +index 016a9c5faa347..5fab2dcd3364c 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -21,6 +21,7 @@ + #include <linux/rcupdate.h> + #include <linux/once.h> + #include <linux/fs.h> ++#include <linux/mm.h> + + #include <uapi/linux/net.h> + +@@ -290,6 +291,21 @@ do { \ + #define net_get_random_once_wait(buf, nbytes) \ + get_random_once_wait((buf), (nbytes)) + ++/* ++ * E.g. XFS meta- & log-data is in slab pages, or bcache meta ++ * data pages, or other high order pages allocated by ++ * __get_free_pages() without __GFP_COMP, which have a page_count ++ * of 0 and/or have PageSlab() set. We cannot use send_page for ++ * those, as that does get_page(); put_page(); and would cause ++ * either a VM_BUG directly, or __page_cache_release a page that ++ * would actually still be referenced by someone, leading to some ++ * obscure delayed Oops somewhere else. ++ */ ++static inline bool sendpage_ok(struct page *page) ++{ ++ return !PageSlab(page) && page_count(page) >= 1; ++} ++ + int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, + size_t num, size_t len); + int kernel_sendmsg_locked(struct sock *sk, struct msghdr *msg, +diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h +index cf2468da68e91..601ad6957bb9d 100644 +--- a/include/linux/pagemap.h ++++ b/include/linux/pagemap.h +@@ -54,7 +54,8 @@ static inline void mapping_set_error(struct address_space *mapping, int error) + __filemap_set_wb_err(mapping, error); + + /* Record it in superblock */ +- errseq_set(&mapping->host->i_sb->s_wb_err, error); ++ if (mapping->host) ++ errseq_set(&mapping->host->i_sb->s_wb_err, error); + + /* Record it in flags for now, for legacy callers */ + if (error == -ENOSPC) +diff --git a/include/linux/watch_queue.h b/include/linux/watch_queue.h +index 5e08db2adc319..c994d1b2cdbaa 100644 +--- a/include/linux/watch_queue.h ++++ b/include/linux/watch_queue.h +@@ -122,6 +122,12 @@ static inline void remove_watch_list(struct watch_list *wlist, u64 id) + */ + #define watch_sizeof(STRUCT) (sizeof(STRUCT) << WATCH_INFO_LENGTH__SHIFT) + ++#else ++static inline int watch_queue_init(struct pipe_inode_info *pipe) ++{ ++ return -ENOPKG; ++} ++ + #endif + + #endif /* _LINUX_WATCH_QUEUE_H */ +diff --git a/include/net/act_api.h b/include/net/act_api.h +index 8c39348806705..7dc88ebb6e3e7 100644 +--- a/include/net/act_api.h ++++ b/include/net/act_api.h +@@ -166,8 +166,6 @@ int tcf_idr_create_from_flags(struct tc_action_net *tn, u32 index, + struct nlattr *est, struct tc_action **a, + const struct tc_action_ops *ops, int bind, + u32 flags); +-void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a); +- + void tcf_idr_cleanup(struct tc_action_net *tn, u32 index); + int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index, + struct tc_action **a, int bind); +diff --git a/include/net/netlink.h b/include/net/netlink.h +index c0411f14fb53b..6df0c91870a3e 100644 +--- a/include/net/netlink.h ++++ b/include/net/netlink.h +@@ -1936,7 +1936,8 @@ void nla_get_range_signed(const struct nla_policy *pt, + int netlink_policy_dump_start(const struct nla_policy *policy, + unsigned int maxtype, + unsigned long *state); +-bool netlink_policy_dump_loop(unsigned long *state); ++bool netlink_policy_dump_loop(unsigned long state); + int netlink_policy_dump_write(struct sk_buff *skb, unsigned long state); ++void netlink_policy_dump_free(unsigned long state); + + #endif +diff --git a/include/net/xfrm.h b/include/net/xfrm.h +index 51f65d23ebafa..2e32cb10ac16b 100644 +--- a/include/net/xfrm.h ++++ b/include/net/xfrm.h +@@ -1767,21 +1767,17 @@ static inline unsigned int xfrm_replay_state_esn_len(struct xfrm_replay_state_es + static inline int xfrm_replay_clone(struct xfrm_state *x, + struct xfrm_state *orig) + { +- x->replay_esn = kzalloc(xfrm_replay_state_esn_len(orig->replay_esn), ++ ++ x->replay_esn = kmemdup(orig->replay_esn, ++ xfrm_replay_state_esn_len(orig->replay_esn), + GFP_KERNEL); + if (!x->replay_esn) + return -ENOMEM; +- +- x->replay_esn->bmp_len = orig->replay_esn->bmp_len; +- x->replay_esn->replay_window = orig->replay_esn->replay_window; +- +- x->preplay_esn = kmemdup(x->replay_esn, +- xfrm_replay_state_esn_len(x->replay_esn), ++ x->preplay_esn = kmemdup(orig->preplay_esn, ++ xfrm_replay_state_esn_len(orig->preplay_esn), + GFP_KERNEL); +- if (!x->preplay_esn) { +- kfree(x->replay_esn); ++ if (!x->preplay_esn) + return -ENOMEM; +- } + + return 0; + } +diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h +index 4953e9994df34..8e174a24c5757 100644 +--- a/include/soc/mscc/ocelot.h ++++ b/include/soc/mscc/ocelot.h +@@ -468,6 +468,7 @@ struct ocelot; + + struct ocelot_ops { + int (*reset)(struct ocelot *ocelot); ++ u16 (*wm_enc)(u16 value); + }; + + struct ocelot_acl_block { +diff --git a/kernel/bpf/sysfs_btf.c b/kernel/bpf/sysfs_btf.c +index 3b495773de5ae..11b3380887fa0 100644 +--- a/kernel/bpf/sysfs_btf.c ++++ b/kernel/bpf/sysfs_btf.c +@@ -30,15 +30,15 @@ static struct kobject *btf_kobj; + + static int __init btf_vmlinux_init(void) + { +- if (!__start_BTF) ++ bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF; ++ ++ if (!__start_BTF || bin_attr_btf_vmlinux.size == 0) + return 0; + + btf_kobj = kobject_create_and_add("btf", kernel_kobj); + if (!btf_kobj) + return -ENOMEM; + +- bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF; +- + return sysfs_create_bin_file(btf_kobj, &bin_attr_btf_vmlinux); + } + +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index 94cead5a43e57..89b07db146763 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -5490,8 +5490,8 @@ static void scalar32_min_max_or(struct bpf_reg_state *dst_reg, + bool src_known = tnum_subreg_is_const(src_reg->var_off); + bool dst_known = tnum_subreg_is_const(dst_reg->var_off); + struct tnum var32_off = tnum_subreg(dst_reg->var_off); +- s32 smin_val = src_reg->smin_value; +- u32 umin_val = src_reg->umin_value; ++ s32 smin_val = src_reg->s32_min_value; ++ u32 umin_val = src_reg->u32_min_value; + + /* Assuming scalar64_min_max_or will be called so it is safe + * to skip updating register for known case. +@@ -5514,8 +5514,8 @@ static void scalar32_min_max_or(struct bpf_reg_state *dst_reg, + /* ORing two positives gives a positive, so safe to + * cast result into s64. + */ +- dst_reg->s32_min_value = dst_reg->umin_value; +- dst_reg->s32_max_value = dst_reg->umax_value; ++ dst_reg->s32_min_value = dst_reg->u32_min_value; ++ dst_reg->s32_max_value = dst_reg->u32_max_value; + } + } + +diff --git a/kernel/events/core.c b/kernel/events/core.c +index 856d98c36f562..fd8cd00099dae 100644 +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -99,7 +99,7 @@ static void remote_function(void *data) + * retry due to any failures in smp_call_function_single(), such as if the + * task_cpu() goes offline concurrently. + * +- * returns @func return value or -ESRCH when the process isn't running ++ * returns @func return value or -ESRCH or -ENXIO when the process isn't running + */ + static int + task_function_call(struct task_struct *p, remote_function_f func, void *info) +@@ -115,7 +115,8 @@ task_function_call(struct task_struct *p, remote_function_f func, void *info) + for (;;) { + ret = smp_call_function_single(task_cpu(p), remote_function, + &data, 1); +- ret = !ret ? data.ret : -EAGAIN; ++ if (!ret) ++ ret = data.ret; + + if (ret != -EAGAIN) + break; +diff --git a/kernel/umh.c b/kernel/umh.c +index 79f139a7ca03c..6aaf456d402d9 100644 +--- a/kernel/umh.c ++++ b/kernel/umh.c +@@ -14,6 +14,7 @@ + #include <linux/cred.h> + #include <linux/file.h> + #include <linux/fdtable.h> ++#include <linux/fs_struct.h> + #include <linux/workqueue.h> + #include <linux/security.h> + #include <linux/mount.h> +@@ -75,6 +76,14 @@ static int call_usermodehelper_exec_async(void *data) + flush_signal_handlers(current, 1); + spin_unlock_irq(¤t->sighand->siglock); + ++ /* ++ * Initial kernel threads share ther FS with init, in order to ++ * get the init root directory. But we've now created a new ++ * thread that is going to execve a user process and has its own ++ * 'struct fs_struct'. Reset umask to the default. ++ */ ++ current->fs->umask = 0022; ++ + /* + * Our parent (unbound workqueue) runs with elevated scheduling + * priority. Avoid propagating that into the userspace child. +diff --git a/lib/fonts/font_10x18.c b/lib/fonts/font_10x18.c +index 532f0ff89a962..0e2deac97da0d 100644 +--- a/lib/fonts/font_10x18.c ++++ b/lib/fonts/font_10x18.c +@@ -8,8 +8,8 @@ + + #define FONTDATAMAX 9216 + +-static const unsigned char fontdata_10x18[FONTDATAMAX] = { +- ++static struct font_data fontdata_10x18 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ +@@ -5129,8 +5129,7 @@ static const unsigned char fontdata_10x18[FONTDATAMAX] = { + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ +- +-}; ++} }; + + + const struct font_desc font_10x18 = { +@@ -5138,7 +5137,7 @@ const struct font_desc font_10x18 = { + .name = "10x18", + .width = 10, + .height = 18, +- .data = fontdata_10x18, ++ .data = fontdata_10x18.data, + #ifdef __sparc__ + .pref = 5, + #else +diff --git a/lib/fonts/font_6x10.c b/lib/fonts/font_6x10.c +index 09b2cc03435b9..87da8acd07db0 100644 +--- a/lib/fonts/font_6x10.c ++++ b/lib/fonts/font_6x10.c +@@ -1,8 +1,10 @@ + // SPDX-License-Identifier: GPL-2.0 + #include <linux/font.h> + +-static const unsigned char fontdata_6x10[] = { ++#define FONTDATAMAX 2560 + ++static struct font_data fontdata_6x10 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +@@ -3074,14 +3076,13 @@ static const unsigned char fontdata_6x10[] = { + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +- +-}; ++} }; + + const struct font_desc font_6x10 = { + .idx = FONT6x10_IDX, + .name = "6x10", + .width = 6, + .height = 10, +- .data = fontdata_6x10, ++ .data = fontdata_6x10.data, + .pref = 0, + }; +diff --git a/lib/fonts/font_6x11.c b/lib/fonts/font_6x11.c +index d7136c33f1f01..5e975dfa10a53 100644 +--- a/lib/fonts/font_6x11.c ++++ b/lib/fonts/font_6x11.c +@@ -9,8 +9,8 @@ + + #define FONTDATAMAX (11*256) + +-static const unsigned char fontdata_6x11[FONTDATAMAX] = { +- ++static struct font_data fontdata_6x11 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +@@ -3338,8 +3338,7 @@ static const unsigned char fontdata_6x11[FONTDATAMAX] = { + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +- +-}; ++} }; + + + const struct font_desc font_vga_6x11 = { +@@ -3347,7 +3346,7 @@ const struct font_desc font_vga_6x11 = { + .name = "ProFont6x11", + .width = 6, + .height = 11, +- .data = fontdata_6x11, ++ .data = fontdata_6x11.data, + /* Try avoiding this font if possible unless on MAC */ + .pref = -2000, + }; +diff --git a/lib/fonts/font_7x14.c b/lib/fonts/font_7x14.c +index 89752d0b23e8b..86d298f385058 100644 +--- a/lib/fonts/font_7x14.c ++++ b/lib/fonts/font_7x14.c +@@ -8,8 +8,8 @@ + + #define FONTDATAMAX 3584 + +-static const unsigned char fontdata_7x14[FONTDATAMAX] = { +- ++static struct font_data fontdata_7x14 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ +@@ -4105,8 +4105,7 @@ static const unsigned char fontdata_7x14[FONTDATAMAX] = { + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ +- +-}; ++} }; + + + const struct font_desc font_7x14 = { +@@ -4114,6 +4113,6 @@ const struct font_desc font_7x14 = { + .name = "7x14", + .width = 7, + .height = 14, +- .data = fontdata_7x14, ++ .data = fontdata_7x14.data, + .pref = 0, + }; +diff --git a/lib/fonts/font_8x16.c b/lib/fonts/font_8x16.c +index b7ab1f5fbdb8a..37cedd36ca5ef 100644 +--- a/lib/fonts/font_8x16.c ++++ b/lib/fonts/font_8x16.c +@@ -10,8 +10,8 @@ + + #define FONTDATAMAX 4096 + +-static const unsigned char fontdata_8x16[FONTDATAMAX] = { +- ++static struct font_data fontdata_8x16 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +@@ -4619,8 +4619,7 @@ static const unsigned char fontdata_8x16[FONTDATAMAX] = { + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +- +-}; ++} }; + + + const struct font_desc font_vga_8x16 = { +@@ -4628,7 +4627,7 @@ const struct font_desc font_vga_8x16 = { + .name = "VGA8x16", + .width = 8, + .height = 16, +- .data = fontdata_8x16, ++ .data = fontdata_8x16.data, + .pref = 0, + }; + EXPORT_SYMBOL(font_vga_8x16); +diff --git a/lib/fonts/font_8x8.c b/lib/fonts/font_8x8.c +index 2328ebc8bab5d..8ab695538395d 100644 +--- a/lib/fonts/font_8x8.c ++++ b/lib/fonts/font_8x8.c +@@ -9,8 +9,8 @@ + + #define FONTDATAMAX 2048 + +-static const unsigned char fontdata_8x8[FONTDATAMAX] = { +- ++static struct font_data fontdata_8x8 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +@@ -2570,8 +2570,7 @@ static const unsigned char fontdata_8x8[FONTDATAMAX] = { + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +- +-}; ++} }; + + + const struct font_desc font_vga_8x8 = { +@@ -2579,6 +2578,6 @@ const struct font_desc font_vga_8x8 = { + .name = "VGA8x8", + .width = 8, + .height = 8, +- .data = fontdata_8x8, ++ .data = fontdata_8x8.data, + .pref = 0, + }; +diff --git a/lib/fonts/font_acorn_8x8.c b/lib/fonts/font_acorn_8x8.c +index 0ff0e85d4481b..069b3e80c4344 100644 +--- a/lib/fonts/font_acorn_8x8.c ++++ b/lib/fonts/font_acorn_8x8.c +@@ -3,7 +3,10 @@ + + #include <linux/font.h> + +-static const unsigned char acorndata_8x8[] = { ++#define FONTDATAMAX 2048 ++ ++static struct font_data acorndata_8x8 = { ++{ 0, 0, FONTDATAMAX, 0 }, { + /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ^@ */ + /* 01 */ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, /* ^A */ + /* 02 */ 0x7e, 0xff, 0xbd, 0xff, 0xc3, 0xe7, 0xff, 0x7e, /* ^B */ +@@ -260,14 +263,14 @@ static const unsigned char acorndata_8x8[] = { + /* FD */ 0x38, 0x04, 0x18, 0x20, 0x3c, 0x00, 0x00, 0x00, + /* FE */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, + /* FF */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +-}; ++} }; + + const struct font_desc font_acorn_8x8 = { + .idx = ACORN8x8_IDX, + .name = "Acorn8x8", + .width = 8, + .height = 8, +- .data = acorndata_8x8, ++ .data = acorndata_8x8.data, + #ifdef CONFIG_ARCH_ACORN + .pref = 20, + #else +diff --git a/lib/fonts/font_mini_4x6.c b/lib/fonts/font_mini_4x6.c +index 838caa1cfef70..1449876c6a270 100644 +--- a/lib/fonts/font_mini_4x6.c ++++ b/lib/fonts/font_mini_4x6.c +@@ -43,8 +43,8 @@ __END__; + + #define FONTDATAMAX 1536 + +-static const unsigned char fontdata_mini_4x6[FONTDATAMAX] = { +- ++static struct font_data fontdata_mini_4x6 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /*{*/ + /* Char 0: ' ' */ + 0xee, /*= [*** ] */ +@@ -2145,14 +2145,14 @@ static const unsigned char fontdata_mini_4x6[FONTDATAMAX] = { + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ +-}; ++} }; + + const struct font_desc font_mini_4x6 = { + .idx = MINI4x6_IDX, + .name = "MINI4x6", + .width = 4, + .height = 6, +- .data = fontdata_mini_4x6, ++ .data = fontdata_mini_4x6.data, + .pref = 3, + }; + +diff --git a/lib/fonts/font_pearl_8x8.c b/lib/fonts/font_pearl_8x8.c +index b15d3c342c5bb..32d65551e7ed2 100644 +--- a/lib/fonts/font_pearl_8x8.c ++++ b/lib/fonts/font_pearl_8x8.c +@@ -14,8 +14,8 @@ + + #define FONTDATAMAX 2048 + +-static const unsigned char fontdata_pearl8x8[FONTDATAMAX] = { +- ++static struct font_data fontdata_pearl8x8 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +@@ -2575,14 +2575,13 @@ static const unsigned char fontdata_pearl8x8[FONTDATAMAX] = { + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +- +-}; ++} }; + + const struct font_desc font_pearl_8x8 = { + .idx = PEARL8x8_IDX, + .name = "PEARL8x8", + .width = 8, + .height = 8, +- .data = fontdata_pearl8x8, ++ .data = fontdata_pearl8x8.data, + .pref = 2, + }; +diff --git a/lib/fonts/font_sun12x22.c b/lib/fonts/font_sun12x22.c +index 955d6eee3959d..641a6b4dca424 100644 +--- a/lib/fonts/font_sun12x22.c ++++ b/lib/fonts/font_sun12x22.c +@@ -3,8 +3,8 @@ + + #define FONTDATAMAX 11264 + +-static const unsigned char fontdata_sun12x22[FONTDATAMAX] = { +- ++static struct font_data fontdata_sun12x22 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + /* 0 0x00 '^@' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ +@@ -6148,8 +6148,7 @@ static const unsigned char fontdata_sun12x22[FONTDATAMAX] = { + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ +- +-}; ++} }; + + + const struct font_desc font_sun_12x22 = { +@@ -6157,7 +6156,7 @@ const struct font_desc font_sun_12x22 = { + .name = "SUN12x22", + .width = 12, + .height = 22, +- .data = fontdata_sun12x22, ++ .data = fontdata_sun12x22.data, + #ifdef __sparc__ + .pref = 5, + #else +diff --git a/lib/fonts/font_sun8x16.c b/lib/fonts/font_sun8x16.c +index 03d71e53954ab..193fe6d988e08 100644 +--- a/lib/fonts/font_sun8x16.c ++++ b/lib/fonts/font_sun8x16.c +@@ -3,7 +3,8 @@ + + #define FONTDATAMAX 4096 + +-static const unsigned char fontdata_sun8x16[FONTDATAMAX] = { ++static struct font_data fontdata_sun8x16 = { ++{ 0, 0, FONTDATAMAX, 0 }, { + /* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + /* */ 0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00, + /* */ 0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00, +@@ -260,14 +261,14 @@ static const unsigned char fontdata_sun8x16[FONTDATAMAX] = { + /* */ 0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + /* */ 0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00, + /* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +-}; ++} }; + + const struct font_desc font_sun_8x16 = { + .idx = SUN8x16_IDX, + .name = "SUN8x16", + .width = 8, + .height = 16, +- .data = fontdata_sun8x16, ++ .data = fontdata_sun8x16.data, + #ifdef __sparc__ + .pref = 10, + #else +diff --git a/lib/fonts/font_ter16x32.c b/lib/fonts/font_ter16x32.c +index 3f0cf1ccdf3a4..91b9c283bd9cc 100644 +--- a/lib/fonts/font_ter16x32.c ++++ b/lib/fonts/font_ter16x32.c +@@ -4,8 +4,8 @@ + + #define FONTDATAMAX 16384 + +-static const unsigned char fontdata_ter16x32[FONTDATAMAX] = { +- ++static struct font_data fontdata_ter16x32 = { ++ { 0, 0, FONTDATAMAX, 0 }, { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfc, 0x7f, 0xfc, + 0x70, 0x1c, 0x70, 0x1c, 0x70, 0x1c, 0x70, 0x1c, +@@ -2054,8 +2054,7 @@ static const unsigned char fontdata_ter16x32[FONTDATAMAX] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 255 */ +- +-}; ++} }; + + + const struct font_desc font_ter_16x32 = { +@@ -2063,7 +2062,7 @@ const struct font_desc font_ter_16x32 = { + .name = "TER16x32", + .width = 16, + .height = 32, +- .data = fontdata_ter16x32, ++ .data = fontdata_ter16x32.data, + #ifdef __sparc__ + .pref = 5, + #else +diff --git a/mm/khugepaged.c b/mm/khugepaged.c +index e6fc7c3e7dc98..bc2812bb1a010 100644 +--- a/mm/khugepaged.c ++++ b/mm/khugepaged.c +@@ -56,6 +56,9 @@ enum scan_result { + #define CREATE_TRACE_POINTS + #include <trace/events/huge_memory.h> + ++static struct task_struct *khugepaged_thread __read_mostly; ++static DEFINE_MUTEX(khugepaged_mutex); ++ + /* default scan 8*512 pte (or vmas) every 30 second */ + static unsigned int khugepaged_pages_to_scan __read_mostly; + static unsigned int khugepaged_pages_collapsed; +@@ -914,6 +917,18 @@ static struct page *khugepaged_alloc_hugepage(bool *wait) + + static bool khugepaged_prealloc_page(struct page **hpage, bool *wait) + { ++ /* ++ * If the hpage allocated earlier was briefly exposed in page cache ++ * before collapse_file() failed, it is possible that racing lookups ++ * have not yet completed, and would then be unpleasantly surprised by ++ * finding the hpage reused for the same mapping at a different offset. ++ * Just release the previous allocation if there is any danger of that. ++ */ ++ if (*hpage && page_count(*hpage) > 1) { ++ put_page(*hpage); ++ *hpage = NULL; ++ } ++ + if (!*hpage) + *hpage = khugepaged_alloc_hugepage(wait); + +@@ -2292,8 +2307,6 @@ static void set_recommended_min_free_kbytes(void) + + int start_stop_khugepaged(void) + { +- static struct task_struct *khugepaged_thread __read_mostly; +- static DEFINE_MUTEX(khugepaged_mutex); + int err = 0; + + mutex_lock(&khugepaged_mutex); +@@ -2320,3 +2333,11 @@ fail: + mutex_unlock(&khugepaged_mutex); + return err; + } ++ ++void khugepaged_min_free_kbytes_update(void) ++{ ++ mutex_lock(&khugepaged_mutex); ++ if (khugepaged_enabled() && khugepaged_thread) ++ set_recommended_min_free_kbytes(); ++ mutex_unlock(&khugepaged_mutex); ++} +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index 898ff44f2c7b2..43f6d91f57156 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -69,6 +69,7 @@ + #include <linux/nmi.h> + #include <linux/psi.h> + #include <linux/padata.h> ++#include <linux/khugepaged.h> + + #include <asm/sections.h> + #include <asm/tlbflush.h> +@@ -7884,6 +7885,8 @@ int __meminit init_per_zone_wmark_min(void) + setup_min_slab_ratio(); + #endif + ++ khugepaged_min_free_kbytes_update(); ++ + return 0; + } + postcore_initcall(init_per_zone_wmark_min) +diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c +index 4877a0db16c66..a8fa622e2d9b4 100644 +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -404,6 +404,8 @@ void br_fdb_delete_by_port(struct net_bridge *br, + + if (!do_all) + if (test_bit(BR_FDB_STATIC, &f->flags) || ++ (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags) && ++ !test_bit(BR_FDB_OFFLOADED, &f->flags)) || + (vid && f->key.vlan_id != vid)) + continue; + +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index c50bd7a7943ab..72f4a3730ecf0 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -5621,7 +5621,7 @@ int skb_mpls_push(struct sk_buff *skb, __be32 mpls_lse, __be16 mpls_proto, + lse->label_stack_entry = mpls_lse; + skb_postpush_rcsum(skb, lse, MPLS_HLEN); + +- if (ethernet) ++ if (ethernet && mac_len >= ETH_HLEN) + skb_mod_eth_type(skb, eth_hdr(skb), mpls_proto); + skb->protocol = mpls_proto; + +@@ -5661,7 +5661,7 @@ int skb_mpls_pop(struct sk_buff *skb, __be16 next_proto, int mac_len, + skb_reset_mac_header(skb); + skb_set_network_header(skb, mac_len); + +- if (ethernet) { ++ if (ethernet && mac_len >= ETH_HLEN) { + struct ethhdr *hdr; + + /* use mpls_hdr() to get ethertype to account for VLANs. */ +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 30c1142584b1a..06a8242aa6980 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -970,7 +970,8 @@ ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset, + long timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); + + if (IS_ENABLED(CONFIG_DEBUG_VM) && +- WARN_ONCE(PageSlab(page), "page must not be a Slab one")) ++ WARN_ONCE(!sendpage_ok(page), ++ "page must not be a Slab one and have page_count > 0")) + return -EINVAL; + + /* Wait for a connection to finish. One exception is TCP Fast Open +diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c +index 04bfcbbfee83a..ab4576ac1fe6e 100644 +--- a/net/ipv4/tcp_ipv4.c ++++ b/net/ipv4/tcp_ipv4.c +@@ -1787,12 +1787,12 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb) + + __skb_pull(skb, hdrlen); + if (skb_try_coalesce(tail, skb, &fragstolen, &delta)) { +- thtail->window = th->window; +- + TCP_SKB_CB(tail)->end_seq = TCP_SKB_CB(skb)->end_seq; + +- if (after(TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(tail)->ack_seq)) ++ if (likely(!before(TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(tail)->ack_seq))) { + TCP_SKB_CB(tail)->ack_seq = TCP_SKB_CB(skb)->ack_seq; ++ thtail->window = th->window; ++ } + + /* We have to update both TCP_SKB_CB(tail)->tcp_flags and + * thtail->fin, so that the fast path in tcp_rcv_established() +diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c +index 9395ee8a868db..a4148ef314ea2 100644 +--- a/net/netlink/genetlink.c ++++ b/net/netlink/genetlink.c +@@ -1079,7 +1079,7 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) + if (err) + return err; + +- while (netlink_policy_dump_loop(&cb->args[1])) { ++ while (netlink_policy_dump_loop(cb->args[1])) { + void *hdr; + struct nlattr *nest; + +@@ -1113,6 +1113,12 @@ nla_put_failure: + return skb->len; + } + ++static int ctrl_dumppolicy_done(struct netlink_callback *cb) ++{ ++ netlink_policy_dump_free(cb->args[1]); ++ return 0; ++} ++ + static const struct genl_ops genl_ctrl_ops[] = { + { + .cmd = CTRL_CMD_GETFAMILY, +@@ -1123,6 +1129,7 @@ static const struct genl_ops genl_ctrl_ops[] = { + { + .cmd = CTRL_CMD_GETPOLICY, + .dumpit = ctrl_dumppolicy, ++ .done = ctrl_dumppolicy_done, + }, + }; + +diff --git a/net/netlink/policy.c b/net/netlink/policy.c +index 2b3e26f7496f5..ceeaee157b2f6 100644 +--- a/net/netlink/policy.c ++++ b/net/netlink/policy.c +@@ -84,7 +84,6 @@ int netlink_policy_dump_start(const struct nla_policy *policy, + unsigned int policy_idx; + int err; + +- /* also returns 0 if "*_state" is our ERR_PTR() end marker */ + if (*_state) + return 0; + +@@ -140,21 +139,11 @@ static bool netlink_policy_dump_finished(struct nl_policy_dump *state) + !state->policies[state->policy_idx].policy; + } + +-bool netlink_policy_dump_loop(unsigned long *_state) ++bool netlink_policy_dump_loop(unsigned long _state) + { +- struct nl_policy_dump *state = (void *)*_state; +- +- if (IS_ERR(state)) +- return false; +- +- if (netlink_policy_dump_finished(state)) { +- kfree(state); +- /* store end marker instead of freed state */ +- *_state = (unsigned long)ERR_PTR(-ENOENT); +- return false; +- } ++ struct nl_policy_dump *state = (void *)_state; + +- return true; ++ return !netlink_policy_dump_finished(state); + } + + int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state) +@@ -309,3 +298,10 @@ nla_put_failure: + nla_nest_cancel(skb, policy); + return -ENOBUFS; + } ++ ++void netlink_policy_dump_free(unsigned long _state) ++{ ++ struct nl_policy_dump *state = (void *)_state; ++ ++ kfree(state); ++} +diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c +index 4340f25fe390f..67dacc7152f75 100644 +--- a/net/openvswitch/conntrack.c ++++ b/net/openvswitch/conntrack.c +@@ -903,15 +903,19 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, + } + err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype); + +- if (err == NF_ACCEPT && +- ct->status & IPS_SRC_NAT && ct->status & IPS_DST_NAT) { +- if (maniptype == NF_NAT_MANIP_SRC) +- maniptype = NF_NAT_MANIP_DST; +- else +- maniptype = NF_NAT_MANIP_SRC; +- +- err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, +- maniptype); ++ if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { ++ if (ct->status & IPS_SRC_NAT) { ++ if (maniptype == NF_NAT_MANIP_SRC) ++ maniptype = NF_NAT_MANIP_DST; ++ else ++ maniptype = NF_NAT_MANIP_SRC; ++ ++ err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, ++ maniptype); ++ } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { ++ err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, ++ NF_NAT_MANIP_SRC); ++ } + } + + /* Mark NAT done if successful and update the flow key. */ +diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c +index d8252fdab851a..934999b56d60a 100644 +--- a/net/qrtr/ns.c ++++ b/net/qrtr/ns.c +@@ -193,12 +193,13 @@ static int announce_servers(struct sockaddr_qrtr *sq) + struct qrtr_server *srv; + struct qrtr_node *node; + void __rcu **slot; +- int ret; ++ int ret = 0; + + node = node_get(qrtr_ns.local_node); + if (!node) + return 0; + ++ rcu_read_lock(); + /* Announce the list of servers registered in this node */ + radix_tree_for_each_slot(slot, &node->servers, &iter, 0) { + srv = radix_tree_deref_slot(slot); +@@ -206,11 +207,14 @@ static int announce_servers(struct sockaddr_qrtr *sq) + ret = service_announce_new(sq, srv); + if (ret < 0) { + pr_err("failed to announce new service\n"); +- return ret; ++ goto err_out; + } + } + +- return 0; ++err_out: ++ rcu_read_unlock(); ++ ++ return ret; + } + + static struct qrtr_server *server_add(unsigned int service, +@@ -335,7 +339,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from) + struct qrtr_node *node; + void __rcu **slot; + struct kvec iv; +- int ret; ++ int ret = 0; + + iv.iov_base = &pkt; + iv.iov_len = sizeof(pkt); +@@ -344,11 +348,13 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from) + if (!node) + return 0; + ++ rcu_read_lock(); + /* Advertise removal of this client to all servers of remote node */ + radix_tree_for_each_slot(slot, &node->servers, &iter, 0) { + srv = radix_tree_deref_slot(slot); + server_del(node, srv->port); + } ++ rcu_read_unlock(); + + /* Advertise the removal of this client to all local servers */ + local_node = node_get(qrtr_ns.local_node); +@@ -359,6 +365,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from) + pkt.cmd = cpu_to_le32(QRTR_TYPE_BYE); + pkt.client.node = cpu_to_le32(from->sq_node); + ++ rcu_read_lock(); + radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) { + srv = radix_tree_deref_slot(slot); + +@@ -372,11 +379,14 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from) + ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); + if (ret < 0) { + pr_err("failed to send bye cmd\n"); +- return ret; ++ goto err_out; + } + } + +- return 0; ++err_out: ++ rcu_read_unlock(); ++ ++ return ret; + } + + static int ctrl_cmd_del_client(struct sockaddr_qrtr *from, +@@ -394,7 +404,7 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from, + struct list_head *li; + void __rcu **slot; + struct kvec iv; +- int ret; ++ int ret = 0; + + iv.iov_base = &pkt; + iv.iov_len = sizeof(pkt); +@@ -434,6 +444,7 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from, + pkt.client.node = cpu_to_le32(node_id); + pkt.client.port = cpu_to_le32(port); + ++ rcu_read_lock(); + radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) { + srv = radix_tree_deref_slot(slot); + +@@ -447,11 +458,14 @@ static int ctrl_cmd_del_client(struct sockaddr_qrtr *from, + ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); + if (ret < 0) { + pr_err("failed to send del client cmd\n"); +- return ret; ++ goto err_out; + } + } + +- return 0; ++err_out: ++ rcu_read_unlock(); ++ ++ return ret; + } + + static int ctrl_cmd_new_server(struct sockaddr_qrtr *from, +@@ -554,6 +568,7 @@ static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from, + filter.service = service; + filter.instance = instance; + ++ rcu_read_lock(); + radix_tree_for_each_slot(node_slot, &nodes, &node_iter, 0) { + node = radix_tree_deref_slot(node_slot); + +@@ -568,6 +583,7 @@ static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from, + lookup_notify(from, srv, true); + } + } ++ rcu_read_unlock(); + + /* Empty notification, to indicate end of listing */ + lookup_notify(from, NULL, true); +diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c +index 447f55ca68860..6e972b4823efa 100644 +--- a/net/rxrpc/conn_event.c ++++ b/net/rxrpc/conn_event.c +@@ -340,18 +340,18 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, + return ret; + + spin_lock(&conn->channel_lock); +- spin_lock(&conn->state_lock); ++ spin_lock_bh(&conn->state_lock); + + if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING) { + conn->state = RXRPC_CONN_SERVICE; +- spin_unlock(&conn->state_lock); ++ spin_unlock_bh(&conn->state_lock); + for (loop = 0; loop < RXRPC_MAXCALLS; loop++) + rxrpc_call_is_secure( + rcu_dereference_protected( + conn->channels[loop].call, + lockdep_is_held(&conn->channel_lock))); + } else { +- spin_unlock(&conn->state_lock); ++ spin_unlock_bh(&conn->state_lock); + } + + spin_unlock(&conn->channel_lock); +diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c +index 0c98313dd7a8c..85a9ff8cd236a 100644 +--- a/net/rxrpc/key.c ++++ b/net/rxrpc/key.c +@@ -903,7 +903,7 @@ int rxrpc_request_key(struct rxrpc_sock *rx, char __user *optval, int optlen) + + _enter(""); + +- if (optlen <= 0 || optlen > PAGE_SIZE - 1) ++ if (optlen <= 0 || optlen > PAGE_SIZE - 1 || rx->securities) + return -EINVAL; + + description = memdup_user_nul(optval, optlen); +@@ -941,7 +941,7 @@ int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval, + if (IS_ERR(description)) + return PTR_ERR(description); + +- key = request_key_net(&key_type_keyring, description, sock_net(&rx->sk), NULL); ++ key = request_key(&key_type_keyring, description, NULL); + if (IS_ERR(key)) { + kfree(description); + _leave(" = %ld", PTR_ERR(key)); +@@ -1073,7 +1073,7 @@ static long rxrpc_read(const struct key *key, + + switch (token->security_index) { + case RXRPC_SECURITY_RXKAD: +- toksize += 9 * 4; /* viceid, kvno, key*2 + len, begin, ++ toksize += 8 * 4; /* viceid, kvno, key*2, begin, + * end, primary, tktlen */ + toksize += RND(token->kad->ticket_len); + break; +@@ -1108,7 +1108,8 @@ static long rxrpc_read(const struct key *key, + break; + + default: /* we have a ticket we can't encode */ +- BUG(); ++ pr_err("Unsupported key token type (%u)\n", ++ token->security_index); + continue; + } + +@@ -1139,6 +1140,14 @@ static long rxrpc_read(const struct key *key, + memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3)); \ + xdr += (_l + 3) >> 2; \ + } while(0) ++#define ENCODE_BYTES(l, s) \ ++ do { \ ++ u32 _l = (l); \ ++ memcpy(xdr, (s), _l); \ ++ if (_l & 3) \ ++ memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3)); \ ++ xdr += (_l + 3) >> 2; \ ++ } while(0) + #define ENCODE64(x) \ + do { \ + __be64 y = cpu_to_be64(x); \ +@@ -1166,7 +1175,7 @@ static long rxrpc_read(const struct key *key, + case RXRPC_SECURITY_RXKAD: + ENCODE(token->kad->vice_id); + ENCODE(token->kad->kvno); +- ENCODE_DATA(8, token->kad->session_key); ++ ENCODE_BYTES(8, token->kad->session_key); + ENCODE(token->kad->start); + ENCODE(token->kad->expiry); + ENCODE(token->kad->primary_flag); +@@ -1216,7 +1225,6 @@ static long rxrpc_read(const struct key *key, + break; + + default: +- BUG(); + break; + } + +diff --git a/net/sched/act_api.c b/net/sched/act_api.c +index 8ac7eb0a83096..aa69fc4ce39d9 100644 +--- a/net/sched/act_api.c ++++ b/net/sched/act_api.c +@@ -307,6 +307,8 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, + + mutex_lock(&idrinfo->lock); + idr_for_each_entry_ul(idr, p, tmp, id) { ++ if (IS_ERR(p)) ++ continue; + ret = tcf_idr_release_unsafe(p); + if (ret == ACT_P_DELETED) { + module_put(ops->owner); +@@ -467,17 +469,6 @@ int tcf_idr_create_from_flags(struct tc_action_net *tn, u32 index, + } + EXPORT_SYMBOL(tcf_idr_create_from_flags); + +-void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a) +-{ +- struct tcf_idrinfo *idrinfo = tn->idrinfo; +- +- mutex_lock(&idrinfo->lock); +- /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */ +- WARN_ON(!IS_ERR(idr_replace(&idrinfo->action_idr, a, a->tcfa_index))); +- mutex_unlock(&idrinfo->lock); +-} +-EXPORT_SYMBOL(tcf_idr_insert); +- + /* Cleanup idr index that was allocated but not initialized. */ + + void tcf_idr_cleanup(struct tc_action_net *tn, u32 index) +@@ -902,6 +893,26 @@ static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = { + [TCA_ACT_HW_STATS] = NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY), + }; + ++static void tcf_idr_insert_many(struct tc_action *actions[]) ++{ ++ int i; ++ ++ for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { ++ struct tc_action *a = actions[i]; ++ struct tcf_idrinfo *idrinfo; ++ ++ if (!a) ++ continue; ++ idrinfo = a->idrinfo; ++ mutex_lock(&idrinfo->lock); ++ /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc if ++ * it is just created, otherwise this is just a nop. ++ */ ++ idr_replace(&idrinfo->action_idr, a, a->tcfa_index); ++ mutex_unlock(&idrinfo->lock); ++ } ++} ++ + struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, + struct nlattr *nla, struct nlattr *est, + char *name, int ovr, int bind, +@@ -989,6 +1000,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, + if (err < 0) + goto err_mod; + ++ if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN) && ++ !rcu_access_pointer(a->goto_chain)) { ++ tcf_action_destroy_1(a, bind); ++ NL_SET_ERR_MSG(extack, "can't use goto chain with NULL chain"); ++ return ERR_PTR(-EINVAL); ++ } ++ + if (!name && tb[TCA_ACT_COOKIE]) + tcf_set_action_cookie(&a->act_cookie, cookie); + +@@ -1002,13 +1020,6 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, + if (err != ACT_P_CREATED) + module_put(a_o->owner); + +- if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN) && +- !rcu_access_pointer(a->goto_chain)) { +- tcf_action_destroy_1(a, bind); +- NL_SET_ERR_MSG(extack, "can't use goto chain with NULL chain"); +- return ERR_PTR(-EINVAL); +- } +- + return a; + + err_mod: +@@ -1051,6 +1062,11 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla, + actions[i - 1] = act; + } + ++ /* We have to commit them all together, because if any error happened in ++ * between, we could not handle the failure gracefully. ++ */ ++ tcf_idr_insert_many(actions); ++ + *attr_size = tcf_action_full_attrs_size(sz); + return i - 1; + +diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c +index 54d5652cfe6ca..a4c7ba35a3438 100644 +--- a/net/sched/act_bpf.c ++++ b/net/sched/act_bpf.c +@@ -365,9 +365,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (res == ACT_P_CREATED) { +- tcf_idr_insert(tn, *act); +- } else { ++ if (res != ACT_P_CREATED) { + /* make sure the program being replaced is no longer executing */ + synchronize_rcu(); + tcf_bpf_cfg_cleanup(&old); +diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c +index f901421b0634d..e19885d7fe2cb 100644 +--- a/net/sched/act_connmark.c ++++ b/net/sched/act_connmark.c +@@ -139,7 +139,6 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, + ci->net = net; + ci->zone = parm->zone; + +- tcf_idr_insert(tn, *a); + ret = ACT_P_CREATED; + } else if (ret > 0) { + ci = to_connmark(*a); +diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c +index c60674cf25c4f..8b3f45cdc319d 100644 +--- a/net/sched/act_csum.c ++++ b/net/sched/act_csum.c +@@ -110,9 +110,6 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, + if (params_new) + kfree_rcu(params_new, rcu); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); +- + return ret; + put_chain: + if (goto_ch) +diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c +index 41d8440deaf14..0eb4722cf7cd9 100644 +--- a/net/sched/act_ct.c ++++ b/net/sched/act_ct.c +@@ -1293,8 +1293,6 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla, + tcf_chain_put_by_act(goto_ch); + if (params) + call_rcu(¶ms->rcu, tcf_ct_params_free); +- if (res == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + + return res; + +diff --git a/net/sched/act_ctinfo.c b/net/sched/act_ctinfo.c +index b5042f3ea079e..6084300e51adb 100644 +--- a/net/sched/act_ctinfo.c ++++ b/net/sched/act_ctinfo.c +@@ -269,9 +269,6 @@ static int tcf_ctinfo_init(struct net *net, struct nlattr *nla, + if (cp_new) + kfree_rcu(cp_new, rcu); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); +- + return ret; + + put_chain: +diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c +index 4160657727196..1a29751ae63b5 100644 +--- a/net/sched/act_gact.c ++++ b/net/sched/act_gact.c +@@ -140,8 +140,6 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + release_idr: + tcf_idr_release(*a, bind); +diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c +index 323ae7f6315d4..c86e7fa7b2208 100644 +--- a/net/sched/act_gate.c ++++ b/net/sched/act_gate.c +@@ -437,9 +437,6 @@ static int tcf_gate_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); +- + return ret; + + chain_put: +diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c +index 5c568757643b2..a2ddea04183af 100644 +--- a/net/sched/act_ife.c ++++ b/net/sched/act_ife.c +@@ -627,9 +627,6 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, + if (p) + kfree_rcu(p, rcu); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); +- + return ret; + metadata_parse_err: + if (goto_ch) +diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c +index 400a2cfe84522..8dc3bec0d3258 100644 +--- a/net/sched/act_ipt.c ++++ b/net/sched/act_ipt.c +@@ -189,8 +189,6 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla, + ipt->tcfi_t = t; + ipt->tcfi_hook = hook; + spin_unlock_bh(&ipt->tcf_lock); +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + + err3: +diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c +index 83dd82fc9f40c..cd3a7f814fc8c 100644 +--- a/net/sched/act_mirred.c ++++ b/net/sched/act_mirred.c +@@ -194,8 +194,6 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, + spin_lock(&mirred_list_lock); + list_add(&m->tcfm_list, &mirred_list); + spin_unlock(&mirred_list_lock); +- +- tcf_idr_insert(tn, *a); + } + + return ret; +diff --git a/net/sched/act_mpls.c b/net/sched/act_mpls.c +index 8118e26409796..e298ec3b3c9e3 100644 +--- a/net/sched/act_mpls.c ++++ b/net/sched/act_mpls.c +@@ -273,8 +273,6 @@ static int tcf_mpls_init(struct net *net, struct nlattr *nla, + if (p) + kfree_rcu(p, rcu); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + put_chain: + if (goto_ch) +diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c +index 855a6fa16a621..1ebd2a86d980f 100644 +--- a/net/sched/act_nat.c ++++ b/net/sched/act_nat.c +@@ -93,9 +93,6 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); +- + return ret; + release_idr: + tcf_idr_release(*a, bind); +diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c +index d41d6200d9dec..ed1700fe662e1 100644 +--- a/net/sched/act_pedit.c ++++ b/net/sched/act_pedit.c +@@ -238,8 +238,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, + spin_unlock_bh(&p->tcf_lock); + if (goto_ch) + tcf_chain_put_by_act(goto_ch); +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + + put_chain: +diff --git a/net/sched/act_police.c b/net/sched/act_police.c +index 8b7a0ac96c516..2d236b9a411f0 100644 +--- a/net/sched/act_police.c ++++ b/net/sched/act_police.c +@@ -201,8 +201,6 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, + if (new) + kfree_rcu(new, rcu); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + + failure: +diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c +index 5e2df590bb58a..3ebf9ede3cf10 100644 +--- a/net/sched/act_sample.c ++++ b/net/sched/act_sample.c +@@ -116,8 +116,6 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + put_chain: + if (goto_ch) +diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c +index 9813ca4006dd1..a4f3d0f0daa96 100644 +--- a/net/sched/act_simple.c ++++ b/net/sched/act_simple.c +@@ -157,8 +157,6 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, + goto release_idr; + } + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + put_chain: + if (goto_ch) +diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c +index b2b3faa57294c..8012ae84847b8 100644 +--- a/net/sched/act_skbedit.c ++++ b/net/sched/act_skbedit.c +@@ -224,8 +224,6 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + put_chain: + if (goto_ch) +diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c +index 39e6d94cfafbf..81a1c67335be6 100644 +--- a/net/sched/act_skbmod.c ++++ b/net/sched/act_skbmod.c +@@ -190,8 +190,6 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + put_chain: + if (goto_ch) +diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c +index 536c4bc31be60..23cf8469a2e7c 100644 +--- a/net/sched/act_tunnel_key.c ++++ b/net/sched/act_tunnel_key.c +@@ -536,9 +536,6 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); +- + return ret; + + put_chain: +diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c +index c91d3958fcbb8..68137d7519d01 100644 +--- a/net/sched/act_vlan.c ++++ b/net/sched/act_vlan.c +@@ -229,8 +229,6 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, + if (p) + kfree_rcu(p, rcu); + +- if (ret == ACT_P_CREATED) +- tcf_idr_insert(tn, *a); + return ret; + put_chain: + if (goto_ch) +diff --git a/net/sctp/auth.c b/net/sctp/auth.c +index 83e97e8892e05..155aac6943d1b 100644 +--- a/net/sctp/auth.c ++++ b/net/sctp/auth.c +@@ -494,6 +494,7 @@ int sctp_auth_init_hmacs(struct sctp_endpoint *ep, gfp_t gfp) + out_err: + /* Clean up any successful allocations */ + sctp_auth_destroy_hmacs(ep->auth_hmacs); ++ ep->auth_hmacs = NULL; + return -ENOMEM; + } + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 24f64bc0de18c..d8bc02b185f32 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -2142,10 +2142,15 @@ void tls_sw_release_resources_tx(struct sock *sk) + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context_tx *ctx = tls_sw_ctx_tx(tls_ctx); + struct tls_rec *rec, *tmp; ++ int pending; + + /* Wait for any pending async encryptions to complete */ +- smp_store_mb(ctx->async_notify, true); +- if (atomic_read(&ctx->encrypt_pending)) ++ spin_lock_bh(&ctx->encrypt_compl_lock); ++ ctx->async_notify = true; ++ pending = atomic_read(&ctx->encrypt_pending); ++ spin_unlock_bh(&ctx->encrypt_compl_lock); ++ ++ if (pending) + crypto_wait_req(-EINPROGRESS, &ctx->async_wait); + + tls_tx_records(sk, -1); +diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c +index 279c87a2a523b..4d7b255067225 100644 +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -4172,6 +4172,9 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) + if (err) + return err; + ++ if (key.idx < 0) ++ return -EINVAL; ++ + if (info->attrs[NL80211_ATTR_MAC]) + mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index 3700266229f63..dcce888b8ef54 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -375,15 +375,30 @@ static int xsk_generic_xmit(struct sock *sk) + skb_shinfo(skb)->destructor_arg = (void *)(long)desc.addr; + skb->destructor = xsk_destruct_skb; + ++ /* Hinder dev_direct_xmit from freeing the packet and ++ * therefore completing it in the destructor ++ */ ++ refcount_inc(&skb->users); + err = dev_direct_xmit(skb, xs->queue_id); ++ if (err == NETDEV_TX_BUSY) { ++ /* Tell user-space to retry the send */ ++ skb->destructor = sock_wfree; ++ /* Free skb without triggering the perf drop trace */ ++ consume_skb(skb); ++ err = -EAGAIN; ++ goto out; ++ } ++ + xskq_cons_release(xs->tx); + /* Ignore NET_XMIT_CN as packet might have been sent */ +- if (err == NET_XMIT_DROP || err == NETDEV_TX_BUSY) { ++ if (err == NET_XMIT_DROP) { + /* SKB completed but not sent */ ++ kfree_skb(skb); + err = -EBUSY; + goto out; + } + ++ consume_skb(skb); + sent_frame = true; + } + +diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c +index 827ccdf2db57f..1f08ebf7d80c5 100644 +--- a/net/xfrm/espintcp.c ++++ b/net/xfrm/espintcp.c +@@ -29,8 +29,12 @@ static void handle_nonesp(struct espintcp_ctx *ctx, struct sk_buff *skb, + + static void handle_esp(struct sk_buff *skb, struct sock *sk) + { ++ struct tcp_skb_cb *tcp_cb = (struct tcp_skb_cb *)skb->cb; ++ + skb_reset_transport_header(skb); +- memset(skb->cb, 0, sizeof(skb->cb)); ++ ++ /* restore IP CB, we need at least IP6CB->nhoff */ ++ memmove(skb->cb, &tcp_cb->header, sizeof(tcp_cb->header)); + + rcu_read_lock(); + skb->dev = dev_get_by_index_rcu(sock_net(sk), skb->skb_iif); +diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c +index b615729812e5a..ade2eba863b39 100644 +--- a/net/xfrm/xfrm_interface.c ++++ b/net/xfrm/xfrm_interface.c +@@ -292,7 +292,7 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) + } + + mtu = dst_mtu(dst); +- if (!skb->ignore_df && skb->len > mtu) { ++ if (skb->len > mtu) { + skb_dst_update_pmtu_no_confirm(skb, mtu); + + if (skb->protocol == htons(ETH_P_IPV6)) { +diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c +index 8be2d926acc21..158510cd34ae8 100644 +--- a/net/xfrm/xfrm_state.c ++++ b/net/xfrm/xfrm_state.c +@@ -1019,7 +1019,8 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, + */ + if (x->km.state == XFRM_STATE_VALID) { + if ((x->sel.family && +- !xfrm_selector_match(&x->sel, fl, x->sel.family)) || ++ (x->sel.family != family || ++ !xfrm_selector_match(&x->sel, fl, family))) || + !security_xfrm_state_pol_flow_match(x, pol, fl)) + return; + +@@ -1032,7 +1033,9 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, + *acq_in_progress = 1; + } else if (x->km.state == XFRM_STATE_ERROR || + x->km.state == XFRM_STATE_EXPIRED) { +- if (xfrm_selector_match(&x->sel, fl, x->sel.family) && ++ if ((!x->sel.family || ++ (x->sel.family == family && ++ xfrm_selector_match(&x->sel, fl, family))) && + security_xfrm_state_pol_flow_match(x, pol, fl)) + *error = -ESRCH; + } +@@ -1072,7 +1075,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, + tmpl->mode == x->props.mode && + tmpl->id.proto == x->id.proto && + (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) +- xfrm_state_look_at(pol, x, fl, encap_family, ++ xfrm_state_look_at(pol, x, fl, family, + &best, &acquire_in_progress, &error); + } + if (best || acquire_in_progress) +@@ -1089,7 +1092,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, + tmpl->mode == x->props.mode && + tmpl->id.proto == x->id.proto && + (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) +- xfrm_state_look_at(pol, x, fl, encap_family, ++ xfrm_state_look_at(pol, x, fl, family, + &best, &acquire_in_progress, &error); + } + +@@ -1441,6 +1444,30 @@ out: + EXPORT_SYMBOL(xfrm_state_add); + + #ifdef CONFIG_XFRM_MIGRATE ++static inline int clone_security(struct xfrm_state *x, struct xfrm_sec_ctx *security) ++{ ++ struct xfrm_user_sec_ctx *uctx; ++ int size = sizeof(*uctx) + security->ctx_len; ++ int err; ++ ++ uctx = kmalloc(size, GFP_KERNEL); ++ if (!uctx) ++ return -ENOMEM; ++ ++ uctx->exttype = XFRMA_SEC_CTX; ++ uctx->len = size; ++ uctx->ctx_doi = security->ctx_doi; ++ uctx->ctx_alg = security->ctx_alg; ++ uctx->ctx_len = security->ctx_len; ++ memcpy(uctx + 1, security->ctx_str, security->ctx_len); ++ err = security_xfrm_state_alloc(x, uctx); ++ kfree(uctx); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ + static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, + struct xfrm_encap_tmpl *encap) + { +@@ -1497,6 +1524,10 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, + goto error; + } + ++ if (orig->security) ++ if (clone_security(x, orig->security)) ++ goto error; ++ + if (orig->coaddr) { + x->coaddr = kmemdup(orig->coaddr, sizeof(*x->coaddr), + GFP_KERNEL); +@@ -1510,6 +1541,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, + } + + memcpy(&x->mark, &orig->mark, sizeof(x->mark)); ++ memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark)); + + if (xfrm_init_state(x) < 0) + goto error; +@@ -1521,7 +1553,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, + x->tfcpad = orig->tfcpad; + x->replay_maxdiff = orig->replay_maxdiff; + x->replay_maxage = orig->replay_maxage; +- x->curlft.add_time = orig->curlft.add_time; ++ memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft)); + x->km.state = orig->km.state; + x->km.seq = orig->km.seq; + x->replay = orig->replay; |